天天看點

二叉樹的四種周遊方式(遞歸+非遞歸)

關于二叉樹的大多數算法題目都是與二叉樹的周遊方式有關的,大都解決方案對二叉樹的周遊改寫即可。下面就主要介紹下二叉樹的常用的四種周遊方式。

其中DFS(Depth-First-Search)的有三種分别為先序周遊,中序周遊及後序周遊,而使用BFS的隻有層序周遊。

一.先序周遊

先序周遊的順序為對于目前結點先通路其本身,再通路其左子樹,最後通路其右子樹。

遞歸實作由于過于簡單就隻給代碼不加描述了,

public static void recurrentSelution(TreeNode root) {
         if (root == null) {
             return;
         }
         System.out.print(root.val + " ");
         recurrentSelution(root.left);
         recurrentSelution(root.right);
     }
           

非遞歸實作大體思路:

需要借助一個堆棧,首先将頭結點入棧。

若堆棧不為空:

       從堆棧彈出一個元素進行通路;

       若其右孩子存在,則将右孩子入棧;

       若其左孩子存在,則将左孩子入棧。

如此即可完成非遞歸的先序周遊。

代碼如下:

public static void nonRecurrentSelution(TreeNode root) {
         Stack<TreeNode> stack = new Stack<>();
         stack.push(root);
         while (!stack.isEmpty()) {
             TreeNode current = stack.pop();
             System.out.print(current.val + " ");
             if (current.right != null) {
                 stack.push(current.right);
             }
             if (current.left != null) {
                 stack.push(current.left);
             }
             
         }
     }
           

二.中序周遊

中序周遊的順序為對于每一個結點先通路左子樹,在通路目前結點,在通路右子樹。對于二叉搜尋樹而言中序周遊的結果即為排序輸入的結果。

遞歸實作代碼:

public static void inOrderRecurr(TreeNode root) {
         if (root == null) {
             return ;
         }
         inOrderRecurr(root.left);
         System.out.print(root.val + " ");
         inOrderRecurr(root.right);
     }
           

非遞歸實作:

首先申請一個堆棧,并令目前結點等于根結點。

若堆棧不為空或目前結點不為空時:

       将目前結點及其左孩子以及左孩子的左孩子....依次入棧,直到左孩子為空為止;

       從堆棧中彈出一個元素并通路它;

       令目前元素等于上一步彈出元素的右孩子。

如此即可完成先序周遊的非遞歸實作

實作代碼如下:

public static void nonInOrderRecurr(TreeNode root) {
         Stack<TreeNode> stack = new Stack<>();
         TreeNode current = root;
         while (current != null || !stack.isEmpty()) {
             while (current != null) {
                 stack.push(current);
                 current = stack.peek().left;
             }
             current = stack.pop();
             System.out.print(current.val + " ");
             current = current.right;
         }
     }
           

三.後序周遊

後序周遊的順序為先通路目前結點的左子樹,再通路目前節點的右子樹,最後通路目前結點。

遞歸實作代碼如下:

public static void postOrderRecurr(TreeNode root) {
         if (root == null) {
             return ;
         }
         postOrderRecurr(root.left);
         postOrderRecurr(root.right);
         System.out.print(root.val + " ");
     }
           

非遞歸實作:

細心地小夥伴可能已經發現了,後序周遊的逆序跟先序周遊類似,後序周遊為左,右,中,而先序周遊為中,左,右是以可以對先序周遊的代碼進行稍微的改寫完成中,右,左的周遊順序,然後再将該方式逆序即可。

在先序的非遞歸周遊中,我們是先讓右孩子入棧,再将左孩子入棧的;将其改寫為先讓左孩子入棧,再讓右孩子入棧。同時通路的時候将其先存到另一堆棧,最後将存入新堆棧的元素依次倒入通路即可。

實作代碼如下:

public static void postOrderNonRecurr(TreeNode root) {
         Stack<TreeNode> stack1 = new Stack<>();
         Stack<TreeNode> stack2 = new Stack<>();
         stack1.push(root);
         while (!stack1.isEmpty()) {
             TreeNode current = stack1.pop();
             stack2.push(current);
             if (current.left != null) {
                 stack1.push(current.left);
             }
             if(current.right != null) {
                 stack1.push(current.right);
             }
         }
         while(!stack2.isEmpty()) {
             System.out.print(stack2.pop().val + " ");
         }
     }
           

四.層序周遊

層序周遊,顧名思義一層一層的周遊啊。從上至下,從左至右依次周遊。

層序周遊的非遞歸實作:

首先建立一個隊列,并将頭結點入隊

若隊列不為空時:

       從隊列中彈出一個元素,并通路他;

       若其左孩子存在則将左孩子入隊;

       若其右孩子存在則将右孩子入隊。

如此即可完成對于隊列的層序周遊。

實作代碼如下:

public static void levelOrderTraversal(TreeNode root) {
         Queue<TreeNode> queue = new LinkedList<>();
         queue.add(root);
         while (!queue.isEmpty()) {
             TreeNode current = queue.remove();
             if (current.left != null) {
                 queue.add(current.left);
             }
             if (current.right != null) {
                 queue.add(current.right);
             }
             System.out.print(current.val + " ");
         }
     }
           

對于層序周遊更進一步的要求是在周遊過程中需要輸出換行,使得同一層元素保持的同一行。

對于該問題使用雙指針的方案解決。

初始化時定義另個結點,currentLast和nextLast,分别維持目前層的最後一個結點及下一層的最後一個結點。currentLast = root,nextLast = null。

在通路結點的時候若其等于currentLast時,輸出換行,currentLast = nextLast

在每一個結點入隊時,将nextLast指向入隊結點。這樣就可以保證在通路到目前層最後一個結點時,此時的nextLast指向的是下一層的最後一個結點。

實作代碼如下:

public static void levelOrderTraversal(TreeNode root) {
         Queue<TreeNode> queue = new LinkedList<>();
         TreeNode last = root;
         TreeNode nLast = root.right == null ? root.left : root.right;
         queue.add(root);
         while (!queue.isEmpty()) {
             TreeNode current = queue.remove();
             if (current.left != null) {
                 queue.add(current.left);
                 nLast = current.left;
             }
             if (current.right != null) {
                 queue.add(current.right);
                 nLast = current.right;
             }
             System.out.print(current.val + " ");
             if (current == last) {
                System.out.println();
                last = nLast;
             }
         }
     }
           

繼續閱讀