Leetcode 二叉树专题周

105.从前序与中序遍历序列构造二叉树(图文详解)

Leetcode 总题单:https://blog.csdn.net/m0_46272108/article/details/109269407

本题思想

给出前序遍历和中序遍历
前序遍历的顺序是: 根节点 —— 左子树 —— 右子树
中序遍历的顺序是: 左子树 —— 根节点 —— 右子树
这道题用递归思想(代码中会有详细的解释):

  1. 通过前序遍历 得到根节点(第一个数)
  2. 在中序遍历中找到 根节点
  3. 此时中序遍历的根节点左边就是左子树,右边就是右子树
  4. 继续递归寻找剩下的。

举例 + 图文详解

那么此时:9为左子树,20,15,7为右子树
右子树还需要继续找该右子树的根节点,思路跟刚开始一样,所以是一直递归。

用哈希表记录中序遍历里面每个数的位置。时间复杂度为O(1),如果用遍历的话就是O(n);
剩下就是递归算左右子树。

C++代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    unordered_map<int, int> pos;//用哈希表存一下,可以做到时间复杂度为O(1),如果直接遍历的话是O(n)的。记录中序遍历里面每个数的位置。
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        //把中序遍历里面每个数做一个映射,记录每个数的下标
        for (int i = 0; i < inorder.size(); ++i) {
            pos[inorder[i]] = i;
        }
        //递归建立整棵树,把前序遍历和中序遍历传下去,最开始的区间均是,0到最大值-1,
        return build(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);
    }
    //这个函数是返回当前子树的根节点,前序遍历的左右端点pl, pr, 中序遍历的左右端点il, ir
    TreeNode* build(vector<int>&preorder, vector<int>&inorder, int pl, int pr, int il, int ir) {
        if (pl > pr) return NULL;//如果pl>pr区间没有数,所以为空,返回空
        auto root = new TreeNode(preorder[pl]);//创建一个根节点, 根节点是前序遍历的第一个点
        //k是在中序遍历中根节点的位置。
        int k = pos[root->val];
        //递归算左子树
        root->left = build(preorder, inorder, pl + 1, pl + 1 + k - 1 - il, il, k - 1);
        //递归算右子树
        root->right = build(preorder, inorder, pl + 1 + k - 1 - il + 1, pr, k + 1, ir);
        return root;
    }
};

递归的时候,这些左右子树前、中序遍历区间解释:

左子树的中序遍历区间:[il, k - 1]
右子树的中序遍历区间:[k + 1, ir]

左子树的前序遍历区间:[pl+1,pl + 1 + k - 1 - il]
右子树的前序遍历区间:[pl + 1 + k - 1 - il + 1,pr]

Java代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    //用哈希表存一下,可以做到时间复杂度为O(1),如果直接遍历的话是O(n)的。记录中序遍历里面每个数的位置。
    HashMap<Integer, Integer> pos = new HashMap<Integer, Integer>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        //把中序遍历里面每个数做一个映射,记录每个数的下标
        for (int i = 0; i < inorder.length; i++){
            pos.put(inorder[i], i);
        }
        //递归建立整棵树,把前序遍历和中序遍历传下去,最开始的区间均是,0到最大值-1,
        return build(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
    }
    public TreeNode build(int[] preorder, int[] inorder, int pl, int pr, int il, int ir) {
        //如果pl>pr区间没有数,所以为空,返回空
        if (pl > pr) return null;
        TreeNode root = new TreeNode(preorder[pl]);//创建一个根节点, 根节点是前序遍历的第一个点
        //k是在中序遍历中根节点的位置。
        int k = pos.get(root.val);
        //递归算左子树
        root.left = build(preorder, inorder, pl + 1, pl + 1 + k - 1 - il, il, k - 1);
        //递归算右子树
        root.right = build(preorder, inorder, pl + 1 + k - 1 - il + 1, pr, k + 1, ir);
        return root;
    }

}

106. 从中序与后序遍历序列构造二叉树

Leetcode 总题单:https://blog.csdn.net/m0_46272108/article/details/109269407

本题思想:
给出中序遍历和后序遍历
中序遍历的顺序是: 左子树 —— 根节点 —— 右子树
后序遍历的顺序是: 左子树 —— 右子树 —— 根节点
这道题用递归思想(代码中会有详细的解释):

  1. 通过后序遍历 得到根节点(最后一个数)
  2. 在中序遍历中找到 根节点
  3. 此时中序遍历的根节点左边就是左子树,右边就是右子树
  4. 继续递归寻找剩下的。

详细图文分析可参考:
Leetcode 105.从前序与中序遍历序列构造二叉树(图文详解https://blog.csdn.net/m0_46272108/article/details/109269408

下面给出详细代码:

C++代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    unordered_map<int, int> pos;//用哈希表存一下,可以做到时间复杂度为O(1),如果直接遍历的话是O(n)的。记录中序遍历里面每个数的位置。
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        //把中序遍历里面每个数做一个映射,记录每个数的下标
        for (int i = 0; i < inorder.size(); ++i) {
            pos[inorder[i]] = i;
        }
        //递归建立整棵树,把中序遍历和后序遍历传下去,最开始的区间均是,0到最大值-1,
        return build(inorder, postorder, 0, inorder.size() - 1, 0, postorder.size() - 1);
    }

    TreeNode* build(vector<int>& inorder, vector<int>& postorder, int il, int ir, int pl, int pr) {
        if (pl > pr) return NULL;//如果pl>pr区间没有数,所以为空,返回空
        auto root = new TreeNode(postorder[pr]);//创建一个根节点, 根节点是后序遍历的最后一个点
        int k = pos[root->val];//k是在中序遍历中根节点的位置。
        //递归算左子树
        root->left = build(inorder, postorder, il, k - 1, pl, pl + k - 1 - il);
        //递归算右子树
        root->right = build(inorder, postorder, k + 1, ir, pl + k - 1 - il + 1, pr - 1);
        return root;
    }
};

108.将有序数组转换为二叉搜索树

本题主要思想:
理解二叉搜索树的概念:
二叉搜索树的性质:
二叉搜索树的中序遍历是有序的。
给定一个升序排列的有序数组,即给出该二叉搜索树的中序遍历

高度平衡二叉树是指一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1(其实就是看起来左右比较匀称)

例子:
[-10,-3,0,5,9] (有序数组刚好对应中序遍历)

其实本体的知识点,是基于 线段树建树思想 —— 递归建树

在一个 总节点为 n 的二叉搜索树中,如果为满二叉树,则该树的高度为 [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]

如果不是满二叉树,那么高度一定 ≥ [ l o g 2 ( n + 1 ) ] ≥ [log_2(n+1)] [log2(n+1)]
那么高度为多少呢?
下面就是见证奇迹(证明过程)的时刻:

结论:高度为: [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]

相同的总节点数,非满二叉树的高度 >满二叉树的高度。
则:高度 ≥ [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]

证明:高度 ≤ [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]
数学归纳法:
假设在总结点数小于n时:
如果n为奇数,那么左子树节点数 == 右子树节点数 == n − 1 2 \frac{n-1}{2} 2n1

则左右两边高度相等 == [ l o g 2 ( n − 1 2 + 1 ) ] [log_2(\frac{n-1}{2}+1)] [log2(2n1+1)]

则总高度为: [ l o g 2 ( n − 1 2 + 1 ) ] + 1 ( 根 节 点 ) [log_2(\frac{n-1}{2}+1)] + 1(根节点) [log2(2n1+1)]+1()

那么就是判断: [ l o g 2 ( n − 1 2 + 1 ) ] + 1 ( 根 节 点 ) [log_2(\frac{n-1}{2}+1)] + 1(根节点) [log2(2n1+1)]+1() [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]
的大小。

l o g 2 ( n − 1 2 + 1 ) + 1 log_2(\frac{n-1}{2}+1) + 1 log2(2n1+1)+1 = l o g 2 [ ( n + 1 2 ) × 2 ] log_2[(\frac{n+1}{2}) × 2] log2[(2n+1)×2] = [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]
相等,则成立。

如果n为偶数且不为0,那么因为要保持平衡所以,假设左子树的节点数比右子树的节点数少1,那么左子树的节点数为: n 2 − 1 \frac{n}{2} - 1 2n1,右子树的节点数为: n 2 \frac{n}{2} 2n

则需要证明: [ l o g 2 ( n 2 + 1 ) ] ≤ [ l o g 2 ( n + 1 ) ] [log_2(\frac{n}{2}+1)] ≤ [log_2(n+1)] [log2(2n+1)][log2(n+1)]

化简: l o g 2 ( n 2 + 1 ) = = l o g 2 ( n + 2 ) > l o g 2 ( n + 1 ) log_2(\frac{n}{2}+1) == log_2(n+2)> log_2(n+1) log2(2n+1)==log2(n+2)log2(n+1)

但是: [ l o g 2 ( n + 2 ) ] = = [ l o g 2 ( n + 1 ) ] [log_2(n+2)] == [log_2(n+1)] [log2(n+2)]==[log2(n+1)]

证明: [ l o g 2 ( n + 2 ) ] = = [ l o g 2 ( n + 1 ) ] [log_2(n+2)] == [log_2(n+1)] [log2(n+2)]==[log2(n+1)]

只需证明如果这个 l o g 2 ( n + 2 ) log_2(n+2) log2(n+2)>k 且 l o g 2 ( n + 1 ) < k log_2(n+1) < k log2(n+1)<k(其中k为整数)不存在,则上式成立。

两边取幂:
2 l o g 2 ( n + 2 ) > 2 k > 2 l o g 2 ( n + 1 ) 2^{log_2(n+2)} > 2^k >2^{log_2(n+1)} 2log2(n+2)>2k>2log2(n+1)

即: ( n + 2 ) > 2 k > ( n + 1 ) (n+2) > 2^k >(n+1) (n+2)>2k>(n+1) (当n≠0且n为偶数时,显然不成立)

所以:最终高度 ≤ [ l o g 2 ( n + 1 ) ] ≤ [log_2(n+1)] [log2(n+1)] (得证)

综上所述: [ l o g 2 ( n + 1 ) ] ≤ [log_2(n+1)] ≤ [log2(n+1)] 最终高度 ≤ [ l o g 2 ( n + 1 ) ] ≤ [log_2(n+1)] [log2(n+1)]

由夹逼准则得:最终高度 == [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]

C++代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return build(nums, 0, nums.length - 1);
    }
    TreeNode build(int[] nums, int l, int r) {
        if (l > r) return null;
        int mid = l + r >> 1;//取根节点
        TreeNode root = new TreeNode(nums[mid]);
        root.left = build(nums, l, mid - 1);//递归建立左子树
        root.right = build(nums, mid + 1, r);//递归建立右子树
        return root;
    }
};

Java代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return build(nums, 0, nums.length - 1);
    }
    TreeNode build(int[] nums, int l, int r) {
        if (l > r) return null;
        int mid = l + r >> 1;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = build(nums, l, mid - 1);
        root.right = build(nums, mid + 1, r);
        return root;
    }
};

110.平衡二叉树

题意:判断是否是高度平衡二叉树?
题解:

只需要判断:
∣ l h − r h ∣ ≤ 1 |lh-rh|≤1 lhrh1是否成立,如果成立就是高度平衡二叉树,反之则不是。
总高度是: m a x ( l h , r h ) + 1 ( r o o t ) max(lh, rh) + 1(root) max(lh,rh)+1(root)

C++代码

/**
 * 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:
    bool ans;//定义一个全局变量
    bool isBalanced(TreeNode* root) {
        ans = true;
        dfs(root);
        return ans;
    }
    int dfs (TreeNode* root) {
        if (!root) return 0;//如果根节点为空,树高度为0
        //递归
        int lh = dfs(root->left);
        int rh = dfs(root->right);
        //如果两子树的高度差大于1,则不是高度平衡的二叉树
        if (abs(lh - rh) > 1) {
            ans = false;
        }
        return max(lh, rh) + 1;//返回左子树和右子树高度的最大值 + 根节点那一层(1) == 最终树的高度
    }
};

Java代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    boolean ans;
    public boolean isBalanced(TreeNode root) {
        ans = true;
        dfs(root);
        return ans;
    }
    int dfs (TreeNode root) {
        if (root == null) return 0;//如果根节点为空,树高度为0
        //递归
        int lh = dfs(root.left);
        int rh = dfs(root.right);
        //如果两子树的高度差大于1,则不是高度平衡的二叉树
        if (Math.abs(lh - rh) > 1) {
            ans = false;
        }
        //返回左子树和右子树高度的最大值 + 根节点那一层(1) == 最终树的高度
        return Math.max(lh, rh) + 1;
    }
}
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页