重拾演算法(1)——優雅地非遞迴遍歷二叉樹及其它

weixin_34119545發表於2014-07-17

重拾演算法(1)——優雅地非遞迴遍歷二叉樹及其它

本文中非遞迴遍歷二叉樹的思想和程式碼都來自這裡(http://jianshu.io/p/49c8cfd07410#)。我認為其思想和程式碼都足夠優雅動人了,於是稍作整理,得到如下的程式。

前中後序遍歷二叉樹

  1     public class BinaryTreeNode<T>
  2     {
  3         public T Value { get;set; }
  4         public BinaryTreeNode<T> Parent { get;set; }
  5         public BinaryTreeNode<T> Left { get;set; }
  6         public BinaryTreeNode<T> Right { get;set; }
  7         
  8         public virtual void Traverse(TraverseOrder order, NodeWorker<T> worker)
  9         {
 10             if (worker == null) { return; }
 11 
 12             var left = this.Left;
 13             var right = this.Right;
 14             switch (order)
 15             {
 16             case TraverseOrder.Preorder:
 17                 /* recursive preorder traverse
 18                 worker.DoActionOnNode(this);
 19                 if (left != null) { Left.Traverse(order, worker); }
 20                 if (right != null) { Right.Traverse(order, worker); }
 21                 */
 22                 PreorderTraverse(worker);
 23                 break;
 24             case TraverseOrder.Inorder:
 25                 /* recursive inorder traverse
 26                 if (left != null) { Left.Traverse(order, worker); }
 27                 worker.DoActionOnNode(this);
 28                 if (right != null) { Right.Traverse(order, worker); }
 29                 */
 30                 InorderTraverse(worker);
 31                 break;
 32             case TraverseOrder.Postorder:
 33                 /* recursive postorder traverse
 34                 if (left != null) { Left.Traverse(order, worker); }
 35                 if (right != null) { Right.Traverse(order, worker); }
 36                 worker.DoActionOnNode(this);
 37                 */
 38                 PostorderTraverse(worker);
 39                 break;
 40             default:
 41                 break;
 42             }
 43         }
 44         
 45         void PreorderTraverse(NodeWorker<T> worker)
 46         {
 47             var stack = new Stack<BinaryTreeNode<T>>();
 48             stack.Push(this); 
 49             
 50             while (stack.Count > 0)
 51             {
 52                 var node = stack.Pop(); 
 53                 //if (node == null) { continue; }
 54                 //else
 55                 {
 56                     var right = node.Right;
 57                     var left = node.Left;
 58                     worker.DoActionOnNode(node);
 59                     if (right != null) { stack.Push(right); }
 60                     if (left != null) { stack.Push(left); }
 61                 }
 62             }
 63         }        
 64         /* This works fine and is better for tuition. The code above is an optimized version.
 65         void PreorderTraverse(NodeWorker<T> worker)
 66         {
 67             var stack = new Stack<BinaryTreeNode<T>>(); var stackReady4Visit = new Stack<bool>();
 68             
 69             stack.Push(this); stackReady4Visit.Push(false);
 70             
 71             while (stack.Count > 0)
 72             {
 73                 var node = stack.Pop(); var ready4Visit = stackReady4Visit.Pop();
 74                 //if (node == null) { continue; }
 75                 if (ready4Visit)
 76                 {
 77                     worker.DoActionOnNode(node);
 78                 }
 79                 else
 80                 {
 81                     var right = node.Right;
 82                     var left = node.Left;
 83                     if (right != null) { stack.Push(right); stackReady4Visit.Push(false); }
 84                     if (left != null) { stack.Push(left); stackReady4Visit.Push(false); }
 85                     stack.Push(node); stackReady4Visit.Push(true);
 86                 }
 87             }
 88         }
 89         */
 90         
 91         void InorderTraverse(NodeWorker<T> worker)
 92         {
 93             var stack = new Stack<BinaryTreeNode<T>>(); var stackReady4Visit = new Stack<bool>();
 94             
 95             stack.Push(this); stackReady4Visit.Push(false);
 96             
 97             while (stack.Count > 0)
 98             {
 99                 var node = stack.Pop(); var ready4Visit = stackReady4Visit.Pop();
100                 //if (node == null) { continue; }
101                 if (ready4Visit)
102                 {
103                     worker.DoActionOnNode(node);
104                 }
105                 else
106                 {
107                     var right = node.Right;
108                     var left = node.Left;
109                     if (right != null) { stack.Push(right); stackReady4Visit.Push(false); }
110                     stack.Push(node); stackReady4Visit.Push(true);
111                     if (left != null) { stack.Push(left); stackReady4Visit.Push(false); }
112                 }
113             }
114         }
115         
116         void PostorderTraverse(NodeWorker<T> worker)
117         {
118             var stack = new Stack<BinaryTreeNode<T>>(); var stackReady4Visit = new Stack<bool>();
119             
120             stack.Push(this); stackReady4Visit.Push(false);
121             
122             while (stack.Count > 0)
123             {
124                 var node = stack.Pop(); var ready4Visit = stackReady4Visit.Pop();
125                 //if (node == null) { continue; }
126                 if (ready4Visit)
127                 {
128                     worker.DoActionOnNode(node);
129                 }
130                 else
131                 {
132                     var right = node.Right;
133                     var left = node.Left;
134                     stack.Push(node); stackReady4Visit.Push(true);
135                     if (right != null) { stack.Push(right); stackReady4Visit.Push(false); }
136                     if (left != null) { stack.Push(left); stackReady4Visit.Push(false); }
137                 }
138             }
139         }
140     }
141     public abstract class NodeWorker<T>
142     {
143         public abstract void DoActionOnNode(BinaryTreeNode<T> node);
144     }
前中後序遍歷二叉樹

以上三種遍歷實現程式碼行數一模一樣,如同遞迴遍歷一樣,只有三行核心程式碼的先後順序有區別。用原作者的話解釋就是:"得以統一三種更簡單的非遞迴遍歷方法的基本思想:有重合元素的區域性有序一定能導致整體有序。基於這種思想,我就構思三種非遞迴遍歷的統一思想:不管是前序,中序,後序,只要我能保證對每個結點而言,該結點,其左子結點,其右子結點都滿足以前序/中序/後序的訪問順序,整個二叉樹的這種三結點區域性有序一定能保證整體以前序/中序/後序訪問,因為相鄰的區域性必有重合的結點,即一個區域性的"根"結點是另外一個區域性的"子"結點。"。

 

層次遍歷

層次遍歷類似圖的廣度優先搜尋。

 1     public class BinaryTree<T>
 2     {
 3         BinaryTreeNode<T> Node { get;set; }
 4         public BinaryTree(BinaryTreeNode<T> node)
 5         {
 6             this.Node = node;
 7         }
 8         
 9         public void Traverse(TraverseOrder order, NodeWorker<T> worker)
10         {
11             if (worker == null) { return; }
12             var node = this.Node;
13             if (node == null) { return; }
14             
15             switch (order)
16             {
17             case TraverseOrder.Preorder:
18                 node.Traverse(order, worker);
19                 break;
20             case TraverseOrder.Inorder:
21                 node.Traverse(order, worker);
22                 break;
23             case TraverseOrder.Postorder:
24                 node.Traverse(order, worker);
25                 break;
26             case TraverseOrder.Layer:
27                 TraverseLayer(worker);
28                 break;
29             default:
30                 break;
31             }
32         }
33         
34         private void TraverseLayer(NodeWorker<T> worker)
35         {
36             var queue = new Queue<BinaryTreeNode<T>>();
37             queue.Enqueue(this.Node);
38             while (queue.Count > 0)
39             {
40                 var element = queue.Dequeue();
41                 if (element != null)
42                 {
43                     worker.DoActionOnNode(element);
44                     var left = element.Left;
45                     var right = element.Right;
46                     if (left != null) { queue.Enqueue(left); }
47                     if (right != null) { queue.Enqueue(right); }
48                 }
49             }
50         }
51     }
層次遍歷

 

借用Traverse方法實現其他功能

 1     public class BinaryTree<T>
 2     {
 3         /**/
 4         
 5         public int GetNodeCount()
 6         {
 7             var counter = new NodeCounter<T>();
 8             this.Traverse(TraverseOrder.Preorder, counter);
 9             return counter.Count;
10         }
11         
12         public int GetLeaveCount()
13         {
14             var counter = new LeaveCounter<T>();
15             this.Traverse(TraverseOrder.Preorder, counter);
16             return counter.Count;
17         }
18         
19         public int GetNonLeaveCount()
20         {
21             var counter = new NonLeaveCounter<T>();
22             this.Traverse(TraverseOrder.Preorder, counter);
23             return counter.Count;
24         }
25     }
26     
27     class NonLeaveCounter<T> : NodeWorker<T>
28     {
29         public int Count { get;set; }
30         public override void DoActionOnNode(BinaryTreeNode<T> node)
31         {
32             if (node != null)
33             {
34                 if (node.Left != null || node.Right != null)
35                 {
36                     this.Count++;
37                 }
38             }
39         }
40     }
41     
42     class LeaveCounter<T> : NodeWorker<T>
43     {
44         public int Count { get;set; }
45         public override void DoActionOnNode(BinaryTreeNode<T> node)
46         {
47             if (node != null)
48             {
49                 if (node.Left == null && node.Right == null)
50                 {
51                     this.Count++;
52                 }
53             }
54         }
55     }
56     
57     class NodeCounter<T> : NodeWorker<T>
58     {
59         public int Count { get;set; }
60         public override void DoActionOnNode(BinaryTreeNode<T> node)
61         {
62             if (node != null)
63             {
64                 this.Count++;
65             }
66         }
67     }
獲取結點數、葉結點數、非葉結點數

相關文章