數據結構-二叉樹(先序、中序、後序遍歷二叉樹的非遞歸算法)

 

文章目錄

 

思路

上一篇博文咱們講了二叉樹的遞歸算法,這裏咱們來寫一波二叉樹的非遞歸算法java

爲何說遍歷二叉樹能夠用遞歸node

二叉樹每一個結點都知足某個遍歷次序,而後從根結點開始遍歷,這個流程很是知足遞歸的模型,就是一個大的問題按照某種方式能夠劃分爲許多細小的問題,而後這些細小的問題又能夠用一樣的方式繼續劃分,直到爲空或者說直到有個出口算法

全部遞歸能夠轉化成非遞歸數組

那非遞歸我該如何實現呢?這就不得不從遞歸的有一個特色提及了,遞歸老是這樣的形式:a<b<c<d,a 表示第一層方法,b 表示第二層的遞歸,c 表示第三層的遞歸,d 表示第四層的遞歸,那麼咱們程序運行的效果確定是:a 到 b 到 c 到 d,而後 d 找到出口所有執行完了,返回 c,c 又所有執行完了,再到 b,b 所有執行完了,再到 a,也就是 a→b→c→d→d→c→b→a 的次序,這不就是棧的數據結構嗎???對了,就是棧,所以若是咱們不用遞歸,那咱們確定得用循環再加上棧的使用數據結構

先序&中序和後序有什麼不一樣ide

咱們知道先序是根左右,中序是左根右,後序是左右根,先序和中序沒啥大的花樣,可是後續有些不一樣,爲何呢?由於對於先序遍從來講,最早記錄結點的數據,而後能找到左結點就一直找左結點,找不到能夠再找棧頂的右結點,並同時釋放棧頂結點;若是是中序的話,先找左結點,找不到爲止,就記錄棧頂結點的值,釋放棧頂結點,同時再去找該棧頂結點的右結點;可是對後序遍歷可不一樣了,由於後序是先去找左結點,一直到找不到爲止,咱們再去找棧頂的右結點,可是要注意的是後序遍歷在左右結點轉換的時候,咱們並不知道這個根結點何時彈出棧頂,最大的不一樣就在於這裏。先序和中序在左結點找不到時切換右結點時候會彈出根結點,根結點這顆棋子已經沒有用了,可是後序遍歷可不這樣,當左結點實在找不到時候,去找棧頂的右結點,此時棧頂結點還不能獲得釋放,由於右結點的後序遍歷沒有找盡每個結點,因此還不能記錄該根結點的值post

後序遍歷算法的思路this

若是咱們找的到左結點就一直找,找不到的話,咱們拿到棧頂結點,看棧頂的右結點是否找的到,找的到的話,那就指向右結點,而後繼續按照後序遍歷的模式,若是棧頂的右結點找不到,那麼咱們就記錄棧頂的結點,並彈出棧頂結點,而後咱們將新結點令爲空,這樣下次會重新棧頂結點往右邊找。這樣這個算法好像就完了是否是?不是,這裏有個很大的漏洞!我舉個例子說明:假如咱們一直找左結點找到了 a 結點,而後 a 結點的左結點爲 null,a 結點的右結點是 b 結點,b 結點的左結點爲 null,b 結點的右結點也爲 null,當咱們找到 a 結點時候,按照上面的算法思路,a->left 爲空,可是棧頂結點 a 的右結點 b 不爲空,因此新結點是 b 併入棧,b 的左結點爲空,而後棧頂結點 b 的右結點爲空,b 又變成了 a 結點,發現了嗎?這不返回的了嗎?這不從 a 到 b,b 又到 a,這不沒完沒了了?因此算法漏洞是咱們少加了一個東西,咱們應該在當咱們右結點找不到時,須要記錄棧頂的結點,而且當下一次循環訪問右結點時,右結點不只不爲空,並且右結點不能是上次循環中彈出的那個結點!code

Java 實現
// 結點
class Node {
    int data;
    Node left = null;
    Node right = null;
}

// 二叉樹
public class BinaryTree {
    // 根結點
    private Node root;
    // 輸入的數組
    private int[] arr_in;
    // 輸出的數組
    private int[] arr_out;
    // 記錄數組下標
    private static int index;
    
    // 初始化
    public BinaryTree(int[] arr) {
        root = new Node();
        this.arr_in = arr;
        arr_out = new int[arr.length];
        index = 0;
    }
   
    // 先序遍歷二叉樹(非遞歸)根→左→右
    public int[] preorderTraversal(Node r) {
        Stack stack = new Stack();
        Node node = r;
        while (!stack.empty() || node != null) {
            if (node != null) {
                stack.push(node);
                // 根
                arr_out[index++] = node.data;
                // 左
                node = node.left;
            }
            else {
                Node top = stack.pop();
                // 右
                node = top.right;
            }
        }
        index = 0;
        return arr_out;
    }

    // 中序遍歷二叉樹(非遞歸)左→根→右
    public int[] inorderTraversal(Node r) {
        Stack stack = new Stack();
        Node node = r;
        while (!stack.empty() || node != null) {
            if (node != null) {
                stack.push(node);
                // 左
                node = node.left;
            }
            else {
                Node top = stack.pop();
                // 根
                arr_out[index++] = top.data;
            	// 右
                node = top.right;
            }
        }
        index = 0;
        return arr_out;
    }

    // 後序遍歷二叉樹(非遞歸)左→右→根
    public int[] postorderTraversal(Node r) {
        Stack stack = new Stack();
        Node node = r;
        Node top = null;
        while (!stack.empty() || node != null) {
            if (node != null) {
                stack.push(node);
                // 左
                node = node.left;
            }
            else {
                if (stack.peek().right != null && stack.peek().right != top)
                    // 右
                    node = stack.peek().right;
                else {
                    top = stack.pop();
                    // 根
                    arr_out[index++] = top.data;
                    node = null;
                }
            }
        }
        index = 0;
        return arr_out;
    }
}
相關文章
相關標籤/搜索