普通二叉树
二叉树节点的一般定义
class TreeNode {
constructor(val) {
this.val = val
this.left = null
this.right = null
}
}
二叉树的遍历:以某种方式访问二叉树的全部节点
// 先序遍历
function tranverse(root) {
// root操作
tranverse(root.left)
tranverse(root.right)
}
// 中序遍历
function tranverse(root) {
tranverse(root.left)
// root操作
tranverse(root.right)
}
// 后序遍历
function tranverse(root) {
tranverse(root.left)
tranverse(root.right)
// root操作
}
二叉树的技巧
多利用函数的入参和返回值 如:求二叉树最大深度,
// 利用函数返回值
var maxDepth = function(root) {
if (!root) return 0
const left = maxDepth(root.left)
const right = maxDepth(root.right)
return 1 + Math.max(left, right)
};
// 利用函数参数
var maxDepth = function(root) {
let res = 0
const help = (node, depth) => {
if (!node) {
res = Math.max(res, depth)
return
}
help(node.left, depth + 1)
help(node.right, depth + 1)
}
help(root, 0)
return res
};
二叉树是否相等
var isSameTree = function(p, q) {
if (!p || !q) return p === q
return p.val === q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right)
};
二叉树是否对称
需要注意在help函数中,需要p.left和q.right比对,p.right和q.left比对。
var isSymmetric = function(root) {
if (!root) return true
const help = (p, q) => {
if (!p || !q) return p === q
return p.val === q.val && help(p.left, q.right) && help(p.right, q.left)
}
return help(root.left, root.right)
};
平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
function getTreeHeight(root) {
if (!root) return 0
return 1 + Math.max(getTreeHeight(root.left), getTreeHeight(root.right))
}
var isBalanced = function(root) {
if (!root) return true
const left = getTreeHeight(root.left)
const right = getTreeHeight(root.right)
return Math.abs(left - right) <= 1 && isBalanced(root.left) && isBalanced(root.right)
};
优化解法:
- 当左右两颗子树的高度差大于1时,提前返回,避免无效计算
- 返回特殊值-1,表示树是不平衡的
function getTreeHeight(root) {
// 当左右两颗子树的高度差大于1时,提前返回特殊值-1,表示树是不平衡的
if (!root) return 0
const lh = getTreeHeight(root.left)
if (lh === -1) return -1
const rh = getTreeHeight(root.right)
if (rh === -1 || Math.abs(lh - rh) > 1) return -1
return 1 + Math.max(lh, rh)
}
var isBalanced = function(root) {
return getTreeHeight(root) !== -1
};
二叉树的右视图
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
- 遍历树时,需要优先遍历树的右节点
- 需要记住当前depth是否已经保存了节点,每个depth中,只有最右边的节点需要保存
var rightSideView = function(root) {
const cache = new Map()
const help = (node, depth) => {
if (!node) return
if (!cache.has(depth)) {
cache.set(depth, node)
}
help(node.right, depth + 1)
help(node.left, depth + 1)
}
help(root, 0)
return Array.from(cache.values()).map(node => node.val)
};
二叉树的最近公共祖先
/**
* 当前节点情况
* - 为空:返回空
* - 为p:返回p
* - 为q:返回q
* - 其他情况
* - p,q在左右子树中:返回当前节点
* - p,q在左子树:返回左子树结果
* - p,q在右子树:返回右子树结果
* - p,q左右子树均不在:返回空
*/
var lowestCommonAncestor = function(root, p, q) {
if (root === null || root === p || root === q) return root
const left = lowestCommonAncestor(root.left, p, q)
const right = lowestCommonAncestor(root.right, p, q)
if (left && right) return root
if (left) return left
return right
};
二叉树的层序遍历
var levelOrder = function(root) {
const res = [], nodes = []
if (root) nodes.push(root)
while (nodes.length) {
const cur = []
const length = nodes.length
for (let i = 0; i < length; i++) {
const node = nodes.shift()
cur.push(node.val)
if (node.left) nodes.push(node.left)
if (node.right) nodes.push(node.right)
}
res.push(cur)
}
return res
};
二叉树的锯齿形遍历
var zigzagLevelOrder = function (root) {
const res = [],
nodes = []
if (root) nodes.push(root)
let flag = false
while (nodes.length) {
const cur = [],
length = nodes.length
for (let i = 0; i < length; i++) {
const node = nodes.shift()
cur.push(node.val)
if (node.left) nodes.push(node.left)
if (node.right) nodes.push(node.right)
}
if (flag) cur.reverse()
flag = !flag
res.push(cur)
}
return res
}
二叉搜索树
二叉搜索树:任意节点的值大于所有左子节点的值,且要小于所有右子节点的值。其中序遍历的结果是有序的
二叉搜索树遍历框架
function tranverse(root, target) {
if (root.val === target) {
// root操作
}
if (root.val > target) {
tranverse(root.left, target)
}
if (root.val < target) {
tranverse(root.right, target)
}
}
二叉搜索树有效性判断
// 前序遍历,首先判断根节点是否满足区间条件,然后依次判断左右子节点
var isValidBST = function(root, min = -Infinity, max = Infinity) {
if (!root) return true
const val = root.val
return val > min && val < max && isValidBST(root.left, min, val) && isValidBST(root.right, val, max)
};
// 中序遍历,对应的节点是一个升序数组
// 将节点值存入数组再依次判断,占用O(n)空间
// 但其实只需要保存前一个节点值即可
var isValidBST = function(root) {
let pre = -Infinity
const help = node => {
if (!node) return
help(node.left)
if (node.val > pre) {
pre = node.val
} else {
pre = Infinity
}
help(node.right)
}
help(root)
return pre !== Infinity
};
// 后序遍历,将左右子树的取值范围向上传递,判断根节点是否在合理的范围内
var isValidBST = function(root) {
// help函数返回当前数的最小值和最大值
const help = node => {
// [Infinity, -Infinity]确保node.val <= l_max || node.val >= r_min不成立
if (!node) return [Infinity, -Infinity]
const [l_min, l_max] = help(node.left)
const [r_min, r_max] = help(node.right)
//[-Infinity, Infinity] 表示非二叉搜索树,且可以保证将此结果一直上传,直到函数入口
if (node.val <= l_max || node.val >= r_min) return [-Infinity, Infinity]
return [Math.min(l_min, node.val), Math.max(r_max, node.val)]
}
return help(root)[0] === -Infinity ? false : true
};
二叉搜索树的最近公共祖先
/**
* 当前节点情况
* - 为空:返回空(无此分支)
* - 为p:返回p
* - 为q:返回q
* - 其他情况
* - p,q在左右子树中:返回当前节点
* - p,q在左子树:返回左子树结果
* - p,q在右子树:返回右子树结果
* - p,q左右子树均不在:返回空(无此分支)
*/
var lowestCommonAncestor = function(root, p, q) {
if (root.val > Math.max(p.val, q.val)) {
return lowestCommonAncestor(root.left, p, q)
} else if (root.val < Math.min(p.val, q.val)) {
return lowestCommonAncestor(root.right, p, q)
}
return root
};
二叉搜索树增、删、改、查
// 在BST中查找一个数是否存在
function isInBST(root, target) {
if(root === null) return false
if(root.val === target) return true
if(root.val > target) return isInBST(root.left)
if(root.val < target) return isInBST(root.right)
}
// 在BST中插入一个数
function insertIntoBST(root, val) {
if(root === null) return new TreeNode(val)
if(root.val === val) return root
if(root.val > target) {
root.left = insertIntoBST(root.left, val)
}
if(root.val < target) {
root.right = insertIntoBST(root.right, val)
}
return root
}
// 在BST中删除一个数
function deleteNode(root, val) {
function getMin(node) {
while(node.left !== null) {
node = node.left
}
return node
}
if(root === null) return null
if(root.val === val) {
if(root.left === null) return root.right
if(root.right === null) return root.left
// 使用右子树中的最小值替换当前节点
const minNode = getMin(root.right)
root.val = minNode.val
root.right = deleteNode(root.right, minNode.val)
}
if(root.val > target) {
root.left = deleteNode(root.left, val)
}
if (root.val < target) {
root.right = deleteNode(root.right, val)
}
return root
}
满二叉树
满二叉树:每一层节点都是满的(叶子节点无子节点,非叶子节点均有两个子节点),又称完美二叉树 Perfect Binary Tree.
完全二叉树
完全二叉树:从根结点到倒数第二层满足完美二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐,Complete Binary Tree
完全二叉树节点数统计:
function countNodes(root) {
let l = root, r = root
let hl = 0, hr = 0
while(l !== null) {
l = l.left
hl++
}
while(r !== null) {
r = r.right
hr++
}
if (hl === hr) {
return Math.pow(2, hl) - 1
}
return 1 + countNodes(root.left) + countNodes(root.right)
}