public class TreeNode { public string Key { get; set; } public object Data { get; set; } public TreeNode Parent; public List<TreeNode> Children { get; set; } public TreeNode(string Key,object Data) { Children = new List<TreeNode>(); this.Key = Key; this.Data = Data; } public void AddChild(TreeNode node) { this.Children.Add(node); node.Parent = this; } public void DelChild(TreeNode node) { this.Children.Remove(node); } } public class Trees { public TreeNode Root { get; set; } /// <summary> /// 非遞迴遍曆樹方法,這裡採用的先父後子的順序遍曆.如果要先子後父的話,則需要用到兩個棧,兩個迴圈. /// </summary> /// <param name="ListCurrNode"></param> public void ListChildren(Action<TreeNode> ListCurrNode) { if (Root == null) { return; } //先父後子 Stack<TreeNode> _nodesStack1 = new Stack<TreeNode>(); while (_nodesStack1.Count > 0) { TreeNode node = _nodesStack1.Pop(); ListCurrNode(node); foreach (var item in node.Children) { _nodesStack1.Push(item); } } //先子後父.其實對於二叉樹的左序和右序訪問,也需要兩個棧來實現. _nodesStack1.Clear(); _nodesStack1.Push(Root); Stack<TreeNode> _nodesStack2 = new Stack<TreeNode>(); while (_nodesStack1.Count > 0) { TreeNode node = _nodesStack1.Pop(); _nodesStack2.Push(node); foreach (var item in node.Children) { _nodesStack1.Push(item); } } while (_nodesStack2.Count > 0) { TreeNode node = _nodesStack1.Pop(); ListCurrNode(node); } } /// <summary> /// 非遞迴中序,線性時間,常數額外輔助空間方式,不能修改樹(臨時的也不行),可以遍曆任意的叉樹.對於K叉樹,每次退回的比較時間為<=K, /// 因為每個節點至多需退回一次,則退回比較為<=Kn次,樹遍曆本身的O(n).遍曆還是線性時間。 /// 其實如果可以修改樹,比如設定訪問標誌,則直接可以退回,然後繼續訪問第1個未訪問的子節點。 /// 下面的演算法其實就是利用這種思想,只是不是直接設定標誌,而是利用了數組指標的特性。 /// 對於左子右兄弟的表達是任意叉樹也可以實現,只是退回遍曆方法不同而已(更簡單). /// 如果採用的是K個孩子指標變數的表示孩子節點的方式,也是適用的,只是需要預設一個訪問邏輯順序即可. /// 注意:這裡其實採用的是先父後子的訪問順序,如果需要先子後父,也是可以的,需要增加一個子訪問完成退回標記,稍稍修改 /// 一下代碼即可. /// </summary> /// <param name="ListCurrNode">遍曆訪問者</param> public void ListAll1(Action<TreeNode> ListCurrNode) { int theQuitFrom = -1;//-1表示從父節點來,0-n表示從第k個孩子回來. TreeNode theCurrNode = Root; while (theCurrNode != null) { TreeNode theTmp = null; if (theQuitFrom < 0)//剛從父節點過來(theQuitFrom=-1) { ListCurrNode(theCurrNode); //從父節點過來時,訪問第1個孩子(索引為0). if (theCurrNode.Children.Count > 0) { theTmp = theCurrNode.Children[0]; } else { theTmp = null; } } else //從孩子節點遍曆退回來,則去下一個孩子節點。 { if (theQuitFrom + 1 < theCurrNode.Children.Count) { theTmp = theCurrNode.Children[theQuitFrom + 1]; theQuitFrom = -1; } else//孩子節點訪問完成,theTmp置空,繼續執行後退策略. { theTmp = null;//如果要先子後父,需要在這裡做標記 } } if (theTmp == null) //如果theTmp為空白,則表示無法繼續向下遍曆,需後退訪問兄弟節點 { //如果當前節點是root,表示已經需從root退回,則表示整個樹遍曆完成, //這裡也保證了GetChildrenIndex不會返回-1的情況. if (theCurrNode == Root) break; theQuitFrom = GetChildrenIndex(theCurrNode.Parent, theCurrNode); theCurrNode = theCurrNode.Parent; } else //不為空白,則將theTmp節點作為當前節點進行訪問。 { //這種情況下,theQuitFrom一定是-1.因為初始為-1,只有退回的時候才會改變這個值, //如果退回有下一個兄弟可訪問,則theQuitFrom一定會被設定為-1,如果沒有,則只能繼續 //退.如果能繼續訪問,theQuitFrom一定保持著-1. theCurrNode = theTmp; } } } /// <summary> /// 擷取孩子節點在父節點中的位置. /// </summary> /// <param name="Parent"></param> /// <param name="Child"></param> /// <returns></returns> private int GetChildrenIndex(TreeNode Parent, TreeNode Child) { for (int i = 0; i < Parent.Children.Count; i++) { if (Parent.Children[i] == Child) { return i; } } //實際上這裡放回-1是沒法執行的. return -1; } }
本文參考了CSDN網友的文章:http://blog.csdn.net/acrazer/article/details/4454508