1. 程式人生 > >一種較為高效的TreeList生成演算法(Delphi實現)

一種較為高效的TreeList生成演算法(Delphi實現)

記得不久前曾寫過篇關於TreeList生成的文章。雖然那個演算法裡,我已經有對葉節點做判斷,避免無用的Filter操作。但是非葉節點的Filter操作依然是無可避免的。而Filter又是影響整個生成的最重要因素,因此當帶子節點的節點很多時,速度還是要被拖下去的。

後來我看到了一種覺得不錯的思路,就是用調整節點的方法來形成樹。先一次過把所有資料當根節點加進TreeList中,然後再根據它們之間的ID和PID關係來進行節點調整,這樣就可以避免資料集的Filter操作了。而影響演算法的主要因素,變成了查詢關係時的IndexOf操作,和節點移動的方法MoveTo,不過相對於Filter來說,這個肯定要更高效了。
 

另一種較為高效的TreeList生成演算法(Delphi實現)

演算法實現如下:

procedure GenerateTreeListEx(AKeyField, AParentField: string; ADataSet: TDataSet; ATree: TTreeView);

 function CreateNewNode(ADataSet: TDataSet; ATree: TTreeView): TTreeNode;
 var
  pCodeValue: PString;
 begin
  // 這裡寫你的TreeNode讀寫邏輯,新增到根節點下就行
  // 現以用Data屬性來儲存程式碼值為例,假設程式碼名欄位叫"CodeName",程式碼值欄位叫"CodeValue"
  result := ATree.Add(nil, ADataSet['CodeName']);
  New(pCodeValue);
  pCodeValue^ := ADataSet['CodeValue'] ;
  result.Data := pCodeValue;
 end;

var
 APidList, AItems, AChildItems: TStringList;
 i, j, k, iIndex: integer;
 sPID: string;
begin
 APidList := TStringList.Create;

 APidList.Sorted := true; //把結果進行排序,這樣可以通過搜尋演算法(Find的二分查詢)提高索引效率,重要
 ATree.Items.BeginUpdate;
 try
  // 讀取資料集
  for i := 1 to ADataSet.RecordCount do
  begin
   if VarIsNull(ADataSet[AParentField]) then
    sPID := 'NULL'
   else
    sPID := ADataSet[AParentField];

   // 查詢該PID是不是已經在PidList裡存在
   if not APidList.Find(sPID, iIndex) then
    iIndex := APidList.AddObject(ADataSet[AParentField], TStringList.Create);
   with TStringList(APidList.Objects[iIndex]) do
    AddObject(ADataSet[AKeyField], CreateTreeNode(ADataSet, ATree));

   ADataSet.Next;
  end;

  // 調整節點
  for i := 0 to APidList.Count - 1 do
  begin
   AItems := TStringList(APidList.Objects[i]);
   for j := 0 to AItems.Count - 1 do
   begin
    if APidList.Find(AItems[j], iIndex) then
    begin
     AChildItems := TStringList(APidList.Objects[iIndex]);
     for k := 0 to AChildItems.Count - 1 do
      TTreeNode(AChildItems.Objects[k]).MoveTo(TTreeNode(AItems.Objects[j]), naAddChild);

     // 由於MoveTo會導致節點展開,因此把它重新摺疊起來

     TTreeNode(AItems.Objects[j]).Collapse(false);
    end;
   end;
  end;
 finally
  // 釋放資源等
  for i := 0 to APidList.Count - 1 do APidList.Objects[i].Free;
  APidList.Free;
  ATree.Items.EndUpdate;
 end;
end;

比起上次的方法,除了效率外,我覺得這次的方法程式碼也更為簡潔點,沒有用到什麼棧結構;也不用什麼RootFilterText來過濾根節點了。它能得出哪些是根節點(對於一些丟失父節點的節點,也能自動被處理成根節點),這是我覺得比較好的地方。