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

时间:2010-12-05 13:25:39

标签: delphi

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

aa cd
aa ef
..
..
ab cd    // want to get all ab item(s), may be there is only ONE ab item, 
(ab ef) // may be there is more than one ab item, the number of ab item is unknown.
..
..
ac cd
ac ef
..
ak cd
ak ef
ak gh
..
..

下一个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?

我不知道我上面说的是否清楚? 用简单的英语这是一个明确的问题吗?

2 个答案:

答案 0 :(得分:4)

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

  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

答案 1 :(得分:4)

为什么previous answer不行?

您的版本使用二进制搜索,这是一个好主意。但是它适用于TListView,因此每个Items []调用都会变慢。

建议是:

  1. 使用TStringList存储数据
  2. 在虚拟模式下使用TListView,按事件
  3. 绘制TStringList内容
  4. 使用一些优化的代码,无需二进制搜索,因为代码更容易调试,性能也足够好
  5. 以下是代码:

    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;
    

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