关于树的一些题目
关于树的一些题目
1、二叉树减枝
-
描述:
给定一个二叉树 根节点 root ,树的每个节点的值要么是 0,要么是 1。请剪除该二叉树中所有节点的值为 0 的子树。节点 node 的子树为 node 本身,以及所有 node 的后代。
-
解法:
要求减除的是所有值为 0 并且子节点值也为 0 的子树。使用递归分别对左右子树进行判断,返回条件:节点为空时返回 null , 如果左右子树都为空并且该节点的值为 0 ,就返回null,表示该节点被剪掉了。否咋返回该节点。
-
源码:
class Solution { public TreeNode pruneTree(TreeNode root) { if(root == null) return root; root.left = pruneTree(root.left); root.right = pruneTree(root.right); if(root.left==null && root.right==null && root.val==0) return null; return root; } }
2、从根节点到叶子节点的路径的数字之和
-
描述:
给定一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。每条从根节点到叶节点的路径都代表一个数字:例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。计算从根节点到叶节点生成的 所有数字之和 。叶节点 是指没有子节点的节点。
-
解法:
每往下遍历一层,就要对上面的层的值 ×10 。我们可以使用一个队列来对每一层的值进行保存,再结合广度优先遍历的方法,对上面一层的数值 ×10,并一层一层进行累加。
-
源码:
class Solution { public int sumNumbers(TreeNode root) { if(root == null) return 0; //用来广度优先遍历的队列 Queue<TreeNode> nodeQue = new LinkedList<>(); //用来进行存储上一层累加值的队列 Queue<Integer> numQue = new LinkedList<>(); //记录总和 int sum = 0; nodeQue.add(root); numQue.add(root.val); while(! nodeQue.isEmpty()){ TreeNode temp = nodeQue.poll(); int num = numQue.poll(); if(temp.left==null && temp.right==null) { return sum += num; } else{ if(temp.left != null){ numQue.add(num*10 + temp.left.val); nodeQue.add(temp.left); } if(temp.right != null){ numQue.add(num*10 +temp.right.val); nodeQue.add(temp.right); } } } return sum; } }
3、向下的节点路径之和
-
描述:
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)
-
解法:
对于一个节点,我们想知道它和它的子节点的和是否满足 targerSum,要对它进行递归操作,每往下一层, targetSum 的值就减去该节点的值,如果 targetSum 的值刚好等于这个节点的值了,就说明存在这条路径。路径的总数就 ++ ;
以上是对于一个节点,若是对于整棵树,就需要对每个节点进行遍历。我们使用递归进行深度优先遍历。
-
源码:
class Solution { public int pathSum(TreeNode root, int targetSum) { if(root==null) return 0; // 递归遍历所有节点 int res = nodeSum(root,targetSum); res += pathSum(root.left,targetSum); res += pathSum(root.right,targetSum); return res; } //对单个节点进行递归求 路径个数 public int nodeSum(TreeNode node,int targetSum){ int res =0; if(node==null) return 0; int val = node.val; if(val == targetSum) res++; res += nodeSum(node.left,targetSum-val); res += nodeSum(node.right,targetSum-val); return res; } }
4、节点之和的最大路径
-
描述:
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。给定一个二叉树的根节点 root ,返回其 最大路径和,即所有路径上节点值之和的最大值。
-
解法:
同样对节点和树分别计算:先对于单个节点(非叶子节点,叶子节点就是他本身的值):它的贡献值是本身的值加上 Max( left.val, right.val ) 这里有一点就是如果左右节点的值小于0,那么就直接 +0 表示路径不包含这个节点。求出单个节点的贡献值后,对于整个树的路径,就是 node.left + node + node.right 的最大值就能包含整个树的节点了。
-
源码:
class Solution { private int maxSum = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) { maxGain(root); return maxSum; } public int maxGain(TreeNode node){ if(node==null) return 0; int leftGain = Math.max(maxGain(node.left),0); int rightGain = Math.max(maxGain(node.right),0); int preSum = node.val + leftGain + rightGain; maxSum = Math.max(maxSum,preSum); return node.val+Math.max(leftGain,rightGain); } }
5、展平二叉搜索树
-
描述:
给你一棵二叉搜索树,请 按中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。
-
解法:
就是对给定的树进行中序遍历,创建一个根节点,使它的 right 指向每一个遍历到的节点,同时将遍历到的节点的 left 指向空(取消原来的指针),将它的 right 指向下一个遍历到的节点。
-
源码:
class Solution { //创建根节点 private TreeNode resNode; public TreeNode increasingBST(TreeNode root) { if(root == null) return null; resNode = new TreeNode(-1); //用来保存根节点的值 TreeNode head = resNode; midTra(root); return head.right; } //中序遍历 public void midTra(TreeNode root){ if(root == null) return; midTra(root.left); resNode.right = root; root.left = null; resNode = root; midTra(root.right); } }
6、二叉搜索树的中序后继
-
描述:
给定一棵二叉搜索树和其中的一个节点 p ,找到该节点在树中的中序后继。如果节点没有中序后继,请返回 null 节点 p 的后继是值比 p.val 大的节点中键值最小的节点,即按中序遍历的顺序节点 p 的下一个节点。
-
解法:
该树是二叉搜索树,左节点 < 中节点 < 右节点, 刚好与中序遍历的顺序相同,也就是说后继节点的值要比该节点的值大。而且是紧邻的大的那个,所以当节点值比目标节点值大时,要继续往左子树查找,找到刚好大的那个。如果节点值小于给定节点的话,就应该往右子树去找了。
-
源码:
class Solution { public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { if(root == null) return null; TreeNode res = null; while(root != null){ //节点值大于目标值 if(root.val > p.val){ res = root; root = root.left; }else{ //节点值小于目标值 root = root.right; } } return res; } }
7、所有大于等于该节点的值的和
-
描述:
给定一个二叉搜索树,请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。
-
解法:
从上个题我们知道二叉搜索树的特点值小的在左边,大的在右边。所以本题我们应该从右子树来遍历,右-》中-》左。然后每个节点的值是前面节点的值加上自己的值。递归是个好东西。
-
源码:
class Solution { int sum = 0; public TreeNode convertBST(TreeNode root) { if(root != null){ convertBST(root.right); sum += root.val; root.val = sum; convertBST(root.left); } return root; } }
8、二叉搜索树的迭代器
-
描述:
实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:
BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
int next()将指针向右移动,然后返回指针处的数字。
注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。 你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。
-
解法:
1、其实就是书的中序遍历,我们很容易想到把中序遍历里的结果存储到一个集合中,然后通过取集合中的值和判断集合中是否右后续值来取下一个值 和 判断是否有下一个值。我们这里分别使用ArrayList 和 Deque 来实现。Deque 相对于 ArrayList 的优点在于可以不用一次性将树中的所有节点存放到集合中,而是先存储左子树,每弹出一个节点,再将它的右子树入栈。实现较少内存遍历一次树。时间复杂度都要将整个树遍历一次,都是 O(n)
-
源码:
//ArrayList 解法 class BSTIterator { List<TreeNode> list ; int index = 0; public BSTIterator(TreeNode root) { list = new ArrayList<>(); inorder(root); } public int next() { return list.get(index++).val; } public boolean hasNext() { return index+1>list.size()?false:true; } public void inorder(TreeNode node){ if(node == null) return; inorder(node.left); list.add(node); inorder(node.right); } } //Deque 解法 class BSTIterator { Deque<TreeNode> stack; TreeNode res = new TreeNode(Integer.MIN_VALUE); int index = 0; public BSTIterator(TreeNode root) { res = root; stack = new LinkedList<>(); } public int next() { //先将左子树都入栈,其实中间节点和左节点都入栈了 while(res != null){ stack.push(res); res = res.left; } //弹出节点,将右节点入栈,(栈是先进后出, //所以在遍历中间节点后会紧接着遍历右节点(左子节点莫得右孩子了),符合中序顺序)并将节点值返回 res = stack.pop(); int ret = res.val; res = res.right; return ret; } public boolean hasNext() { return res!=null || !stack.isEmpty(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)