二叉樹的非遞歸遍歷

 

二叉樹的非遞歸遍歷

    二叉樹的遍歷如果使用遞歸調用基本沒什麼問題,這裏主要是講如何使用非遞歸方法實現二叉樹的遍歷。

    由於遞歸調用程序實際上使用了棧來保存方法中的變量值,在非遞歸遍歷的方法中我們需要基於棧的方法。先來看看這個方法

 
 
01/// <summary>
02/// 非遞歸中序遍歷二叉樹
03/// </summary>
04/// <param name="root"></param>
05static void InOrderTraverse(BinaryTreeNode root)
06{
07    BinaryTreeNode temp = root;
08    Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
09    stack.Push(root);
10    while (stack.Count > 0)
11    {
12        while (temp != null)
13        {
14            temp = temp.left;
15            stack.Push(temp);
16        }
17        stack.Pop();
18        //如果爲0證明這時右邊節點爲null
19        if (stack.Count > 0)
20        {
21            temp = stack.Pop();
22            Console.WriteLine(temp.data);
23            temp = temp.right;
24            stack.Push(temp);
25        }
26    }
27}

      節點temp在這裏是起一個標識的作用,首先沿根節點往左下方進行查找,將存在的節點壓入棧,裏面的那個while循環結束後棧的最頂端一定是一個null,所以棧pop一下,然後這時進行讀取操作,讀取後壓入讀取節點的右子節點,進入下一個while循環,temp指向右子節點。

      在這裏使用棧能保證左邊子節點訪問後找到父節點,父節點訪問後也彈出棧,將右子節點壓入。這裏右子節點的壓入和前面一部分是對應的,保證stack.Pop()這句語句的正確性。如果我們不想在棧中壓入多餘的那個null這時該怎麼辦呢?將程序改成這樣

 
 
01/// <summary>
02/// 非遞歸中序遍歷二叉樹
03/// </summary>
04/// <param name="root"></param>
05static void InOrderTraverse2(BinaryTreeNode root)
06{
07    BinaryTreeNode temp = root.left;
08    Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
09    stack.Push(root);
10    while (stack.Count > 0 || temp != null)
11    {
12        while (temp != null)
13        {
14            stack.Push(temp);
15            temp = temp.left;
16        }
17        temp = stack.Pop();
18        Console.WriteLine(temp.data);
19        temp = temp.right;
20    }
21}

      只有確定是非null纔將節點壓入棧,但是這裏會有一個問題,當temp指向根節點的右節點的時候,棧是空的,我們需要在while循環處多加一個判斷,如果temp是null證明右節點不存在,循環結束。

到這裏,程序基本上已經比較完美了,不過我還是要在這裏折騰一下。

    while循環中的while循環的條件是temp是否爲null,所以,我可以用一個if/else來換一下

 
 
01static void InOrderTraverse3(BinaryTreeNode root)
02{
03    BinaryTreeNode temp = root.left;
04    Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
05    stack.Push(root);
06    while (stack.Count > 0 || temp != null)
07    {
08        if (temp != null)
09        {
10            stack.Push(temp);
11            temp = temp.left;
12        }
13        else
14        {
15            temp = stack.Pop();
16            Console.WriteLine(temp.data);
17            temp = temp.right;
18        }
19    }
20}

      呵呵,有意思吧。編程真奇妙~

      上面三個都是二叉樹的非遞歸中序遍歷方法,非遞歸先序遍歷和中序差不多,開始從上往下把節點入棧的時候對節點進行操作就行了,比如第二個的中序遍歷改成先序遍歷就是

 
 
01/// <summary>
02/// 非遞歸先序遍歷二叉樹
03/// </summary>
04/// <param name="root"></param>
05static void PreOrderTraverse(BinaryTreeNode root)
06{
07    BinaryTreeNode temp = root.left;
08    Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
09    Console.WriteLine(root.data);
10    stack.Push(root);
11    while (stack.Count > 0 || temp != null)
12    {
13        while (temp != null)
14        {
15            Console.WriteLine(temp.data);
16            stack.Push(temp);
17            temp = temp.left;
18        }
19        temp = stack.Pop();
20        temp = temp.right;
21    }
22}

      其他的幾種對着中序改一下就行了

      下面來講一講後序遍歷,後序遍歷由於遍歷父節點是在遍歷子節點之後,而且左節點和右節點遍歷後的行爲不一樣,所以需要用變量來記錄前一次訪問的節點,根據前一次節點和現在的節點的關係來確定具體執行什麼操作

 
 
01static void PostOrderTraversa1(BinaryTreeNode root)
02{
03    Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
04    stack.Push(root);
05    BinaryTreeNode prev = null;
06    BinaryTreeNode curr = null;
07    while (stack.Count > 0)
08    {
09        curr = stack.Peek();
10        if (prev == null || prev.left == curr || prev.right == curr)
11        {
12            if (curr.left != null)
13            {
14                stack.Push(curr.left);
15            }
16            else if (curr.right != null)
17            {
18                stack.Push(curr.right);
19            }
20            else
21            {
22                Console.WriteLine(curr.data);
23                stack.Pop();
24            }
25        }
26        else if (curr.left == prev)
27        {
28            if (curr.right != null)
29            {
30                stack.Push(curr.right);
31            }
32            else
33            {
34                Console.WriteLine(curr.data);
35                stack.Pop();
36            }
37        }
38        else if (curr.right == prev)
39        {
40            Console.WriteLine(curr.data);
41            stack.Pop();
42        }
43        prev = curr;
44    }
45}
 
  這個方法我繼續折騰,可以簡化成這樣
 
 
01static void PostOrderTraversa2(BinaryTreeNode root)
02{
03    Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
04    stack.Push(root);
05    BinaryTreeNode prev = null;
06    BinaryTreeNode curr = null;
07    while (stack.Count > 0)
08    {
09        curr = stack.Peek();
10        if (prev == null || prev.left == curr || prev.right == curr)
11        {
12            if (curr.left != null)
13                stack.Push(curr.left);
14            else if (curr.right != null)
15                stack.Push(curr.right);
16        }
17        else if (curr.left == prev)
18        {
19            if (curr.right != null)
20                stack.Push(curr.right);
21        }
22        else
23        {
24            Console.WriteLine(curr.data);
25            stack.Pop();
26        }
27        prev = curr;
28    }
29}
 
  好了,最後來一個壓軸的吧。老實說我開始想過這麼搞,但是沒有想清楚就否定了,後來在網上看到別人這麼寫纔看懂。
 
01/// <summary>
02/// 使用雙棧
03/// </summary>
04/// <param name="root"></param>
05static void PostOrderTraversa3(BinaryTreeNode root)
06{
07    Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
08    Stack<BinaryTreeNode> output = new Stack<BinaryTreeNode>();
09    stack.Push(root);
10    BinaryTreeNode curr = null;
11    while (stack.Count > 0)
12    {
13        curr = stack.Pop();
14        output.Push(curr);
15        if (curr.left != null)
16            stack.Push(curr.left);
17        if (curr.right != null)
18            stack.Push(curr.right);
19    }
20    while (output.Count > 0)
21    {
22        Console.WriteLine(output.Peek().data);
23        output.Pop();
24    }
25}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章