【问题标题】:TList and BinarySearch errorTList 和 BinarySearch 错误
【发布时间】:2011-11-27 17:18:38
【问题描述】:

我在使用 TList 和 BinarySearch 时遇到了一些问题。我有这个结构:

  PDoubleEstr = record
    Double: array [1..2] of Integer;
    Count: Integer;
  end;
  TDoubleEstr = TList<PDoubleEstr>;

并声明:

var oDoubleEstr: TDoubleEstr;

然后,我使用这个函数正确初始化列表:

procedure Initialize;
var
  iIndex1, iIndex2: Integer;
  rDoubleEstr: PDoubleEstr;
begin
  oDoubleEstr.Clear;
  for iIndex1 := 1 to 89 do
    for iIndex2 := Succ(iIndex1) to 90 do
    begin
      with rDoubleEstr do
      begin
        Double[1] := iIndex1;
        Double[2] := iIndex2;
        Count := 0;
      end;
      oDoubleEstr.Add(rDoubleEstr);
    end;
end;

现在,我定义这个过程:

procedure Element(const First: Integer; const Second: Integer; var Value: PDoubleEstr);
begin
  with Value do
  begin
    Double[1] := First;
    Double[2] := Second;
  end; 
end;

然后在我的主程序中:

procedure Main;
var 
  Value: PDoubleEstr;
  Index: Integer;
  flag: boolean;
begin
  Element(89, 90, Value);
  flag := oDoubleEstr.BinarySearch(Value, Index, TDelegatedComparer<PDoubleEstr>.Construct(Compare));
  Writeln(Flag:5, oDoubleEstr[Index].Double[1]:5, oDoubleEstr[Index].Double[2]:5);  
end;

这让我犯了一个错误。从某种意义上说,索引为“Index”的元素与我输入的元素不对应。 当然, oDoubleEstr 排序正确,不明白我错在哪里。 构造比较是这样定义的:

function TDouble.Compare(const Left, Right: PDoubleEstr): Integer;
begin
  Result := Sign(Left.Double[1] - Right.Double[2]);
end;

我认为错误是在构造中,但不被理解为解决它。 一般来说,我想检查元素是否存在,如果存在则获取索引。作为元素,我的意思是在我的情况下只有字段 Double。 我试着解释得更好,我的名单是如此之多:

 1   2    // element 0
 1   3    
......
 1  90    
......
88  89
88  90
89  90    // element 4004

如果我将 Element 设置为 (89,90),它应该将我作为索引值:4004,如果找到它则为 true,否则为 false。 感谢您的帮助。

【问题讨论】:

    标签: delphi delphi-xe2


    【解决方案1】:

    我不确定您要做什么,但 Compare 函数无效。比较函数需要具有以下对称性:

    Compare(a, b) = -Compare(b, a)
    

    您的函数没有此属性,因为您将Double[1]Double[2] 进行比较。

    比较函数中的减法也会带来范围错误的风险。我会改用&lt;&gt; 运算符。

    我不太愿意建议比较函数应该是什么,因为我不确定您想要的排序标准是什么。如果您想要字典比较(即字母顺序),则首先比较 Double[1] 值,如果它们比较相等,则对 Double[2] 值进行第二次比较。

    但是,既然我再次查看了您构建列表的方式,并且现在我看到您断言此列表是按构造排序的,那么 order 函数应该是什么就很清楚了。像这样的:

    function CompareInt(const Left, Right: Integer): Integer;
    begin
      if Left<Right then begin
        Result := -1
      else if Left>Right then
        Result := 1
      else
        Result := 0;
    end;
    
    function Compare(const Left, Right: PDoubleEstr): Integer;
    begin
      Result := CompareInt(Left.Double[1], Right.Double[1]);
      if Result=0 then
        Result := CompareInt(Left.Double[2], Right.Double[2]);
    end;
    

    请注意,我已将比较函数的符号与您的原始版本(尽管存在缺陷)相反。您的次要问题(参见 cmets 对 Ville 的回答)是因为列表未按照您用于搜索的相同顺序进行排序。您必须使用相同的比较进行排序和搜索。二分查找算法就是以此为基础的。


    顺便说一句,我认为Double 是一个糟糕的变量名,因为它也是一种基本类型。使用PDoubleEstr 命名记录非常令人困惑,因为P 前缀的常规用法是用于指针。

    【讨论】:

    • 嗯,我应该写它吗?我只想在我的列表元素“Double”中找到仅包含两个数字的元素,并检查它是否存在以及是否存在于它的位置(索引)。我试图通过一些例子来研究这个解决方案。你能给我更多的细节吗?再次非常感谢。
    • 如果您想根据Double[]数组中的两个值进行比较和搜索,请使用Ville提供的功能。但是,如果您的值足够大,那仍然会出现溢出错误。我怀疑是这种情况,但我指出它的完整性。
    • @Marcello 是的,我想我现在已经深入了解了。见更新。仅当列表按照您提供的比较函数确定的顺序排序时,二进制搜索才会起作用。
    【解决方案2】:

    是的,大卫赫弗曼是正确的。如果您像这样定义比较函数,它将起作用:

    function Compare(const Left, Right: PDoubleEstr): Integer;
    begin
      Result := Sign(Left.Double[1] - Right.Double[1]);
      if Result=0 then
        Result := Sign(Left.Double[2] - Right.Double[2]);
    end;
    

    重要的是您需要比较双精度数组中的两个值才能得到匹配。

    【讨论】:

    • 您好,我尝试过这样做,但没有读取正确的值,只是结果为“随机”。
    • 我相信您也在使用此比较功能进行排序?您必须使用相同的比较进行排序和搜索。
    • 不理解对不起:(我只使用比较进行研究,当我初始化它调用过程时,列表的排序正确:初始化。我只感兴趣检查字段是否为双精度(定义为:数组 [1 ..2] 整数)存在于我的列表 oDoubleEstr 中。我认为错误地使用了比较器。我有点困惑。
    • 我再次更详细地查看了这一点。这个答案中比较函数的问题是它与构建列表的顺序不匹配。此答案中的顺序是降序,但列表是升序。如上所述,排序和搜索必须使用相同的顺序。
    猜你喜欢
    • 2020-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 2011-01-20
    • 2015-07-06
    相关资源
    最近更新 更多