跟着代码随想录练算法——二叉树
- [106. 从中序与后序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/)
- [105. 从前序与中序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
- [654. 最大二叉树](https://leetcode.cn/problems/maximum-binary-tree/)
- [617. 合并二叉树](https://leetcode.cn/problems/merge-two-binary-trees/)
- [700. 二叉搜索树中的搜索](https://leetcode.cn/problems/search-in-a-binary-search-tree/)
- [98. 验证二叉搜索树](https://leetcode.cn/problems/validate-binary-search-tree/)
- [530. 二叉搜索树的最小绝对差](https://leetcode.cn/problems/minimum-absolute-difference-in-bst/)
- [501. 二叉搜索树中的众数](https://leetcode.cn/problems/find-mode-in-binary-search-tree/)
- [236. 二叉树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/)
- [235. 二叉搜索树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/)
- [701. 二叉搜索树中的插入操作](https://leetcode.cn/problems/insert-into-a-binary-search-tree/)
- [450. 删除二叉搜索树中的节点](https://leetcode.cn/problems/delete-node-in-a-bst/)
- [669. 修剪二叉搜索树](https://leetcode.cn/problems/trim-a-binary-search-tree/)
- [108. 将有序数组转换为二叉搜索树](https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/)
- [538. 把二叉搜索树转换为累加树](https://leetcode.cn/problems/convert-bst-to-greater-tree/)
106. 从中序与后序遍历序列构造二叉树
- 当中序遍历和后序遍历都为空时,返回null
- 当中序遍历和后序遍历只有一个值时,构造根节点,返回根节点即可
- 拿出后序遍历最后一个元素,这个是当前根节点的val,根据它去分割中序序列,分成左子树的中序结果和右子树的中序结果
- 由于中序和后序的序列长度一定是相等的,于是可以根据上一步分割出来的左子树的中序结果和右子树的中序结果的长度来分割后序序列,分为左子树的后序结果和右子树的后序结果
- 根据前面两部得到的左右子树的中序和后序来递归建立左子树和右子树
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {number[]} inorder
* @param {number[]} postorder
* @return {TreeNode}
*/
var buildTree = function(inorder, postorder) {
if(inorder.length == 0) return null
let rootValue = postorder[postorder.length - 1]
let root = new TreeNode(rootValue)
if(postorder.length == 1) return root
let i
for(i = 0; i < inorder.length; i++){
if(inorder[i] == rootValue) break
}
let inLeft = inorder.slice(0, i)
let inRight = inorder.slice(i + 1)
// console.log('inLeft, inRight:',inLeft, inRight)
let lenLeft = inLeft.length
let lenRight = inRight.length
let postleft = postorder.slice(0, lenLeft)
let postRight = postorder.slice(lenLeft, lenLeft + lenRight)
// console.log('postleft, postRight:',postleft, postRight)
root.left = buildTree(inLeft, postleft)
root.right = buildTree(inRight, postRight)
return root
};
105. 从前序与中序遍历序列构造二叉树
与上一题类似,只是根节点的值取自前序序列的第一个元素,需要注意左右子树的前序序列分割的边界下标。
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {number[]} preorder
* @param {number[]} inorder
* @return {TreeNode}
*/
var buildTree = function(preorder, inorder) {
if(preorder.length === 0) return null
let rootVal = preorder[0]
let root = new TreeNode(rootVal)
if(preorder.length === 1) return root
let i
for(i = 0; i < inorder.length; i++){
if(inorder[i] == rootVal) break
}
let inLeft = inorder.slice(0, i)
let inRight = inorder.slice(i + 1)
let lenLeft = inLeft.length
let lenRight = inRight.length
let preLeft = preorder.slice(1, 1 + lenLeft)
let preRight = preorder.slice(1 + lenLeft)
root.left = buildTree(preLeft, inLeft)
root.right = buildTree(preRight, inRight)
return root
};
654. 最大二叉树
- 当 nums 中只有一个元素时:则到了叶子结点,直接返回构造出的叶子结点
- 当 nums 中元素大于1个时:
- 找到nums中的最大元素和它的下标
- 构造最大元素节点
- 分割数组
- 递归构造当前节点左子树和右子树
- 返回节点
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {number[]} nums
* @return {TreeNode}
*/
var constructMaximumBinaryTree = function(nums) {
if(nums.length === 1){
let node = new TreeNode(nums[0])
return node
}
let max = nums[0]
let index = 0
for(let i = 1; i <nums.length; i++){
if(nums[i] > max){
max = nums[i]
index = i
}
}
let node = new TreeNode(max)
let left = nums.slice(0, index)
if(left.length > 0) node.left = constructMaximumBinaryTree(left)
let right = nums.slice(index+1)
if(right.length > 0) node.right = constructMaximumBinaryTree(right)
return node
};
617. 合并二叉树
- 当 root1 为 null 时则返回root2(如果此时2也是null,也没关系,相当于返回null)
- 当 root2 为 null 时则返回root1
- root1,2都不是null时,将2对应的val加到1上
- 递归左子树和右子树
- 返回root1
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root1
* @param {TreeNode} root2
* @return {TreeNode}
*/
var mergeTrees = function(root1, root2) {
if(!root1) return root2
if(!root2) return root1
root1.val = root1.val + root2.val
root1.left = mergeTrees(root1.left, root2.left)
root1.right = mergeTrees(root1.right, root2.right)
return root1
};
700. 二叉搜索树中的搜索
- 当前节点为null或者当前节点的val和目标val相等,则返回当前节点
- 如果目标val大于当前节点val ,返回递归右子树的节点
- 如果目标val消于当前节点val ,返回递归左子树的节点
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} val
* @return {TreeNode}
*/
var searchBST = function(root, val) {
if(!root || root.val == val) return root
if(val > root.val) return searchBST(root.right, val)
if(val < root.val) return searchBST(root.left,val)
};
98. 验证二叉搜索树
搜索二叉树的中序遍历结果是递增的
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isValidBST = function(root) {
let ans = []
function dfs(node){
if(!node) return
dfs(node.left)
ans.push(node.val)
dfs(node.right)
}
dfs(root)
for(let i = 1; i < ans.length; i++ ){
if(ans[i] <= ans[i-1]) return false
}
return true
};
也可以在递归的过程中判断:
- 如果当前节点是null,返回true
- 递归左子树
- pre用来记录中序遍历当前节点的前一个节点,如果当前节点值小于等于pre的值,返回false
- 递归右子树
- 左右子树都是搜索二叉树,返回true
var isValidBST = function(root) {
let pre = null
function fn(node){
if(!node) return true
let left = fn(node.left)
if(pre && node.val <= pre.val) return false
pre = node
let right = fn(node.right)
return left && right
}
return fn(root)
};
530. 二叉搜索树的最小绝对差
可以先中序遍历二叉树,得到递增数组,然后在这个数组上求得最小绝对值
也可以在中序遍历时记录上一个节点pre,以及当前节点和上一个节点的差值,更新差值
这里实现一下方法2
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var getMinimumDifference = function(root) {
let pre = null
let min = 100001
function fn(node){
if(!node) return
fn(node.left)
if(pre && node.val - pre.val< min) min = node.val - pre.val
pre = node
fn(node.right)
}
fn(root)
return min
};
501. 二叉搜索树中的众数
可以先得到中序遍历数组然后再数组上操作,也可以在遍历时操作,这里选择后者
ans 存返回的众数结果,max 存最多次数,pre 是中序对应的前一个节点,count 用于计数
如果当前节点val等于pre的val,则计数器 + 1,
如果当前节点val不等于pre,比较当前计数器与max比较:
- count大于max,则先清空ans,然后将pre对应的值放入ans,再更新max为当前count
- count等于max,将pre对应的值push进ans
- 小于则忽略
最后还要再加一次判断
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var findMode = function(root) {
let ans = []
let max = 0
let count = 1
let pre = null
function fn(node){
if(!node) return
fn(node.left)
if(pre){
if(pre.val == node.val){
count ++
}else{
if(count > max){
ans = [pre.val]
max = count
}else if(count == max){
ans.push(pre.val)
}
count = 1
}
}
pre = node
fn(node.right)
}
fn(root)
if(count > max){
ans = [pre.val]
}else if(count == max){
ans.push(pre.val)
}
return ans
};
236. 二叉树的最近公共祖先
左右子树中一边有p,一边有q;或者当前节点是p或者q,自己孩子节点有q或者q
要从叶子节点不断向根节点寻找,只能使用携带返回值的回溯算法
递归函数返回值:
- 如果当前节点等于p或者q,返回当前节点
- 如果找到祖先节点,返回祖先节点
递归函数的逻辑:
- 如果当前节点等于p,或者q,或者null 返回该节点
- 递归左子树和右子树,得到返回值
- 如果左子树和右子树的返回值都不是null,表明一边有p,一边有q,则该节点就是需要寻找的最近祖先节点,将该节点作为返回值
- 如果左右子树只有一个返回值不是null,则将该返回值返回
- 如果左右子树的返回值都是null,则返回 null
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
if(root == null || root == p ||root == q){
return root
}
let left = lowestCommonAncestor(root.left, p, q)
let right = lowestCommonAncestor(root.right, p, q)
if(left && right) return root
else if(!left && right) return right
else if(!right && left) return left
else return null
};
235. 二叉搜索树的最近公共祖先
在寻找公共祖先时利用好搜索二叉树的性质,如果当前节点处于[p,q]或者[q,p]区间内,则为最近公共祖先。
当节点值大于p,q的值,则继续在其左子树上搜索
当节点值小于p,q的值在其右子树上搜索
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
if(!root) return null
if(root.val > p.val && root.val > q.val){
let left = lowestCommonAncestor(root.left, p, q)
if(left) return left
}else if(root.val < p.val && root.val < q.val){
let right = lowestCommonAncestor(root.right, p, q)
if(right) return right
}
return root
};
701. 二叉搜索树中的插入操作
遍历二叉搜索树,如果节点值小于插入元素,向右;如果节点值大于插入元素,向左,当遍历到空节点,则为插入元素的合适位置。
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} val
* @return {TreeNode}
*/
var insertIntoBST = function(root, val) {
// 找到合适位置
if(!root){
let node = new TreeNode(val)
return node
}
if(root.val < val){
root.right = insertIntoBST(root.right, val)
}else if(root.val > val){
root.left = insertIntoBST(root.left, val)
}
return root
};
450. 删除二叉搜索树中的节点
像增加节点一样,使用递归函数的返回值来删除节点,删除节点的几种情况:
- 没有找到删除节点:递归到空节点,返回 null
- 找到删除节点:
- 节点左右孩子都是空节点:直接删除该节点,返回null即可
- 节点没有左孩子,有右孩子:返回节点右孩子
- 节点有左孩子,没有右孩子:返回节点左孩子
- 节点既有左孩子也有右孩子(比较难处理):将节点左孩子放到右孩子最左边节点的左孩子上,返回该节点的右孩子作为新节点
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} key
* @return {TreeNode}
*/
var deleteNode = function(root, key) {
if(!root) return null
if(root.val < key){
root.right = deleteNode(root.right, key)
}else if(root.val > key){
root.left = deleteNode(root.left, key)
}else{
if(!root.left && !root.right) return null
else if(!root.left && root.right) return root.right
else if(!root.right && root.left) return root.left
else{
let p = root.right
while(p.left){
p = p.left
}
// console.log('右子树最左边的孩子:',p.val)
p.left = root.left
return root.right
}
}
return root
};
669. 修剪二叉搜索树
如果当前节点为空节点,则直接返回 null
如果当前节点值小于下界low,则递归右子树,将递归右子树的结果替代当前节点作为返回值
如果当前节点值大于上界high,则递归左子树,将递归左子树的结果头戴当前节点作为返回值
如果当前节点在[low,high]中,当前节点符合条件,递归其左子树和右子树
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} low
* @param {number} high
* @return {TreeNode}
*/
var trimBST = function(root, low, high) {
if(!root) return null
if(root.val < low){
let right = trimBST(root.right, low, high)
return right
}else if(root.val > high){
let left = trimBST(root.left, low, high)
return left
}
root.left = trimBST(root.left, low, high)
root.right = trimBST(root.right, low, high)
return root
};
108. 将有序数组转换为二叉搜索树
和前面的构造最大二叉树类似,不断寻找切割点然后再递归左边和右边,不过这里的切割点就是中点,这样够构造的二叉树就是平衡二叉树。如果有偶数个元素的话切割点两个都可以。
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {number[]} nums
* @return {TreeNode}
*/
var sortedArrayToBST = function(nums) {
if(!nums || nums.length == 0) return null
let index = Math.floor(nums.length/2)
let numsLeft = nums.slice(0,index)
let numsRight = nums.slice(index+1)
let node = new TreeNode(nums[index])
node.left = sortedArrayToBST(numsLeft)
node.right = sortedArrayToBST(numsRight)
return node
};
538. 把二叉搜索树转换为累加树
要将大于自身的节点累加到当前节点上,所有需要先遍历右子树,然后需要一个指针表示前一个节点,将前一个节点的值累加到当前节点,再去遍历左子树。
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
var convertBST = function(root) {
let pre = null
function fn(node){
if(!node) return
fn(node.right)
if(pre){
node.val = node.val + pre.val
}
pre = node
fn(node.left)
}
fn(root)
return root
};