leetcode_1382 将二叉搜索树变平衡树

1. 题意

给你一颗二叉搜索树,让你返回一颗平衡二叉搜索树(avl树)。

2. 题解

avl旋转的做法后面再补吧!

也有可能不补了~

2.1 贪心构造

我们可以先将二叉搜索树序列化,

将它的中序遍历序列放到一个数组v中。

接下来就是策略了,

对于一个有序序列 v [ l , r ] v[l,r] v[l,r]

我们每次构造平衡二叉树选择根的时候都选择数组的中间位置

m i d : = ⌊ l + r 2 ⌋ r o o t . v a l : = v [ m i d ] \begin{align*} mid &:=\ \lfloor \frac{l+r}{2}\rfloor\\ root.val & := v[mid] \end{align*} midroot.val:= ⌊2l+r⌋:=v[mid]

再分别递归的构造左右子树。

具体的意思还是看代码,其实就是二分地构造平衡二叉搜索树。

还是看代码吧

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    
    void inorder_output(TreeNode* rt, vector<int> &v) {
        stack<TreeNode *> st;

        TreeNode *root = rt;


        while ( !st.empty() || root != NULL ) {

            if ( root ) {
                st.push(root);
                root = root->left;
                continue;
            }

            if ( !st.empty()) {

                TreeNode *tp = st.top();
                st.pop();
                v.push_back( tp->val );

                if ( tp->right)
                    root = tp->right;    

           }
        }
    }
    TreeNode* build_AVL_by_inorder(vector<int> &v, int l, int r) {
        if ( l > r)
            return NULL;

        int mid = l + ((r - l) >> 1);

        TreeNode *rt = new TreeNode(v[mid]);
        rt->left = build_AVL_by_inorder( v, l, mid - 1);
        rt->right = build_AVL_by_inorder(  v, mid + 1, r);

        return rt;
    }

    TreeNode* balanceBST(TreeNode* root) {
        
        vector<int> v;
        inorder_output( root, v);

        TreeNode *ret;
        int l = 0;
        int r = static_cast<int>(v.size() - 1);

        return build_AVL_by_inorder( v, l, r);
    }
};

这个策略的正确性证明:

我们用数学归纳法来证明,

我们令k表示树中的节点的个数。

显然 k = 1 k=1 k=1或者 k = 2 k=2 k=2时,这样构造的二叉树平衡。

我们现在假设 k = n k=n k=n时这个策略构造的二叉树平衡,我们要

推出 k = n + 1 k=n+1 k=n+1时这个策略构造的二叉树也平衡。

事实上我们并只需要关心左右两子树的高度差是否平衡就可以了。

我们用 l n ln ln各 r n rn rn分别表示由这种策略构造的左子树和右子树

分成两种情况:

当 n n n为偶数时,对于节点数为 n + 1 n+1 n+1构造的树 l n = r n = n 2 ln=rn=\frac{n}{2} ln=rn=2n,

由于是同一种策略同样的节点数,因此树高肯定一样,

因此 n n n为偶数时树平衡。

当 n n n为奇数时,对于节点数为 n + 1 n+1 n+1构造的树 l n = n − 1 2 , r n = n + 1 2 ln=\frac{n-1}{2},rn=\frac{n+1}{2} ln=2n−1,rn=2n+1。

我们令 d k d_k dk表示,以上面这种策略构造的二叉树的深度。

现在我们只需要证明对于

d k + 1 − d k ≤ 1 d_{k+1}-d_{k} \le 1 dk+1−dk≤1

上面的树就平衡了,整个证明就成立了。

首先我们容易得到

d k = ⌊ log ⁡ 2 k ⌋ + 1 d_k = \lfloor \log_2 k\rfloor +1 dk=⌊log2k⌋+1

实际上并不容易得到,我也不知道怎么得到的真接看的答案。

应该是一个个试,最后得出规律的。

首先, 我们可以得出,当 k ≥ 1 k \ge 1 k≥1时

log ⁡ 2 ( k + 1 ) − log ⁡ 2 k ≤ 1 \log_2 (k+1) - \log_2k \le 1 log2(k+1)−log2k≤1

因为
log ⁡ 2 ( k + 1 ) − log ⁡ 2 k = log ⁡ 2 k + 1 k = log ⁡ 2 ( 1 + 1 k ) k ≥ 1 , 1 + 1 k ≤ 2 log ⁡ 2 ( 1 + 1 k ) ≤ 1 \log_2{(k+1)}-\log_2k = \log_2{\frac{k+1}{k}}\\=\log_2({1+\frac{1}{k}})\\ k\ge1,1+\frac{1}{k} \le2\\ \log_2{(1+\frac{1}{k})} \le1 log2(k+1)−log2k=log2kk+1=log2(1+k1)k≥1,1+k1≤2log2(1+k1)≤1

现在我们要由这个结论来推我们要的结果,有两种方式。

先来说反证法

我们令 a = log ⁡ 2 k a=\log_2 k a=log2k, b = log ⁡ 2 ( k + 1 ) b=\log_2{(k+1)} b=log2(k+1),

我们假设存在某个 k ≥ 1 k\ge1 k≥1,使得

b − a ≥ 2 b-a \ge 2 b−a≥2成立;

那么必然存在某个整数 n n n使得 a < n a<n a<n且 b ≥ n + 2 b\ge n+2 b≥n+2成立,

因此 b − a > ( n + 2 ) − n = 2 b-a >(n+2)-n=2 b−a>(n+2)−n=2即 b − a > 2 b-a>2 b−a>2成立,但由上面我们

的结论知道 b − a = log ⁡ 2 ( k + 1 ) − log ⁡ 2 k ≤ 1 b-a=\log_2(k+1)-\log_2k \le 1 b−a=log2(k+1)−log2k≤1。

因此产生了矛盾,因此假设不成立,

即对所有的 k ≥ 1 k\ge1 k≥1

b − a = ⌊ log ⁡ 2 ( k + 1 ) ⌋ − ⌊ log ⁡ 2 k ⌋ ≤ 1 b-a=\lfloor \log_2{(k+1)}\rfloor -\lfloor \log_2{k}\rfloor \le1 b−a=⌊log2(k+1)⌋−⌊log2k⌋≤1

成立。

下面再来说正向证明:

我们令 k = 2 r + b , 0 ≤ b < 2 r , 2 r ≤ k < 2 r + 1 k=2^r+b, 0\le b < 2^r, 2^r \le k < 2^{r+1} k=2r+b,0≤b<2r,2r≤k<2r+1,即 r = ⌊ log ⁡ 2 k ⌋ r = \lfloor \log_2 k\rfloor r=⌊log2k⌋,

我们容易得到: k + 1 ∈ ( 2 r , 2 r + 1 ] k+1 \in (2^r,2^{r+1}] k+1∈(2r,2r+1] ,

因此 r + 1 = ⌈ log ⁡ 2 ( k + 1 ) ⌉ r+1 = \lceil \log_2{(k+1)}\rceil r+1=⌈log2(k+1)⌉,

我们要证的式子实际上就是

⌊ log ⁡ 2 ( k + 1 ) ⌋ − ⌊ log ⁡ 2 k ⌋ ≤ 1 ⇔ ⌊ log ⁡ 2 ( k + 1 ) ⌋ ≤ r + 1 \lfloor \log_2{(k+1)}\rfloor -\lfloor \log_2{k}\rfloor \le1 \Leftrightarrow \\ \lfloor \log_2{(k+1)}\rfloor \le r+1 ⌊log2(k+1)⌋−⌊log2k⌋≤1⇔⌊log2(k+1)⌋≤r+1

我们将 r + 1 r+1 r+1替换成 ⌈ log ⁡ 2 ( k + 1 ) ⌉ \lceil \log_2{(k+1)}\rceil ⌈log2(k+1)⌉得

⌊ log ⁡ 2 ( k + 1 ) ⌋ ≤ ⌈ log ⁡ 2 ( k + 1 ) ⌉ \lfloor \log_2{(k+1)} \rfloor \le \lceil \log_2{(k+1)} \rceil ⌊log2(k+1)⌋≤⌈log2(k+1)⌉

这个式子显然成立,因此证明成立。

同样我们可以由 k + 1 ∈ ( 2 r , 2 r + 1 ] k+1 \in(2^r,2^r+1] k+1∈(2r,2r+1], ⌊ log ⁡ 2 ( k + 1 ) ⌋ \lfloor \log_2{(k+1)}\rfloor ⌊log2(k+1)⌋必然

只能为 r r r或者 r + 1 r+1 r+1,从而得到

⌊ log ⁡ 2 ( k + 1 ) ⌋ − ⌊ log ⁡ 2 k ⌋ \lfloor \log_2{(k+1)}\rfloor -\lfloor \log_2{k}\rfloor \ ⌊log2(k+1)⌋−⌊log2k⌋

只能为 0 0 0或者 1 1 1得到证明。

3. 参考

leetcode

相关推荐
greentea_20133 小时前
Codeforces Round 173 B. Digits(2043)
c++·算法
m0_743106464 小时前
LOBE-GS:分块&致密化效率提升
人工智能·算法·计算机视觉·3d·几何学
徐子童4 小时前
优选算法---字符串
java·算法·字符串·笔试·高精度相乘
西瓜啵啵奶茶4 小时前
LeetCode 热题 100 : 普通数组
算法
jikiecui4 小时前
信奥崔老师:C++ 程序设计入门
算法
Q741_1475 小时前
C++ 位运算 高频面试考点 力扣 面试题 17.19. 消失的两个数字 题解 每日一题
c++·算法·leetcode·面试·位运算
Jacob00005 小时前
[Decision Tree] H(D) & IG & IGR
算法·面试
vadvascascass5 小时前
平滑加权轮询负载均衡的底层逻辑
java·算法·负载均衡
CoovallyAIHub5 小时前
Transformer作者开源进化计算新框架,样本效率暴增数十倍!
深度学习·算法·计算机视觉