1. 程式人生 > 其它 >通過feign上傳檔案[原]

通過feign上傳檔案[原]

前言


  二叉搜尋樹的中序遍歷是升序序列,題目給定的陣列是按照升序排序的有序陣列,因此可以確保陣列是二叉搜尋樹的中序遍歷序列。

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)。