LeetCode刷题笔记_第二周

二叉树搜索一条边/整个树的写法

搜索一条边的写法:

if (递归函数(root->left)) return ;
if (递归函数(root->right)) return ;

搜索整个树的写法

left = 递归函数(root->left);
right = 递归函数(root->right);
left与right的逻辑处理;

二叉树

100. 相同的树

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

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

输入:p = [1,2,3], q = [1,2,3]
输出:true

迭代法

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

    boolean leftIsSame = compare(left.left, right.left);
    boolean rightIsSame = compare(left.right, right.right);

    return leftIsSame && rightIsSame;
}

404. 左叶子之和

给定二叉树的根节点 root ,返回所有左叶子之和。

递归法:

public int sumOfLeftLeaves(TreeNode root){
    if (root == null) return 0;
    int midValue = 0;
    if (root.left != null && root.left.left == null && root.left.right == null){
        midValue = root.left.val;
    }
    return midValue + sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
}

迭代:


513. 找树左下角的值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

输入: root = [2,1,3]
输出: 1

public int findBottomLeftValue(TreeNode root) {
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    int res = 0;
    while (!queue.isEmpty()) {
        int size = queue.size();
        for (int i = 0; i < size; i++) {
            TreeNode poll = queue.poll();
            if (i == 0) {
                res = poll.val;
            }
            if (poll.left != null) {
                queue.offer(poll.left);
            }
            if (poll.right != null) {
                queue.offer(poll.right);
            }
        }
    }
    return res;
}

112. 路径总和(简单) 返回值判断

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

  • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
  • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
  • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
public boolean hasPathSum(TreeNode root, int targetSum) {
    if(root == null) return false;
    return traversal(root, targetSum - root.val);
}
public boolean traversal(TreeNode node, int count){
    if(node.left == null && node.right == null && count == 0) return true;
    if(node.left == null && node.right == null) return false;

    if(node.left != null){
        count -= node.left.val;
        if(traversal(node.left, count)) return true;
        count += node.left.val;
    }
    if(node.right != null){
        count -= node.right.val;
        if(traversal(node.right, count)) return true;
        count += node.right.val;
    }
    return false;
}

113. 路径总和 II(中等)

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径

List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
    if(root == null) return resList;
    List<Integer> list = new ArrayList<>();
    traversal(root, list, targetSum - root.val);
    return resList;
}
public void traversal(TreeNode node ,List<Integer> list, int count){
    list.add(node.val);
    if(node.left == null && node.right == null && count == 0){
        resList.add(new ArrayList<>(list));
        return;
    }
    if(node.left == null && node.right == null){
        return;
    }

    if(node.left != null){
        count -= node.left.val;
        traversal(node.left, list, count);
        list.remove(list.size() - 1);
        count += node.left.val;
    }
    if(node.right != null){
        count -= node.right.val;
        traversal(node.right, list, count);
        list.remove(list.size() - 1);
        count += node.right.val;
    }
    return;
}

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

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

迭代思路:

  1. 若数组为0则为空节点
  2. 不为空,则取后序最后一个节点作为根节点
  3. 找到根节点在中序数组中的位置,作为切割点
  4. 切割中序数组,切成中序左数组和中序后数组
  5. 切割后序数组,切成后序左数组和后序右数组
  6. 递归处理左区间和右区间

重点:坚持循环不变量
首先切割中序数组(因为后序最后一个元素就是切割点,好找,坚持左闭右开)
左闭右开区间[0, delimiterIndex), [delimiterIndex,end)

然后切割后序数组,按照中序数组的大小来切割,切割成左数组和右数组
[0, leftInorder.size),[leftInorder.size, end)

递归:
root.left = traversal(leftInoreder, leftPostorder);
root.right = traversal(rightInorder, rightPostorder);

public TreeNode buildTree(int[] inorder, int[] postorder) {
    return buildTree1(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
private TreeNode buildTree1(int[] inorder, int inLeft, int inRight,
                            int[] postorder, int postLeft, int postRight){
    //没有元素了
    if (inRight - inLeft < 1) return null;
    if (inRight - inLeft == 1) return new TreeNode(inorder[inLeft]);

    int rootVal = postorder[postRight - 1];
    TreeNode root = new TreeNode(rootVal);
    int rootIndex = 0;
    for (int i = inLeft; i < inRight; i++) {
        if (inorder[i] == rootVal){
            rootIndex = i;
            break;
        }
    }
    
    System.out.println();
    root.left = buildTree1(inorder, inLeft, rootIndex,
            postorder, postLeft, postLeft + (rootIndex - inLeft));
    root.right = buildTree1(inorder, rootIndex + 1, inRight,
            postorder, postLeft + (rootIndex - inLeft), postRight - 1);
    return root;
}

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

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

思路:与后序+中序的思路一样,范围同样是左包右闭

public TreeNode buildTree(int[] preorder, int[] inorder) {
    return buildTree2(preorder, 0, preorder.length, inorder, 0, inorder.length);
}
public TreeNode buildTree2(int[] preorder, int preLeft, int preRight,int[] inorder, int inLeft, int inRight){
    if(inRight - inLeft < 1) return null;
    if(inRight - inLeft == 1) return new TreeNode(inorder[inLeft]);
    int rootVal = preorder[preLeft];
    TreeNode root = new TreeNode(rootVal);
    int rootIndex = 0;
    for(int i = inLeft; i < inRight; i++){
        if(inorder[i] == rootVal){
            rootIndex = i;
            break;
        } 
    }

    root.left = buildTree2(preorder, preLeft + 1, preLeft + rootIndex - inLeft + 1, inorder, inLeft, rootIndex);
    root.right = buildTree2(preorder, preLeft + rootIndex - inLeft + 1, preRight, inorder, rootIndex + 1, inRight);
    return root;
}

654. 最大二叉树(简单)

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

  • 二叉树的根是数组中的最大元素。
  • 左子树是通过数组中最大值左边部分构造出的最大二叉树
  • 右子树是通过数组中最大值右边部分构造出的最大二叉树。

通过给定的数组构建最大二叉树,并且输出这个树的根节点。

请添加图片描述

public TreeNode constructMaximumBinaryTree(int[] nums) {
return travel(nums, 0, nums.length);
}
public TreeNode travel(int[] elems, int left, int right){
    if(right - left < 1) return null;
    if(right - left == 1) return new TreeNode(elems[left]);
    int rootVal = Integer.MIN_VALUE;
    int rootIndex = 0;
    for(int i = left; i < right; i++){
        if(elems[i] > rootVal){
            rootVal = elems[i];
            rootIndex = i;
        }
    }
    TreeNode root = new TreeNode(rootVal);

    root.left = travel(elems, left, rootIndex);
    root.right = travel(elems, rootIndex + 1, right);
    return root;
}

617. 合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

请添加图片描述
思路:
使用前序

public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
    if(root1 == null) return root2;
    if(root2 == null) return root1;
    root1.val += root2.val;
    root1.left = mergeTrees(root1.left, root2.left);
    root1.right = mergeTrees(root1.right, root2.right);
    return root1;
}

700. 二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
请添加图片描述

public TreeNode searchBST(TreeNode root, int val) {
    Deque<TreeNode> deque = new LinkedList<>();
    deque.offerLast(root);
    while(!deque.isEmpty()){
        int len = deque.size();
        while(len > 0){
            TreeNode tmp = deque.pollFirst();
            if(tmp.val == val) return tmp;
            if(tmp.left != null) deque.offerLast(tmp.left);
            if(tmp.right != null) deque.offerLast(tmp.right);
            len--;
        }
    }
    return null;
}

98. 验证二叉搜索树(中等)

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

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

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

思路:
要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。
有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。

TreeNode max;
public boolean isValidBST(TreeNode root) {
     if(root == null) return true;
     //左
     boolean left = isValidBST(root.left);
     if(!left) return false;
     //中
     if(max != null && root.val <= max.val){
         return false;
     }
     max = root;
     //右
     boolean right = isValidBST(root.right);
     return right;
 }

530. 二叉搜索树的最小绝对差(简单)

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

差值是一个正数,其数值等于两值之差的绝对值。

递归:

TreeNode pre;
int res = Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
    if(root == null) return 0;
    inOrder(root);
    return res;
}
public void inOrder(TreeNode root){
    if(root == null) return;
    inOrder(root.left);
    if(pre != null) res = Math.min(res, root.val - pre.val);
    pre = root;
    inOrder(root.right);
}

迭代:

TreeNode pre;
Stack<TreeNode> stack;
public int getMinimumDifference(TreeNode root) {
   if (root == null) return 0;
   stack = new Stack<>();
   TreeNode cur = root;
   int result = Integer.MAX_VALUE;
   while (cur != null || !stack.isEmpty()) {
       if (cur != null) {
           stack.push(cur); // 将访问的节点放进栈
           cur = cur.left; // 左
       }else {
           cur = stack.pop(); 
           if (pre != null) { // 中
               result = Math.min(result, cur.val - pre.val);
           }
           pre = cur;
           cur = cur.right; // 右
       }
   }
   return result;
}

501. 二叉搜索树中的众数

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。

如果树中有不止一个众数,可以按 任意顺序 返回。

假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值
  • 结点右子树中所含节点的值 大于等于 当前节点的值
  • 左子树和右子树都是二叉搜索树
ArrayList<Integer> resList;
int maxCount;
int count;
TreeNode pre;
public int[] findMode(TreeNode root) {
    resList = new ArrayList<>();
    maxCount = 0;
    count = 0;
    pre = null;
    findMode1(root);
    int[] res = new int[resList.size()];
    for(int i = 0; i < res.length; i++){
        res[i] = resList.get(i);
    }
    return res;
}
public void findMode1(TreeNode root){
    if(root == null) return;
    findMode1(root.left);
    int rootValue = root.val;
    if(pre == null || rootValue != pre.val){
        count = 1;
    }else{
        count++;
    }
    if(count > maxCount){
        resList.clear();
        resList.add(rootValue);
        maxCount = count;
    }else if(count == maxCount){
        resList.add(rootValue);
    }
    pre = root;
    findMode1(root.right);
}

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

思路总结:

  • 求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从低向上的遍历方式。
  • 在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。
  • 要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if(root == q || root == p || root == null) return root;
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    if(left != null && right != null) return root;
    if(left != null && right == null) return left;
        else if(left == null && right != null){return right;
    }else{
        return null;
    } 
}

235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

思路:其实只要从上到下遍历的时候,cur节点是数值在[p, q]区间中则说明该节点cur就是最近公共祖先了。

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if(root == null) return root;
    if(root.val > p.val && root.val > q.val){
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        if(left != null) return left;
    }
    if(root.val < p.val && root.val < q.val){
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(right != null) return right;
    }
    return root;
}

701. 二叉搜索树中的插入操作

给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

public TreeNode insertIntoBST(TreeNode root, int val) {
    if(root == null) return new TreeNode(val);
    travel(root, val);
    return root;
}
public void travel(TreeNode root, int val){
    if(root.val >= val){
        if(root.left != null){
            travel(root.left, val); 
        }else{
            root.left = new TreeNode(val);
            return;
        }
    }else{
        if(root.right != null){
            travel(root.right, val);
        }else{
            root.right = new TreeNode(val);
            return;
        }
    }
}

450. 删除二叉搜索树中的节点

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。

 public TreeNode deleteNode(TreeNode root, int key) {
  if (root == null) return root;
  if (root.val == key) {
    if (root.left == null) {
      return root.right;
    } else if (root.right == null) {
      return root.left;
    } else {
      TreeNode cur = root.right;
      while (cur.left != null) {
        cur = cur.left;
      }
      cur.left = root.left;
      root = root.right;
      return root;
    }
  }
  if (root.val > key) root.left = deleteNode(root.left, key);
  if (root.val < key) root.right = deleteNode(root.right, key);
  return root;
}

669. 修剪二叉搜索树

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

思路:
若当前节点小于范围内的最小值,则递归右子树,并返回右子树符合范围的节点
对于大于同理

public TreeNode trimBST(TreeNode root, int low, int high) {
    if(root == null) return root;
    if(root.val < low){
        return trimBST(root.right, low, high);
    }
    if(root.val > high){
        return trimBST(root.left, low, high);
    }
    root.left = trimBST(root.left, low, high);
    root.right = trimBST(root.right, low, high);
    return root;
}

108. 将有序数组转换为二叉搜索树(简单)

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

public TreeNode sortedArrayToBST(int[] nums) {
    return travel(nums, 0, nums.length - 1);
}
public TreeNode travel(int[] nums, int left, int right){
    if(left > right) return null;
    int mid = left + ((right - left) / 2);
    TreeNode root = new TreeNode(nums[mid]);
    root.left = travel(nums, left , mid - 1);
    root.right = travel(nums, mid + 1, right);
    return root;
}

538. 把二叉搜索树转换为累加树

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

//反中序遍历
    int pre;
    public TreeNode convertBST(TreeNode root) {
        pre = 0;
        travel(root);
        return root;
    }
    public void travel(TreeNode cur){
        if(cur == null) return;
        travel(cur.right);
        cur.val += pre;
        pre = cur.val;
        travel(cur.left);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值