1. 介绍
二叉搜索树是一种特殊的二叉树,初衷是为了方便查找,满足以下性质:
- 非空左子树的所有键值都小于根节点的键值
- 非空右子树的所有键值都大于根节点的键值
- 左右子树也分布是二叉搜索树
- 没有键值相等的节点
由于这个性质,二叉搜索树有一个重要结论:中序遍历二叉搜索树,得到的结果一定是有序数组
同样由于性质,二叉搜索树有一个最大的弊端:可能退化成链表,为了防止这种情况出现,又设计出二叉平衡树
2. 基本操作
1. 查找
由于二叉搜索树的特性,在二叉搜索树上查找的过程就像做二分查找一样:
- 若 X 小于根值,在左子树继续查找
- 若 X 大于根值,在右子树继续查找
- 若 X 等于根植,找到返回
其模板如下:
func find(root *TreeNode, val int) *TreeNode {
if root == nil {
return nil
}
if root.Val < val {
return find(root.Right, val)
} else if root.Val > val {
return find(root.Left, val)
} else {
return root
}
}
2. 查找极值
由于二叉搜索树的特性,极值肯定存在于:
- 最大值一定在树的最右子树
- 最小值一定在树的最左子树
// 如果有右子树,就去右子树找,再没有右子树了,就是当前这个节点
func findMax(root *TreeNode) *TreeNode {
if root == nil {
return nil
} else if root.Right != nil {
return findMax(root.Right)
} else {
return root
}
}
// 如果有左子树,就去左子树找,再没有左子树了,就是当前这个节点
func findMin(root *TreeNode) *TreeNode {
if root == nil {
return nil
} else if root.Left != nil {
return findMin(root.Left)
} else {
return root
}
}
3. 插入
为了维护二叉搜索树的特性,需要找到合适位置插入:
- 当 X 等于空,在该点构造节点
- 当 X 大于当前值,在右子树查找合适位置插入
- 当 X 小于当前值,在左子树查找合适位置插入
- 当 X 等于当前值,啥也不干
其模板如下:
func insert(root *TreeNode, val int) *TreeNode {
if root == nil {
root = &TreeNode{Val: val}
} else {
if root.Val > val {
root.Left = insert(root.Left, val)
} else if root.Val < val {
root.Right = insert(root.Right, val)
}
}
return root
}
4. 删除
为了维护二叉搜索树的特性,删除有三种情况:
- 要删除的是叶节点,直接删除这个节点
- 要删除的节点只有一个子树,用子树代替被删除节点
- 要删除的节点有两个子树,用右子树中的最小值或左子树的最大值替代被删除节点
其模板如下:
func delete(root *TreeNode, val int) *TreeNode {
if root == nil {
return nil
}
if root.Val < val {
root.Right = delete(root.Right, val)
} else if root.Val > val {
root.Left = delete(root.Left, val)
} else {
if root.Left == nil && root.Right == nil {
root = nil
} else if root.Left == nil {
root = root.Right
} else if root.Right == nil {
root = root.Left
} else if root.Left != nil && root.Right != nil {
// 找到右子树的最小值
minRoot := findMin(root.Right)
root.Val = minRoot.Val
// 删除右子树的最小值
root.Right = delete(root.Right, root.Val)
}
}
return root
}
3. 常见考点
1. 中序有序
二叉搜素树中序是有序数组,一定要优先考虑是否能中序解决
4. 常见题目
1. 二叉搜索树的属性
通常利用二叉搜索树的性质,前一个元素和当前元素做比较,其模板为:
var pre *TreeNode
func dfs(root *TreeNode) {
if root == nil {
return
}
dfs(root.Left)
(逻辑写这里面)
pre = root
dfs(root.Right)
}
98. 验证二叉搜索树
中序遍历,遍历过程中比较前一个值是否比当前值小
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
var pre *TreeNode
func isValidBST(root *TreeNode) bool {
pre = nil
return dfs(root)
}
func dfs(root *TreeNode) bool {
if root == nil {
return true
}
left := dfs(root.Left)
if pre != nil && pre.Val >= root.Val {
return false
}
pre = root
right := dfs(root.Right)
return left && right
}
501. 二叉搜索树中的众数
(暴力法)遍历二叉树的过程中使用 map 记录频率,最后输出最高频率对应的值
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
var m map[int]int
func findMode(root *TreeNode) []int {
m = make(map[int]int)
dfs(root)
res := []int{}
maxCount := 0
for _, v := range m {
if v > maxCount {
maxCount = v
}
}
for k, v := range m {
if v == maxCount {
res = append(res, k)
}
}
return res
}
func dfs(root *TreeNode) {
if root == nil {
return
}
dfs(root.Left)
m[root.Val]++
dfs(root.Right)
}
利用二叉树的性质,中序遍历的过程中:
- 当 pre 与当前值相等,计数 + 1
- 当 pre 与当前树不等,计数 = 1
记录最大计数
- 当当前计数与最大计数相等,将当前结果加入结果集
- 当当前计数大于最大计数,清除当前结果集,将当前结果加入结果集
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
var pre *TreeNode
var count int
var maxCount int
var res []int
func findMode(root *TreeNode) []int {
pre = nil
count = 0
maxCount = 0
res = make([]int, 0)
dfs(root)
return res
}
func dfs(root *TreeNode) {
if root == nil {
return
}
dfs(root.Left)
if pre == nil || pre.Val != root.Val {
count = 1
} else {
count++
}
if count == maxCount {
res = append(res, root.Val)
}
if count > maxCount {
maxCount = count
res = make([]int, 0)
res = append(res, root.Val)
}
pre = root
dfs(root.Right)
}
783. 二叉搜索树节点最小距离
照着模板算最小距离就行
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
var pre *TreeNode
var minDiff int
func minDiffInBST(root *TreeNode) int {
minDiff = math.MaxInt32
pre = nil
dfs(root)
return minDiff
}
func dfs(root *TreeNode) {
if root == nil {
return
}
dfs(root.Left)
if pre != nil && minDiff > root.Val - pre.Val {
minDiff = root.Val - pre.Val
}
pre = root
dfs(root.Right)
}
700. 二叉搜索树中的搜索
前面有提到过的搜索,和要搜索的值进行比较,选择不同路径走
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func searchBST(root *TreeNode, val int) *TreeNode {
if root == nil {
return root
} else if root.Val > val {
return searchBST(root.Left, val)
} else if root.Val < val {
return searchBST(root.Right, val)
}
return root
}
1038. 把二叉搜索树转换为累加树
中序拿到有序数组,累加和后再次中序加上去
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
var res []int
var prefixSum []int
var count int
func bstToGst(root *TreeNode) *TreeNode {
res = []int{}
dfs(root)
prefixSum = make([]int, len(res))
prefixSum[len(prefixSum)-1] = res[len(res)-1]
for i := len(prefixSum)-2; i >= 0; i-- {
prefixSum[i] = prefixSum[i+1] + res[i]
}
count = 0
redfs(root)
return root
}
func redfs(root *TreeNode) {
if root == nil {
return
}
redfs(root.Left)
root.Val = prefixSum[count]
count++
redfs(root.Right)
}
func dfs(root *TreeNode) {
if root == nil {
return
}
dfs(root.Left)
res = append(res, root.Val)
dfs(root.Right)
}
倒着中序,遍历一个累加一个
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
var pre int
func bstToGst(root *TreeNode) *TreeNode {
pre = 0
dfs(root)
return root
}
func dfs(root *TreeNode) {
if root == nil {
return
}
dfs(root.Right)
root.Val += pre
pre = root.Val
dfs(root.Left)
}
235. 二叉搜索树的最近公共祖先
二叉搜索树遍历,公共祖先的值肯定在区间 [p,q] 或 [q,p] 中
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root == nil {
return root
}
// 比两边都大了,要往左找
if root.Val > p.Val && root.Val > q.Val {
return lowestCommonAncestor(root.Left, p, q)
}
// 比两边都小了,要往右找
if root.Val < p.Val && root.Val < q.Val {
return lowestCommonAncestor(root.Right, p, q)
}
// 确实在 [p,q] 或 [q,p] 区间
return root
}
2. 二叉搜索树的修改
701. 二叉搜索树中的插入操作
如果比当前节点值大,往右边插;如果比当前节点值小,往左边插
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func insertIntoBST(root *TreeNode, val int) *TreeNode {
if root == nil {
root = &TreeNode{Val: val}
} else {
if root.Val < val {
root.Right = insertIntoBST(root.Right, val)
} else if root.Val > val {
root.Left = insertIntoBST(root.Left, val)
}
}
return root
}
450. 删除二叉搜索树中的节点
三种情况:
- 被删除节点无子树,直接删除
- 被删除节点有一个子树,使用子树当作当前节点
- 被删除节点有两个子树,选择右子树最大值替代当前节点,再删除被选择的节点
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func deleteNode(root *TreeNode, key int) *TreeNode {
if root == nil {
return root
} else 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 == nil && root.Right == nil {
root = nil
} else if root.Left == nil { // 有一个子树
root = root.Right
} else if root.Right == nil { // 有一个子树
root = root.Left
} else { // 有两个子树
node := findMin(root.Right)
root.Val = node.Val
root.Right = deleteNode(root.Right, node.Val)
}
}
return root
}
func findMin(root *TreeNode) *TreeNode {
if root == nil {
return root
} else if root.Left != nil {
return findMin(root.Left)
} else {
return root
}
}
108. 将有序数组转换为二叉搜索树
要求平衡,乍一看好像要二叉平衡树,其实不需要的,只需要挑最中间的作为根,建出来的树自然满足题意
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func sortedArrayToBST(nums []int) *TreeNode {
if len(nums) == 0 {
return nil
}
rootIdx := len(nums)/2
rootVal := nums[rootIdx]
root := &TreeNode{Val: rootVal}
root.Left = sortedArrayToBST(nums[:rootIdx])
root.Right = sortedArrayToBST(nums[rootIdx+1:])
return root
}
669. 修剪二叉搜索树
前序遍历,遍历过程中如果发现有节点在范围外,就不返回这个节点,而是返回可能存在返回内的节点(比如小了返回右儿子)
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func trimBST(root *TreeNode, low int, high int) *TreeNode {
if root == nil {
return nil
}
if root.Val < low {
return trimBST(root.Right, low, high)
} else if root.Val > high {
return trimBST(root.Left, low, high)
}
root.Left = trimBST(root.Left, low, high)
root.Right = trimBST(root.Right, low, high)
return root
}