📅  最后修改于: 2023-12-03 15:27:23.007000             🧑  作者: Mango
符号表是计算机科学中的一种抽象数据类型,用于存储键值对并支持基本操作,如插入、删除和查找。在理论和实践中,可以通过各种方式实现符号表,每种实现方式都有其优缺点和适用场景。本文将介绍几种常见的符号表实现方式。
无序链表实现符号表的基本思想是使用链表数据结构来存储键值对。每个节点都包含一个键值对和指向下一个节点的指针,而键可以是任意可比较类型。当需要执行插入、删除和查找操作时,顺序遍历链表并逐一比较键,以找到目标元素。
class Node:
def __init__(self, key, value, next=None):
self.key = key
self.value = value
self.next = next
class UnorderedLinkedListST:
def __init__(self):
self.head = None
def get(self, key):
curr = self.head
while curr != None:
if curr.key == key:
return curr.value
curr = curr.next
return None
def put(self, key, value):
curr = self.head
while curr != None:
if curr.key == key:
curr.value = value
return
curr = curr.next
self.head = Node(key, value, self.head)
def delete(self, key):
if self.head == None:
return
if self.head.key == key:
self.head = self.head.next
return
curr, prev = self.head, None
while curr != None:
if curr.key == key:
prev.next = curr.next
return
prev, curr = curr, curr.next
无序链表实现简单直观,易于理解和实现。但是,当符号表中的键不是有序的时,此实现方式的效率会很低,插入和查找操作的平均时间复杂度为$O(n)$。因此,如果需要快速的插入、删除和查找操作,则有序链表或平衡搜索树更为合适。
有序数组实现符号表的基本思想是,维护一个有序的键数组和一个与之对应的值数组。当需要执行插入、删除和查找操作时,使用二分查找算法找到插入、删除或查找的位置。由于数组是连续的存储空间,因此此实现方式对缓存更友好,对于小规模的符号表,性能优于链表实现方式。
class OrderedArrayST:
def __init__(self, capacity=10):
self.keys = [None] * capacity
self.values = [None] * capacity
self.size = 0
def rank(self, key):
lo, hi = 0, self.size-1
while lo <= hi:
mid = lo + (hi - lo) // 2
if key < self.keys[mid]:
hi = mid - 1
elif key > self.keys[mid]:
lo = mid + 1
else:
return mid
return lo
def get(self, key):
i = self.rank(key)
if i < self.size and self.keys[i] == key:
return self.values[i]
return None
def put(self, key, value):
i = self.rank(key)
if i < self.size and self.keys[i] == key:
self.values[i] = value
return
for j in range(self.size, i, -1):
self.keys[j] = self.keys[j-1]
self.values[j] = self.values[j-1]
self.keys[i] = key
self.values[i] = value
self.size += 1
def delete(self, key):
i = self.rank(key)
if i < self.size and self.keys[i] == key:
for j in range(i, self.size-1):
self.keys[j] = self.keys[j+1]
self.values[j] = self.values[j+1]
self.size -= 1
有序数组实现适用于小规模的符号表以及不需要频繁插入或删除操作的场景。由于二分查找算法的时间复杂度为$O(log_2n)$,因此插入、删除和查找操作的平均时间复杂度为$O(n)$。另外,由于底层数据结构是数组,因此空间利用率不如链表实现方式高。
二分查找树(Binary Search Tree,简称BST)实现符号表的基本思想是,使用二叉树数据结构来存储键值对。每个节点都包含一个键、一个值和指向左右子节点的指针。当需要执行插入、删除和查找操作时,通过比较键和节点内的键,递归地向左或右子树遍历,以找到目标元素。
class Node:
def __init(self, key, value):
self.key = key
self.value = value
self.left = None
self.right = None
class BinarySearchTreeST:
def __init__(self):
self.root = None
def get(self, key):
return self._get(self.root, key)
def _get(self, node, key):
if node == None:
return None
if key < node.key:
return self._get(node.left, key)
elif key > node.key:
return self._get(node.right, key)
else:
return node.value
def put(self, key, value):
self.root = self._put(self.root, key, value)
def _put(self, node, key, value):
if node == None:
return Node(key, value)
if key < node.key:
node.left = self._put(node.left, key, value)
elif key > node.key:
node.right = self._put(node.right, key, value)
else:
node.value = value
return node
def delete(self, key):
self.root = self._delete(self.root, key)
def _delete(self, node, key):
if node == None:
return None
if key < node.key:
node.left = self._delete(node.left, key)
elif key > node.key:
node.right = self._delete(node.right, key)
else:
if node.left == None:
return node.right
elif node.right == None:
return node.left
else:
min_node = self._min(node.right)
node.key = min_node.key
node.value = min_node.value
node.right = self._delete(node.right, min_node.key)
return node
def _min(self, node):
if node.left == None:
return node
return self._min(node.left)
二分查找树实现方式比链表和有序数组实现方式更有效,插入、删除和查找的平均时间复杂度为$log_2n$,最坏情况下为$n$。另外,当键在符号表中分布均匀时,二分查找树的高度会趋近于$log_2n$,因此,此实现方式最适合于符号表中的键随机分布。
平衡搜索树实现符号表的基本思想是,在二分查找树的基础上,重复地旋转子树以保持树的平衡性。颜色红、黑节点的红黑树和AVL树等都是平衡搜索树的例子。由于平衡搜索树可以保证搜索树的高度近似于$log_2n$,使得所有操作的时间复杂度都为$log_2n$,因此,此实现方式是非常高效的。
class RBNode:
def __init__(self, key, value, color=BLACK):
self.key = key
self.value = value
self.left = None
self.right = None
self.color = color
class RedBlackTreeST:
def __init__(self):
self.root = None
def get(self, key):
return self._get(self.root, key)
def _get(self, node, key):
if node == None:
return None
if key < node.key:
return self._get(node.left, key)
elif key > node.key:
return self._get(node.right, key)
else:
return node.value
def put(self, key, value):
self.root = self._put(self.root, key, value)
self.root.color = BLACK
def _put(self, node, key, value):
if node == None:
return RBNode(key, value, RED)
if key < node.key:
node.left = self._put(node.left, key, value)
elif key > node.key:
node.right = self._put(node.right, key, value)
else:
node.value = value
if self._is_red(node.right) and not self._is_red(node.left):
node = self._rotate_left(node)
if self._is_red(node.left) and self._is_red(node.left.left):
node = self._rotate_right(node)
if self._is_red(node.left) and self._is_red(node.right):
self._flip_color(node)
return node
def delete(self, key):
self.root = self._delete(self.root, key)
def _delete(self, node, key):
if node == None:
return None
if key < node.key:
node.left = self._delete(node.left, key)
elif key > node.key:
node.right = self._delete(node.right, key)
else:
if node.left == None:
return node.right
elif node.right == None:
return node.left
else:
min_node = self._min(node.right)
node.key = min_node.key
node.value = min_node.value
node.right = self._delete(node.right, min_node.key)
if self._is_red(node.right) and not self._is_red(node.left):
node = self._rotate_left(node)
if self._is_red(node.left) and self._is_red(node.left.left):
node = self._rotate_right(node)
if self._is_red(node.left) and self._is_red(node.right):
self._flip_color(node)
return node
def _is_red(self, node):
if node == None:
return False
return node.color == RED
def _rotate_left(self, node):
x = node.right
node.right = x.left
x.left = node
x.color = node.color
node.color = RED
return x
def _rotate_right(self, node):
x = node.left
node.left = x.right
x.right = node
x.color = node.color
node.color = RED
return x
def _flip_color(self, node):
node.color = not node.color
node.left.color = not node.left.color
node.right.color = not node.right.color
平衡搜索树实现方式最适合任何符号表的应用场景。由于平衡搜索树可以保证搜索树的高度近似于$log_2n$,使得所有操作的时间复杂度都为$log_2n$,因此,此实现方式是非常高效的。另外,平衡搜索树实现方式具有与二分查找树实现方式相同的简洁性、易实现性和可读性。