代码随想录 | 进阶二叉树
- 二叉树的构造默认如下:
/**
* 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;
* }
* }
*/
654. 最大二叉树
给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
TreeNode node = construct(nums, 0, nums.length);//本题数组区间为左闭右开
return node;
}
TreeNode construct(int[] nums,int begin,int end){
if(nums.length==1){
return new TreeNode(nums[0]);//数组的长度为1,返回数组中的数构建的结点
}
//遍历数组,得到最大值的下标
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int max = 0;
for (int i = begin; i < end; i++) {
map.put(nums[i], i);//数组中的值为key,下标为value
max = Math.max(max,nums[i]);//找到数组中的最大值
}
int index = map.get(max);//数组中最大值的下标
TreeNode node = new TreeNode(max);//构建结点
if(index>begin){//如果左区间>=1
node.left = construct(nums,begin,index);
}
if(index<end-1){//如果右区间>=1
node.right = construct(nums,index+1,end);
}
return node;
}
}
- 还是构造二叉树的题目,但是比着昨天的两道要简单太多了,感觉自己也慢慢理解二叉树的递归构造
617. 合并二叉树
给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
ψ(`∇´)ψ 我的思路
- 前序遍历两棵树的所有结点(思路题目上写的很清楚了)
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
TreeNode node;
if (root1 == null && root2 == null) {
return null;
} else if (root1 == null && root2 != null) {
node = root2;
} else if (root2 == null && root1 != null) {
node = root1;
} else {
node = new TreeNode(root1.val+ root2.val);
node.left = mergeTrees(root1.left,root2.left);
node.right = mergeTrees(root1.right,root2.right);
}
return node;
}
}
- 麻麻,我会递归喇😭
700. 二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
- 考察BST的特性:当前结点左子树所有结点的值都小于当前结点,当前结点右子树所有结点的值都大于当前结点
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root==null){
return null;
}
if(root.val==val){
return root;
}
else if(root.val>val){
return searchBST(root.left,val);}
else
return searchBST(root.right,val);
}
}
时间复杂度O(n)
- 今天的题写的特别顺欸,不知道是我进步了,还是题变简单了
98. 验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
BST用中序遍历,结点值递增
class Solution {
//中序遍历
long max = Long.MIN_VALUE;//因为题目中最小结点的最小值是从Integer.MIN_VALUE开始的,要找比这个数还小的值,所以选了Long.MIN_VALUE
public boolean isValidBST(TreeNode root) {
if(root==null){return true;}//空结点既是二叉搜索树,又是平衡树,又是完全二叉树,又是满二叉树
boolean left = isValidBST(root.left);//左
if (root.val>max){//中
max = root.val;//把当前值赋给max
} else {
return false;//当前结点的值小于等于上一个结点的值,不是BST
}
boolean right = isValidBST(root.right);//右
return left && right;
}
}
- 代码还可以改进成双指针,这样就不用想着怎么找比结点的最小值还小的数了
530. 二叉搜索树的最小绝对差
ψ(`∇´)ψ 我的思路
- 中序遍历得到递增的链表,遍历链表求两个相邻元素的差值,取最小
class Solution {
public int getMinimumDifference(TreeNode root) {
List<Integer> path = new ArrayList<>();
inOrder(root,path);
Integer[] nums = path.toArray(new Integer[0]);
int fast=1;
int slow =0;
int min = Integer.MAX_VALUE;
for (int i = 0; i < nums.length-1; i++) {
min = Math.min(min,nums[fast]-nums[slow]);
fast++;
slow++;
}
return min;
}
void inOrder(TreeNode root, List<Integer> path){
if(root.left!=null){
inOrder(root.left,path);
}
path.add(root.val);
if(root.right!=null){
inOrder(root.right,path);
}
}
}
501. 二叉搜索树中的众数
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
结点左子树中所含节点的值 小于等于 当前节点的值
结点右子树中所含节点的值 大于等于 当前节点的值
左子树和右子树都是二叉搜索树
本题BST的定义发生改变:
结点左子树中所含节点的值 小于等于 当前节点的值
结点右子树中所含节点的值 大于等于 当前节点的值
- ❗标记,标记,这题很好,双指针中序遍历BST,通过更新结果集的方式减少一次遍历
- 下面是我听完视频后的第一次实现,把要用到的东西全加在了参数里,结果不行,参数一直在变。
class Solution {
TreeNode pre;
int count;
public int[] findMode(TreeNode root) {
List<Integer> res = new ArrayList<>();
int maxCount = 1;
inOrder(root,count,maxCount,res);
int[] result = new int[res.size()];
for (int i = 0; i < result.length; i++) {
result[i] = res.get(i);
}
return result;
}
// 中序遍历
void inOrder(TreeNode cur,int count,int maxCount,List<Integer> res){
if(cur==null){return;}
if(cur.left!=null){inOrder(cur.left,count,maxCount,res);}//左
if(pre == null){
count = 1;//上一结点为空时,当前结点是第一个结点
}
else if (cur.val == pre.val){
count++;//当前结点和上一结点的值相等,count++
} else {
count = 1;//当前结点和上一结点的值不等,count=1
}
if(count==maxCount){
res.add(cur.val);//如果当前结点的次数=最大次数时,当前结点的值加入到结果集中
}
if(count>maxCount){
//如果当前结点的count>最大次数
maxCount = count;//更新maxCount
res.clear();//清空res
res.add(cur.val);
}
pre = cur;
if(cur.right!=null){inOrder(cur.right,count,maxCount,res);}//右
}
}
- 把参数拿出来,测试通过
class Solution {
int count;
List<Integer> res = new ArrayList<>();
TreeNode pre;
int maxCount;
public int[] findMode(TreeNode root) {
inOrder(root);
int[] result = new int[res.size()];
for (int i = 0; i < result.length; i++) {
result[i] = res.get(i);
}
return result;
}
// 中序遍历
void inOrder(TreeNode cur){
if(cur==null){return;}
if(cur.left!=null){inOrder(cur.left);}//左
if(pre == null){
count = 1;//上一结点为空时,当前结点是第一个结点
}
else if (cur.val == pre.val){
count++;//当前结点和上一结点的值相等,count++
} else {
count = 1;
}
if(count==maxCount){
res.add(cur.val);//如果当前结点的次数=最大次数时,当前结点的值加入到结果集中
}
if(count>maxCount){
//如果当前结点的count>最大次数
maxCount = count;//更新maxCount
res.clear();//清空res
res.add(cur.val);
}
pre = cur;
if(cur.right!=null){inOrder(cur.right);}//右
}
}
- 挺难的这题,卡我一天
236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。
-
要找最近的公共祖先,就要从下往上遍历,选择后序遍历。找到目标(p,q)就向上返回,如果当前结点的左右孩子都不空,就是结果,把结果再向上传递。
-
有一种特例就是,一个目标结点a在另一个目标结点p里面,此时结果为p。这时我们只会找到一个目标结点p,当遇到结点p时就返回p,并不会再遍历到a。因为找不到另一个结点,p会被一直向上传递,最终返回。所以即使我们不再特殊处理这种情况,也对结果没有影响。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null){return null;}
if(root==p||root==q){
return root;//找到目标值解返回
}
TreeNode left = null;
if(root.left!=null){
left = lowestCommonAncestor(root.left, p, q);//左
}
TreeNode right = null;
if(root.right!=null){
right = lowestCommonAncestor(root.right, p, q);//右
}
//下面是中的逻辑
if(left!=null&&right!=null){
return root;//如果左右都有目标,返回当前结点
} else if (left!=null&&right==null) {
return left;//如果左孩子有目标值返回左孩子
} else if (left==null&&right!=null) {
return right;//如果右孩子有目标值返回右孩子
} else {
return null;//左右都没有找到目标,返回null
}
}
}
235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
- 用上题的方法完全可解,但是BST有它自己的特性(如果当前结点的值在p,q之间,当前结点即为所求)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null){return null;}
if(root.val>p.val&&root.val>q.val){//如果当前结点的值大于p,q的值,就在当前左子树中寻找
root = lowestCommonAncestor(root.left, p, q);
} else if(root.val<p.val&&root.val<q.val){//如果当前结点的值小于p,q的值,就在当前右子树中寻找
root = lowestCommonAncestor(root.right,p,q);
} else if (root.val>p.val&&root.val<q.val) {//如果当前的结点值在p和q之间,当前结点即为所求
return root;
}
return root;
}
}
701. 二叉搜索树中的插入操作
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
- 思路还是比较清晰的,插入的值小于当前结点的时候,左子树空直接插,左子树不空调用递归插入左子树中。右边一样
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root==null){return new TreeNode(val);}
if(val< root.val){
//要插入的值小于当前结点的值
if(root.left==null){root.left = new TreeNode(val);}//结点的左子树为空,直接插入
else {root.left = insertIntoBST(root.left,val);}//左子树不空,就在左子树里插入
}
else if (val> root.val) {
//要插入的值大于当前结点的值
if(root.right==null){root.right = new TreeNode(val);}//结点的右子树为空,直接插入
else {root.right = insertIntoBST(root.right,val);}//就在右子树里插入
}
return root;
}
}
450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
分以下5种情况:
-
要删除的结点在二叉树中不存在
-
要删除的结点存在于二叉树中:
(1) 要删除的结点下没有孩子,直接返回null
(2) 要删除的结点下只有左孩子,返回他的左孩子
(3) 要删除的结点下只有右孩子,返回他的右孩子
(4) 要删除的结点下有两个孩子,临时变量保存这两个孩子,右孩子继位并找右孩子的最左侧结点,把保存的左孩子挂在上面
class Solution {
TreeNode temp1;
TreeNode temp2;
public TreeNode deleteNode(TreeNode root, int key) {
if(root==null){return null;}
if(root.val==key){//找到要删除的结点时
if(root.left==null&&root.right==null){
return null;//如果当前结点是叶子结点
} else if (root.left!=null&&root.right==null) {
return root.left;//当前结点只有左孩子,左孩子继位
} else if (root.left==null&&root.right!=null) {
return root.right;//当前结点只有右孩子,右孩子继位
} else {
//当前结点有两个孩子
temp1 = root.left;//先把root的左孩子存起来
temp2 = root.right;//把当前结点储存起来
root = root.right;//让右孩子继位
while (root.left!=null){root = root.left;}//找到右子树下最左侧的结点
root.left = temp1;//把原来结点的左孩子挂在找到的结点下
return temp2;
}
}
if(key<root.val){
//去左子树下删
root.left = deleteNode(root.left, key);
}
if(key>root.val){
//去右子树下删
root.right = deleteNode(root.right, key);
}
return root;
}
}
669. 修剪二叉搜索树
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if(root==null){
return null;
}
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 ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
//递归区间为左闭右开
TreeNode root = sortedArrayToBST(nums,0, nums.length);
return root;
}
TreeNode sortedArrayToBST(int[] nums,int left,int right) {
if(left >= right){
return null;//区间里没有值
}if(right-left == 1){
return new TreeNode(nums[left]);//区间里只有一个值
}
int mid = (right + left) / 2; //数组下标中间值
TreeNode node = new TreeNode(nums[mid]);//新建结点
node.left = sortedArrayToBST(nums,left,mid);//在中点的左区间里递归
node.right = sortedArrayToBST(nums, mid+1, right);//在中点的右区间里递归
return node;
}
}
538. 把二叉搜索树转换为累加树
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
class Solution {
int sum;
public TreeNode convertBST(TreeNode root) {
sum = 0;
convert(root);//改变树中结点的值
return root;
}
TreeNode convert(TreeNode root) {
if(root==null){
return null;
}
// 反中序遍历:右中左
convert(root.right);
sum += root.val;
root.val = sum;
convert(root.left);
return root;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?