• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
MKT-porter
博客园    首页    新随笔    联系   管理    订阅  订阅
reLeetCode 热题 100-1 两数之和-扩展1 unordered_map实现
1

C++ unordered_map 实现

unordered_map是 C++ STL 中的关联容器,它存储键值对,使用哈希表实现,提供平均 O(1) 时间复杂度的查找、插入和删除操作。

1基本用法

#include <iostream>
#include <unordered_map>
#include <string>

int main() {
    // 创建一个 unordered_map
    std::unordered_map<std::string, int> wordCount;
    
    // 插入元素
    wordCount["apple"] = 5;
    wordCount["banana"] = 3;
    wordCount.insert({"orange", 2});
    
    // 访问元素
    std::cout << "apple count: " << wordCount["apple"] << std::endl;
    
    // 检查元素是否存在
    if (wordCount.find("banana") != wordCount.end()) {
        std::cout << "banana exists" << std::endl;
    }
    
    // 遍历所有元素
    for (const auto& pair : wordCount) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    
    // 删除元素
    wordCount.erase("orange");
    
    return 0;
}

2 unordered_map默认支持那些数据?

#include <iostream>
#include <unordered_map>
#include <string>

int main() {
    // 默认支持的键类型示例
    std::unordered_map<std::string, int> stringMap;  // OK
    std::unordered_map<int, double> intMap;          // OK
    std::unordered_map<char*, bool> ptrMap;          // OK(但需注意指针比较)

    // 需要自定义哈希的示例(编译错误)
    // std::unordered_map<std::pair<int, int>, int> pairMap;  // 错误:没有默认哈希
}

  

在 C++ 中,std::unordered_map默认支持以下类型的键(Key):

1. ​​基本数据类型​​

  • int

  • unsigned int

  • short

  • long

  • long long

  • float

  • double

  • char

  • bool

  • std::string(来自 <string>)

2. ​​标准库类型​​

  • std::string_view(C++17 起)

  • std::bitset

  • std::vector<bool>

  • std::pair(但需要自定义哈希函数,除非使用 C++11 的 std::hash特化)

3. ​​指针类型​​

  • 原始指针(如 int*, char*)

  • std::shared_ptr

  • std::unique_ptr

4. ​​C++11 标准哈希支持​​

C++11 标准库已经为以下类型提供了 std::hash特化:

  • 所有整数类型(int, long, 等)

  • 所有浮点类型(float, double)

  • 指针类型(T*)

  • std::string

  • std::wstring

  • std::u16string

  • std::u32string

  • std::string_view(C++17)

  • std::error_code

  • std::thread::id

5. ​​不支持的默认类型​​

以下类型 ​​不能直接​​ 作为 unordered_map的键,除非自定义哈希函数和相等比较:

  • 自定义结构体/类

  • std::pair(除非使用 C++11 的 std::hash特化)

  • std::vector(除非自定义哈希)

  • std::list

  • std::map

  • 其他没有 std::hash特化的类型

3如何支持自定义类型?

如果想用自定义类型作为键,需要:

  1. ​​自定义哈希函数​​(可以是函数对象或 lambda)

  2. ​​重载 operator==​​ 或提供比较函数

示例:自定义类型作为键

#include <iostream>
#include <unordered_map>
#include <string>

struct Point {
    int x, y;
    
    // 必须定义相等运算符
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }
};

// 自定义哈希函数
struct PointHash {
    std::size_t operator()(const Point& p) const {
        return std::hash<int>()(p.x) ^ std::hash<int>()(p.y);
    }
};

int main() {
    std::unordered_map<Point, std::string, PointHash> pointMap;
    
    pointMap[{1, 2}] = "A";
    pointMap[{3, 4}] = "B";
    
    std::cout << pointMap[{1, 2}] << std::endl;  // 输出 "A"
}

  必须定义相等运算符,为什么?

image

image

image

 

image

 

替代方案:自定义比较函数

如果不想重载 operator==,可以通过 ​​自定义比较函数​​ 实现:

 

struct Point {
    int x, y;
};

// 自定义比较函数
struct PointEqual {
    bool operator()(const Point& a, const Point& b) const {
        return a.x == b.x && a.y == b.y;
    }
};

// 在 unordered_map 中传入自定义比较函数
std::unordered_map<Point, std::string, PointHash, PointEqual> map;

  

 

源码实现

image

 

image

 

image

 std::hash默认支持的参数

image

 

image

 

使用示例

#include <iostream>
#include <functional>
#include <string>

int main() {
    // 整数哈希
    std::hash<int> intHash;
    std::cout << "Hash of 42: " << intHash(42) << "\n";
    
    // 浮点数哈希
    std::hash<double> doubleHash;
    std::cout << "Hash of 3.14: " << doubleHash(3.14) << "\n";
    
    // 字符串哈希
    std::hash<std::string> stringHash;
    std::cout << "Hash of 'hello': " << stringHash("hello") << "\n";
    
    // 指针哈希
    int x = 10;
    std::hash<int*> ptrHash;
    std::cout << "Hash of &x: " << ptrHash(&x) << "\n";
    
    return 0;
}

  

自定义类型支持

对于用户自定义类型,需要​​特化 std::hash​​ 模板:

#include <functional>

struct Person {
    std::string name;
    int age;
};

// 特化 std::hash 模板
namespace std {
    template<>
    struct hash<Person> {
        size_t operator()(const Person& p) const {
            return hash<string>()(p.name) ^ hash<int>()(p.age);
        }
    };
}

// 使用示例
int main() {
    Person alice{"Alice", 30};
    std::hash<Person> personHash;
    std::cout << "Hash of Alice: " << personHash(alice) << "\n";
    return 0;
}

  

image

 

image

 

单向链表实现

#include <iostream>
#include <vector>
#include <functional>

template<typename Key, typename Value>
class CloserToOriginalUnorderedMap {
private:
    // 哈希节点结构(使用单向链表)

    struct HashNode {
        Key key;        // 存储的键
        Value value;    // 存储的值
        HashNode* next; // 指向下一个节点的指针

        // 构造函数:创建一个新的哈希节点
        // @param k 要存储的键
        // @param v 要存储的值
        // @param n 下一个节点的指针(默认为空)
        HashNode(const Key& k, const Value& v, HashNode* n = nullptr)
            : key(k),     // 初始化键
            value(v),   // 初始化值
            next(n)     // 初始化下一个节点指针
        {} // 空函数体
    };


    // 桶数组存储链表头指针
    std::vector<HashNode*> buckets;

    // 哈希函数
    std::hash<Key> hashFunction;

    // 元素计数
    size_t numElements = 0;

    // 最大负载因子
    const float maxLoadFactor = 1.0f;

    // 获取桶索引
    size_t getBucketIndex(const Key& key) const {

        std::cout<< "哈希索引计算 key: "<< key << " 原始哈希值hashFunction(key):" << hashFunction(key) << " 桶大小 buckets.size(): " << buckets.size() << "  分配的索引(存在第几个桶)"<< hashFunction(key) % buckets.size() << std::endl;
        return hashFunction(key) % buckets.size();
    }

    // 重新哈希(扩容)
    void rehash(size_t newSize) {
        std::vector<HashNode*> newBuckets(newSize, nullptr);

        for (size_t i = 0; i < buckets.size(); ++i) {
            HashNode* node = buckets[i];
            while (node != nullptr) {
                HashNode* next = node->next;
                size_t newIndex = hashFunction(node->key) % newSize;

                // 插入到新桶的链表头部
                node->next = newBuckets[newIndex];
                newBuckets[newIndex] = node;

                node = next;
            }
        }

        buckets = std::move(newBuckets);
    }

public:
    CloserToOriginalUnorderedMap(size_t initialSize = 10) {
        buckets.resize(initialSize, nullptr);
    }

    ~CloserToOriginalUnorderedMap() {
        clear();
    }

    // 清空哈希表
    void clear() {
        for (size_t i = 0; i < buckets.size(); ++i) {
            HashNode* node = buckets[i];
            while (node != nullptr) {
                HashNode* next = node->next;
                delete node;
                node = next;
            }
            buckets[i] = nullptr;
        }
        numElements = 0;
    }

    // 插入元素
    void insert(const Key& key, const Value& value) {
        // 检查是否需要扩容
        if (loadFactor() > maxLoadFactor) {
            rehash(buckets.size() * 2);
        }

        size_t index = getBucketIndex(key);
        HashNode* node = buckets[index];// 取出目标位置的数据

 
        // 1-1检查是否已存在该键
        while (node != nullptr) {
            // 1-1-1  存在 不允许相同的多个,直接更新数值
            if (node->key == key) {
                node->value = value; // 更新值
                return;
            }
            // 1-1-2 不存在 
            node = node->next;  // 检查下一个节点
        }

        // 1-2插入新节点到链表头部
               /*
        *
        map.insert("apple", 1);  // 假设哈希到桶3
        map.insert("banana", 2); // 假设哈希到桶3(冲突)
        map.insert("orange", 3); // 假设哈希到桶5

         桶数组:
        [0] → nullptr
        [1] → nullptr
        [2] → nullptr
        banana插入前
        [3] → ["apple":1] → nullptr
        banana插入后
        [3] → ["banana":2] → ["apple":1] → nullptr
        [4] → nullptr
        [5] → ["orange":3] → nullptr
        ...
        */

        buckets[index] = new HashNode(key, value, buckets[index]);
        numElements++;
        std::cout << "加入数据 key: " << key << " value:" << value << "  " << "  哈希索引:" << index << std::endl;
    }

    // 查找元素
    Value* find(const Key& key) {
        size_t index = getBucketIndex(key);
        HashNode* node = buckets[index];

        while (node != nullptr) {
            if (node->key == key) {
                return &node->value;
            }
            node = node->next;
        }

        return nullptr;
    }

    // 删除元素
    bool erase(const Key& key) {
        size_t index = getBucketIndex(key);
        HashNode* node = buckets[index];
        HashNode* prev = nullptr;

        while (node != nullptr) {
            if (node->key == key) {
                if (prev == nullptr) {
                    // 删除的是链表头节点
                    buckets[index] = node->next;
                }
                else {
                    prev->next = node->next;
                }

                delete node;
                numElements--;
                return true;
            }

            prev = node;
            node = node->next;
        }

        return false;
    }

    // 获取当前负载因子
    float loadFactor() const {
        return static_cast<float>(numElements) / buckets.size();
    }

    // 打印哈希表状态(用于调试)
    void printStats() const {
        std::cout << "\n Hash Table Stats:\n";
        std::cout << "  Buckets: " << buckets.size() << "\n";
        std::cout << "  Elements: " << numElements << "\n";
        std::cout << "  Load Factor: " << loadFactor() << "\n";

        size_t emptyBuckets = 0;
        size_t maxChainLength = 0;

        for (size_t i = 0; i < buckets.size(); ++i) {
            size_t chainLength = 0;
            HashNode* node = buckets[i];

            if (node == nullptr) {
                emptyBuckets++;
            }

            while (node != nullptr) {
                chainLength++;
                node = node->next;
            }

            if (chainLength > maxChainLength) {
                maxChainLength = chainLength;
            }
        }

        std::cout << "  Empty Buckets: " << emptyBuckets << "\n";
        std::cout << "  Max Chain Length: " << maxChainLength << "\n";
    }
};

int main() {
    CloserToOriginalUnorderedMap<std::string, int> map;

    map.insert("apple", 1);
    map.insert("banana", 2);
    map.insert("orange", 3);
    map.insert("apple", 4); // 更新已有的键

    // 查找测试
    if (auto ptr = map.find("banana")) {
        std::cout << "Found banana: " << *ptr << "\n";
    }

    // 删除测试
    map.erase("orange");

    // 打印哈希表状态
    map.printStats();

    return 0;
}

  

 

posted on 2025-09-09 17:07  MKT-porter  阅读(6)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3