哈希表实现与解析:从理论到实践

一、哈希表概念与特性

哈希表(Hash Table)是一种高效的数据结构,它通过键(Key)直接访问在内存存储位置的数据结构。它通过一个哈希函数将键映射到表中的某个位置,从而实现 O (1) 平均时间复杂度的插入、删除和查找操作。

1.1 核心概念

  • 哈希函数:将键映射到哈希表中索引的函数,理想情况下每个键都能映射到唯一索引
  • 哈希冲突:不同的键通过哈希函数得到相同索引的现象
  • 负载因子:哈希表中存储的元素个数与哈希表大小的比值,通常用 α 表示
  • 哈希桶:哈希表中的每个位置,当发生冲突时,通常通过链表或红黑树等数据结构存储多个元素

1.2 哈希表特性

  • 高效性:平均情况下,插入、删除和查找操作的时间复杂度为 O (1)
  • 空间换时间:通过额外的存储空间来换取高效的操作速度
  • 无序性:哈希表中的元素是无序存储的,与插入顺序无关
  • 冲突处理:需要专门的机制来处理哈希冲突,常见的有链地址法和开放地址法

二、哈希冲突解决:链地址法

本文实现的哈希表采用链地址法(Separate Chaining)来解决哈希冲突,这是一种非常常用的哈希冲突解决方法。

2.1 链地址法原理

链地址法的基本思想是:将哈希表中的每个桶(Bucket)作为一个链表的头节点,当发生哈希冲突时,将冲突的元素插入到对应桶的链表中。

2.2 链地址法优缺点

优点

  • 处理冲突简单,且无堆积现象
  • 链表节点可以动态申请,适合无法确定表长的情况
  • 删除节点操作易于实现

缺点

  • 存储指针需要额外空间
  • 当链表较长时,查询效率会下降到 O (n)

三、哈希表完整实现代码

3.1 哈希表模板类 (Hash_Tables_template.h)

该文件实现了哈希表的核心功能,采用链地址法处理哈希冲突,以下是完整代码:

复制代码
#pragma once
#include <utility>  // 用于 std::pair
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

// 链地址法(哈希桶)
namespace hash_bucket {
    template<class T>
    struct HashData {
        T _data;
        HashData<T>* _next;  // 下一个结点(单链表)
        HashData(const T& data)
            :_data(data)
            ,_next(nullptr)
        {
        }
    };

    // 前置声明
    template<class K, class T, class Hash, class KeyOfValue>
    class HashTable; 

    // 迭代器
    template<class K, class T, class Ptr, class Ref, class KeyOfValue, class Hash>
    struct HashIterator {
        typedef HashData<T> Node;
        typedef HashIterator<K, T, Ptr, Ref, KeyOfValue, Hash> Self;

        Node* _node;
        const HashTable<K, T, Hash, KeyOfValue>* _pht;  // 传进来的表对象

        HashIterator(Node* node, const HashTable<K, T, Hash, KeyOfValue>* pht)
            :_node(node)
            ,_pht(pht)
        {}

        Ref operator*() {
            return _node->_data;
        }

        Ptr operator->()
        {
            return &_node->_data;
        }

        bool operator!=(const Self& s)
        {
            return _node != s._node;
        }

        bool operator==(const Self& s) {
            return !(*this != s);
        }

        Self& operator++() {
            if (_node->_next) {   // 当前桶还有元素就继续往下遍历
                _node = _node->_next;
            }
            else {   // 找不为空的桶
                Hash hash;
                KeyOfValue kof;
                size_t hashi = hash(kof(_node->_data)) % _pht->_tables.size();  // 计算进来的值相对映射的位置
                hashi++; // 从下一个桶开始寻找
                while (hashi < _pht->_tables.size()) {  // 哈希表没走完就继续找
                    // 找到不为空的桶就跳出循环
                    if (_pht->_tables[hashi]) {
                        break;
                    }
                    hashi++;
                }
                if (hashi == _pht->_tables.size())  // 相等说明哈希表已经走完
                {
                    _node = nullptr;  // end
                }
                else {
                    _node = _pht->_tables[hashi];
                }
            }
            return *this;
        }
    };

    // 哈希表核心实现
    template<class K, class T, class Hash, class KeyOfValue>
    class HashTable {
        // 友元类
        template<class K, class T, class Ptr, class Ref, class KeyOfValue, class Hash>
        friend struct HashIterator;

        typedef HashData<T> Node;
    public:
        // 迭代器
        typedef HashIterator<const K, T, T*, T&, KeyOfValue, Hash>  Iterator;
        typedef HashIterator<const K, T, const T*, const T&, KeyOfValue, Hash>  ConstIterator;

        Iterator Begin() {
            if (_n == 0) return End();
            for (size_t i = 0; i < _tables.size(); i++) {  // 找到第一个不为空的表
                if (_tables[i]) {
                    return Iterator(_tables[i], this);
                }
            }
            return End();
        }

        Iterator End() {
            return Iterator(nullptr, this);
        }

        ConstIterator Begin() const {
            if (_n == 0) return End();
            for (size_t i = 0; i < _tables.size(); i++) {  // 找到第一个不为空的表
                if (_tables[i]) {
                    return ConstIterator(_tables[i], this);
                }
            }
            return End();
        }

        ConstIterator End() const {
            return ConstIterator(nullptr, this);
        }

        // 扩容时,确保容量总为距离2的幂次方较远的素数
        inline unsigned long __stl_next_prime(unsigned long n)
        {
            // Note: assumes long is at least 32 bits.
            static const int __stl_num_primes = 28;
            static const unsigned long __stl_prime_list[__stl_num_primes] =
            {
                53,         97,         193,       389,       769,
                1543,       3079,       6151,      12289,     24593,
                49157,      98317,      196613,    393241,    786433,
                1572869,    3145739,    6291469,   12582917,  25165843,
                50331653,   100663319,  201326611, 402653189, 805306457,
                1610612741, 3221225473, 4294967291
            };

            const unsigned long* first = __stl_prime_list;
            const unsigned long* last = __stl_prime_list + __stl_num_primes;
            const unsigned long* pos = lower_bound(first, last, n);
            return pos == last ? *(last - 1) : *pos;
        }

        HashTable()
        {
            _tables.resize(__stl_next_prime(0));
            _n = 0;
        }

        ~HashTable() {
            for (size_t i = 0; i < _tables.size(); i++) {
                Node* cur = _tables[i];
                while (cur) {
                    Node* next = cur->_next;  // 保存下一个待删除的节点
                    delete cur;
                    cur = next;
                }
                _tables[i] = nullptr;
            }
            _n = 0;
        }

        pair<Iterator,bool> Insert(const T& data) {
            KeyOfValue kof;
            Iterator it = Find(kof(data));
            if (it != End()) {
                return make_pair(it, false);  // 已经存在就直接返回
            }
            Hash hash;
            // 负载因子等于1时扩容
            if (_n == _tables.size()) {
                vector<Node*> newtables;
                newtables.resize(__stl_next_prime(static_cast<unsigned long>(_tables.size() + 1)), nullptr);  // 扩容
                // 把结点挂到新表上
                for (size_t i = 0; i < _tables.size(); i++) {
                    Node* cur = _tables[i];
                    while (cur) {
                        Node* next = cur->_next;  // 保存当前结点的下一个结点
                        size_t hashi = hash(kof(cur->_data)) % newtables.size();  // 计算进来的值相对映射的位置
                        cur->_next = newtables[hashi];
                        newtables[hashi] = cur;
                        cur = next;
                    }
                    _tables[i] = nullptr;
                }
                _tables.swap(newtables);
            }

            // 头插
            size_t hashi = hash(kof(data)) % _tables.size();  // 计算进来的值相对映射的位置
            Node* newnode = new Node(data);  // 创建新结点
            newnode->_next = _tables[hashi];  // 链接原来结点
            _tables[hashi] = newnode;  // 替换原来结点(头插)
            _n++;
            return  make_pair(Iterator(newnode, this), true);
        }

        Iterator Find(const K& key) {
            Hash hash;
            KeyOfValue kof;
            size_t hashi = hash(key) % _tables.size();  // 计算进来的值相对映射的位置
            Node* cur = _tables[hashi];
            while (cur) {
                if (kof(cur->_data) == key) {
                    return Iterator(cur, this);
                }
                cur = cur->_next;
            }
            return End();
        }

        bool Erase(const K& key) {
            Hash hash;
            KeyOfValue kof;
            size_t hashi = hash(key) % _tables.size();  // 计算进来的值相对映射的位置
            Node* cur = _tables[hashi];
            Node* prev = nullptr;
            while (cur) {
                if (kof(cur->_data) == key) {
                    if (prev == nullptr) {  // 头结点的情况
                        _tables[hashi] = cur->_next;
                    }
                    else {   // 中间节点或尾结点的情况
                        prev->_next = cur->_next;
                    }
                    delete cur;
                    _n--;
                    return true;
                }
                prev = cur;
                cur = cur->_next;
            }
            return false;  // 找不到
        }

    private:
        vector<Node*> _tables;  // 哈希表
        size_t _n = 0;  // 有效数据个数
    };
}
代码解析
  1. HashData 结构体:哈希表的节点结构,包含数据域和指向下一个节点的指针,用于构建单链表
  2. HashIterator 迭代器
    • 实现了哈希表的遍历功能,支持解引用、箭头访问、不等于 / 等于比较、自增操作
    • 自增操作是核心难点:当前桶遍历完后,自动寻找下一个非空桶
  3. HashTable 核心类
    • 素数容量设计:使用素数作为哈希表容量,减少哈希冲突
    • 扩容机制:负载因子达到 1 时触发扩容,重新计算所有元素的哈希值
    • 插入操作:头插法插入新元素,保证 O (1) 插入效率
    • 查找操作:先哈希定位桶,再遍历链表查找
    • 删除操作:处理头节点、中间节点、尾节点三种情况

3.2 哈希函数实现 (HashFun)

哈希函数是哈希表的关键组成部分,以下是完整的哈希函数实现:

复制代码
// 哈希函数模板
template<class K>
struct HashFun {
    size_t operator()(const K& key) const {
        return (size_t)key;
    }
};

// string类型特化版本
template<>
struct HashFun<string> {
    size_t operator()(const string& key) const {
        size_t hash = 0;
        for (auto e : key) {
            hash *= 131;  // 131是经验值,能提供较好的哈希分布
            hash += e;
        }
        return hash;
    }
};
代码解析
  • 通用版本:对于整数类型,直接将其转换为 size_t 类型作为哈希值
  • string 特化版本
    • 采用多项式哈希算法:hash = hash * 131 + char_code
    • 131 是经验值,能有效减少字符串哈希冲突
    • 能区分不同顺序的相同字符组成的字符串(如 "abc" 和 "cba")

3.3 unordered_map 完整实现 (My_unordered_map.h)

复制代码
#pragma once
#include "Hash_Tables_template.h"

// 哈希函数模板
template<class K>
struct HashFun {
    size_t operator()(const K& key) const {
        return (size_t)key;
    }
};

// string类型特化
template<>
struct HashFun<string> {
    size_t operator()(const string& key) const {
        size_t hash = 0;
        for (auto e : key) {
            hash *= 131;  // 让不同顺序但相同的字母分布在其他地方,防止堆积
            hash += e;
        }
        return hash;
    }
};

namespace yzq {
    template<class K, class V, class Hash = HashFun<K>>
    class unordered_map {
    public:
        struct MapKeyOfValue
        {
            const K& operator()(const pair<K, V>& kv)
            {
                return kv.first;
            }
        };
        typedef typename hash_bucket::HashTable<const K, pair<K, V>, Hash, MapKeyOfValue>::Iterator iterator;
        typedef typename hash_bucket::HashTable<const K, pair<K, V>, Hash, MapKeyOfValue>::ConstIterator const_iterator;
        
        iterator begin() {
            return _ht.Begin();
        }

        iterator end() {
            return _ht.End();
        }

        const_iterator begin() const
        {
            return _ht.Begin();
        }

        const_iterator end() const
        {
            return _ht.End();
        }

        pair<iterator, bool> insert(const pair<K, V>& kv)
        {
            return _ht.Insert(kv);
        }

        V& operator[](const K& key) {
            pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
            return ret.first->second;
        }

        bool erase(const K& key) {
            return _ht.Erase(key);
        }

        iterator find(const K& key) {
            return _ht.Find(key);
        }

        const_iterator find(const K& key) const {
            return _ht.Find(key);
        }

    private:
        hash_bucket::HashTable<const K, pair<K, V>, Hash, MapKeyOfValue> _ht;
    };
}
代码解析
  1. MapKeyOfValue 结构体:从键值对中提取键,供哈希表使用
  2. 迭代器类型定义:复用哈希表的迭代器
  3. 核心接口
    • begin ()/end ():提供迭代器访问
    • insert ():插入键值对,返回迭代器和插入是否成功
    • operator []:便捷访问,不存在则插入默认值
    • erase ()/find ():删除和查找操作

3.4 unordered_set 完整实现 (My_unordered_set.h)

复制代码
#pragma once
#include "Hash_Tables_template.h"

// 哈希函数模板
template<class K>
struct HashFun {
    size_t operator()(const K& key) const {
        return (size_t)key;
    }
};

// string类型特化
template<>
struct HashFun<string> {
    size_t operator()(const string& key) const {
        size_t hash = 0;
        for (auto e : key) {
            hash *= 131;  // 让不同顺序但相同的字母分布在其他地方,防止堆积
            hash += e;
        }
        return hash;
    }
};

namespace yzq {
    template<class K, class Hash = HashFun<K>>
    class unordered_set {
    public:
        struct SetKeyOfValue
        {
            const K& operator()(const K& key)
            {
                return key;
            }
        };
        typedef typename hash_bucket::HashTable<const K, const K, Hash, SetKeyOfValue>::Iterator iterator;
        typedef typename hash_bucket::HashTable<const K, const K, Hash, SetKeyOfValue>::ConstIterator const_iterator;

        iterator begin() {
            return _ht.Begin();
        }

        iterator end() {
            return _ht.End();
        }

        const_iterator begin() const
        {
            return _ht.Begin();
        }

        const_iterator end() const
        {
            return _ht.End();
        }

        pair<iterator, bool> insert(const K& key)
        {
            return _ht.Insert(key);
        }

        bool erase(const K& key) {
            return _ht.Erase(key);
        }

        iterator find(const K& key) {
            return _ht.Find(key);
        }

        const_iterator find(const K& key) const {
            return _ht.Find(key);
        }

    private:
        hash_bucket::HashTable<const K, const K, Hash, SetKeyOfValue> _ht;
    };
}
代码解析
  1. SetKeyOfValue 结构体:直接返回元素本身作为键
  2. 模板参数:只需要键类型,不需要值类型
  3. 元素特性:存储 const K 类型,保证元素不可修改(避免哈希值变化)
  4. 接口设计:与 unordered_map 类似,但操作的是单个元素而非键值对

四、完整测试代码

4.1 unordered_set 测试代码

复制代码
#include <iostream>
#include <cassert>
#include "My_unordered_set.h"

using namespace std;
using namespace yzq;

// 基础功能测试
void test_set_basic() {
    cout << "=== Testing unordered_set basic operations ===" << endl;

    unordered_set<int> s;

    // 测试插入
    assert(s.insert(1).second == true);
    assert(s.insert(2).second == true);
    assert(s.insert(3).second == true);
    assert(s.insert(1).second == false); // 重复插入

    // 测试范围for
    cout << "Set elements: ";
    for (auto e : s) {
        cout << e << " ";
    }
    cout << endl;

    // 测试迭代器
    unordered_set<int>::iterator it = s.begin();
    int sum = 0;
    while (it != s.end()) {
        sum += *it;
        ++it;
    }
    cout << "Sum of elements: " << sum << endl;
    assert(sum == 6);

    // 测试查找
    assert(s.find(2) != s.end());
    assert(s.find(4) == s.end());

    // 测试删除
    assert(s.erase(2) == true);
    assert(s.erase(4) == false);

    // 验证删除结果
    sum = 0;
    for (auto e : s) {
        sum += e;
    }
    cout << "Sum after erase 2: " << sum << endl;
    assert(sum == 4);

    cout << "Set basic tests passed!" << endl << endl;
}

// 字符串元素测试
void test_set_string() {
    cout << "=== Testing unordered_set with string ===" << endl;

    unordered_set<string> s;

    s.insert("apple");
    s.insert("banana");
    s.insert("cherry");
    s.insert("apple"); // 重复

    // 遍历
    cout << "String set elements: ";
    for (const auto& str : s) {
        cout << str << " ";
    }
    cout << endl;

    // 查找
    assert(s.find("banana") != s.end());
    assert(s.find("date") == s.end());

    // 删除
    s.erase("cherry");
    assert(s.find("cherry") == s.end());

    cout << "Set string tests passed!" << endl << endl;
}

// 自定义类型测试
struct Person {
    std::string name;
    int age;

    Person() : name(""), age(0) {}
    Person(const std::string& n, int a) : name(n), age(a) {}

    bool operator==(const Person& other) const {
        return name == other.name && age == other.age;
    }
};

// 自定义哈希函数
struct PersonHash {
    size_t operator()(const Person& p) const {
        size_t nameHash = 0;
        for (char c : p.name) {
            nameHash = nameHash * 131 + c;
        }
        return nameHash ^ (p.age * 2654435761U);
    }
};

void test_set_custom_type() {
    cout << "=== Testing unordered_set with custom type ===" << endl;

    unordered_set<Person, PersonHash> s;

    Person p1("Alice", 25);
    Person p2("Bob", 30);
    Person p3("Charlie", 35);

    s.insert(p1);
    s.insert(p2);
    s.insert(p3);
    s.insert(p1); // 重复

    // 遍历
    cout << "Custom type set elements: " << endl;
    for (const auto& p : s) {
        cout << "Name: " << p.name << ", Age: " << p.age << endl;
    }

    // 查找
    assert(s.find(p2) != s.end());
    
    Person p4("David", 40);
    assert(s.find(p4) == s.end());

    // 删除
    s.erase(p3);
    assert(s.find(p3) == s.end());

    cout << "Set custom type tests passed!" << endl << endl;
}

// 边界情况测试
void test_set_edge_cases() {
    cout << "=== Testing unordered_set edge cases ===" << endl;

    // 空集合测试
    unordered_set<int> s;
    assert(s.begin() == s.end());
    assert(s.erase(1) == false);
    assert(s.find(1) == s.end());

    // 大量元素测试(触发扩容)
    for (int i = 0; i < 100; ++i) {
        s.insert(i);
    }

    // 验证所有元素都存在
    for (int i = 0; i < 100; ++i) {
        assert(s.find(i) != s.end());
    }

    // 删除所有元素
    for (int i = 0; i < 100; ++i) {
        assert(s.erase(i) == true);
    }

    // 验证为空
    assert(s.begin() == s.end());

    cout << "Set edge cases tests passed!" << endl << endl;
}

int main() {
    test_set_basic();
    test_set_string();
    test_set_custom_type();
    test_set_edge_cases();

    cout << "All unordered_set tests passed successfully!" << endl;

    return 0;
}

4.2 unordered_map 测试代码

复制代码
#include <iostream>
#include <cassert>
#include "My_unordered_map.h"

using namespace std;
using namespace yzq;

// 基础功能测试
void test_map_basic() {
    cout << "=== Testing unordered_map basic operations ===" << endl;

    unordered_map<int, string> dict;

    // 测试insert
    assert(dict.insert({ 1, "one" }).second == true);
    assert(dict.insert({ 2, "two" }).second == true);
    assert(dict.insert({ 3, "three" }).second == true);
    assert(dict.insert({ 1, "ONE" }).second == false); // 重复键

    // 测试operator[]
    dict[4] = "four";
    dict[5] = "five";
    dict[2] = "TWO"; // 修改已存在的值

    // 测试访问不存在的键(应创建默认值)
    dict[6]; // 应创建空字符串

    // 遍历并验证
    cout << "Map elements:" << endl;
    int count = 0;
    for (auto& kv : dict) {
        cout << kv.first << " -> " << kv.second << endl;
        count++;
    }

    assert(count == 6);
    assert(dict[1] == "one");
    assert(dict[2] == "TWO");
    assert(dict[6] == "");

    // 测试查找
    auto it = dict.find(3);
    assert(it != dict.end());
    assert(it->second == "three");
    assert(dict.find(7) == dict.end());

    // 测试删除
    assert(dict.erase(4) == true);
    assert(dict.erase(7) == false);
    assert(dict.find(4) == dict.end());

    cout << "Map basic tests passed!" << endl << endl;
}

// 字符串键测试
void test_map_string_key() {
    cout << "=== Testing unordered_map with string key ===" << endl;

    unordered_map<string, int> scoreMap;

    scoreMap["Alice"] = 90;
    scoreMap["Bob"] = 85;
    scoreMap["Charlie"] = 95;

    // 遍历
    cout << "Score map:" << endl;
    for (const auto& pair : scoreMap) {
        cout << pair.first << ": " << pair.second << endl;
    }

    // 验证值
    assert(scoreMap["Alice"] == 90);
    assert(scoreMap["Bob"] == 85);

    // 修改值
    scoreMap["Bob"] = 88;
    assert(scoreMap["Bob"] == 88);

    // 查找
    assert(scoreMap.find("Charlie") != scoreMap.end());
    assert(scoreMap.find("David") == scoreMap.end());

    // 删除
    scoreMap.erase("Charlie");
    assert(scoreMap.find("Charlie") == scoreMap.end());

    cout << "Map string key tests passed!" << endl << endl;
}

// 复杂值类型测试
struct Student {
    string name;
    int age;
    double gpa;

    Student() : name(""), age(0), gpa(0.0) {}
    Student(const string& n, int a, double g) : name(n), age(a), gpa(g) {}
};

void test_map_complex_value() {
    cout << "=== Testing unordered_map with complex value ===" << endl;

    unordered_map<int, Student> studentMap;

    // 插入
    studentMap[101] = Student("Alice", 20, 3.8);
    studentMap[102] = Student("Bob", 21, 3.5);
    studentMap[103] = Student("Charlie", 19, 3.9);

    // 访问和修改
    studentMap[102].gpa = 3.7;

    // 验证
    assert(studentMap[101].name == "Alice");
    assert(studentMap[102].gpa == 3.7);
    assert(studentMap[103].age == 19);

    // 遍历
    cout << "Student map:" << endl;
    for (const auto& pair : studentMap) {
        cout << "ID: " << pair.first 
             << ", Name: " << pair.second.name
             << ", Age: " << pair.second.age
             << ", GPA: " << pair.second.gpa << endl;
    }

    cout << "Map complex value tests passed!" << endl << endl;
}

// 边界情况测试
void test_map_edge_cases() {
    cout << "=== Testing unordered_map edge cases ===" << endl;

    // 空map测试
    unordered_map<int, string> emptyMap;
    assert(emptyMap.begin() == emptyMap.end());
    assert(emptyMap.find(1) == emptyMap.end());
    assert(emptyMap.erase(1) == false);

    // operator[] 插入空值
    emptyMap[1];
    assert(emptyMap.find(1) != emptyMap.end());
    assert(emptyMap[1] == "");

    // 大量元素测试(触发扩容)
    unordered_map<int, int> bigMap;
    for (int i = 0; i < 200; ++i) {
        bigMap[i] = i * 10;
    }

    // 验证所有元素
    for (int i = 0; i < 200; ++i) {
        assert(bigMap[i] == i * 10);
    }

    cout << "Map edge cases tests passed!" << endl << endl;
}

int main() {
    test_map_basic();
    test_map_string_key();
    test_map_complex_value();
    test_map_edge_cases();

    cout << "All unordered_map tests passed successfully!" << endl;

    return 0;
}

五、实现要点总结

5.1 核心设计思路

  1. 模板化设计:通过模板实现通用的哈希表,支持任意键值类型
  2. 分离关注点
    • HashTable:核心哈希表实现
    • unordered_map/unordered_set:基于哈希表的具体容器
    • HashFun:哈希函数策略
  3. 迭代器设计:支持标准的迭代器操作,符合 STL 容器规范

5.2 性能优化点

  1. 素数容量:使用素数作为哈希表容量,减少哈希冲突
  2. 负载因子控制:负载因子达到 1 时扩容,保证操作效率
  3. 头插法:链表头插,插入操作 O (1) 时间复杂度
  4. 高效哈希函数:字符串哈希采用 131 多项式算法,分布更均匀

5.3 注意事项

  1. const 正确性:迭代器分为普通迭代器和 const 迭代器
  2. 内存管理:析构函数正确释放所有节点内存
  3. 异常安全:扩容时先创建新表,再迁移数据,最后交换
  4. 哈希冲突:链地址法处理冲突,保证稳定性
相关推荐
程序员-周李斌1 小时前
CopyOnWriteArrayList 源码分析
java·开发语言·哈希算法·散列表
浅川.251 小时前
xtuoj 哈希
算法·哈希算法·散列表
zore_c2 小时前
【C语言】文件操作详解3(文件的随机读写和其他补充)
c语言·开发语言·数据结构·笔记·算法
Pluchon2 小时前
硅基计划4.0 算法 记忆化搜索
java·数据结构·算法·leetcode·决策树·深度优先
CoderYanger2 小时前
动态规划算法-简单多状态dp问题:13.删除并获得点数
java·开发语言·数据结构·算法·leetcode·动态规划·1024程序员节
浅川.252 小时前
xtuoj Balls
数据结构·算法
天骄t2 小时前
深入解析栈:数据结构与系统栈
java·开发语言·数据结构
松涛和鸣2 小时前
24、数据结构核心:队列与栈的原理、实现与应用
c语言·开发语言·数据结构·学习·算法
little~钰2 小时前
线段树和扫描线结合
数据结构·算法