所用代码 java
二叉搜索树的最小绝对差 LeetCode 530
题目链接:二叉搜索树的最小绝对差 LeetCode 530 - 简单
思路
这个题和昨天的验证二叉搜索树时一样的,中序遍历的二叉搜索树,他的结点是单调递增的,所以可以用一个指针指向前一个结点,用现结点的值减前向结点的值。
递归法:
class Solution {
int min = Integer.MAX_VALUE;
// 用来指向二叉搜索树的前一个结点
TreeNode pre = null;
public int getMinimumDifference(TreeNode root) {
traversal(root);
return min;
}
// 二叉搜索树一般中序遍历
public void traversal(TreeNode root){
if (root == null) return;
// 左
traversal(root.left);
// 中 - 后一个结点值减前一个结点值便是最小值
if (pre != null && root.val - pre.val < min){
min = root.val - pre.val;
}
// 当 root=null, return
// 当 root=1, pre=null
// 继续递归 root=2, pre=1
pre = root;
// System.out.println("min = " + min);
// 右
traversal(root.right);
}
}
迭代法:
class Solution {
public int getMinimumDifference(TreeNode root) {
if (root == null) return 0;
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null;
int min = Integer.MAX_VALUE;
stack.push(root);
// 中序:左 中 右
while (!stack.isEmpty()) {
// 要把访问过的结点先拿出来
TreeNode node = stack.pop();
if (node != null) {
if (node.right != null) stack.push(node.right);
stack.push(node);
stack.push(null); // 存入标记
if (node.left != null) stack.push(node.left);
} else {
// 取出除null的最上层结点
node = stack.pop();
if (pre != null && node.val - pre.val < min){
min = node.val - pre.val;
}
pre = node;
}
}
return min;
}
}
总结
只要知道中序遍历二叉搜索树是一个单调递增的结果,那么就可以直接把结果保存在一个list里面进行各种操作,进阶一点就是直接再遍历的时候进行操作,最后就是双指针操作前一个结点和后一个结点
双指针逻辑:
if (pre != null && 操作逻辑){
操作逻辑
}
// 每次操作了之和把该结点赋值给pre,然后node回溯(或者往右搜索右子树)
pre = node;
二叉搜索树中的众数 LeetCode 501
题目链接:二叉搜索树中的众数 LeetCode 501 - 简单
思路
一种方法就是中序遍历所有结果存入list,再遍历,遇到相同的结果就拿出 - 暴力
另一个就是递归,同样遇到相同的结点就存入
- pre=null时,cur遍历到了左子树的叶子结点,此时cur所出现的次数为1
- 若pre.val=cur.val时,证明前向结点值后向结点值相等,即cur所出现次数+1
- 否则pre.val!=cur.val,证明前向结点值和后向结点值不等,即cur出现1次
- 判断该结点所出现的次数是否等于maxCount,是的话就加入list
- 若不是则更新maxCount,以及更新list
class Solution {
TreeNode pre = null;
int count = 0;
int maxCount = 0;
List<Integer> list = new ArrayList<>();
public int[] findMode(TreeNode root) {
traversal(root);
// 将list转换为int[]数组
// return res.stream().mapToInt(Integer::intValue).toArray(); 这个效率慢一点
int[] res = new int[list.size()];
int index = 0;
for (Integer i : list) {
res[index++] = i;
}
return res;
}
public void traversal(TreeNode cur){
if (cur == null) return;
traversal(cur.left);
// 中 - 处理逻辑
if (pre == null) count = 1;
else if(pre.val == cur.val) count++;
else count = 1; // 前向结点值和当前结点值不等,当前结点就只出现了一次
// 更新pre
pre = cur;
// 添加进list的逻辑
if (count == maxCount) list.add(cur.val);
if (count > maxCount) {
maxCount = count;
list.clear();
list.add(cur.val);
}
traversal(cur.right);
}
}
迭代法:
class Solution {
public int[] findMode(TreeNode root) {
if (root == null) return new int[]{0};
List<Integer> list = new ArrayList<>();
int count = 0;
int maxCount = 0;
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode cur = root;
TreeNode pre = null;
while (cur != null || !stack.isEmpty()){
if (cur != null){
stack.push(cur);
cur = cur.left; // 左
}else {
// 中
TreeNode node = stack.pop();
if (pre == null || pre.val != node.val) count = 1;
else if (pre.val == node.val) count ++;
// 更新pre,使其一直在node的前面
pre = node;
if (count == maxCount) list.add(node.val);
if (count > maxCount){
maxCount = count;
list.clear();
list.add(node.val);
}
cur = node.right; // 右
}
}
// 用stream流的方式比直接new一个数组再赋值要慢
return list.stream().mapToInt(Integer::intValue).toArray();
}
}
二叉树的最近公共祖先 LeetCode 236
题目链接:二叉树的最近公共祖先 LeetCode 236 - 中等
思路
无。
二叉树要从下往上遍历,需要用他的回溯过程去实现,而要在回溯中达到这种效果就需要用后序遍历 - 左 右 中
情况1:p 和 q 的祖先需往上搜索
情况2:p 和 q 某一个结点本身就是祖先
- 首先返回条件:遍历到空结点 返回null
- 当遇到p或q的其中一个结点,直接返回该结点
- 左右递归
- 中,接到左右递归传来的值进行处理
- 中 - 左右子树都不为null,则左右子树分别存在一个p或p,就返回 中 ,此时中为祖先
- 中 - 左为空,右不为空,就向上返回 右;反之左不为空,右为空,就返回 左
- 中 - 左右都为空,则中的孩子没有p或q,就返回null
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return traversal(root, p, q);
}
public TreeNode traversal(TreeNode root, TreeNode p, TreeNode q){
if (root == null) return root;
// 当遍历都某一结点时刚好时p或q,就把这个结点返回
// 此时会报情况2给包含,因为遇到祖先(如p),则返回p(root),后面就会一直返回下层传来的祖先p
if (root == p || root == q) return root;
// 后序:左 -> 右
TreeNode left = traversal(root.left, p, q);
TreeNode right = traversal(root.right, p, q);
// 中 - 处理逻辑 什么情况该结束遍历
// 当左子树有一个结点 且 右子树有一个结点,就把这个根结点给返回,这个结点就是祖先
if (left != null && right != null) return root;
// 只有根结点的左边包含p或q,则返回左子树
if (left != null && right == null) return left;
// 只有右边有
else if (left == null && right != null) return right;
// 左右都没
else return null;
}
}
总结
二叉树进行递归时,对于有没有返回值就是要看我们是不是要处理一整条边,需注意返回之后应该如何处理。另外对于需要从底向上进行操作的二叉树一般都是后序遍历,因为后序遍历存在着由底向上的回溯过程,这个回溯过程就可以看做从下到上的递归逻辑。