617.合并二叉树
题意:给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
1、思路:递归法
- 本题使用哪种遍历都是可以的!
- 确定递归函数的参数和返回值:
TreeNode mergeTrees(TreeNode root1, TreeNode root2)
,参数传入两个树的根节点 - **确定终止条件:**传入两棵树,遍历时就会存在t1和t2两个节点,当t1为空时,直接返回t2,当t2为空时,直接返回t1
- **确定单层递归的逻辑:**可以借助t1重复利用,也可以重新创建一个树
TreeNode root = new TreeNode(root1.val + root2.val);
,并依次合并左子树和右子树
- 确定递归函数的参数和返回值:
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
//DFS 递归
if (root1 == null) return root2;
if (root2 == null) return root1;
TreeNode root = new TreeNode(root1.val + root2.val);
root.left = mergeTrees(root1.left, root2.left);
root.right = mergeTrees(root1.right, root2.right);
return root;
}
2、思路:迭代法
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
//BFS 迭代
if (root1 == null) return root2;
if (root2 == null) return root1;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root1);
queue.offer(root2);
while (!queue.isEmpty()) {
TreeNode node1 = queue.poll();
TreeNode node2 = queue.poll();
//只处理两个节点都不为空的情况
node1.val = node1.val + node2.val;
//如果两棵树的左节点都不为空,则加入队列中
if (node1.left != null && node2.left != null) {
queue.offer(node1.left);
queue.offer(node2.left);
}
//如果两棵树的右节点都不为空,则加入队列中
if (node1.right != null && node2.right != null) {
queue.offer(node1.right);
queue.offer(node2.right);
}
//如果node1的左节点为空,node2的左节点不为空
if (node1.left == null && node2.left != null) {
node1.left = node2.left;
}
//如果node1的右节点为空,node2的右节点不为空
if (node1.right == null && node2.right != null) {
node1.right = node2.right;
}
}
return root1;
}
700.二叉搜索树中的搜索
题意:给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
1、思路:递归法
- 二叉搜索树是一个有序树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉搜索树
- 普通二叉树方式
- 确定递归函数的参数和返回值:
TreeNode searchBST(TreeNode root, int val)
,参数传入二叉树根节点和要查找的值 - 确定终止条件:如果当前节点等于空或者当前节点的值等于要查找的值,就直接返回当前节点
- 确定单次递归的逻辑:
- 先递归左子树,如果左子树上存在该值,就直接返回节点
- 如果左子树不存在,则递归右子树,并直接返回右子树
public TreeNode searchBST(TreeNode root, int val) {
//DFS 递归 普通二叉树方式
//如果当前节点等于null,则直接返回该节点,或者当前节点不等于null,当前节点的值等于val
if (root == null || root.val == val) return root;
TreeNode left = searchBST(root.left, val);
if (left != null) {
return left;
}
return searchBST(root.right, val);
}
- 针对二叉搜索树
- 递归函数的参数和返回值,终止条件都相同
- 对于单次递归逻辑来说,因为二叉搜索树是有序的,所以可以判断当前节点值和要查找值之间关系,如果当前节点值小于要查找值,则直接查找右子树并返回,否则就直接查找左子树并直接返回
public TreeNode searchBST(TreeNode root, int val) {
//DFS 递归 二叉搜索树
//如果当前节点等于null,则直接返回该节点,或者当前节点不等于null,当前节点的值等于val
if (root == null || root.val == val) return root;
if (root.val > val) {
return searchBST(root.left, val);
}else {
return searchBST(root.right, val);
}
}
2、思路:迭代法
- 利用栈实现前序遍历,当碰到当前节点的值与查找的值相等时,直接返回当前节点
public TreeNode searchBST(TreeNode root, int val) {
//DFS 迭代 前序 普通二叉树
if (root == null) return null;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
if (node != null && node.val == val) {
return node;
}
if (node.left != null) stack.push(node.left);
if (node.right != null) stack.push(node.right);
}
return null;
}
- 二叉树遍历的迭代法,可能立刻想起使用栈来模拟深度遍历,使用队列来模拟广度遍历
- 对于二叉搜索树可就不一样了,因为二叉搜索树的特殊性,也就是节点的有序性,可以不使用辅助栈或者队列就可以写出迭代法。
- 循环判断当前节点是否为null,在循环体中判断当前节点值与查找值是否相等
public TreeNode searchBST(TreeNode root, int val) {
//DFS 迭代 前序 搜索二叉树
while (root != null) {
if (root.val == val) return root;
if (root.val > val) root = root.left;
else root = root.right;
}
return null;
}
98.验证二叉搜索树
题意:给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
1、思路:递归法
- 第一种方法:采用中序遍历,转变成一个数组,只需要判断这个数组是否有序
- 第二种方法:不需要创建数据,直接比较,同样需要采用中序遍历
- 确定递归函数和返回值,
boolean isValidBST(TreeNode root)
- 确定终止条件,
if (root == null) return true;
- 确定单次递归的逻辑
- 利用中序进行递归遍历左右子树
- 在中间节点进行处理,当max节点不为空并且当前节点的值小于等于max节点的值,直接返回false
- 无论max节点是否为空,都将当前节点赋值给max,max主要用来记录上一个节点
- 确定递归函数和返回值,
private TreeNode max;
public boolean isValidBST(TreeNode root) {
//DFS 递归 不能单纯比较左右节点与当前节点的大小
//需要采用中序遍历
if (root == null) return true;
//左
boolean left = isValidBST(root.left);
if (!left) return false;
//中
if (max != null && max.val >= root.val) return false;
max = root;
//右
boolean right = isValidBST(root.right);
return right;
}
2、思路:迭代法
- 中序迭代法,没采用统一迭代法
- 创建一个栈,循环判断当前节点不为空或者栈不为空
- 当当前节点不为空,就一直往左节点遍历
- 中间节点处理逻辑与递归类似,如果max节点不为空并且当前节点的值小于max节点,就直接返回false
public boolean isValidBST(TreeNode root) {
//DFS 迭代法
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
TreeNode max = null;
while (node != null || !stack.isEmpty()) {
//左
while (node != null) {
stack.push(node);
node = node.left;
}
//中
TreeNode pop = stack.pop();
if (max != null && pop.val <= max.val) return false;
max = pop;
//右
node = pop.right;
}
return true;
}
530.二叉搜索树的最小绝对差
题意:给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
1、思路:递归法
- 因为是二叉搜索树,所以最小绝对差一定是中序遍历的相邻两个值
- 采用中序遍历
- 确定递归的函数和返回值,
void getMinimum(TreeNode root)
,参数传入根节点 - 确定终止条件,当当前节点为空,直接返回
- 确定单次递归的逻辑
- 首先递归左子树
- 对中间节点进行处理,当前继节点不为空,比较当前节点与前继节点值的差值与结果比价,并将较小值赋给结果
- 递归右子树
- 确定递归的函数和返回值,
private int result = Integer.MAX_VALUE;
private TreeNode pre;
public int getMinimumDifference(TreeNode root) {
//DFS 递归 中序
if (root == null) return 0;
getMinimum(root);
return result;
}
private void getMinimum(TreeNode root) {
if (root == null) return;
//左
getMinimum(root.left);
//中
if (pre != null) result = Math.min(result, root.val - pre.val);
pre = root;
//右
getMinimum(root.right);
}
2、思路:迭代法
- 采用中序迭代法
- 其余逻辑与中序递归法一样
public int getMinimumDifference(TreeNode root) {
//DFS 迭代法 中序
if (root == null) return 0;
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
TreeNode pre = null;
int result = Integer.MAX_VALUE;
while (node != null || !stack.isEmpty()) {
//左
if (node != null) {
stack.push(node);
node = node.left;
} else {
//中
TreeNode pop = stack.pop();
if (pre != null) result = Math.min(result, pop.val - pre.val);
pre = pop;
//右
node = pop.right;
}
}
return result;
}
501.二叉搜索树中的众数
题意:给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
假定 BST 有如下定义:
- 结点左子树中所含结点的值小于等于当前结点的值
- 结点右子树中所含结点的值大于等于当前结点的值
- 左子树和右子树都是二叉搜索树
1、思路:递归法
- 普通二叉树方式
- 采用任何遍历顺序都可以,采用前序遍历,并用map记录每个数出现的次数
- 重点在于如果对Map的value进行排序
- 通过
map.entrySet()
获取map的entryset集合 - 将entrySet转换成流,调用流的sorted方法,对map中的value进行排序
- 排序完成调用
collect(Collectors.toList())
转换成List集合
- 通过
public int[] findMode(TreeNode root) {
//DFS 递归法 遍历整个二叉树 当成普通二叉树来操作
Map<Integer, Integer> map = new HashMap<>();
List<Integer> list = new ArrayList<>();
if (root == null) return list.stream().mapToInt(Integer::intValue).toArray();
findMode1(root, map);
List<Map.Entry<Integer, Integer>> mapList = map.entrySet().stream().sorted(new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
return o2.getValue() - o1.getValue();
}
}).collect(Collectors.toList());
list.add(mapList.get(0).getKey());
for (int i = 1; i < mapList.size(); i++) {
if (mapList.get(i).getValue() == mapList.get(i - 1).getValue()) {
list.add(mapList.get(i).getKey());
} else {
break;
}
}
return list.stream().mapToInt(Integer::intValue).toArray();
}
private void findMode1(TreeNode node, Map<Integer, Integer> map) {
if (node == null) return;
//前序遍历
map.put(node.val, map.getOrDefault(node.val, 0) + 1);
findMode1(node.left, map);
findMode1(node.right, map);
}
- 二叉搜索树
- 必须使用中序遍历,因为中序遍历是递增数组
- 定义前继节点pre、当前值出现的次数count,以及出现的最大次数maxCount
- 主要在于中间节点的处理逻辑
- 如果前继节点为空或着前继节点的值不等于当前节点的值,count赋值为1,否则count++
- 如果count大于maxCount,那么就将结果集情况,并将当前节点值存放到结果集中,maxCount修改为count,如果count与maxCount相等,将当前节点值存放到结果集中
- 前继节点pre修改为当前节点
ArrayList<Integer> list;
private TreeNode pre;
private int count;
private int maxCount;
public int[] findMode(TreeNode root) {
//DFS 递归法 遍历整个二叉树 当成搜索二叉树来操作
//中序遍历
if (root == null) return null;
list = new ArrayList<>();
count = 0;
maxCount = 0;
findMode1(root);
return list.stream().mapToInt(Integer::intValue).toArray();
}
private void findMode1(TreeNode root) {
if (root == null) return;
//左
findMode1(root.left);
//中
if (pre == null || pre.val != root.val) {
count = 1;
} else {
count++;
}
//更新结果
if (count > maxCount) {
list.clear();
list.add(root.val);
maxCount = count;
} else if (count == maxCount) {
list.add(root.val);
}
pre = root;
findMode1(root.right);
}
2、思路:迭代法
- 与递归法完全一样
public int[] findMode(TreeNode root) {
//DFS 迭代法
if (root == null) return null;
ArrayList<Integer> list = new ArrayList<>();
TreeNode pre = null;
int count = 0;
int maxCount = 0;
TreeNode node = root;
Stack<TreeNode> stack = new Stack<>();
while (node != null || !stack.isEmpty()) {
if (node != null) {
stack.push(node);
node = node.left;
} else {
node = stack.pop();
//中
if (pre == null || pre.val != node.val) {
count = 1;
} else {
count++;
}
//更新结果
if (count > maxCount) {
list.clear();
list.add(node.val);
maxCount = count;
} else if (count == maxCount) {
list.add(node.val);
}
pre = node;
node = node.right;
}
}
return list.stream().mapToInt(Integer::intValue).toArray();
}
236. 二叉树的最近公共祖先
题意:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
1、思路:递归法
- 首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了
- 后序遍历
- 确定递归函数返回值以及参数:
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
,参数传入树的根节点、两个节点p和q - 确定终止条件:如果找到了 节点p或者q,或者遇到空节点,就返回。
- 确定单次递归的逻辑:
- 需要遍历整个树,想利用left和right做逻辑处理, 不能立刻返回,而是要等left与right逻辑处理完之后才能返回。
- 如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解
- 如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然。
- 如果left和right都为空,就直接返回空
- 确定递归函数返回值以及参数:
package com.yzu.lee.treenode;
/**
* @ClassName: LowestCommonAncestor
* @Description:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
* 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x
* ,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
* @author: Leekuangyew
* @date: 2022/5/30 16:19
*/
public class LowestCommonAncestor {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//终止条件
if (root == null || root == p || root == q) return root;
//后序遍历
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
//单次递归条件
if (left == null && right == null) return null;
else if (left == null && right != null) return right;
else if (right == null && left != null) return left;
else return root;
}
}