- 广度优先的实现
- 力扣中常见的二叉树相关问题及基本解决方案
tips: 在解决问题时,先确保问题能解决,再去考虑效率,这是解题的关键,切不可为追求效率而变成了技巧性解答。
广度优先
广度优先(层序遍历)遍历的方式是按层次从上到下,从左到右的逐层访问。
[36, 21, 56, 8, 23, 38,72]
public void levelTraversal(TreeNode root) {
Deque<TreeNode> queue = new ArrayDeque<>();
if (root == null) return;
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode peek = queue.pop();
System.out.println(peek.val);
if (peek.left != null) {
queue.offer(peek.left);
}
if (peek.right != null) {
queue.offer(peek.right);
}
}
}
力扣问题
- 102. 二叉树的层序遍历 - 力扣(LeetCode)
- 107. 二叉树的层序遍历 II - 力扣(LeetCode)
二叉树经典例题
计算二叉树的深度
问题
[LCR 175] LCR 175. 计算二叉树的深度 - 力扣(LeetCode)
问题描述
某公司架构以二叉树形式记录,请返回该公司的层级数。
示例 1:
输入:root = [1, 2, 2, 3, null, null, 5, 4, null, null, 4]
输出: 4
解释: 上面示例中的二叉树的最大深度是 4,沿着路径 1 -> 2 -> 3 -> 4 或 1 -> 2 -> 5 -> 4 到达叶节点的最长路径上有 4 个节点。
解决方案
参考实现
class Solution {
public int calculateDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(calculateDepth(root.left) , calculateDepth(root.right))+1;
}
}
将有序数组转换为二叉搜索树
问题
108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
问题描述
给你一个整数数组 nums
,其中元素已经按 升序 排列,请你将其转换为一棵
平衡二叉搜索树。
示例 1:
输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
示例 2:
输入:nums = [1,3]
输出:[3,1]
解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。
解决方案
有序数组拆半放入二叉搜索树。
- 左边部分放入搜索树左侧
- 右边部分放入搜索树右侧
参考实现
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
if (nums.length == 0) {
return null;
}
int mid = nums.length / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = sortedArrayToBST(Arrays.copyOfRange(nums, 0, mid));
root.right = sortedArrayToBST(Arrays.copyOfRange(nums, mid + 1, nums.length));
return root;
}
}
寻找二叉搜索树中的目标节点
题目
[LCR174] LCR 174. 寻找二叉搜索树中的目标节点 - 力扣(LeetCode)
题目描述
某公司组织架构以二叉搜索树形式记录,节点值为处于该职位的员工编号。请返回第 cnt
大的员工编号。
示例 1:
输入:root = [7, 3, 9, 1, 5], cnt = 2
7
/ \
3 9
/ \
1 5
输出:7
示例 2:
输入: root = [10, 5, 15, 2, 7, null, 20, 1, null, 6, 8], cnt = 4
10
/ \
5 15
/ \ \
2 7 20
/ / \
1 6 8
输出: 8
解决方案
中序解决排序问题
参考实现
class Solution {
public int findTargetNode(TreeNode root, int cnt) {
List<Integer> list = new ArrayList<Integer>();
inOrder(root,list);
// System.out.println(list);
return list.get(list.size()-cnt);
}
private void inOrder(TreeNode root,List<Integer> list){
if(root ==null){
return ;
}
inOrder(root.left,list);
list.add(root.val);
inOrder(root.right,list);
}
}
计算布尔二叉树的值
题目
[力扣2331] 2331. 计算布尔二叉树的值 - 力扣(LeetCode)
题目描述
给你一棵 完整二叉树 的根,这棵树有以下特征:
- 叶子节点 要么值为
0
要么值为1
,其中0
表示False
,1
表示True
。 - 非叶子节点 要么值为
2
要么值为3
,其中2
表示逻辑或OR
,3
表示逻辑与AND
。
计算 一个节点的值方式如下:
- 如果节点是个叶子节点,那么节点的 值 为它本身,即
True
或者False
。 - 否则,计算 两个孩子的节点值,然后将该节点的运算符对两个孩子值进行 运算 。
返回根节点 root
的布尔运算值。
完整二叉树 是每个节点有 0
个或者 2
个孩子的二叉树。
叶子节点 是没有孩子的节点。
示例 1:
输入:root = [2,1,3,null,null,0,1]
输出:true
解释:上图展示了计算过程。
AND 与运算节点的值为 False AND True = False 。
OR 运算节点的值为 True OR False = True 。
根节点的值为 True ,所以我们返回 true 。
示例 2:
输入:root = [0]
输出:false
解释:根节点是叶子节点,且值为 false,所以我们返回 false 。
解决方案
递归根据情况进行分类。
参考实现
class Solution {
public boolean evaluateTree(TreeNode root) {
if(root.val ==1) return true;
else if(root.val == 0) return false;
else if(root.val ==2) return evaluateTree(root.left) || evaluateTree(root.right);
else return evaluateTree(root.left) && evaluateTree(root.right);
}
}
二叉搜索树的范围和
问题
[力扣938] 938. 二叉搜索树的范围和 - 力扣(LeetCode)
问题描述
给定二叉搜索树的根结点 root
,返回值位于范围 [low, high]
之间的所有结点的值的和。
示例 1:
输入:root = [10,5,15,3,7,null,18], low = 7, high = 15
输出:32
示例 2:
输入:root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10
输出:23
解决方案
从根节点递归到叶节点的过程中,如果小于low,则走右侧(值的范围必须大于等于low)
如果大于high,则走左侧(值的范围必须小于等于high),
如果在范围内[low, high], 则继续遍历其子节点。
参考实现
放入List集合中,按照集合中数据的访问遍历。
class Solution {
public int rangeSumBST(TreeNode root, int low, int high) {
List<Integer> list = new ArrayList<>();
inOrder(root, list);
int sum=0;
for(int t : list){
if(t >=low && t <=high){
sum+=t;
}
}
return sum;
}
private void inOrder(TreeNode root, List<Integer> list){
if(root == null) return ;
inOrder(root.left, list);
list.add(root.val);
inOrder(root.right, list);
}
}
当前递归节点与low和high进行比较
- root< low : 说明左侧没有合适的值,那么继续递归右侧
- root > right: 说明右侧没有合适的值,那么继续递归左侧
- root 在范围之内,则将左侧和右侧的结果相加+ 当前节点的值。
class Solution {
public int rangeSumBST(TreeNode root, int low, int high) {
if(root == null) return 0;
if(root.val < low){
return rangeSumBST(root.right, low, high);
}
if(root.val > high){
return rangeSumBST(root.left, low, high);
}
return rangeSumBST(root.left,low,high)
+ rangeSumBST(root.right,low,high)
+ root.val;
}
}
二叉搜索树中第K小的元素
题目
230. 二叉搜索树中第 K 小的元素 - 力扣(LeetCode)
题目描述
给定一个二叉搜索树的根节点 root
,和一个整数 k
,请你设计一个算法查找其中第 k
小的元素(从 1 开始计数)。
示例 1:
输入:root = [3,1,4,null,2], k = 1
输出:1
示例 2:
输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3
解决方案
中序遍历存入list集合
找到list中的第k小项
参考实现
class Solution {
public int kthSmallest(TreeNode root, int k) {
List<Integer> list = new ArrayList<>();
inOrder(root,list);
return list.get(k-1);
}
private void inOrder(TreeNode root,List<Integer> list){
if(root==null) return ;
inOrder(root.left, list);
list.add(root.val);
inOrder(root.right,list);
}
}
二叉搜索树中的中序后继
题目
[力扣LCR053] LCR 053. 二叉搜索树中的中序后继 - 力扣(LeetCode)
题目分析
给定一棵二叉搜索树和其中的一个节点 p
,找到该节点在树中的中序后继。如果节点没有中序后继,请返回 null
。
节点 p
的后继是值比 p.val
大的节点中键值最小的节点,即按中序遍历的顺序节点 p
的下一个节点。
示例 1:
输入:root = [2,1,3], p = 1
输出:2
解释:这里 1 的中序后继是 2。请注意 p 和返回值都应是 TreeNode 类型。
示例 2:
输入:root = [5,3,6,2,4,null,null,1], p = 6
输出:null
解释:因为给出的节点没有中序后继,所以答案就返回 null 了。
解决方案
- 中序记录在List集合中,找到p在list中的位置索引,如果索引>=0 || 不是最后一个位置则返回,否则返回null。
- 记录上一个节点pre, 当前curr。
参考实现
class Solution {
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
TreeNode pre = null;
while(curr!=null || !stack.isEmpty()){
while(curr!=null){
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
if(pre == p){
return curr;
}
pre = curr;
curr = curr.right;
}
return null;
}
}
两数之和IV-输入二叉搜索树
问题
[力扣653] 653. 两数之和 IV - 输入二叉搜索树 - 力扣(LeetCode)
问题描述
给定一个二叉搜索树 root
和一个目标结果 k
,如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果,则返回 true
。
示例 1:
输入: root = [5,3,6,2,4,null,7], k = 9
输出: true
示例 2:
输入: root = [5,3,6,2,4,null,7], k = 28
输出: false
解决方案
- 将数据存入一个List集合
- 问题转换为找到一个集合中的和为K的两个数。
参考实现
class Solution {
public boolean findTarget(TreeNode root, int k) {
Map<Integer, Integer> map = new HashMap<>();
List<Integer> list = new ArrayList<>();
final int DEFAULT_VAL = 999;
inOrder(root, list);
//System.out.println(list);
for(int t : list){
int target = k - t;
if(map.containsKey(target)){
return true;
}
map.put(t, DEFAULT_VAL);
}
return false;
}
private void inOrder(TreeNode root, List<Integer> list) {
if (root == null) return ;
inOrder(root.left, list);
list.add(root.val);
inOrder(root.right, list);
}
}
class Solution {
public boolean findTarget(TreeNode root, int k) {
Map<Integer,Integer> map = new HashMap<>();
List<TreeNode> stack = new ArrayList<>();
TreeNode curr = root;
while(curr!=null || !stack.isEmpty()){
while(curr!=null){
int target= k - curr.val;
if(map.containsKey(target)){
return true;
}
map.put(curr.val,999);
stack.add(curr);
curr = curr.left;
}
curr = stack.remove(stack.size()-1);
curr = curr.right;
}
return false;
}
}