【JavaScript数据结构】散列(哈希)

114 阅读2分钟

散列(或哈希)是一种数据结构,允许我们按键存储和检索值。JavaScript中的对象实际上就是哈希表,但为了教学目的,我们将从头开始实现一个简单的哈希表。

以下是一个简单的哈希表实现,以及一些常见操作的详细说明:

class HashTable {
    constructor(size = 50) {
        // 初始化一个固定大小的数组来存储数据。
        this.buckets = Array(size);
        for (let i = 0; i < this.buckets.length; i++) {
            this.buckets[i] = new LinkedList();
        }
        this.size = size;
    }

    // 使用简单的哈希函数将键转换为数组索引。
    hash(key) {
        let hashValue = 0;
        for (let char of key) {
            hashValue += char.charCodeAt(0);
        }
        return hashValue % this.size;
    }

    /**
     * 将键值对插入哈希表。
     * @param {string} key - 要插入的键。
     * @param {*} value - 要插入的值。
     */
    insert(key, value) {
        const index = this.hash(key);
        this.buckets[index].append({key, value});
    }

    /**
     * 从哈希表中按键检索值。
     * @param {string} key - 要检索的键。
     * @returns {*} 返回与键关联的值,如果找不到则返回 null。
     */
    retrieve(key) {
        const index = this.hash(key);
        const bucketLinkedList = this.buckets[index];
        const node = bucketLinkedList.find({ callback: (nodeValue) => nodeValue.key === key });
        return node ? node.value.value : null;
    }

    /**
     * 从哈希表中删除键值对。
     * @param {string} key - 要删除的键。
     */
    remove(key) {
        const index = this.hash(key);
        const bucketLinkedList = this.buckets[index];
        const node = bucketLinkedList.find({ callback: (nodeValue) => nodeValue.key === key });
        if (node) {
            bucketLinkedList.delete(node.value);
        }
    }
}

class LinkedListNode {
    constructor(value, next = null) {
        this.value = value;
        this.next = next;
    }
}

class LinkedList {
    constructor() {
        this.head = null;
        this.tail = null;
    }

    append(value) {
        const newNode = new LinkedListNode(value);
        if (!this.head) {
            this.head = newNode;
            this.tail = newNode;
            return this;
        }
        this.tail.next = newNode;
        this.tail = newNode;
        return this;
    }

    find({ value = undefined, callback = undefined }) {
        if (!this.head) {
            return null;
        }

        let currentNode = this.head;
        while (currentNode) {
            if (callback && callback(currentNode.value)) {
                return currentNode;
            }
            if (value !== undefined && currentNode.value === value) {
                return currentNode;
            }
            currentNode = currentNode.next;
        }
        return null;
    }

    delete(value) {
        if (!this.head) {
            return null;
        }
        let deletedNode = null;
        while (this.head && this.head.value === value) {
            deletedNode = this.head;
            this.head = this.head.next;
        }
        let currentNode = this.head;
        if (currentNode !== null) {
            while (currentNode.next) {
                if (currentNode.next.value === value) {
                    deletedNode = currentNode.next;
                    currentNode.next = currentNode.next.next;
                } else {
                    currentNode = currentNode.next;
                }
            }
        }
        if (this.tail.value === value) {
            this.tail = currentNode;
        }
        return deletedNode;
    }
}

使用示例

const hashTable = new HashTable();

hashTable.insert("name", "Alice");
hashTable.insert("age", 25);
console.log(hashTable.retrieve("name")); // 输出: Alice
console.log(hashTable.retrieve("age"));  // 输出: 25

hashTable.remove("name");
console.log(hashTable.retrieve("name")); // 输出: null

以上代码首先定义了一个哈希表类,其中使用了一个简单的哈希函数将字符串键转换为索引。这是一个非常简单的哈希函数,仅用于示例目的,在实际应用中可能需要更复杂和高效的哈希函数。为了处理哈希冲突,我们使用了链地址法,即每个桶存储一个链表。

然后,我们定义了一个链表节点类和一个链表类来支持哈希表的操作。

注意:这是一个简单的实现,仅用于教学目的。实际应用中的哈希表实现可能会更复杂,并包括更多的优化。