
对于频繁插入、删除和范围查询的场景,推荐使用跳表,它能以O(log n)时间完成插入、删除和范围查询操作,实现相对简单,适合内存操作。
跳表是一种概率性平衡的多层链表,底层是普通有序链表,上层是“跳跃层”,通过随机提升节点到上层实现平衡。类比“城市立交桥”:底层道路覆盖所有节点(如普通链表),上层道路可快速跨越多个路口(如跳跃层),查询时从上层开始跳过大量节点,快速定位目标。
| 数据结构 | 定义/特性 | 时间复杂度(插入/删除/查询) | 适用场景 | 注意点 |
|---|---|---|---|---|
| 跳表 | 多层有序链表,概率性平衡 | O(log n) | 频繁插入删除、范围查询(内存操作) | 实现简单,空间开销较大 |
| 平衡二叉搜索树(如红黑树) | 自平衡二叉树,严格平衡 | O(log n) | 需严格平衡,范围查询 | 实现复杂,性能稳定 |
| B+树 | 多叉树,叶子节点有序 | O(log n)(磁盘) | 磁盘存储,大规模数据 | 需磁盘I/O,内存开销小 |
假设记录为(时间戳,值),操作包括插入、删除、范围查询。伪代码示例:
class Node:
def __init__(self, key, value, level):
self.key = key
self.value = value
self.next = [None] * level # 多个next指针,对应不同层
class SkipList:
def __init__(self):
self.head = Node(-float('inf'), None, 1) # 头节点
self.level = 1 # 当前层数
def random_level(self):
level = 1
while random.random() < 0.5 and level < 32: # 随机层数,概率0.5
level += 1
return level
def insert(self, key, value):
level = self.random_level()
new_node = Node(key, value, level)
current = self.head
for i in range(level, 0, -1):
while current.next[i] and current.next[i].key < key:
current = current.next[i]
new_node.next[i] = current.next[i]
current.next[i] = new_node
if level > self.level:
self.level = level
def delete(self, key):
current = self.head
for i in range(self.level, 0, -1):
while current.next[i] and current.next[i].key < key:
current = current.next[i]
if current.next[i] and current.next[i].key == key:
current.next[i] = current.next[i].next[i]
while self.level > 1 and not self.head.next[self.level]:
self.level -= 1
def range_query(self, start, end):
result = []
current = self.head
for i in range(self.level, 0, -1):
while current.next[i] and current.next[i].key < start:
current = current.next[i]
current = current.next[1] # 从底层开始遍历
while current and current.key <= end:
result.append((current.key, current.value))
current = current.next[1]
return result
示例操作:
面试官您好,对于频繁插入、删除和范围查询的场景,我会选择跳表。跳表是一种多层有序链表结构,底层是普通有序链表,上层是“跳跃层”,通过随机提升节点到上层实现概率性平衡。查询时从上层开始跳过大量节点,时间复杂度O(log n),插入删除时调整指针,同样O(log n)。相比平衡树,跳表实现更简单(无需维护颜色或平衡因子),适合内存操作。比如插入时,根据随机数决定节点是否提升,删除时调整相邻节点的指针,范围查询从最高层定位后,逐层向下遍历,最终返回区间内的所有记录。这样既能高效支持插入删除,又能快速完成范围查询,适合这类高频操作的场景。