通过选择中间元素将排序数组转换为高度平衡的二叉搜索树 - 为什么它有效?

问题描述 投票:0回答:1

我正在做一个练习,将排序数组转换为二叉搜索树,其中树中的每个节点都有其高度最多相差 1 的子节点。

一个简单的解决方案是挑选中间元素作为根。中间为 (0+size) 整数除以 2,然后分别对剩余数组的左半部分、右半部分进行递归调用,生成左子元素、右子元素。

并且可以通过一些测试来检查该解决方案是否有效。 (最后附有 C++ 示例代码。)

但是,我无法证明为什么这个解决方案有效。

为什么?


我尝试归纳推理:对于任何高度K>3的节点,假设所有高度k

由于最大尺寸严格随高度增加,(2) 表示如果高度差 ≥2,则尺寸差 ≥2。

很容易证明任何具有高度 K 的节点都与归纳假设高度平衡。但我在证明 K 的第 (2) 部分时遇到困难。

下面是尺码表,还有基本情况。

高度 最小尺寸 最大尺寸
0 0 0
1 1 1
2 2 3
3 4 7
4 7 15

最小和最大尺寸具有以下递推关系:

  • 高度 K 个节点的最小尺寸 = 1 + 高度 K−1 个节点的最小尺寸 + 高度 K−2 个节点的最小尺寸 = Fib(n)+n *

  • 高度 K 节点的最大尺寸 = K−1 节点的最大尺寸 x 2 + 1 =2^(n-1) − 1

* 最后一个等号是“可能”

Fib(n)+n 似乎大于 2^(n-1),尤其是维基百科页面上显示的 舍入关系。但接下来我就需要证明这相当涉及不平等,通过舍入关系等。

我缺少一些简单的东西吗?

有没有更简单的方法来完全证明解决方案的正确性?


    struct TreeNode {
        int val;
        TreeNode* left;
        TreeNode* right;
        TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    };
    void deleteTree(TreeNode* root) {
        if (root) {     
            if (root->left) {
                deleteTree(root->left);
            }
            if (root->right) {
                deleteTree(root->right);
            }
            delete root;
        }
    }

// one can print the tree to Graphviz script for example for inspection; my code for that has a lot of other things together so I'll skip that

// technically meant to be a different file below

#include<vector>
#include<iostream>

#define DEBUG

    TreeNode* sortedArrayToBST(std::vector<int>& nums, std::size_t i, std::size_t j) {     
#ifdef DEBUG
        std::cout << "i = " << i << "\tj = " << j << '\n';
#endif // DEBUG
        if (i >= j) {
            return nullptr;
        }
        else {
            std::size_t k{ (i + j) / 2 };
#ifdef DEBUG
            std::cout << "\tk = " << k << '\n';
#endif // DEBUG
            TreeNode* node{ new TreeNode(nums[k]) };
            node->left = sortedArrayToBST(nums, i, k);
            node->right = sortedArrayToBST(nums, k+1, j);
            return node;
        }
    }

    TreeNode* sortedArrayToBST(std::vector<int>& nums) {
        return sortedArrayToBST(nums, 0, nums.size());
    }

int main() {
    std::vector<int> q1{ -4, -3, 0, 1, 2 };
    TreeNode* node{ sortedArrayToBST(q1) };
    deleteTree(node);
}
algorithm data-structures binary-tree binary-search-tree
1个回答
0
投票

让我们先考虑简单的情况。

根据数组长度 n 将树的叶子的最大深度写为 max(n),最小值为 min(n)。让我们定义空树的高度 为零,具有一个元素的树的高度为 1,依此类推(这又是 1 比传统的树高定义要好,但它使事情变得更清晰,所以。)

引理: (∀k∈ℕ)(max(2ᵏ - 1) = min(2ᵏ - 1) = k)

基本情况:k = 0给出n = 0,这是空树,并且max(0) = min(0) = 0.

归纳:给定k∈ℕ-{0}。考虑长度为 n = 2ᵏ - 1 的数组。这 算法以一个元素作为中心节点,然后在每个子数组上递归 长度 (2ᵏ - 2) / 2 = 2ᵏ⁻1 - 1。 根据归纳假设, max(2ᵏ⁻1 - 1) = min(2ᵏ⁻1 - 1) = k - 1。所以 *max(2ᵏ - 1) = min(2ᵏ - 1) = k)。

现在考虑不太圆的情况。

定理: (∀nεℕ)(max(n) = ceil(log2(n + 1)) 和 min(n) = Floor(log2(n + 1)))

基本情况:和以前一样。

归纳:给定n∈ℕ-{0}。让 k = 下限(log2(n + 1)).

如果 k = log2(n + 1),则 n = 2ᵏ - 1,因此根据引理,我们有 max(n) = min(n) = log2(n + 1) = ceil(log2(n + 1)) = Floor(log2(n + 1)) 我们就完成了。

否则, n ≥ 2ᵏn ≤ 2ᵏ⁺¹(k + 1) - 2 (否则我们可以得到 与 k) 的定义直接矛盾。

对长度为 n 的数组进行操作的算法将取一个元素 长度为 floor((n - 1)/ 2)ceil((n - 1) 的子节点的根和递归 / 2),我们将其长度分别定义为 lr

注意 maxmin 不减,因此 min(l) ≤ min(r)max(l) ≤ 最大(r)。我们只需要检查 min(l) 并且 最大(r)

l = 下限((n - 1) / 2) ≥ (n - 2) / 2 = n / 2 - 1 所以 min(l) ≥ 楼层(log2(n / 2)), 我们知道 n ≥ 2ᵏ 所以我们有 min(l) ≥ 下限(log2(2ᵏ / 2)) = k - 1.

r = ceil((n - 1) / 2) ≤ n / 2 所以 max(r) ≤ 下限(log2(n / 2 + 1)),并且 我们有 n ≤ 2ᵏ⁺1 - 2 所以 max(r) ≤ 下限(log2((2ᵏ⁺1 - 2)/2 + 1)) = 楼层(log2((2ᵏ⁺1)/2)) = k.

因此我们有 min(n) >= kmax(n) <= k + 1,

当然我们知道 min(n) < max(n) 因为仅在平衡情况下 min(n) = max(n) 我们已经排除了上面的情况,所以 min(n) = kmax(n) = k + 1

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.