这个问题本质上是问如何在没有递归的情况下遍历一棵树。遍历一棵树的方法有很多种;您的树恰好用可视控件中的节点表示这一事实是无关紧要的。
对于某些算法,用递归术语来考虑遍历更容易。这样,您就可以让编程语言通过将当前活动节点作为参数保留在堆栈中来跟踪您在树中的位置。如果您不想使用递归,那么您只需要自己跟踪进度即可。常用的工具包括堆栈和队列。
前序遍历意味着当您访问一个节点时,您先对该节点的数据执行操作,然后再对该节点的子节点执行操作。它对应于从上到下访问树视图控件的每个节点。你可以用一个堆栈来实现它:
procedure PreorderVisit(Node: TTreeNode; Action: TNodeAction);
var
Worklist: TStack<TTreeNode>;
i: Integer;
begin
Worklist := TStack<TTreeNode>.Create;
try
Worklist.Push(Node);
repeat
Node := Worklist.Pop;
for i := Pred(Node.Items.Count) downto 0 do
Worklist.Push(Node.Items[i]);
Action(Node);
until Worklist.Empty;
finally
Worklist.Free;
end;
end;
以相反的顺序将孩子推入堆栈,以便它们以所需的顺序弹出。
在该代码中,Action
代表您需要对每个节点执行的任何任务。您可以按照代码中的指定将其用作外部函数,也可以编写PreorderVisit
包含特定任务代码的专用版本。
不过, TTreeView 实际上并不代表一棵树。它真的是一片森林(树木的集合)。那是因为没有代表根的单个节点。但是,您可以轻松地使用上面的函数来处理树中的所有节点:
procedure PreorderVisitTree(Tree: TTreeView; Action: TNodeAction);
var
i: Integer;
begin
for i := 0 to Pred(Tree.Items.Count) do
PreorderVisit(Tree.Items[i], Action);
end;
另一种利用 TTreeView 的特定结构进行前序遍历的方法是使用GetNext
每个节点的内置方法:
procedure PreorderVisitTree(Tree: TTreeView; Action: TNodeAction);
var
Node: TTreeNode;
begin
if Tree.Items.Count = 0 then
exit;
Node := Tree.Items[0];
repeat
Action(Node);
Node := Node.GetNext;
until not Assigned(Node);
end;
似乎无法获取 Firemonkey 树视图的隐藏节点。通过迭代内部树数据结构而不是尝试从 GUI 中提取信息,您可能会找到更好的结果。