算法-優雅的進行二叉樹的後序遍歷

優雅的進行二叉樹的後序遍歷

樹的遍歷是一個常用的操作,分爲遞歸和非遞歸遍歷,經典的遞歸遍歷如下

	List<Integer> lists=new ArrayList<>();
    /**
     * 後序遞歸
     * @param node
     * @return
     */
    public List<Integer> backRecuErgodic(TreeNode node){
        if(node!=null){
            backRecuErgodic(node.left);
            backRecuErgodic(node.right);
            lists.add(node.val);
        }
        return lists;
    }

這種方式非常簡潔,而且前序,中序,後序遍歷的格式幾乎完全統一,很好記憶

不過在非遞歸的情況下,大家的解法就複雜了起來,對於前序和中序遍歷的非遞歸實現,代碼風格是一致的,而對於後序遍歷,大家的解法就複雜多樣了

    /**
     * 先序非遞歸
     * @param node
     * @return
     */
    public List<Integer> preNoRecuErgodic(TreeNode node){
        Stack<TreeNode> stack=new Stack<>();
        while (node!=null||stack.isEmpty()==false){
            if(node!=null){
                lists.add(node.val);
                stack.push(node);
                node=node.left;
            }else {
                node=stack.pop();
                node=node.right;
            }
        }
        return lists;
    }
    
    /**
     * 中序非遞歸
     * @param node
     * @return
     */
    public List<Integer> inNoRecuErgodic(TreeNode node){
        Stack<TreeNode> stack=new Stack<>();
        while (stack.isEmpty()==false||node!=null){
            if(node!=null){
                stack.push(node);
                node=node.left;
            }else {
                node=stack.pop();
                lists.add(node.val);
                node=node.right;
            }
        }
        return lists;
    }

其實後序遍歷也可以有這樣統一的格式,試一下

			1
	 2			   3
4		5		6		7

仔細觀察一下上面的樹,其後序遍歷的結果爲

4 5 2 6 7 3 1

前序遍歷的結果爲

1 2 4 5 3 6 7

若我們換個思路,按照前序遍歷的思想,只不過我們先遍歷右兒子,那遍歷結果爲

1 3 7 6 2 5 4

怎麼樣,是不是和後序遍歷結果很像?按照先序遍歷的規則,把先遍歷左兒子轉爲先遍歷右兒子,我們就可以得到一個神奇的結論,這種規則情況下的遍歷的操作,得到的結果是後序遍歷的相反的順序。

其實後序遍歷和前序遍歷有着一種鏡像關係,後序遍歷可以有原來樹的鏡像樹(鏡像樹可以看leetcode上的一道題)的前序遍歷的逆序得到

於是,我們可以得到一個與前序和後序遍歷同樣形式的代碼

	List<Integer> lists=new ArrayList<>();
    public List<Integer> backNoRecuErgodic(TreeNode node){
        Stack<TreeNode> stack=new Stack<>();
        while (stack.isEmpty()==false||node!=null){
            if(node!=null){
                stack.push(node);
                lists.add(0,node.val);//頭插正好逆序。後序遍歷和前序遍歷是鏡像問題
                node=node.right;
            }else {
                node=stack.pop();
                node=node.left;
            }
        }
        return lists;
    }

不過需要注意的是,ArrayList底層是使用數組實現的,頭插的效率較低,需要經過數組拷貝,因此插入頭部的複雜度爲O(N)

LinkedList是雙向鏈表,插入頭部的時間複雜度是O(1)於是我們可以用LinkedList來代替ArrayList,這樣效率更高

	List<Integer> lists=new LinkedList<>();
    public List<Integer> backNoRecuErgodic(TreeNode node){
        Stack<TreeNode> stack=new Stack<>();
        while (stack.isEmpty()==false||node!=null){
            if(node!=null){
                stack.push(node);
                lists.offerFirst(node.val);//頭插正好得到逆序。
                node=node.right;//這裏先去遍歷的右兒子
            }else {
                node=stack.pop();
                node=node.left;
            }
        }
        return lists;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章