常见的搜索树类型
二叉搜索树(Binary Search Tree, BST)
- 性质:对于树中的每一个节点,左子树的所有节点值都小于该节点的值,右子树的所有节点值都大于该节点的值。
- 查找效率:查找、插入和删除操作的时间复杂度在 平均情况下 是 O(log n),最坏情况下是 O(n)(当树退化成链表时)。
- 应用场景:广泛用于需要有序数据访问的场景,如数据库的索引。
python
10
/ \
5 15
/ \ \
3 7 20
// 查找 7:从根节点 10 开始,比较 7 和 10,向左子树搜索;接着比较 7 和 5,向右子树搜索,最终找到节点 7。
平衡二叉搜索树(Balanced Binary Search Tree)
- 特点:为了避免最坏情况的退化,平衡二叉搜索树通过某些规则保持树的平衡,保证树的高度不会太大,从而保证查找效率。
- 常见类型:
AVL 树:通过旋转操作保持树的平衡。
红黑树:通过颜色标记和旋转来保持平衡。 - 应用场景:用于要求较高查找效率的场景,如操作系统中的调度算法、数据库索引等。
搜索树的操作
搜索树支持多种操作,最常见的包括:
1. 查找(Search)
- 从根节点开始,按照树的结构进行比较,寻找特定的值
- 在二叉搜索树中,若当前值小于节点值,则继续查找左子树;若大于节点值,则查找右子树。
2. 插入(Insert)
- 向树中插入一个新的值。首先找到合适的位置,再插入该节点
- 在二叉搜索树中,插入值比节点值小则插入左子树,反之插入右子树。
3. 删除(Delete)
- 从树中删除一个值。删除操作根据节点的子树情况分为三种情况:
- 删除的节点是叶子节点(没有子节点)。
- 删除的节点有一个子节点。
- 删除的节点有两个子节点。此时需要找到节点的中序后继或中序前驱,并替换节点。
4. 遍历(Traversal)
- 对树进行遍历,常见的遍历方法有:
前序遍历(Pre-order)
中序遍历(In-order):用于打印有序数据。
后序遍历(Post-order)
层序遍历(Level-order)
有序数组和二叉搜索树(BST)之间的关系
有序数组就像是 BST 的中序遍历结果。要把一个有序数组变成一棵平衡的 BST,最简单的方法就是:永远挑数组的正中间作为根节点。
- BST 的特性:左边比根小,右边比根大。
- 平衡的秘诀:如果选中间的数做根,那么它左边剩下的数和右边剩下的数,数量是几乎相等的。这样长出来的左右子树高度就一致,树也就"平衡"了。
递归三部曲实现
"分治法":
- 出口:数组为空(或者左边界大于右边界),返回 None。
- 操作:
找到数组的中点 mid。
用 nums[mid] 创建一个新的根节点。 - 递归:
把左半部分交给递归函数,去生成左子树。
把右半部分交给递归函数,去生成右子树。
mid = (left + right) // 2:如果数组长度是偶数,这个写法会选择中间偏左的那个。
空间复杂度: O(logN)O(\log N)O(logN)。主要是递归栈的深度。每次都折半,所以树的高度一定是 logN\log NlogN。
时间复杂度 :O(N)O(N)O(N)。我们需要把数组里的每个数都"摸"一遍,把它们变成节点。
其实就是"二分查找"的逆过程。
二分查找是在有序数组里"找"一个数,而这道题是把二分查找的每一个"中点"连起来变成了一棵树
如果不给"有序"数组呢?
如果数组是乱序的,必须先给它排序(O(NlogN)O(N \log N)O(NlogN)),然后再用这个方法构建。
答案可能不是唯一的。如果 n 是偶数,我们可以取数组正中间左边那个数作为根节点的值,也可以取数组正中间右边那个数作为根节点的值。下面代码取的是正中间右边那个数,即下标为 n/2的数(当 n 是偶数时)。
python
// eg: nums=[−10,−3,0,5,9]
从数组正中间的数 nums[2]=0 开始,把数组一分为二,得到两个小数组:
左:[−10,−3]。
右:[5,9]。
//答案由三部分组成:
根节点:节点值为 nums[2]=0。
把 nums[2] 左边的 [−10,−3] 转换成一棵平衡二叉搜索树,作为答案的左儿子。这是一个和原问题相似的子问题,可以递归解决。
把 nums[2] 右边的 [5,9] 转换成一棵平衡二叉搜索树,作为答案的右儿子。这是一个和原问题相似的子问题,可以递归解决
// 递归边界:如果数组长度等于 0,返回空节点。
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
# 出口:数组为空(或者左边界大于右边界),返回 None。
if not nums:
return None
m = len(nums) // 2
left = self.sortedArrayToBST(nums[:m])
left = self.sortedArrayToBST(nums[m+1:])
return TreeNode(nums[m], left, right)
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
def dfs(left,right):
# 这里的 left == right 对应 [left, right) 区间为空
if left == right:
return None
m = (left + right) // 2
# 先递归,再组装(自底向上)
# 注意:右开区间,所以左边是 [left, m),右边是 [m + 1, right)
left_child = dfs(left, m) # 递归左区间
right_child = dfs(m + 1, right) # 递归右区间
#结束递归,返回给主函数sortedArrayToBST-> Optional[TreeNode]:
return TreeNode(nums[m], left_child, right_child)
return dfs(0, len(nums))