【问题标题】:Compare 2 LISTBOX'es比较 2 个 LISTBOX'es
【发布时间】:2020-09-26 17:41:24
【问题描述】:

我在 Win 64 sp2 上运行 D7 Enterprise。

在我的程序中,我有 2 个列表框 (lb1, Lb2)。

Lb1 最多可包含 35.000 个项目。 (字符串[4]) Lb2 最多可包含 35.000 个项目。 (字符串[4])

两个列表框都已排序。

Thera 可能是列表框中的重复项(从 0 到 30.000 ),我想找到这些重复项。

我尝试过“自下而上”类型(例如:从 listbox2 底部获取元素,从 listbox1 扫描到底部,标记相等的元素,(最终复制到第三个 Listbox),取下一个元素 i Listbox2 do sacn i再次 lisbox1 并继续直到 listbox2 的顶部。)

这种方法确实有效,但它非常乏味且极其缓慢(大约需要 8 分钟 (+/-) / 35000 件 LB1 / 25000 件 LB2)。

我想知道是否可以修改 TStringList 组件:TStringList.NoDuplicates;到
TStringList.getDuplicates;或类似的东西,如果是这样,我该怎么做??

还是我没有在 TStringList 组件中找到“隐藏的”(至少对我而言)可能性/方法?

或者是否有第三种方法可以找到重复项?

请帮忙。

谢谢

克里斯

【问题讨论】:

  • 那么你想找到两个列表框的交集,也就是两个列表框都存在的字符串?
  • @Andreas Rejbrand:是的
  • 不要在第二个列表框中进行线性搜索,而是使用binary search。这将复杂性从 O(n) 降低到 O(log n)。或者你可以使用字典来得到 O(1)。
  • @Andreas Rejbrand - 可能是个好主意。而且我觉得,这种(新)方法可能是最好的方法。 (顺便说一句,我对我的问题文本中的所有 TYPOS 感到遗憾, - 我有一个 DELL lbtop 和一个非常敏感的键盘。我的控制阅读完全失败了。)我会试试这个,然后再问一次,如果应该出现任何问题。再次感谢。
  • @fpiette 是对的。只需将它们复制到两个本地 TStringList 变量:A := TStringList.Create; A.Assign(ListBox1.Items); B := TStringList.Create; B.Assign(ListBox2.Items) (当然还有适当的 try..finally 块)。然后在AB 上完成所有工作。

标签: delphi


【解决方案1】:

最好像手工一样,用手指顺着每个列表向下移动。

这是执行此操作的过程。

procedure TFormTest.CompareLists(const Lista, Listb, Listc: TStrings);
var
  a, b, amax, bmax : integer;
begin
  aMax := Lista.Count;
  bMax := Listb.Count;

  Listc.Clear;

  a:=0;
  b:=0;

  while (a< aMax) and (b<bMax) do
  begin
    // Once either index goes past end of list we are done;
    case Sign( CompareStr( Lista[a], Listb[b])) of  // oer CompareText if not case sensitive
      -1:
      begin
        // ista[a] < List0[ b ]b
        inc( a );
      end;
      0:
      begin
        // a = b
        Listc.add( Lista[a] );  // same so could be either
        inc(a);
        inc(b);
      end;
      1:
      begin
        inc( b );
      end;
    end;
  end;
end;

您可以将其直接复制到您的程序中。

它还可以轻松比较是否值得复制到外部列表。我这样做了,发现使用列表框条目大约需要 4 秒,因为 35000 是条目(比 4 个字符长得多),而对于单独的列表则不到一秒。这种差异远远超过了将项目复制到单独的字符串列表所花费的时间,但两次都比你所取得的要好得多。

这是完整的测试单元:

unit UnitTest;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Math, Vcl.StdCtrls;

type
  TFormTest = class(TForm)
    ListBox1: TListBox;
    ListBox2: TListBox;
    ListBox3: TListBox;
    EditCompareLists: TEdit;
    EditCompareListBoxes: TEdit;
    ButtonTest: TButton;
    EditCount1: TEdit;
    EditCount2: TEdit;
    EditCount3: TEdit;
    procedure ButtonTestClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure CompareLists( const Lista, Listb, Listc : TStrings );
  end;

var
  FormTest: TFormTest;

implementation

{$R *.dfm}

{ TForm1 }

procedure TFormTest.ButtonTestClick(Sender: TObject);
var
  List1, List2, List3 : TStringList;
  iTime : TTime;
  i: Integer;
begin
  // 1. Generate lists - should be string[4] but if bigger should slow things down
  List1 := TStringList.Create;
  Try
    List2 := TStringList.Create;
    try
      List3 := TStringList.Create;
      try
        List1.Sorted := TRUE;
        List2.Sorted := TRUE;
        List1.Duplicates := dupIgnore;
        List2.Duplicates := dupIgnore;

        for i := 1 to 50000 do
        begin
          List1.Add( IntToStr( Random( 65000 )));
          List2.Add( IntToStr( Random( 65000 )));
        end;

        ListBox1.Items.Assign( List1 );
        ListBox2.Items.Assign( List2 );

        EditCount1.Text := IntToStr(ListBox1.Items.Count );
        EditCount2.Text := IntToStr(ListBox2.Items.Count );
        // now see how fast eachcan be sorted
        // first - list boxes
        iTime := Now;
        CompareLists( ListBox1.Items, ListBox2.Items, ListBox3.Items );
        iTime := Now - iTime;
        EditCompareListBoxes.Text := TimeToStr( iTime );
        EditCount3.Text := IntToStr(ListBox3.Items.Count );

        // and Lists
        iTime := Now;
        CompareLists( List1, List2, List3 );
        iTime := Now - iTime;
        EditCompareLists.Text := TimeToStr( iTime );
      finally
        List3.Free;
      end;
    finally
      List2.Free;
    end;
  Finally
    List1.Free;
  End;
end;

procedure TFormTest.CompareLists(const Lista, Listb, Listc: TStrings);
var
  a, b, amax, bmax : integer;
begin
  aMax := Lista.Count;
  bMax := Listb.Count;

  Listc.Clear;

  a:=0;
  b:=0;

  while (a< aMax) and (b<bMax) do
  begin
    // Once either index goes past end of list we are done;
    case Sign( CompareStr( Lista[a], Listb[b])) of  // oer CompareText if not case sensitive
      -1:
      begin
        // ista[a] < List0[ b ]b
        inc( a );
      end;
      0:
      begin
        // a = b
        Listc.add( Lista[a] );  // same so could be either
        inc(a);
        inc(b);
      end;
      1:
      begin
        inc( b );
      end;
    end;
  end;
end;

end.

【讨论】:

  • 感谢您的代码。我会深入研究它,并(如有必要)修改它以满足我的需要。
【解决方案2】:

要比较两个排序列表,您可以比较第一个条目,然后根据该比较推进一个或两个列表:

  • 等于:重复,在两个列表中前进到下一个
  • 第一个 > 比第二个,前进到第二个列表中的下一个
  • second > 首先,前进到第一个列表中的下一个
  • 一个或两个列表结束:完成

正如其他人提到的那样,比较列表控件内的数据会比字符串列表或数组之间的数据要慢得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-26
    • 2011-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    相关资源
    最近更新 更多