1. 程式人生 > 實用技巧 >劍指26.二叉搜尋樹與雙向連結串列

劍指26.二叉搜尋樹與雙向連結串列

題目描述

輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。要求不能建立任何新的結點,只能調整樹中結點指標的指向。

思路

題目要求轉換成排序後的雙向連結串列,根據二叉搜尋樹的性質,值的大小:左子樹<根節點<右子樹。因此遍歷方式為 中序遍歷,中序遍歷有 遞迴實現非遞迴實現 思路1:中序遍歷二叉樹,然後用一個ArrayList儲存遍歷的結果,這樣節點就按順序儲存了,最後修改前後指標。 (最常規的思路,但使用額外的資料結構) 思路2:把樹分成3部分:根節點,左子樹和右子樹。然後把左子樹中最大的節點、根節點、右子樹中最小的節點連結起來。至於左子樹和右子樹內部的節點如何連結成連結串列,可以遞迴
解決。 (優化版本,劍指書上的思路)
思路3:二叉搜尋樹的中序遍歷就是有序序列,因此遍歷時將當前節點與前一個節點進行連線即可。定義全域性變數記錄當前連結串列的末尾節點。 (最優解,中序遍歷的精髓吶!~)

解法1(對應思路1)

/**
 public class TreeNode {
 int val = 0;
 TreeNode left = null;
 TreeNode right = null;

 public TreeNode(int val) {
 this.val = val;

 }

 }
 */
import java.util.ArrayList;
import java.util.Stack;
public class Solution { public TreeNode Convert(TreeNode pRootOfTree) { if (pRootOfTree == null) return null; ArrayList<TreeNode> list = new ArrayList<TreeNode>(); InOrder(pRootOfTree,list); for (int i = 0; i < list.size() - 1; i++){ list.get(i).right
= list.get(i+1); list.get(i+1).left = list.get(i); } return list.get(0); } /* * 遞迴實現中序遍歷 * 先一直向左遍歷,直到為空時返回並且列印最左邊的節點。 */ /*private void InOrder(TreeNode pRootOfTree,ArrayList<TreeNode> list){ if (pRootOfTree == null) return; InOrder(pRootOfTree.left,list); list.add(pRootOfTree); // list儲存的是物件的引用或地址 InOrder(pRootOfTree.right,list); }*/ // 非遞迴實現中序遍歷 private void InOrder(TreeNode pRootOfTree,ArrayList<TreeNode> list){ Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode cur = pRootOfTree; while (cur != null || !stack.isEmpty()){ while (cur != null){ stack.push(cur); cur = cur.left; } if (!stack.isEmpty()){ cur = stack.pop(); list.add(cur); cur = cur.right; } } } }

☆解法2(對應思路2)

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }
}
*/
public class Solution {
    /*
     * ----------解法2 遞迴----------
     * 已知函式返回的是轉換好的雙向連結串列頭結點
     * 左子樹處理完後與根結點連線
     * 右子樹處理,也與根結點連線
     * 最後返回頭結點
     */
    public TreeNode Convert(TreeNode root) {
        if (root == null)
            return null;
        // 1.將左子樹構造成雙鏈表,並返回連結串列頭節點
        TreeNode left = Convert(root.left);
        TreeNode p = left;
        if (left != null){
            // 2.定位至左子樹雙鏈表最後一個節點
            while (p.right != null)
                p = p.right;
            // 3.將當前root追加到左子樹連結串列
            p.right = root;
            root.left = p;
        }
        // 4.將右子樹構造成雙鏈表,並返回連結串列頭節點
        TreeNode right = Convert(root.right);
        // 5.如果右子樹連結串列不為空的話,將該連結串列追加到root節點之後
        if (right != null){
            root.right = right;
            right.left = root;
        }
        return left == null ? root : left;  // 返回頭節點
    }
}

☆☆☆解法3(對應思路3)

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
import java.util.Stack;
public class Solution {
    /*
     *----------- 遞迴版本 -----------
     */
    private TreeNode pLast = null; // 記錄當前連結串列的末尾節點。
    public TreeNode Convert(TreeNode root) {
        if (root == null)
            return null;
        // 如果左子樹為空,那麼根節點root為雙向連結串列的頭節點
        TreeNode head = Convert(root.left);
        if (head == null)
            head = root;

        // 連線當前節點root和當前連結串列的尾節點pLast
        root.left = pLast;
        if (pLast != null)
            pLast.right = root;
        pLast = root;

        Convert(root.right);
        return head;
    }
    /*
     * ------------非遞迴版本-----------
     */
    public TreeNode Convert(TreeNode root) {
        if (root == null)
            return null;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode cur = root;
        TreeNode pre = null;  // 儲存中序遍歷序列的上一節點
        boolean isFirst = true;
        while (cur != null || !stack.isEmpty()){
            while (cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            if (isFirst){
                root = cur; // 將中序遍歷序列中的第一個節點即為root
                pre = cur;
                isFirst = false;
            }else{
                pre.right = cur;
                cur.left = pre;
                pre = cur;
            }
            cur = cur.right;
        }
        return root;
    }
}