提示:努力生活,开心、快乐的一天
文章目录
530. 二叉搜索树的最小绝对差
💡解题思路
- 中序遍历后转数组:根据二叉搜索树的特性,中序遍历得到的数组,是一个有序递增的数组,所以可以先讲二叉树转化为数组,在进行相邻两项的值的差值判断
-
- 递归法:三部曲
- 确定递归函数,返回值以及参数:参数为二叉树
- 确定终止条件:二叉搜索树也可以为空!
- 确定单层递归的逻辑:中序遍历,定义一个指针preNode和一个无穷大的数res,指针指向当前节点的上一个节点,当该指针有值时,Math.min(res, node.val - preNode.val),指针移动,preNode = node
🤔遇到的问题
- 递归法定义的指针,是需要移动的,并且不能为空
- 迭代法流程不熟悉了,此处没有写
💻代码实现
转数组
var getMinimumDifference = function (root) {
//中序遍历,将二叉树转成有序数组
let arr = []
const buildTree = (node) => {
if (node) {
buildTree(node.left)
arr.push(node.val)
buildTree(node.right)
}
}
buildTree(root)
//遍历数组,求相邻两项的最小差值
let res = arr[1] - arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > arr[i - 1]) {
res = Math.min(res, arr[i] - arr[i - 1])
}
}
return res
};
递归
var getMinimumDifference = function (root) {
//定义一个无穷大的数
let res = Infinity
let preNode = null
const inorder = (node) => {
if (!node) return
//左
inorder(node.left)
// 更新res
//中
if (preNode) {
res = Math.min(res, node.val - preNode.val)
}
// 记录前一个节点
preNode = node
//右
inorder(node.right)
}
inorder(root)
return res
};
🎯题目总结
遇到在二叉搜索树上求什么最值,求差值之类的,都要思考一下二叉搜索树可是有序的,要利用好这一特点
在递归遍历的过程中如何记录前后两个指针,这也是一个小技巧
501.二叉搜索树中的众数
💡解题思路
- 普通二叉树的解法:把这个树都遍历了,用map统计频率,把频率排个序,最后取前面高频的元素的集合,具体操作如下:
- 这个树都遍历了,用map统计频率,key:节点值;value:节点值出现的次数
- 定义一个最大出现次数的初始值为root.val的出现次数
- 遍历map,当前值value等于最大出现次数就直接在res增加该key;如果value的值大于原本的maxCount就清空res的所有值,将此时value对应的key放入res中
- 二叉搜索树的解法:既然是搜索树,它中序遍历就是有序的,使用了pre指针和cur指针,具体操作如下:
- 定义count:当前节点出现的次数;定义maxCount:树中出现最多的次数;定义pre:存储当前节点的前一个节点;递归函数,参数传树的根节点
- 确定递归终止条件 :node ===null 叶子节点时终止
- 单层递归逻辑:重点在“中”,前一个节点与当前节点的比较,当两个节点的值相等时,count++,不相等,count=1;count与maxcount做比较,相等,则将node的值,存入res中,当count>maxCount时,maxCount的值更新,res清空,将node的值,存入res中,最后指针移动,保留前一个节点,pre= node
🤔遇到的问题
- map的遍历的时候,key与value的比较及获取出错
- 指针法遍历中的逻辑,不太熟练
💻代码实现
普通二叉树
var findMode = function (root) {
//将数据存储到map中
//key:节点值;value:出现的次数
let map = new Map()
const buildTree = (node) => {
if (node) {
buildTree(node.left)
map.set(node.val, map.has(node.val) ? map.get(node.val) + 1 : 1)
buildTree(node.right)
}
}
buildTree(root)
// 定义一个最大出现次数的初始值为root.val的出现次数
let maxCount = map.get(root.val)
// 定义一个存放结果的数组res
let res = []
for (let [key, value] of map) {
// 如果当前值等于最大出现次数就直接在res增加该值
if (value === maxCount) {
res.push(key)
} else if (value > maxCount) {
// 如果value的值大于原本的maxCount就清空res的所有值,因为找到了更大的
maxCount = value
res = []
res.push(key)
}
}
return res
};
二叉搜索树
var findMode = function (root) {
//当前节点的出现次数
let count = 0
//出现最大次数初始值为1
let maxCount = 1
let pre = root
let res = []
// 1.确定递归函数及函数参数
const travelTree = (node) => {
// 2. 确定递归终止条件 叶子节点
if (node === null) return
travelTree(node.left)
// 3. 单层递归逻辑 中
//当前节点与上一个节点的值相同
if (pre.val === node.val) {
count++
} else {
//不同的话,该节点的就是第一次出现
count = 1
}
//该节点出现次数与最大次数比较,相同则将该节点值放入数组中
if (count === maxCount) {
res.push(node.val)
} else if (count > maxCount) {
//该节点出现次数比最大次数大,那么最大次数的值更新,res清空后,再把该节点值放入
maxCount = count
res = []
res.push(node.val)
}
//指针移动,保留前一个节点
pre =node
travelTree(node.right)
}
travelTree(root)
return res
};
🎯题目总结
二叉搜索树的所有遍历,都可以使用其特性进行,同时普通的二叉树,也有保守的方法可以解决该问题
236. 二叉树的最近公共祖先
💡解题思路
- 查找公共祖先,需要自底向上查,二叉树回溯的过程就是从低到上,后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
- 如何判断一个节点是节点q和节点p的公共祖先?
- 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先
- 就是节点本身p(q),它拥有一个子孙节点q§,遇到 q 或者 p 就返回,这样也包含了 q 或者 p 本身就是 公共祖先的情况
- 递归三部曲:
- 确定递归函数返回值以及参数:需要递归函数返回值,来告诉我们是否找到节点q或者p;参数就是根节点及p、q
- 确定终止条件:遇到空的话,因为树都是空了,所以返回空;如果 root == q,或者 root == p,说明找到 q p ,则将其返回
- 确定单层递归逻辑: 本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,
遍历左右子树
,拿遍历后的结果处理中间节点,如果left 和 right都不为空,说明此时root就是最近公共节点
;如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然
;如果left和right都为空,则返回left或者right都是可以的,也就是返回空
🤔遇到的问题
- 搜索一条边还是搜索一棵树的问题
💻代码实现
递归法
var lowestCommonAncestor = function(root, p, q) {
const travelTree = (node, p, q)=>{
if (node === null || node === p || node === q) return node
let left = travelTree(node.left, p, q)
let right = travelTree(node.right, p, q)
若找到两个节点
if (left !== null && right !== null) return node
若找到一个节点
// if (left !== null && right === null) return left
// if (left === null && right !== null) return right
//没有找到节点
// if (left === null && right === null) return null
//可简化
if(left === null) {
return right;
}
return left;
}
return travelTree(root,p,q)
};
🎯题目总结
1、求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式
2、在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断
3、要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果
4、递归函数又返回值的情况,如何是搜索一条边,如何是搜索一棵树?
如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)
🎈今日心得
二叉树的最近祖先这道题,确实难,也不好理解