当前位置: 首页 > article >正文

用C++使用带头单向非循环链表,实现一个哈西桶

用C++使用带头单向非循环链表,实现一个哈西桶
在C++中使用带头单向非循环链表来实现哈希桶是一种常见的做法,特别是在实现哈希表时。哈希桶是哈希表的基本存储单元,用于存储具有相同哈希值的元素。以下是一个简单的示例,展示了如何使用带头单向非循环链表来实现哈希桶。

首先,我们定义一个节点结构来表示链表中的每个节点:


 #include <iostream>
#include <list>

template <typename K, typename V>
struct Node {
    K key;
    V value;
    Node* next;

    Node(const K& k, const V& v) : key(k), value(v), next(nullptr) {}
};

然而,为了简化实现并利用C++标准库的功能,我们可以直接使用std::list来代替手动实现的链表。std::list是一个双向链表,但在这里我们只会用到它单向遍历的能力,并且它会处理节点的内存管理。为了模拟带头单向非循环链表的行为,我们可以在每个哈希桶内部维护一个空的哨兵节点(头节点),其next指针指向第一个实际存储数据的节点。

接下来,我们定义哈希桶结构:

 template <typename K, typename V>
class HashBucket {
private:
    Node<K, V>* head; // 哨兵节点(头节点)

public:
    HashBucket() {
        head = new Node<K, V>(K(), V()); // 创建一个空的哨兵节点
        head->next = nullptr;
    }

    ~HashBucket() {
        Node<K, V>* current = head->next;
        while (current != nullptr) {
            Node<K, V>* next = current->next;
            delete current;
            current = next;
        }
        delete head;
    }

    void insert(const K& key, const V& value) {
        Node<K, V>* newNode = new Node<K, V>(key, value);
        
        // 插入到链表头部(为了简化,这里不保持顺序)
        newNode->next = head->next;
        head->next = newNode;
    }

    bool search(const K& key, V& result) {
        Node<K, V>* current = head->next;
        while (current != nullptr) {
            if (current->key == key) {
                result = current->value;
                return true;
            }
            current = current->next;
        }
        return false;
    }

    bool remove(const K& key) {
        Node<K, V>* current = head;
        while (current->next != nullptr) {
            if (current->next->key == key) {
                Node<K, V>* toDelete = current->next;
                current->next = toDelete->next;
                delete toDelete;
                return true;
            }
            current = current->next;
        }
        return false;
    }
};

注意:上面的remove函数有一个逻辑错误,因为current应该从head->next开始遍历,而不是从head开始(因为head是哨兵节点)。此外,为了更贴近带头单向非循环链表的概念,我们通常不会在链表中删除节点时释放头节点,因为头节点是固定存在的。下面是修正后的remove函数:

 bool remove(const K& key) {
        Node<K, V>* current = head->next;
        Node<K, V>* prev = head;
        while (current != nullptr) {
            if (current->key == key) {
                prev->next = current->next;
                delete current;
                return true;
            }
            prev = current;
            current = current->next;
        }
        return false;
    }

最后,我们可以定义一个哈希表来使用这些哈希桶:
使用类模板,传入函数作为函数的参数

 template <typename K, typename V>
class HashTable {
private:
    HashBucket<K, V>* buckets;
    size_t bucketCount;
    size_t (*hashFunction)(const K&);

public:
    HashTable(size_t bucketCount, size_t (*hashFunction)(const K&))
        : bucketCount(bucketCount), hashFunction(hashFunction) {
        buckets = new HashBucket<K, V>[bucketCount];
    }

    ~HashTable() {
        delete[] buckets;
    }

    void insert(const K& key, const V& value) {
        size_t bucketIndex = hashFunction(key) % bucketCount;
        buckets[bucketIndex].insert(key, value);
    }

    bool search(const K& key, V& result) {
        size_t bucketIndex = hashFunction(key) % bucketCount;
        return buckets[bucketIndex].search(key, result);
    }

    bool remove(const K& key) {
        size_t bucketIndex = hashFunction(key) % bucketCount;
        return buckets[bucketIndex].remove(key);
    }
};

为了使用这个哈希表,你需要提供一个哈希函数,例如:

 size_t stringHashFunction(const std::string& str) {
    size_t hash = 0;
    for (char c : str) {
        hash = hash * 31 + c;
    }
    return hash;
}

然后你可以这样创建和使用哈希表:

 int main() {
    HashTable<std::string, int> hashTable(10, stringHashFunction);
    hashTable.insert("one", 1);
    hashTable.insert("two", 2);

    int value;
    if (hashTable.search("one", value)) {
        std::cout << "Found 'one' with value " << value << std::endl;
    } else {
        std::cout << "'one' not found" << std::endl;
    }

    hashTable.remove("one");
    if (!hashTable.search("one", value)) {
        std::cout << "'one' has been removed" << std::endl;
    }

    return 0;
}

请注意,这个示例是为了教学目的而简化的。在实际应用中,你可能需要考虑哈希冲突的处理(例如链地址法或开放地址法)、哈希表的动态扩容、以及更高效的哈希函数等因素。


http://www.kler.cn/a/466900.html

相关文章:

  • 力扣23.合并K个升序链表
  • jQuery get 方法内操控vue变量(异步ajax请求方法中操控双向绑定的响应式变量)实现异步请求函数内完成变量的双向响应式绑定
  • MySQL叶子节点为啥使用双向链表?不使用单向呢?
  • 开源存储详解-分布式存储与ceph
  • 基于Web的足球青训俱乐部管理后台系统的设计与开发源码(springboot+mysql+vue)
  • 有效字母异位词力扣--242
  • 《深入浅出HTTPS​​​​​​​​​​​​​​​​​》读书笔记(25):椭圆曲线密码学(续)
  • ScheduledExecutorService详解
  • SpringBoot 2.6 集成es 7.17
  • 实现图的广度优先遍历(BFS)和深度优先遍历(DFS)
  • Tomcat(116) 如何在Tomcat中解决缓存问题?
  • 因果推断核心算法:倾向得分匹配法PSM
  • Linux(Centos 7.6)命令详解:cd
  • 《Rust权威指南》学习笔记(五)
  • 行业商机信息付费小程序系统开发方案
  • 25考研王道数据机构课后习题-----顺序表链表部分
  • 电脑压缩软件哪个好?15款压缩工具分类测评
  • 力扣459 重复的字符串
  • 2025 年春招互联网大厂226 道 Java 高级岗面试题
  • CMS网站管理系统如何选择CMS建站?
  • 使用python将多个Excel表合并成一个表
  • 合同与订单管理:CRM自动化的商业价值
  • 【强化学习】Double DQN(Double Deep Q-Network)算法
  • 个人博客自我介绍
  • 《机器学习》--线性回归模型详解
  • 什么是 GTest?