zl程序教程

您现在的位置是:首页 >  后端

当前栏目

重拾算法(1)——优雅地非递归遍历二叉树及其它

遍历二叉树算法递归 及其 优雅 重拾
2023-09-14 09:08:45 时间

重拾算法(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     }
获取结点数、叶结点数、非叶结点数