VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > 数据结构 >
  • 第13章 高级数据结构

第13章 高级数据结构
13.1 引言
在基础数据结构(如数组、链表、二叉树、哈希表)的学习中,我们掌握了处理线性关系、层次关系和键值关联的基本工具。但面对大规模数据或复杂场景(如高频插入删除的有序数据、超大数据集的存在性判断),基础结构往往因效率不足或空间开销过大而受限。高级数据结构通过引入自平衡机制、概率化存储、多级索引等设计思想,在时间复杂度、空间效率或功能扩展性上实现突破,成为解决工程难题的核心工具。
本章将聚焦四类典型高级数据结构:平衡树(解决二叉搜索树退化为链表的问题)、高级堆结构(扩展二叉堆的合并能力)、哈希冲突高级解决策略(优化哈希表在极端场景下的性能)、布隆过滤器(高效实现海量数据的存在性判断)。每个结构均从底层原理出发,结合Python实现与工程案例,揭示其“为何高效”及“如何使用”。
13.2 平衡树:维持有序数据的高效操作
二叉搜索树(BST)通过“左子树值<根值<右子树值”的特性,支持O(log n)的查找、插入和删除,但在极端情况下(如有序数据插入)会退化为链表,操作效率降至O(n)。平衡树通过在插入/删除后自动调整树结构,维持“左右子树高度差不超过阈值”的平衡状态,确保O(log n)的稳定效率。本节以AVL树为例,详解平衡树的自平衡机制。
13.2.1 平衡树的基本概念
平衡因子(Balance Factor):衡量节点平衡状态的指标,定义为“左子树高度 - 右子树高度”。对AVL树(最早的平衡树之一),要求所有节点的平衡因子绝对值≤1(即左右子树高度差≤1)。
失衡与调整:当插入或删除节点导致某节点平衡因子绝对值>1时,树结构失衡,需通过旋转操作(改变节点间的连接关系)恢复平衡。旋转分为四种基本类型,取决于失衡节点的“重子树”(高度更高的子树)方向:
13.2.2 AVL树的旋转操作

  1. LL旋转(左左型失衡)
    场景:失衡节点的左子树高度 > 右子树高度,且左子树的左子树高度 ≥ 左子树的右子树高度(即“左重左更重”)。
    目标:将失衡节点的左子树提升为新根,失衡节点作为其右子树,原左子树的右子树作为失衡节点的左子树。
    示意图(节点旁数字为高度):
	z(3) y(3) 
	/  /  
	y(2) T4 → x(2) z(2) 
	/  /  
	x(1) T3 T3 T4 
	/  
	T1 T2

旋转逻辑:
设失衡节点为z,左子树为y,y的左子树为x;
z的左子树更新为y的右子树(T3);
y的右子树更新为z;
返回y作为新子树的根。
2. RR旋转(右右型失衡)
场景:失衡节点的右子树高度 > 左子树高度,且右子树的右子树高度 ≥ 右子树的左子树高度(即“右重右更重”)。
目标:与LL旋转对称,将失衡节点的右子树提升为新根,失衡节点作为其左子树,原右子树的左子树作为失衡节点的右子树。
示意图:

	z(3) y(3) 
	 /  
	y(2) → z(2) x(2) 
	/  /  
	T1 x(1) T1 T2 
	/  
	T2 T3

旋转逻辑:
设失衡节点为z,右子树为y,y的右子树为x;
z的右子树更新为y的左子树(T2);
y的左子树更新为z;
返回y作为新子树的根。
3. LR旋转(左右型失衡)
场景:失衡节点的左子树高度 > 右子树高度,但左子树的右子树高度 > 左子树的左子树高度(即“左重右更重”)。
目标:先对左子树执行RR旋转,将其转化为LL失衡,再对失衡节点执行LL旋转。
示意图:

	z(3) z(3) x(3) 
	/  /  /  
	y(2) T4 → x(2) T4 → y(2) z(2) 
	/  /  /  /  
	T1 x(1) y(1) T3 T1 T2 T3 T4 
	/  /  
	T2 T3 T1 T2
  1. RL旋转(右左型失衡)
    场景:失衡节点的右子树高度 > 左子树高度,但右子树的左子树高度 > 右子树的右子树高度(即“右重左更重”)。
    目标:先对右子树执行LL旋转,将其转化为RR失衡,再对失衡节点执行RR旋转。
    13.2.3 AVL树的Python实现
    AVL树的核心操作包括插入(插入后检查平衡因子,必要时旋转)、删除(删除后从底向上回溯调整平衡)和查找(与BST一致,因结构平衡,效率稳定为O(log n))。以下实现聚焦插入操作(删除操作逻辑类似但更复杂,需处理节点替换后的平衡调整)。
    python
	class AVLNode: 
	def __init__(self, val): 
	self.val = val 
	self.left = None # 左子树 
	self.right = None # 右子树 
	self.height = 1 # 节点高度(叶节点高度为1) 
	
	class AVLTree: 
	def __init__(self): 
	self.root = None 
	
	def _get_height(self, node): 
	"""获取节点高度(空节点高度为0)""" 
	return node.height if node else 0 
	
	def _get_balance(self, node): 
	"""计算节点平衡因子(左高-右高)""" 
	return self._get_height(node.left) - self._get_height(node.right) if node else 0 
	
	def _update_height(self, node): 
	"""更新节点高度(基于左右子树高度)""" 
	node.height = 1 + max(self._get_height(node.left), self._get_height(node.right)) 
	
	def _rotate_right(self, z): 
	"""RR旋转(以z为失衡节点)""" 
	y = z.right # y是z的右子树 
	T2 = y.left # T2是y的左子树 
	
	# 执行旋转 
	y.left = z 
	z.right = T2 
	
	# 更新高度(先更新z,再更新y,因y是z的父节点) 
	self._update_height(z) 
	self._update_height(y) 
	
	return y # 返回新根y 
	
	def _rotate_left(self, z): 
	"""LL旋转(以z为失衡节点)""" 
	y = z.left # y是z的左子树 
	T3 = y.right # T3是y的右子树 
	
	# 执行旋转 
	y.right = z 
	z.left = T3 
	
	# 更新高度 
	self._update_height(z) 
	self._update_height(y) 
	
	return y # 返回新根y 
	
	def _insert(self, node, val): 
	"""递归插入节点(返回插入后子树的根,自动处理平衡)""" 
	# 1. 标准BST插入 
	if not node: 
	return AVLNode(val) 
	if val < node.val: 
	node.left = self._insert(node.left, val) 
	elif val > node.val: 
	node.right = self._insert(node.right, val) 
	else: 
	return node # 不允许重复值 
	
	# 2. 更新当前节点高度 
	self._update_height(node) 
	
	# 3. 计算平衡因子,判断是否失衡 
	balance = self._get_balance(node) 
	
	# 4. 处理四种失衡情况 
	# LL型:平衡因子>1且左子树的左子树插入(val < 左子树值) 
	if balance > 1 and val < node.left.val: 
	return self._rotate_left(node) 
	# RR型:平衡因子<-1且右子树的右子树插入(val > 右子树值) 
	if balance < -1 and val > node.right.val: 
	return self._rotate_right(node) 
	# LR型:平衡因子>1且左子树的右子树插入(val > 左子树值) 
	if balance > 1 and val > node.left.val: 
	node.left = self._rotate_right(node.left) # 先对左子树RR旋转 
	return self._rotate_left(node) # 再对当前节点LL旋转 
	# RL型:平衡因子<-1且右子树的左子树插入(val < 右子树值) 
	if balance < -1 and val < node.right.val: 
	node.right = self._rotate_left(node.right) # 先对右子树LL旋转 
	return self._rotate_right(node) # 再对当前节点RR旋转 
	
	# 未失衡,返回原节点 
	return node 
	
	def insert(self, val): 
	"""对外接口:插入值val""" 
	self.root = self._insert(self.root, val) 
	
	def _inorder(self, node): 
	"""中序遍历(验证树的有序性)""" 
	return [] if not node else self._inorder(node.left) + [node.val] + self._inorder(node.right) 
	
	def inorder(self): 
	"""对外接口:中序遍历""" 
	return self._inorder(self.root)

使用示例:
python

	avl = AVLTree() 
	for val in [10, 20, 30, 40, 50, 25]: # 插入可能导致失衡的数据(如25会引发RL旋转) 
	avl.insert(val) 
	print(avl.inorder()) # 输出:[10, 20, 25, 30, 40, 50](保持有序)

性能分析:AVL树的插入、删除、查找操作时间复杂度均为O(log n),因每次插入最多需O(1)次旋转(树高为O(log n),回溯路径长度为O(log n),但每次旋转可使子树恢复平衡),适合对查询性能要求高的场景(如数据库索引)。
13.2.4 红黑树简介
AVL树通过严格控制平衡因子(≤1)保证O(log n)高度,但频繁旋转导致插入删除效率较低。红黑树(Red-Black Tree)通过放松平衡条件(“从根到叶的最长路径不超过最短路径的2倍”),减少旋转次数,在工程中应用更广泛(如Java的TreeMap、C++的std::map)。
红黑树特性:
1.每个节点非红即黑;
2.根节点为黑;
3.叶节点(NIL节点)为黑;
4.红节点的子节点必为黑(无连续红节点);
5.从任一节点到其叶节点的所有路径包含相同数量的黑节点(“黑高”相等)。
与AVL树对比:红黑树插入删除旋转次数少(平均O(1)),空间开销小(无需存储高度),但查询效率略低(最坏树高2log n)。实际场景中,红黑树因综合性能更优,成为有序映射的首选结构。
13.3 高级堆结构:突破二叉堆的合并瓶颈
二叉堆(Binary Heap)通过数组实现,支持O(log n)的插入、删除和O(1)的取最值操作,但合并两个堆需O(n)时间(将一个堆的元素逐个插入另一个堆)。高级堆结构(如二项堆、斐波那契堆)通过设计特殊的树状结构,将合并操作优化至O(log n)甚至O(1),适用于频繁合并堆的场景(如多路归并排序、网络流算法)。
13.3.1 二项堆:基于二项树的可合并堆

  1. 二项树的结构
    二项树(Binomial Tree)是一种递归定义的树结构:
    度数为0的二项树(B₀):单个节点;
    度数为k的二项树(Bₖ):由两个Bₖ₋₁树组成,其中一个Bₖ₋₁的根作为另一个Bₖ₋₁的根的左子树。
    特性:Bₖ树有2ᵏ个节点,高度为k,根节点的度数为k(有k个子节点,分别为B₀~Bₖ₋₁树的根)。
  2. 二项堆的组成
    二项堆由一组满足以下条件的二项树构成:
    每个树是最小堆(或最大堆);
    任意两个树的度数不同(通过度数唯一标识树)。
    表示:用数组trees存储各度数的二项树(trees[k]存储度数为k的树,不存在则为None),堆的大小为所有树的节点数之和。
  3. 核心操作:合并(Union)
    合并是二项堆的核心优势,步骤如下(以最小堆为例):
    1.合并树列表:将两个堆的trees数组合并,按度数从小到大排序;
    2.合并同类树:遍历合并后的列表,若存在度数为k的两棵树,将其合并为度数为k+1的树(较小根作为父节点,较大根作为左子树),重复直至无同类树;
    3.调整最小根:记录所有树的根节点,更新堆的最小根(便于O(1)取最值)。
    时间复杂度:合并操作需遍历度数为O(log n)的树,每步合并耗时O(1),总复杂度O(log n)。
  4. Python实现(最小二项堆)
    python
	class BinomialTreeNode: 
	def __init__(self, val): 
	self.val = val # 节点值 
	self.degree = 0 # 节点度数(子节点数量) 
	self.parent = None # 父节点 
	self.child = None # 第一个子节点(左子树) 
	self.sibling = None # 右兄弟节点 
	
	class BinomialHeap: 
	def __init__(self): 
	self.min_root = None # 最小根节点(堆的入口) 
	self.trees = [] # 度数列表:trees[k]存储度数为k的树的根 
	
	def _merge_trees(self, t1, t2): 
	"""合并两棵度数相同的二项树(返回新树的根,保持最小堆性质)""" 
	if t1.val > t2.val: 
	t1, t2 = t2, t1 # 确保t1是较小根 
	# t2成为t1的子节点(插入到t1的子节点列表头部) 
	t2.parent = t1 
	t2.sibling = t1.child 
	t1.child = t2 
	t1.degree += 1 # t1度数+1 
	return t1 
	
	def union(self, other): 
	"""合并当前堆与other堆(返回合并后的新堆)""" 
	merged = BinomialHeap() 
	i = j = 0 
	# 合并度数列表(类似二进制加法,处理进位) 
	while i < len(self.trees) or j < len(other.trees): 
	t1 = self.trees[i] if i < len(self.trees) else None 
	t2 = other.trees[j] if j < len(other.trees) else None 
	
	# 情况1:仅t1存在 
	if t1 and not t2: 
	merged.trees.append(t1) 
	i += 1 
	# 情况2:仅t2存在 
	elif t2 and not t1: 
	merged.trees.append(t2) 
	j += 1 
	# 情况3:t1和t2度数相同,合并为度数+1的树 
	elif t1.degree == t2.degree: 
	merged.trees.append(self._merge_trees(t1, t2)) 
	i += 1 
	j += 1 
	# 情况4:t1度数 < t2度数,先添加t1 
	elif t1.degree < t2.degree: 
	merged.trees.append(t1) 
	i += 1 
	# 情况5:t2度数 < t1度数,先添加t2 
	else: 
	merged.trees.append(t2) 
	j += 1 
	
	# 更新最小根(遍历所有树的根,找最小值) 
	merged.min_root = min(merged.trees, key=lambda x: x.val) if merged.trees else None 
	return merged 
	
	def insert(self, val): 
	"""插入值(创建单节点二项堆,合并到当前堆)""" 
	new_heap = BinomialHeap() 
	new_heap.trees.append(BinomialTreeNode(val)) 
	new_heap.min_root = new_heap.trees[0] 
	self = self.union(new_heap) 
	
	def get_min(self): 
	"""获取最小值(O(1))""" 
	return self.min_root.val if self.min_root else None

应用场景:二项堆适合需要频繁合并堆的场景,如Kruskal算法(合并最小生成树的边集)、优先队列的多路合并。
13.3.2 斐波那契堆:理论最优的可合并堆
斐波那契堆(Fibonacci Heap)通过“懒惰合并”(延迟合并和删除操作)和“级联剪枝”(减少节点度数),实现了插入、合并操作O(1)的均摊时间复杂度,删除和取最值O(log n)的均摊复杂度,是理论上效率最高的堆结构。但其实现复杂(需维护节点的父、子、兄弟指针及标记位),工程中较少直接使用,更多作为算法复杂度分析的理想模型(如Dijkstra算法用斐波那契堆可优化至O(m + n log n))。
13.4 哈希冲突的高级解决策略
哈希表通过哈希函数将键映射到数组索引,实现O(1)平均操作效率,但哈希冲突不可避免。基础冲突解决策略(链地址法、开放定址法)在极端场景下(如哈希函数较差、数据分布倾斜)效率下降显著。本节介绍两种高级策略,进一步优化哈希表性能。
13.4.1 再哈希法:动态调整哈希函数
再哈希法(Rehashing)通过设计多个哈希函数,当冲突发生时,使用下一个哈希函数计算新索引,直至找到空槽。核心思想是“用多个哈希函数分散冲突键”,避免开放定址法的“聚集效应”(连续槽位被占用)。

  1. 双重哈希(Double Hashing)
    最常用的再哈希策略,定义两个哈希函数:
    主哈希函数 h1(k)=kmod  m h_1(k) = k mod m h1​(k)=kmodm(m为表长);
    次哈希函数 h2(k)=p−(kmod  p) h_2(k) = p - (k mod p) h2​(k)=p−(kmodp)(p为小于m的质数);
    探测序列为 h(k,i)=(h1(k)+i⋅h2(k))mod  m h(k, i) = (h_1(k) + i cdot h_2(k)) mod m h(k,i)=(h1​(k)+i⋅h2​(k))modm(i=0,1,...,m-1)。
    优势:h2(k) h_2(k) h2​(k) 确保探测序列遍历表中所有槽位(避免死循环),且分散性好(冲突键的探测序列不同)。
  2. Python实现(基于双重哈希的哈希表)
    python
	class DoubleHashTable: 
	def __init__(self, capacity=16): 
	self.capacity = capacity # 表长(取质数更优) 
	self.size = 0 # 元素个数 
	self.table = [None] * self.capacity # 哈希表数组 
	self.load_factor = 0.7 # 装填因子阈值 
	
	def _hash1(self, key): 
	"""主哈希函数""" 
	return hash(key) % self.capacity 
	
	def _hash2(self, key): 
	"""次哈希函数(返回1~capacity-1的数,避免0步长)""" 
	p = self._prev_prime(self.capacity) # 小于capacity的最大质数 
	return p - (hash(key) % p) 
	
	def _prev_prime(self, n): 
	"""辅助函数:返回小于n的最大质数(简化实现)""" 
	for i in range(n-1, 1, -1): 
	if all(i % j != 0 for j in range(2, int(i**0.5)+1)): 
	return i 
	return 2 
	
	def _rehash(self): 
	"""扩容(容量翻倍并重新哈希所有元素)""" 
	old_table = self.table 
	self.capacity *= 2 
	self.table = [None] * self.capacity 
	self.size = 0 
	for key in old_table: 
	if key is not None: 
	self.insert(key) 
	
	def insert(self, key): 
	"""插入键(双重哈希解决冲突)""" 
	if self.size / self.capacity > self.load_factor: 
	self._rehash() 
	
	h1 = self._hash1(key) 
	h2 = self._hash2(key) 
	i = 0 
	while True: 
	idx = (h1 + i * h2) % self.capacity 
	if self.table[idx] is None: 
	self.table[idx] = key 
	self.size += 1 
	return 
	if self.table[idx] == key: 
	return # 不允许重复键 
	i += 1 # 冲突,使用下一个探测位置 
	
	def contains(self, key): 
	"""判断键是否存在""" 
	h1 = self._hash1(key) 
	h2 = self._hash2(key) 
	i = 0 
	while i < self.capacity: 
	idx = (h1 + i * h2) % self.capacity 
	if self.table[idx] is None: 
	return False 
	if self.table[idx] == key: 
	return True 
	i += 1 
	return False

适用场景:双重哈希适合对哈希函数分散性要求高的场景(如密码学中的哈希表),但实现复杂度高于链地址法,且删除操作需标记“已删除”状态(避免探测序列中断)。
13.4.2 建立公共溢出区:隔离冲突元素
公共溢出区(Public Overflow Area)将哈希表分为“基本表”和“溢出表”:
基本表:存储哈希函数直接映射的元素;
溢出表:存储所有冲突元素(无论哈希地址是否相同)。
查找流程:
1.计算键的哈希地址,检查基本表对应位置;
2.若命中,返回;若未命中且基本表位置为空,返回不存在;
3.否则,遍历溢出表查找。
优缺点:
优点:实现简单,适合冲突率低的场景(溢出表规模小,遍历快);
缺点:冲突率高时,溢出表退化为线性查找,效率降至O(m)(m为溢出表元素数)。
工程建议:公共溢出区通常作为辅助策略,与链地址法结合(如基本表用链地址法,溢出表存储长链表的元素),平衡时间与空间开销。
13.5 布隆过滤器:海量数据的高效存在性判断
在处理超大数据集(如亿级URL去重、缓存穿透防护)时,哈希表因空间开销过大(存储完整键)而不可行。布隆过滤器(Bloom Filter)通过位数组+多个哈希函数的概率化设计,以极小空间实现“元素是否存在”的快速判断,虽有一定误判率(“假阳性”,即判断存在的元素可能不存在),但无“假阴性”(判断不存在的元素一定不存在)。
13.5.1 基本原理
布隆过滤器结构:
位数组(m位):初始全为0,每个位表示一个“指纹”;
k个哈希函数:将元素映射到位数组的k个不同索引。
插入流程:
1.对元素x,用k个哈希函数计算k个索引 i1,i2,...,ik i_1, i_2, ..., i_k i1​,i2​,...,ik​;
2.将位数组中 i1,...,ik i_1, ..., i_k i1​,...,ik​ 位置设为1。
查询流程:
1.对元素x,计算k个索引 i1,...,ik i_1, ..., i_k i1​,...,ik​;
2.若所有索引位均为1,则x“可能存在”(存在误判);
3.若任一索引位为0,则x“一定不存在”。
误判率来源:不同元素的哈希索引可能重叠,导致位数组中k个位置被同时置1,造成“假阳性”。
13.5.2 误判率与参数选择
布隆过滤器的误判率 ε arepsilon ε 与三个参数相关:
m:位数组长度(bit);
n:插入元素个数;
k:哈希函数个数。
理论误判率公式:
ε=(1−e−kn/m)k arepsilon = left(1 - e{-kn/m} ight)k ε=(1−e−kn/m)k
最优参数选择:
当 k=(m/n)ln⁡2 k = (m/n)ln 2 k=(m/n)ln2 时,误判率最低,此时 ε=(1/2)k arepsilon = (1/2)^k ε=(1/2)k;
若给定n和 ε arepsilon ε,最优m为 m=−nln⁡ε/(ln⁡2)2 m = -n ln arepsilon / (ln 2)^2 m=−nlnε/(ln2)2。
示例:若需存储n=1亿个元素,误判率 ε=1% arepsilon = 1% ε=1%,则最优k≈7,m≈9.6亿bit(约117MB),远小于哈希表的空间开销(存储1亿个64位键需约763MB)。
13.5.3 Python实现与应用
python

	import math 
	from bitarray import bitarray # 需安装bitarray库(pip install bitarray) 
	
	class BloomFilter: 
	def __init__(self, n, eps=0.01): 
	"""初始化布隆过滤器 
	:param n: 预期插入元素个数 
	:param eps: 可接受误判率(默认1%) 
	""" 
	self.n = n 
	self.eps = eps 
	# 计算最优m(位数组长度,bit)和k(哈希函数个数) 
	self.m = int(-n * math.log(eps) / (math.log(2)**2)) 
	self.k = int((self.m / n) * math.log(2)) 
	self.bits = bitarray(self.m) 
	self.bits.setall(0) # 位数组初始化为0 
	
	# 生成k个简单哈希函数(实际应用中可用MD5、SHA等哈希函数的不同切片) 
	self.hash_functions = [ 
	lambda x, i=i: hash(f"{x}{i}") % self.m for i in range(self.k) 
	] 
	
	def add(self, item): 
	"""插入元素""" 
	for h in self.hash_functions: 
	idx = h(item) 
	self.bits[idx] = 1 
	
	def contains(self, item): 
	"""判断元素是否存在(可能误判)""" 
	for h in self.hash_functions: 
	idx = h(item) 
	if not self.bits[idx]: 
	return False 
	return True 
	
	def __str__(self): 
	return f"BloomFilter(m={self.m} bit, k={self.k}, n={self.n}, eps={self.eps})"

使用示例(缓存穿透防护):
缓存穿透指查询不存在的key时,请求穿透缓存直达数据库,导致数据库压力过大。布隆过滤器可前置过滤不存在的key:
python

	# 1. 初始化布隆过滤器(存储所有存在的用户ID,n=1亿,eps=1%) 
	bf = BloomFilter(n=10**8, eps=0.01) 
	# 2. 预加载所有用户ID到布隆过滤器 
	for user_id in load_all_user_ids(): # load_all_user_ids()返回数据库中所有用户ID 
	bf.add(user_id) 
	
	# 3. 处理查询请求 
	def get_user_info(user_id): 
	if not bf.contains(user_id): 
	return "用户不存在" # 直接拦截不存在的ID,不查数据库 
	# 存在则查缓存,缓存未命中再查数据库 
	return cache.get(user_id) or db.query(user_id)

局限性:布隆过滤器不支持删除元素(删除一个元素需将k个位置置0,但可能影响其他元素),若需支持删除,可使用“计数布隆过滤器”(用计数器代替位,删除时减1),但空间开销会增加。
13.6 本章总结
高级数据结构通过创新设计突破了基础结构的限制,解决了特定场景下的效率瓶颈:
平衡树(AVL树、红黑树)通过自平衡机制维持O(log n)高度,确保有序数据的高效插入、删除和查询;
高级堆(二项堆、斐波那契堆)优化了合并操作,适用于频繁合并堆的场景;
哈希冲突高级策略(再哈希法、公共溢出区)通过动态调整哈希函数或隔离冲突元素,提升哈希表在极端数据下的稳定性;
布隆过滤器以概率化存储实现海量数据的高效存在性判断,空间效率远超哈希表,但存在误判率。
选择高级数据结构时,需权衡时间复杂度、空间开销、实现复杂度和场景需求(如是否需要有序性、合并操作、删除支持)。理解其底层原理,不仅能正确应用,更能在面对新问题时,设计出符合需求的定制化数据结构。
13.7 思考题
1.实现红黑树的插入操作,验证其通过变色和旋转维持红黑特性的过程。
2.对比二项堆和斐波那契堆的合并操作,分析斐波那契堆“懒惰合并”如何实现O(1)均摊复杂度。
3.设计一个支持删除的布隆过滤器(提示:用字节数组代替位数组,每个字节存储计数器),分析其误判率和空间开销的变化。
4.用AVL树实现一个有序映射(OrderedMap),支持put(key, val)、get(key)、delete(key)和按key范围查询(range_query(start, end))。
5.已知布隆过滤器的误判率ε=0.001,插入元素n=100万,计算最优位数组长度m和哈希函数个数k(参考误判率公式)。

本站原创,转载请注明出处:https://www.xin3721.com/python3/python49305.html


相关教程