Delphi 如何在tlistview中获取已排序的项目?

Delphi 如何在tlistview中获取已排序的项目?,delphi,Delphi,有一个列表视图,其中包含大约10000个已排序的项目(用于搜索): 下一个binsearch函数的目的是查找以st开头的所有项目(此处例如ab),然后将以st开头的所有项目输出到另一个文件。首先,我们使用二进制搜索并快速查找其中一个ab项,然后使用线性搜索尝试查找二进制搜索找到的ab项之前和之后的所有ab项。我们测试了二进制搜索的部分,效果很好。以下函数中的二进制搜索将返回ab项之一。 如果有多个ab项,binsearch函数将返回所有ab项并正确输出。 如果只有一个ab项,则以下函数中的二进制

有一个列表视图,其中包含大约10000个已排序的项目(用于搜索):

下一个binsearch函数的目的是查找以st开头的所有项目(此处例如ab),然后将以st开头的所有项目输出到另一个文件。首先,我们使用二进制搜索并快速查找其中一个ab项,然后使用线性搜索尝试查找二进制搜索找到的ab项之前和之后的所有ab项。我们测试了二进制搜索的部分,效果很好。以下函数中的二进制搜索将返回ab项之一。 如果有多个ab项,binsearch函数将返回所有ab项并正确输出。 如果只有一个ab项,则以下函数中的二进制搜索部分可以找到它(已插入断点和提示符以进行跟踪),但输出未找到该项。问题可能在这个函数的线性搜索部分,不知道为什么

function TForm1.binsearch(lv: tlistview; st: string): integer;
var
  L, R, M: Integer;
  p: integer;
  cap, wholecap: string;
  CompareResult: Integer;
  alist, newlist: tlistitem;
  fresult: integer;
  newresult: integer;
  found: boolean;
begin
  Result := -1;
  cap := '';
  wholecap := '';
  L := 0;
  R := lv.items.Count - 1;
  M := (L + R) div 2;  
  wholecap := lv.Items[m].caption;
  p := pos(' ', wholecap);
  cap := trim(copy(wholecap, 0, p));
  CompareResult := Comparestr(cap, st);
  while (compareresult <> 0) and (l <= r) do begin
    if CompareResult > 0 then begin
      R := M - 1;
    end;
    if CompareResult < 0
    then begin
      L := M + 1;
    end;
    M := (L + R) div 2;
    wholecap := lv.Items[m].caption;   
    if pos(' ', wholecap) > 0 then
      p := pos(' ', wholecap);
    cap := trim(copy(wholecap, 0, p));
    CompareResult := Comparestr(cap, st);
  end;

  fresult := m;
  result := m;
  newresult := m;

 //  Above is ok, we can find that item starting with st(here, e.g. ab),
 //  The above binary search can find the item starting with ab regardless of the 
 //  number of ab items.
 //  That is to say: ab item maybe one or maybe more than one.
 //  hmmtemplistview below is another listview. 
 //  *** Below is linear search part, trying to find the one(s) that precedes or
 //  follows the one that binary search found. 

  alist := lv.Items[fresult];
  wholecap := alist.caption;
  if pos(' ', wholecap) > 0 then
  p := pos(' ', wholecap);
  cap := trim(copy(wholecap, 0, p));
  if cap = st then
    found := true;

  while (fresult >= 0) and found = true do begin
    newlist := hmmtemplistview.Items.Insert(0);
    newlist.Caption := wholecap;
    hmmtemplistview.items.Item[0] := alist;

    dec(fresult);
    if fresult < 0 then begin
      break;
    end;
    alist := lv.Items[fresult];
    wholecap := alist.caption;
    p := pos(' ', wholecap);
    cap := trim(copy(wholecap, 0, p));
    if cap <> st then
    begin
      found := false;
    end;
  end;

  if result <> -1 then
    newresult := result + 1;
  alist := lv.Items[newresult];
  wholecap := alist.caption;
  if pos(' ', wholecap) > 0 then
    p := pos(' ', wholecap);
  cap := trim(copy(wholecap, 0, p));
  if cap = st then
    found := true;
  while (newresult >= 0) and found = true do begin
    newlist := hmmtemplistview.Items.Insert(0);
    newlist.Caption := wholecap;
    hmmtemplistview.items.Item[0] := alist;
    inc(newresult);
    if newresult > lv.Items.Count then begin
      break;
    end;
    alist := lv.Items[newresult];
    wholecap := alist.caption;
    p := pos(' ', wholecap);
    cap := trim(copy(wholecap, 0, p));
    if cap <> st then
    begin
      found := false;
    end;
  end;
end;
// Output result is: 
// if there is more than one ab items (2 or 3 or 4 items starting with ab), the 
// function output all ab items correctly.
// If there is only one ab item (an item starting with ab), the function output NONE.
// Why is it?
函数TForm1.bin搜索(lv:tlistview;st:string):整数; 变量 五十、 R,M:整数; p:整数; 帽,全帽:弦; 比较结果:整数; 列表,新列表:tlistitem; 结果:整数; 新结果:整数; 发现:布尔型; 开始 结果:=-1; 上限:=''; 全图:=''; L:=0; R:=lv.items.Count-1; M:=(左+右)第二分区; 全图:=lv.Items[m]。标题; p:=位置('',整体图); 封顶:=修剪(复制(整体封顶,0,p)); 比较结果:=比较结果(cap,st); 而(比较结果0)和(l 0)则开始 R:=M-1; 结束; 如果比较结果<0 然后开始 L:=M+1; 结束; M:=(左+右)第二分区; 全图:=lv.Items[m]。标题; 如果pos('',全图)>0,则 p:=位置('',整体图); 封顶:=修剪(复制(整体封顶,0,p)); 比较结果:=比较结果(cap,st); 结束; 结果:=m; 结果:=m; 新结果:=m; //以上是确定的,我们可以找到以st开头的项目(这里,例如ab), //上述二进制搜索可以找到以ab开头的项,而不考虑 //ab项目的数量。 //也就是说:ab项可能是一个或多个。 //下面的hmmtemplistview是另一个列表视图。 //***下面是线性搜索部分,尝试查找在或之前的部分 //跟随二进制搜索找到的一个。 列表:=lv.项目[fresult]; wholecap:=alist.caption; 如果pos('',全图)>0,则 p:=位置('',整体图); 封顶:=修剪(复制(整体封顶,0,p)); 如果cap=st,则 发现:=真; 而(fresult>=0)和found=true则开始 newlist:=hmmtemplistview.Items.Insert(0); newlist.Caption:=wholecap; hmmtemplistview.items.Item[0]:=alist; dec(fresult); 如果fresult<0,则开始 打破 结束; 列表:=lv.项目[fresult]; wholecap:=alist.caption; p:=位置('',整体图); 封顶:=修剪(复制(整体封顶,0,p)); 如果是圣 开始 发现:=假; 结束; 结束; 如果结果为-1,则 newresult:=结果+1; 列表:=lv.Items[newresult]; wholecap:=alist.caption; 如果pos('',全图)>0,则 p:=位置('',整体图); 封顶:=修剪(复制(整体封顶,0,p)); 如果cap=st,则 发现:=真; 而(newresult>=0)和found=true则开始 newlist:=hmmtemplistview.Items.Insert(0); newlist.Caption:=wholecap; hmmtemplistview.items.Item[0]:=alist; 公司(newresult); 如果newresult>lv.Items.Count,则开始 打破 结束; 列表:=lv.Items[newresult]; wholecap:=alist.caption; p:=位置('',整体图); 封顶:=修剪(复制(整体封顶,0,p)); 如果是圣 开始 发现:=假; 结束; 结束; 结束; //输出结果为: //如果有多个ab项目(2个或3个或4个以ab开头的项目),则 //函数正确输出所有ab项。 //如果只有一个ab项(以ab开头的项),则函数输出NONE。 //为什么? 我不知道我上面说的是否清楚?
用简单的英语来说,这是一个明确的问题吗?

我没有看过你的全部代码,但二进制搜索部分完全错了。你需要一个二进制搜索算法来找到满足搜索条件的第一个条目:

  L := 0;
  R := lv.items.Count-1;
  while L < R do begin
    M := (L + R) div 2;  
    wholecap:=lv.Items[m].caption;
    p:=pos(' ',wholecap);
    cap:=copy(wholecap, 1, p - 1);
    if Comparestr(cap, st) < 0
      then L := M + 1
      else R:= M;
  end;
// now you must check that L contains st because
//   it is possible that the search condition is never satisfied
L:=0;
R:=lv.items.Count-1;
当L
为什么不正常

您的版本使用二进制搜索,这是一个好主意。但它在TListView上工作,因此每次Items[]调用都会非常慢

建议是:

  • 使用TStringList存储数据
  • 在虚拟模式下使用TListView,按事件绘制TStringList内容
  • 使用一些优化的代码,无需二进制搜索,因为代码将更易于调试,而且性能也足够好
  • 代码如下:

    procedure Extract(List, Dest: TStrings; Char1, Char2: char);
    var i,j: integer;
        V: cardinal;
    type PC = {$ifdef UNICODE}PCardinal{$else}PWord{$endif};
    begin
      V := ord(Char1)+ord(Char2) shl (8*sizeof(char));
      Dest.BeginUpdate;
      Dest.Clear;
      for i := 0 to List.Count-1 do begin
      if PC(pointer(List[i]))^=V then begin
        for j := i to List.Count-1 do begin
          Dest.Add(List[j]);
          if PC(pointer(List[j]))^<>V then
            break; // end the for j := loop
         end;
         break; // end the for i := loop
      end;
      Dest.EndUpdate;
    end;
    
    过程提取(List,Dest:TStrings;Char1,Char2:char);
    var i,j:整数;
    五:红衣主教;
    键入PC={$ifdef UNICODE}PCardinal{$else}PWord{$endif};
    开始
    V:=ord(Char1)+ord(Char2)shl(8*sizeof(char));
    Dest.BeginUpdate;
    目标明确;
    对于i:=0的列表。计数-1是否开始
    如果PC(指针(列表[i])^=V,则开始
    对于j:=i to List.Count-1 do begin
    目的地添加(列表[j]);
    如果PC(指针(列表[j])^V,则
    break;//结束for j:=循环
    结束;
    break;//结束for i:=循环
    结束;
    Dest.EndUpdate;
    结束;
    

    应用于TStringList(而不是TListView.Items)的此过程将比TListView.Items上的任何二进制搜索快得多。

    我认为我们现在(超过-)给巨魔喂食这段代码真的应该试着最终保护开始/结束更新对谢谢你a.Bouchez.你真的很有帮助,至少提供了可能的答案,并且没有发布修饰语。这是
    procedure Extract(List, Dest: TStrings; Char1, Char2: char);
    var i,j: integer;
        V: cardinal;
    type PC = {$ifdef UNICODE}PCardinal{$else}PWord{$endif};
    begin
      V := ord(Char1)+ord(Char2) shl (8*sizeof(char));
      Dest.BeginUpdate;
      Dest.Clear;
      for i := 0 to List.Count-1 do begin
      if PC(pointer(List[i]))^=V then begin
        for j := i to List.Count-1 do begin
          Dest.Add(List[j]);
          if PC(pointer(List[j]))^<>V then
            break; // end the for j := loop
         end;
         break; // end the for i := loop
      end;
      Dest.EndUpdate;
    end;