开发者

How to get sorted items in tlistview? [closed]

开发者 https://www.devze.com 2023-01-29 10:42 出处:网络
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical andcannot be reasonably answered in its current form. For help clari
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center. Closed 12 years ago.

There is a listview with about 10000 items that has been sorted (for search):

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
..
..

The purpose of the next binsearch function is to find all items starting with st( here for example ab), and then output all items starting with st to another file. First we use binary search and fast find one of ab items, then we use linear search to try to find All ab items that are preceding and following the one that binary search found. We tested the part of binary search and it works well. The binary search in the following function will return one of ab item(s). If there are more than ONE ab item, binsearch function will return all ab items and output them correctly. If there is only ONE ab item, the part of binary search in the following function CAN find it ( breakpoint and prompt has been inserted for tracking), but the output does not find that item. The problem maybe in the linear search part of this function, do not know why?

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?

I do not know if what I said above is clear or not? Is this a clear question in simple English?


I have not looked at your whole code, but binary search part is wrong completely. You need a binary search algorithm that finds the first entry satisfying search condition:

  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


Why was the previous answer not OK?

Your version use a binary search, which is a good idea. But it works on a TListView, so every Items[] call will be dead slow.

The suggestion was:

  1. Use TStringList to store the data
  2. Use your TListView in virtual mode, to draw the TStringList content by events
  3. Use some optimized code, without binary search, because code will be more easy to debug, and performance will be good enough

Here was the code:

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;

This procedure, applied on a TStringList (and not TListView.Items), will be much faster than any binary-search on a TListView.Items.

0

精彩评论

暂无评论...
验证码 换一张
取 消