劍指26.二叉搜尋樹與雙向連結串列
阿新 • • 發佈:2020-08-15
題目描述
輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。要求不能建立任何新的結點,只能調整樹中結點指標的指向。思路
題目要求轉換成排序後的雙向連結串列,根據二叉搜尋樹的性質,值的大小:左子樹<根節點<右子樹。因此遍歷方式為 中序遍歷,中序遍歷有 遞迴實現 和 非遞迴實現。 思路1:中序遍歷二叉樹,然後用一個ArrayList儲存遍歷的結果,這樣節點就按順序儲存了,最後修改前後指標。 (最常規的思路,但使用額外的資料結構) 思路2:把樹分成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; } }