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:
- Use TStringList to store the data
- Use your TListView in virtual mode, to draw the TStringList content by events
- 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.
精彩评论