📜  符号表的各种实现(1)

📅  最后修改于: 2023-12-03 15:27:23.007000             🧑  作者: Mango

符号表的各种实现

符号表是计算机科学中的一种抽象数据类型,用于存储键值对并支持基本操作,如插入、删除和查找。在理论和实践中,可以通过各种方式实现符号表,每种实现方式都有其优缺点和适用场景。本文将介绍几种常见的符号表实现方式。

1. 无序链表实现

无序链表实现符号表的基本思想是使用链表数据结构来存储键值对。每个节点都包含一个键值对和指向下一个节点的指针,而键可以是任意可比较类型。当需要执行插入、删除和查找操作时,顺序遍历链表并逐一比较键,以找到目标元素。

实现代码:
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)$。因此,如果需要快速的插入、删除和查找操作,则有序链表或平衡搜索树更为合适。

2. 有序数组实现

有序数组实现符号表的基本思想是,维护一个有序的键数组和一个与之对应的值数组。当需要执行插入、删除和查找操作时,使用二分查找算法找到插入、删除或查找的位置。由于数组是连续的存储空间,因此此实现方式对缓存更友好,对于小规模的符号表,性能优于链表实现方式。

实现代码:
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)$。另外,由于底层数据结构是数组,因此空间利用率不如链表实现方式高。

3. 二分查找树实现

二分查找树(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$,因此,此实现方式最适合于符号表中的键随机分布。

4. 平衡搜索树实现

平衡搜索树实现符号表的基本思想是,在二分查找树的基础上,重复地旋转子树以保持树的平衡性。颜色红、黑节点的红黑树和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$,因此,此实现方式是非常高效的。另外,平衡搜索树实现方式具有与二分查找树实现方式相同的简洁性、易实现性和可读性。