一種較為高效的TreeList生成演算法(Delphi實現)
阿新 • • 發佈:2018-11-29
記得不久前曾寫過篇關於TreeList生成的文章。雖然那個演算法裡,我已經有對葉節點做判斷,避免無用的Filter操作。但是非葉節點的Filter操作依然是無可避免的。而Filter又是影響整個生成的最重要因素,因此當帶子節點的節點很多時,速度還是要被拖下去的。
後來我看到了一種覺得不錯的思路,就是用調整節點的方法來形成樹。先一次過把所有資料當根節點加進TreeList中,然後再根據它們之間的ID和PID關係來進行節點調整,這樣就可以避免資料集的Filter操作了。而影響演算法的主要因素,變成了查詢關係時的IndexOf操作,和節點移動的方法MoveTo,不過相對於Filter來說,這個肯定要更高效了。
演算法實現如下:
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來過濾根節點了。它能得出哪些是根節點(對於一些丟失父節點的節點,也能自動被處理成根節點),這是我覺得比較好的地方。