劍指Offer27:二叉樹轉雙向鏈表問題

二叉樹轉雙向鏈表問題

問題描述

* 問題描述
 *      輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。“要求不能創建任何新的結點”,只能調整樹中結點指針的指向。例如輸入前序遍歷爲
 *      10、6、4、8、14、12、16的二叉搜索樹,其輸出的轉換後排序雙向鏈表爲:4、6、8、10、12、14、16

問題分析

* 問題分析:
 *      在二叉樹中,每個結點都有兩個指向子結點的指針。在雙向鏈表中,每個結點也有兩個指針,它們分別是指向前一個結點和後一個結點。由於這兩個結點的
 *      結構相似,同時二叉搜索樹也是一種排序的數據結構,因此在理論上有可能實現二叉搜索樹和排序的雙向鏈表的轉換。在二叉搜索樹中,左子結點的值總是小於父結點
 *      的值,有子結點的值總是大於父結點的值。因此在轉換成排序的雙向鏈表時,原先指向左子結點的指針調整爲鏈表中指向前一個結點的指針,原先指向右子結點
 *      的指針調整爲鏈表中指向後一個結點指針。
 *      由於要求轉換後的鏈表是排序好的,因此可以用中序遍歷來操作,對於二叉查找樹而言,其中序遍歷序列就是從小到大排序的。當遍歷到根結點的時候,我們把
 *      樹看成三個部分:值爲10的根結點,根結點值爲6的左子樹,根結點值爲14的右子樹。根據排序鏈表的定義,值爲10的結點將和它的左子樹的最大一個結點(值爲8的結點)
 *      鏈接起來,同時它將和右子樹最小的結點(即值爲12的結點)鏈接起來。
public class Code027 {
    public static void main(String[] args){
        BinaryTreeNode node1=new BinaryTreeNode(10);
        BinaryTreeNode node2=new BinaryTreeNode(6);
        BinaryTreeNode node3=new BinaryTreeNode(14);
        BinaryTreeNode node4=new BinaryTreeNode(4);
        BinaryTreeNode node5=new BinaryTreeNode(8);
        BinaryTreeNode node6=new BinaryTreeNode(12);
        BinaryTreeNode node7=new BinaryTreeNode(16);
        BinaryTreeNode pRoot1=node1;
        node1.pLeft=node2;node1.pRight=node3;
        node2.pLeft=node4;node2.pRight=node5;
        node3.pLeft=node6;node3.pRight=node7;

//        BinaryTreeNode pNowRoot=Convert(pRoot1);
        BinaryTreeNode pNowRoot=ConvertBSTToBiList(pRoot1);
        while (pNowRoot!=null){
            System.out.print(pNowRoot.value+" ");
            pNowRoot=pNowRoot.pRight;
        }
    }

    //定義雙向鏈表的左邊頭結點和右邊頭結點,初始化爲null
    static BinaryTreeNode leftHead=null;
    static BinaryTreeNode rightHead=null;
    private  static BinaryTreeNode Convert(BinaryTreeNode pRootOfTree){
        //遞歸調用葉子結點的左右結點返回null
        if(pRootOfTree==null){
            return null;
        }
        //第一次運行時,它會使最左邊的葉子結點爲鏈表的第一個節點
        Convert(pRootOfTree.pLeft);
        if(rightHead==null){
            leftHead=pRootOfTree;
            rightHead=pRootOfTree;
        }else {
            //把根結點插入到雙向鏈表右邊,rightHead向後移動
            rightHead.pRight=pRootOfTree;
            pRootOfTree.pLeft=rightHead;
            rightHead=pRootOfTree;
        }
        //把右葉子結點也插入到雙向鏈表(rightHead已確定,直接插入)
        Convert(pRootOfTree.pRight);
        //返回左邊頭結點,也可以返回右邊頭結點
        return leftHead;
    }
    //非遞歸法:藉助棧來存儲中序遍歷的結果順序
    private static BinaryTreeNode ConvertBSTToBiList(BinaryTreeNode pRoot){
        if(pRoot==null){
            return null;
        }
        //定義棧用於存儲中序遍歷的結點順序
        Stack<BinaryTreeNode> stack=new Stack<>();
        BinaryTreeNode p=pRoot;
        BinaryTreeNode pre=null;//用於保存中序遍歷的上一個結點
        boolean isFirst=true;
        while (p!=null|| !stack.isEmpty()){
            while (p!=null){
                stack.push(p);
                p=p.pLeft;
            }
            //找到左子樹的最左葉子結點,即爲中序遍歷的第一個結點
            p=stack.pop();
            if(isFirst){
                pRoot=p;//將中序遍歷列中的第一個節點記爲pRoot
                pre=pRoot;
                isFirst=false;
            }else {
                pre.pRight=p;
                p.pLeft=pre;
                pre=p;
            }
            p=p.pRight;
        }
        return pRoot;
    }

    //定義二叉樹的結點類
    private static class BinaryTreeNode{
        int value;
        BinaryTreeNode pLeft;
        BinaryTreeNode pRight;
        public BinaryTreeNode() {
        }
        public BinaryTreeNode(int value) {
            this.value = value;
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章