所有事件的二进制搜索?

时间:2014-07-05 09:30:55

标签: delphi delphi-xe6

如何使用BinarySearch搜索数组中所有值的值? TArray.BinarySearch中的默认System.Generics.Collections仅返回一个索引。

示例数组:

A = [1,2,3,3,3,6,7,8,9];

2 个答案:

答案 0 :(得分:3)

二进制搜索假定您已经对数组进行了排序,因此任何其他匹配元素将聚集在BinarySearch返回的匹配元素周围。 Delphi XE5有助于注意

  

如果数组中有多个元素等于Item,则在FoundIndex中返回第一个匹配的索引。这是任何匹配项的索引,不一定是第一项。"

这表明您需要在数组中向前和向后运行搜索以获取所有匹配元素。

答案 1 :(得分:3)

让我向你解释一下这个问题。找到索引后,顺序搜索和二进制搜索之间的区别取决于您希望找到的数据类型。 10000个元素不相关,您要搜索的项目的值有多少。例如,如果我有一个由仅1,2,3,4和5组成的10000个元素的列表。我们讨论的情况是每个值可能有数千个,并且一系列随后的二进制搜索将更可取。如果值的范围可以从1到1000000,那么我们就不太可能有重复项,二进制搜索后跟两个方向的顺序搜索是最好的方法。

对于二进制和后续顺序方法,查找开始和结束索引的算法如下:

  1. 使用二进制搜索查找索引。
  2. 使用顺序搜索向左搜索以查找第一个索引。
  3. 使用顺序搜索向右搜索以查找最后一个索引。
  4. 如果您想使用二进制搜索,那么您需要切换方法并进行一系列递归搜索,直到找到开始和结束。

    1. 使用二进制搜索查找索引。
    2. 二进制搜索1 ..(index-1)的值。
    3. 如果找到该值,则需要在1和newindex-1之间再次搜索。
    4. 您需要重复此搜索,直到找不到该值为止。
    5. 二进制搜索(索引+ 1)..结束值。
    6. 如果找到该值,则需要在newindex + 1和end。
    7. 之间再次搜索
    8. 您需要重复此搜索,直到找不到该值为止。
    9. 代码示例看起来有点像这样。此代码用于在首次找到匹配项时退出的二进制搜索。

      function GetIndexes(const aSearch: TSearchIntegers; const aValue: Integer; var aStartIndex, aEndIndex: Integer): Boolean;
      var
        foundIndex: Integer;
        lookFor: Integer;
      begin
        if BinarySearch(aSearch, aValue, foundIndex) then
        begin
          Result := True;
          lookFor := foundIndex;
          repeat
            aStartIndex := lookFor;
          until not BinarySearch(aSearch, aValue, lookFor, TComparer<Integer>.Default, 1, aStartIndex - 1);
          lookFor := foundIndex;
          repeat
            aEndIndex := lookFor;
          until not BinarySearch(aSearch, aValue, lookFor, TComparer<Integer>.Default, aEndIndex + 1, High(aSearch) - aEndIndex);
        end
        else
          Result := False;
      end;
      

      最终,您的数据(我们没有)将为您确定最佳行动方案。

      现在让事情变得复杂一点。 Delphi在TArray.BinarySearch中使用的二进制搜索的变化是在找到匹配时不会提前结束的变化。它总是会找到第一个项目的索引,因为它在找到匹配项时不会退出循环。

      Result := False;
      L := Index;
      H := Index + Count - 1;
      while L <= H do
      begin
        mid := L + (H - L) shr 1;
        cmp := Comparer.Compare(Values[mid], Item);
        if cmp < 0 then
          L := mid + 1
        else
        begin
          H := mid - 1;
          if cmp = 0 then
            Result := True;  // <-- It doesn't end here
        end;
      end;
      

      这意味着当你有很多相同的值但你确实有一个很好的副作用时,你会有点惩罚。你可以做这样的事情来找到你想要的东西:

      function GetIndexes(const aSearch: TSearchIntegers; const aValue: Integer; var aStartIndex, aEndIndex: Integer): Boolean;
      begin
        Result := False;
        if TArray.BinarySearch<Integer>(aSearch, aValue, aStartIndex) then
        begin
          TArray.BinarySearch<Integer>(aSearch, aValue+1, aEndIndex);
          if aSearch[aEndIndex] <> aValue then
            Dec(aEndIndex);
          Result := True;
        end;
      end;
      

      这是有效的,因为即使数组中没有找到aValue + 1,搜索也会返回下一个值的索引。最后的if语句用于处理我们的值也是数组的最后一个值的情况。

      这取决于TArray.BinarySearch的代码保持原样。