二叉树高频题目-下-不含树型dp

2024-09-03 05:20

本文主要是介绍二叉树高频题目-下-不含树型dp,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

二叉树高频题目-下-不含树型dp

题目1 : 普通二叉树上寻找两个节点的最近公共祖先(lca问题)

  • 测试链接 : https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/

  • 思路

    • 分别在该树的左右子树上搜索, 当该树为 空 或 p 或 q, 直接返回
      • 若返回的是p 或 q , 那么该结点会一直向上传递直到遇到最近公共祖先
    • 若左右子树都搜到, 返回root
      • 此时pq分别在root两侧, root就是其最近的公共祖先
    • 整颗树都没有搜到, 返回空
    • 若l r搜到其中一个, 返回不为空的那个
      • 此时pq一定在同一侧, 另一个就在不为空的那个子树上
  • 代码

    • 	public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if (root == null || root == p || root == q) {// 遇到空,或者p,或者q,直接返回return root;}// 左树搜索p和q: 遇到空,或者p,或者q,直接返回TreeNode l = lowestCommonAncestor(root.left, p, q);// 右树搜索p和q: 遇到空,或者p,或者q,直接返回TreeNode r = lowestCommonAncestor(root.right, p, q);if (l != null && r != null) {// 左树也搜到,右树也搜到,返回rootreturn root;}if (l == null && r == null) {// 整棵树都没搜到返回空return null;}// l和r一个为空,一个不为空// 返回不空的那个return l != null ? l : r;}
      

题目2 : 搜索二叉树上寻找两个节点的最近公共祖先

搜索二叉树又称: 二叉搜索树,二叉排序树

  • 测试链接 : https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/
  • 思路
    • 若root在p~q的侧, 那么pq都比root的值, 就往root的右子树上搜索
    • 若root在p~q的侧, 那么pq都比root的值, 就往root的左子树上搜索
    • 若root在p~q之间, 则root就是两个节点的最近公共祖先
    • 否则若先遇到了p, 说明最近公共祖先是p, q在p的子树上
    • 否则若先遇到了q, 说明最近公共祖先是q, p在q的子树上

注: 3 4 5 点不可能同时成立, 若3成立, 4 5 就不可能出现, 若 4 5 出现, 说明pq的最近公共祖先大小一定不在pq之间, 否则在3条件满足时就会返回

  • 代码

    • 	public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {// root从上到下// 如果先遇到了p,说明p是答案// 如果先遇到了q,说明q是答案// 如果root在p~q的值之间,不用管p和q谁大谁小,只要root在中间,那么此时的root就是答案// 如果root在p~q的值的左侧,那么root往右移动// 如果root在p~q的值的右侧,那么root往左移动while (root.val != p.val && root.val != q.val) {// 没有遇到p 也没有遇到qif (Math.min(p.val, q.val) < root.val && root.val < Math.max(p.val, q.val)) {break;// root在p 和 q之间, 返回root}// 没有在中间, 也不是p或q, 向需要的方向移动root = root.val < Math.min(p.val, q.val) ? root.right : root.left;}return root;}
      

题目3 : 收集累加和等于aim的所有路径(递归恢复现场)

  • 测试链接 : https://leetcode.cn/problems/path-sum-ii/

  • 思路

    • 设置最终返回的答案和全局路径, 从根节点开始递归
    • 判断当前结点是否为叶节点
      • 是叶节点: 判断是否符合要求
        • 符合: 之前路径累加和 + 当前结点的值 == 目标值
          • 将该节点值加入全局路径
          • 将该路径复制 (全局路径是同一地址, 使用原集合会使后面的路径覆盖之前的路径) 一份放入ans
          • 将该结点从路径中删除(恢复现场)
        • 不符合: 不做任何操作(path不变, 路径不变)
      • 不是叶节点
        • 将该节点加入全局路径中去
        • 若左子树不为空, 跑左侧递归
        • 若右子树不为空, 跑右侧递归
        • 将该结点从路径中删除(恢复现场)
  • 代码

    • 	public static List<List<Integer>> pathSum(TreeNode root, int aim) {List<List<Integer>> ans = new ArrayList<>();// 要返回的大结果if (root != null) {List<Integer> path = new ArrayList<>();// 全局路径: 只维持上方路径的所有节点f(root, aim, 0, path, ans);// 处理}return ans;// 如果整棵树是空的, 没有路径}public static void f(TreeNode cur, int aim, int sum, List<Integer> path, List<List<Integer>> ans) {//			  当前来到的结点     目标和  cur上方路径的累加和  全局path		            结果if (cur.left == null && cur.right == null) {  // 当前结点是叶节点if (cur.val + sum == aim) {// 叶节点达标 当前路径满足要求path.add(cur.val);// 把自己的值 放到 路径中去copy(path, ans);// path拷贝一份, 放入anspath.remove(path.size() - 1);// 返回前 把自己移除掉(当前结点是叶节点:path中的最后一个)}// 叶节点不达标, 什么也不操作, 直接返回} else {// 当前结点不是叶节点path.add(cur.val);// 把自己加到路径中if (cur.left != null) {// 跑左侧的递归f(cur.left, aim, sum + cur.val, path, ans);}if (cur.right != null) {// 跑右侧的递归f(cur.right, aim, sum + cur.val, path, ans);}path.remove(path.size() - 1);// 把自己从路径中删掉 即 恢复现场}}public static void copy(List<Integer> path, List<List<Integer>> ans) {List<Integer> copy = new ArrayList<>();for (Integer num : path) {copy.add(num);}ans.add(copy);}
      

题目4 : 验证平衡二叉树(树型dp沾边)

  • 测试链接 : https://leetcode.cn/problems/balanced-binary-tree/

  • 思路

    • 递归求二叉树的高度, 根据Math.abs(lh - rh) > 1来判断二叉树是否平衡, 不平衡则将全局变量balance改为false, 返回(此时树高可返回任何值), 平衡则返回树高
  • 代码

    • 	public static boolean balance;public static boolean isBalanced(TreeNode root) {// balance是全局变量,所有调用过程共享// 所以每次判断开始时,设置为默认值truebalance = true;// 根本目的是验证平衡二叉树, 查找树高只是辅助判断height(root);return balance;}// 一旦发现不平衡,返回什么高度已经不重要了public static int height(TreeNode cur) {if (!balance || cur == null) {// 此处可以不加 !balance , 在最后也会返回(当!balance成立时, 返回什么就不重要了)return 0;}int lh = height(cur.left);int rh = height(cur.right);if (Math.abs(lh - rh) > 1) {balance = false;}return Math.max(lh, rh) + 1;}
      

题目5 : 验证搜索二叉树(树型dp沾边)

  • 测试链接 : https://leetcode.cn/problems/validate-binary-search-tree/

  • 思路1

    • 中序遍历二叉树, 查看个节点是否满足 左 < 中 < 右
  • 代码1

    • 	public static int MAXN = 10001;public static TreeNode[] stack = new TreeNode[MAXN];public static int r;// 提交时改名为isValidBSTpublic static boolean isValidBST1(TreeNode head) {if (head == null) {// 如果树为空, 该树为BSTreturn true;}// 非递归的形式实现二叉树的遍历TreeNode pre = null;// 前一个节点r = 0;while (r > 0 || head != null) {if (head != null) {// 左stack[r++] = head;head = head.left;} else {head = stack[--r];// 根if (pre != null && pre.val >= head.val) {// 有之前的节点 且 之前的节点和现在的节点不符合要求return false;}pre = head;head = head.right;// 右}}// whilereturn true;}
      
  • 思路2

    • 递归判断二叉树是否满足 左 < 中 < 右
        1. 空树一定为搜索二叉树
        2. 左边是不是搜索二叉树, 用两个变量接住左树上的最大和最小
        3. 右边是不是搜索二叉树, 用两个变量接住左树上的最大和最小
        4. 保存整棵树的 最大最小值 给 全局变量, 使 上游的树 能够接收到
        5. 判断 左树是BST 右树是BST lmax < cur,val < rmin, 返回该树是不是BST
  • 代码2

    • 	public static long min, max;// 全局变量, 用于每个节点确定自己的lmax rmin// 提交时改名为isValidBSTpublic static boolean isValidBST2(TreeNode head) {if (head == null) {// 1. 空树一定为搜索二叉树min = Long.MAX_VALUE;max = Long.MIN_VALUE;// lmax < cur,val < rmin, 使其满足程序要求return true;}boolean lok = isValidBST2(head.left);// 2. 左边是不是搜索二叉树// 用两个变量接住左树上的最大和最小long lmin = min;long lmax = max;boolean rok = isValidBST2(head.right);// 3. 右边是不是搜索二叉树// 用两个变量接住左树上的最大和最小long rmin = min;long rmax = max;// 4. 保存整棵树的 最大最小值 给 全局变量, 使 上游的树 能够接收到min = Math.min(Math.min(lmin, rmin), head.val);// 整棵树的最小值max = Math.max(Math.max(lmax, rmax), head.val);// 整棵树的最大值// 5. 返回该树是不是BST// 左树是BST 右树是BST  lmax < cur,val < rminreturn lok && rok && lmax < head.val && head.val < rmin;}
      

题目6 : 修剪搜索二叉树

  • 测试链接 : https://leetcode.cn/problems/trim-a-binary-search-tree/

  • 思路

    • 递归, 判断整棵树保留什么, 将保留的重新连好, 返回

      • 当前是空节点: 返回空
      • 当前值小于最小值, 则我和左子树(比我小)都不符合条件, 去右子树找
      • 当前值大于最大值, 则我和右子树(比我大)都不符合条件, 去左子树找
      • cur在范围中, cur一定会被保留, 去找cur符合要求的左孩子, 去找cur符合要求的右孩子
    • 代码

      • 	// [low, high]public static TreeNode trimBST(TreeNode cur, int low, int high) {// 当前是空节点: 返回空if (cur == null) {return null;}// 当前值小于最小值, 则我和左子树都不符合条件, 去右子树找if (cur.val < low) {return trimBST(cur.right, low, high);}// 当前值大于最大值, 则我和右子树都不符合条件, 去左子树找if (cur.val > high) {return trimBST(cur.left, low, high);}// cur在范围中, cur一定会被保留cur.left = trimBST(cur.left, low, high);cur.right = trimBST(cur.right, low, high);return cur;}
        

题目7 : 二叉树打家劫舍问题(树型dp沾边)

  • 测试链接 : https://leetcode.cn/problems/house-robber-iii/

  • 思路

    • 递归 + 全局变量

      • yes : 偷头节点的情况下,最大的收益
      • no : 在不偷头节点的情况下,最大的收益
      • 若当前节点为null, yes == no == 0
      • 返回 Math.max(yes, no)
    • 局部变量: y, n表示 当前节点偷头节点的情况下,最大的收益 和 在不偷头节点的情况下,最大的收益

      • y + no-l + no-r: 一旦该结点偷头结点, 那么相邻的子节点就不能偷

      • n + Math.max(yes-l, no-l) + Math.max(yes-r, no-r)
      • 将全局变量yes置为当前的y 将全局变量no置为当前的n

      • 返回程序

  • 代码

    • 	public static int rob(TreeNode root) {f(root);// 整棵树的yes 和 no被返回return Math.max(yes, no);// 返回整棵树中yes 和 no的最大值}// 全局变量,完成了X子树的遍历,返回之后// yes变成,X子树在偷头节点的情况下,最大的收益public static int yes;// 全局变量,完成了X子树的遍历,返回之后// no变成,X子树在不偷头节点的情况下,最大的收益public static int no;public static void f(TreeNode root) {// 计算x子树 偷与不偷头结点 的最大受益的具体函数if (root == null) {// 如果为空, 都是0的受益yes = 0;no = 0;} else {// root自身的yes 和 noint y = root.val;int n = 0;f(root.left);// 该函数调用后, 全局变量yes为左树偷头结点的最好收益, no为左树不偷头结点的最好受益y += no;// 自己的y只能+底层的non += Math.max(yes, no);// 自己的no+底层更大的那个// 同样方式调用并加上右边f(root.right);y += no;n += Math.max(yes, no);// 自己返回前, 要把自己的yes和no更新到全局变量上yes = y;no = n;}}
      

这篇关于二叉树高频题目-下-不含树型dp的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132135

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]

uva 10118 dP

题意: 给4列篮子,每次从某一列开始无放回拿蜡烛放入篮子里,并且篮子最多只能放5支蜡烛,数字代表蜡烛的颜色。 当拿出当前颜色的蜡烛在篮子里存在时,猪脚可以把蜡烛带回家。 问最多拿多少只蜡烛。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cs

uva 10069 DP + 大数加法

代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <cl

uva 10029 HASH + DP

题意: 给一个字典,里面有好多单词。单词可以由增加、删除、变换,变成另一个单词,问能变换的最长单词长度。 解析: HASH+dp 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

XTU 1233 n个硬币连续m个正面个数(dp)

题面: Coins Problem Description: Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face o

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int