leetcode——"树"题目集合

96.不同的二叉搜索树

题目:
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?

示例:

输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

思路:
这其实是一道数学题。假设n个节点组成的二叉树总数为sum[n],而f[n]表示当n为根节点时BST的数目,
所以:
sum[n] = f[1]+f[2]+f[3]…+f[n]
当有n个节点时,i为根节点时,i左边有[1,2,…,i-1]个元素,右边有[i+1,i+2,…n]个元素,所以f[i] = sum[i-1]*sum[n-i],所以:
sum[n] = sum[0] * sum[n-1]+ sum[1] * sum[n-2] + … + sum[n-1] * sum[0]
即:
sum[2] = sum[0]*sum[1]+sum[1]*sum[0];
sum[3] = sum[0]*sum[2]+sum[1]*sum[1]+sum[2]*sum[0]

代码:

class Solution {
    public int numTrees(int n) {
        int[] sum = new int[n+1];
        sum[0] = 1;
        sum[1] = 1;
        
        //外层计算节点为i的BST总数
        for(int i = 2;i <= n;i++){
            for(int j = 1;j <= i;j++){
                sum[i] += sum[j-1] * sum[i-j];
            }
        }
        
        return sum[n];
    }
}

98. 验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:
    2
   / \
  1   3
输出: true

示例 2:

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4 。

思路:
中序遍历保持升序即为BST

代码:
1.递归法

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    double last = -Double.MAX_VALUE;
    public boolean isValidBST(TreeNode root) {
        if(root == null) return true;
        if(isValidBST(root.left)){
            if(last < root.val){
                last = root.val;
                return isValidBST(root.right);
            }
        }
        
        return false;
    }

}

2.非递归法:

class Solution {
    public boolean isValidBST(TreeNode root) {
        if(root == null) return true;
        Stack<TreeNode> stack = new Stack<>();
        double last = -Double.MAX_VALUE;
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            if(cur != null){
                stack.push(cur);
                cur = cur.left;
            }else{
                cur = stack.pop();
                if(last >= cur.val){
                    return false;
                }
                last = cur.val;
                cur = cur.right;
            }
        }
        return true;
    }

}

99. 恢复二叉搜索树

二叉搜索树中的两个节点被错误地交换。

请在不改变其结构的情况下,恢复这棵树。

示例 1:

输入: [1,3,null,null,2]

   1
  /
 3
  \
   2

输出: [3,1,null,null,2]

   3
  /
 1
  \
   2

示例 2:

输入: [3,1,4,null,null,2]

  3
 / \
1   4
   /
  2

输出: [2,1,4,null,null,3]

  2
 / \
1   4
   /
  3

思路:
创建两个集合,通过中序遍历分别储存原先BST的节点值和节点,然后将节点值集合从小到大排序,然后将排好序的值重新按顺序赋给BST节点,即完成规整

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public void recoverTree(TreeNode root) {
        //创建两个集合,分别保存原有的TreeNode顺序和TreeNode值
        List<TreeNode> nodeList = new ArrayList<>();
        List<Integer> valList = new ArrayList<>();
        inorder(root,nodeList,valList);
        //对原有Tree的值进行排序
        Collections.sort(valList);
        //然后将排好序的值再放入原有树中
        for(int i = 0;i < nodeList.size();i++){
            nodeList.get(i).val = valList.get(i);
        }
        
    }
    
    public void inorder(TreeNode node,List<TreeNode> nodeList,List<Integer> valList){
        if(node == null) return;
        inorder(node.left,nodeList,valList);
        nodeList.add(node);
        valList.add(node.val);
        inorder(node.right,nodeList,valList);
    }
    
}

100. 相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:      1         1
          / \       / \
         2   3     2   3

        [1,2,3],   [1,2,3]

输出: true

示例 2:

输入:      1          1
          /           \
         2             2

        [1,2],     [1,null,2]

输出: false

示例 3:

输入:      1         1
          / \       / \
         2   1     1   2

        [1,2,1],   [1,1,2]

输出: false

思路:

用递归解决:
1.递归结束条件:两个节点都为Null,则返回true
2.如果两个节点都不为null且两个节点的值相等,递归比较两个节点的左节点和两个节点的右节点
3.如果不满足上述情况,则返回false

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
       if(p == null && q == null)
           return true;

       if(p != null && q != null && q.val == p.val){
           return isSameTree(p.left,q.left) &&isSameTree(p.right,q.right);
       }else{
           return false;
       }
    }
}

101. 对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

思路:
用递归解决:
1.如果左右节点都为null,则返回true;
2.如果左右节点都部位null且节点值相等,则递归比较左节点的左儿子和右节点的右儿子,以及左节点的右儿子和右节点的左儿子;
3.如果不满足上面的条件,返回false

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null)
            return true;
        return isMirror(root.left,root.right);
    }
    
    public boolean isMirror(TreeNode leftNode,TreeNode rightNode){
        if(leftNode == null && rightNode == null)
            return true;
        
        if(leftNode != null && rightNode != null && leftNode.val == rightNode.val){
            return isMirror(leftNode.left,rightNode.right) && isMirror(leftNode.right,rightNode.left);
        }else{
            return false;
        }
        
    }
}

102. 二叉树的层次遍历

给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。

例如:
给定二叉树: [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

思路:
使用队列来保存路径,栈中每次保存的是每一层的元素。

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        
        while(!queue.isEmpty()){
            //队列中储存的是每一层的节点
            int count = queue.size();
            //每一次大循环即为一层
            List<Integer> list = new ArrayList<>();
            while(count > 0){
                TreeNode cur = queue.poll();
                count--;
                list.add(cur.val);
                if(cur.left != null){
                    queue.add(cur.left);
                }
                if(cur.right != null){
                    queue.add(cur.right);
                }
            }
            res.add(list);
        }
        
        return res;
    }
}

103. 二叉树的锯齿形层次遍历

给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回锯齿形层次遍历如下:

[
  [3],
  [20,9],
  [15,7]
]

思路:
和上题的层次遍历一样,只需要设定一个变量作为flag,判断此层结果是否需要翻转即可。

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        boolean flag = false; //是否需要倒序
        
        while(!queue.isEmpty()){
            int count = queue.size();
            List<Integer> list = new ArrayList<>();
            while(count > 0){
                count--;
                TreeNode cur = queue.poll();
                list.add(cur.val);
                if(cur.left != null)
                    queue.add(cur.left);
                if(cur.right != null)
                    queue.add(cur.right);
            }
            //如果需要倒序,则将结果翻转
            if(flag) Collections.reverse(list);
            flag = !flag;
            res.add(list);
        }
        
        return res;
    }
}

104. 二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

思路:
1.递归法:二叉树的最大深度要不就是在左子树上,要不就是在右子树上,两者谁大取谁。
2.层次遍历法:层次遍历,每层次遍历完后深度+1。

代码:
1.递归法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        int a = maxDepth(root.left) + 1;
        int b = maxDepth(root.right) + 1;
        return Math.max(a,b);
    }
}

2.层次遍历法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        int depth = 0;
        if(root == null) return depth;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        
        while(!queue.isEmpty()){
            int count = queue.size();
            while(count > 0){
                count--;
                TreeNode cur = queue.poll();
                if(cur.left != null){
                    queue.add(cur.left);
                }
                if(cur.right != null){
                    queue.add(cur.right);
                }
            }
            depth++;
        }
        
        return depth;
    }
}

105. 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

思路:
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
我们根据前序遍历和后续遍历的结果能得到如下结论:

  1. 前序遍历结果为【根节点,左子树节点,右子树节点】,数组的第一个元素一定为树的根节点;
  2. 中序遍历结果为【左子树节点,根节点,右子树节点】,从前序遍历中找出的根节点对应到中序遍历中,索引值为midIndex,左边的值为左子树:[leftInorder,mid-1],右边的值为右子树:[mid+1,rightInorder];
  3. 因此可以得出左子树的节点个数为:midIndex-leftInorder;
  4. 右子树节点个数为:rightInorder-midIndex;
  5. 根据左右子树的个数,可以得到前序遍历中左右子树节点的范围:
  6. 前序遍历左子树:[leftPre+1,leftPre+midIndex-leftInorder],右子树:[leftPre+1+midIndex-leftInorder+1,rightPre]。

对于这种构建二叉树的题,关键就是递归和区间分治,区间分治的题,采用左闭右闭最为简便,这题我们也采用左闭右闭。
重建二叉树的基本思路就是先构造根节点,再构造左子树,接下来构造右子树,其中,构造左子树右子树是一个子问题,递归处理即可。因此我们只关心如何构造根节点,以及如何递归构造左子树和右子树。
根节点的值就是前序遍历的第一个值,而根节点的左子树,递归处理得到,前序遍历和中枢遍历数组的区间相应缩小至对应的值,右子树也是一样的,直到数组的起始边界大于终止边界。

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return build(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
    }
    
    public TreeNode build(int[] preorder,int start1,int end1,int[] inorder,int start2,int end2){
        if(start1 > end1 || start2 > end2) return null;
        TreeNode root = new TreeNode(preorder[start1]);
        //找到中序遍历中根元素对应的index
        int mid = -1;
        for(int i = start2;i <= end2;i++){
            if(inorder[i] == preorder[start1])
                mid = i;
        }
        //左子树
        root.left = build(preorder,start1+1,start1+mid-start2,inorder,start2,mid-1);
        root.right = build(preorder,start1+mid-start2+1,end1,inorder,mid+1,end2);
        return root;
    }  
}

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

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

思路:
这题的思路和上一题的思路是类似的。
后序遍历的最后一个元素即为根元素,在中序遍历数组中找到对应的索引mid,中序遍历的左子树范围为:[leftInorder,mid-1],右子树范围为:[mid+1,rightInorder],左子树元素个数为:mid-leftInorder,右子树元素个数为:rightInorder-mid。则后续遍历的左子树范围为:[leftPost,leftPost+mid-leftInorder-1],右子树范围为:[leftPost+mid-leftInorder,rightPost-1]。
递归处理即可

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return build(postorder,0, postorder.length-1,inorder,0, inorder.length-1);
    }
    
    public TreeNode build(int[] postorder,int lo1,int hi1,int[] inorder,int lo2,int hi2){
        if(lo1 > hi1 || lo2 > hi2) return null;
        TreeNode root = new TreeNode(postorder[hi1]);
        int mid = -1;
        //查找在中序遍历中的index
        for(int i = 0;i < inorder.length;i++){
            if(inorder[i] == postorder[hi1])
                mid = i;
        }
        //左子树长为mid-lo2
        root.left = build(postorder,lo1,lo1+mid-lo2-1,inorder,lo2,lo2+mid-lo2-1);
        root.right = build(postorder,lo1+mid-lo2,hi1-1,inorder,mid+1,hi2);
        return root;
    }
}

107. 二叉树的层次遍历 II

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其自底向上的层次遍历为:

[
  [15,7],
  [9,20],
  [3]
]

思路:
使用栈记录遍历的路径,最后将结果反转即可。
具体看题102.

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;
        
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            int count = queue.size();
            List<Integer> list = new ArrayList<>();
            while(count > 0){
                count--;
                TreeNode cur = queue.poll();
                list.add(cur.val);
                if(cur.left != null){
                    queue.add(cur.left);
                }
                if(cur.right != null){
                    queue.add(cur.right);
                }
            }
            res.add(list);
        }
        Collections.reverse(res);
        return res;
    }
}

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

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

思路:
如果只提供了中序遍历,可以构成多个BST,如上述例子中也可以构成如下BST:

    	  0
         / \
       -10  9
         \   \
     	 -3   5

中序遍历的元素为:【左子树节点,根节点,右子树节点】,所以可以通过求中间索引mid构建根节点,根节点的左子树递归处理,传入左子树的对应数组索引范围,右子树相同处理。

代码:

/**
 * 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) {
        //中序遍历的结果反推回树
        if(nums.length == 0) return null;
        if(nums.length == 1) return new TreeNode(nums[0]);
        
        //根节点是数组中中心元素,左边为左子树,右边为右子树
        return getBST(nums,0,nums.length-1);
    }
    
    public TreeNode getBST(int[] nums,int lo,int hi){
        if(lo > hi) return null;
        int mid = lo + ((hi - lo)>>1); //这里右移一位是除以2的优化,因为运算符优先级的关系,要加括号
        TreeNode root = new TreeNode(nums[mid]);
        root.left = getBST(nums,lo,mid-1);
        root.right = getBST(nums,mid+1,hi);
        return root;
    }
}

110. 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4

返回 false 。

思路:
这个题目的关键就是求出当前节点的左子树和右子树的高度差,如果差大于1,则返回false,否则递归求出左右子节点的子树高度差。

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        int left = getHeight(root.left);
        int right = getHeight(root.right);
        if(Math.abs(left-right) > 1)
            return false;
        else{
            return isBalanced(root.left) && isBalanced(root.right);
        }
    }
    
    private int getHeight(TreeNode root){
        if(root == null) return 0;
        return Mat
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值