Linux防火牆Firewall基礎詳細解讀
前言
二叉搜尋樹的中序遍歷是升序序列,題目給定的陣列是按照升序排序的有序陣列,因此可以確保陣列是二叉搜尋樹的中序遍歷序列。
1、給定二叉搜尋樹的中序遍歷,是否可以唯一地確定二叉搜尋樹?
答案是否定的。如果沒有要求二叉搜尋樹的高度平衡,則任何一個數字都可以作為二叉搜尋樹的root節點,因此可能的二叉搜尋樹有多個。
2、如果增加一個限制條件,即要求二叉搜尋樹的高度平衡,是否可以唯一地確定二叉搜尋樹?答案仍然是否定的。
直觀地看,我們可以選擇中間數字作為二叉搜尋樹的根節點,這樣分給左右子樹的數字個數相同或只相差 11,可以使得樹保持平衡。
如果陣列長度是奇數,則根節點的選擇是唯一的,
如果陣列長度是偶數
確定平衡二叉搜尋樹的根節點之後,其餘的數字分別位於平衡二叉搜尋樹的左子樹和右子樹中,左子樹和右子樹分別也是平衡二叉搜尋樹,因此可以通過遞迴的方式建立平衡二叉搜尋樹。
當然,這只是我們直觀的想法,為什麼這麼建樹一定能保證是「平衡」的呢?這裡可以參考「leetcode 1382. 將二叉搜尋樹變平衡」,這兩道題的構造方法完全相同,這種方法是正確的,1382 題解中給出了這個方法的正確性證明:1382 官方題解,感興趣的同學可以戳進去參考。
遞迴的基準情形是平衡二叉搜尋樹不包含任何數字,此時平衡二叉搜尋樹為空。
在給定中序遍歷序列陣列的情況下,每一個子樹中的數字在陣列中一定是連續的,因此可以通過陣列下標範圍確定子樹包含的數字,下標範圍記為 [left,right]。對於整個中序遍歷序列,下標範圍從 left=0 到 right=nums.length−1。當 left>right 時,平衡二叉搜尋樹為空。
以下三種方法中,方法一總是選擇中間位置左邊的數字作為根節點,方法二總是選擇中間位置右邊的數字作為根節點,方法三是方法一和方法二的結合,選擇任意一箇中間位置數字作為根節點。
1、方法一:中序遍歷,總是選擇中間位置左邊的數字作為根節點
選擇中間位置左邊的數字作為根節點,則根節點的下標為mid=(left+right)/2,此處的除法為整數除法。
class Solution { public TreeNode sortedArrayToBST(int[] nums) { return helper(nums, 0, nums.length - 1); } public TreeNode helper(int[] nums, int left, int right) { if (left > right) { return null; } // 總是選擇中間位置左邊的數字作為根節點 int mid = (left + right) / 2; TreeNode root = new TreeNode(nums[mid]); root.left = helper(nums, left, mid - 1); root.right = helper(nums, mid + 1, right); return root; } }
複雜度分析:
時間複雜度:O(n),其中 n 是陣列的長度。每個數字只訪問一次。
空間複雜度:O(log n),其中 n 是陣列的長度。空間複雜度不考慮返回值,因此空間複雜度主要取決於遞迴棧的深度,遞迴棧的深度是O(logn)。
2、方法二:中序遍歷,總是選擇中間位置右邊的數字作為根節點
選擇中間位置右邊的數字作為根節點,則根節點的下標為 mid=(left+right+1)/2,此處的除法為整數除法。
class Solution { public TreeNode sortedArrayToBST(int[] nums) { return helper(nums, 0, nums.length - 1); } public TreeNode helper(int[] nums, int left, int right) { if (left > right) { return null; } // 總是選擇中間位置右邊的數字作為根節點 int mid = (left + right + 1) / 2; TreeNode root = new TreeNode(nums[mid]); root.left = helper(nums, left, mid - 1); root.right = helper(nums, mid + 1, right); return root; } }
複雜度分析:
時間複雜度:O(n),其中 n 是陣列的長度。每個數字只訪問一次。
空間複雜度:O(log n),其中 n 是陣列的長度。空間複雜度不考慮返回值,因此空間複雜度主要取決於遞迴棧的深度,遞迴棧的深度是 O(log n)。
3、方法三:中序遍歷,選擇任意一箇中間位置數字作為根節點
選擇任意一箇中間位置數字作為根節點,則根節點的下標為 mid=(left+right)/2 和 mid=(left+right+1)/2 兩者中隨機選擇一個,此處的除法為整數除法。
class Solution { Random rand = new Random(); public TreeNode sortedArrayToBST(int[] nums) { return helper(nums, 0, nums.length - 1); } public TreeNode helper(int[] nums, int left, int right) { if (left > right) { return null; } // 選擇任意一箇中間位置數字作為根節點 int mid = (left + right + rand.nextInt(2)) / 2; TreeNode root = new TreeNode(nums[mid]); root.left = helper(nums, left, mid - 1); root.right = helper(nums, mid + 1, right); return root; } }
複雜度分析
時間複雜度:O(n),其中 n 是陣列的長度。每個數字只訪問一次。
空間複雜度:O(log n),其中 n 是陣列的長度。空間複雜度不考慮返回值,因此空間複雜度主要取決於遞迴棧的深度,遞迴棧的深度是O(log n)。