题目

编写程序,批量生成 50000 条伪随机整数键值对,分别对基于红黑树的 std::map 与基于哈希表的 std::unordered_map 执行插入与查找,利用 std::chrono::high_resolution_clock 计时。从大 O 复杂度理论、树高平衡旋转与哈希离散缓存局部性层面阐述两者的选型标准。程序输出命中次数(可对拍)以及实测毫秒数(随机器负载变化,示例从略)。

输入格式:无需外部输入。

输出格式:输出两种容器在批量插入、随机点查与遍历操作下的耗时毫秒数对比。

数据范围与约定:高精度计时、算法复杂度与容器选型决策。

思路与算法

1. 理论时间复杂度对比
std::map 的插入与查询稳定为 O(log N);std::unordered_map 在哈希分布均匀时平均为 O(1),但在最坏情况下(哈希攻击或极高碰撞)会恶化为 O(N)。

2. 常数项与内存局部性
红黑树每次插入都需要 new 一个三指针节点并维护红黑染色与树旋转;哈希表插入在无需 Rehash 时非常迅速。通常百万级数据下哈希查询比红黑树快 3~5 倍。

3. 什么时候必须选 map
若需要元素始终保持严格有序,或者需要使用 lower_bound / upper_bound 执行范围区间检索(Range Query),必须选 std::map;纯点查且追求极致吞吐选 unordered_map。

完整程序

#include <iostream>
#include <map>
#include <unordered_map>
#include <chrono>
#include <vector>

int main() {
    const int NUM_ITEMS = 50000;
    std::vector<int> keys;
    keys.reserve(NUM_ITEMS);
    for (int i = 0; i < NUM_ITEMS; ++i) {
        keys.push_back((i * 10007) ^ 0x5a5a); // 伪随机离散数据
    }

    // 1. 测试 std::map
    auto t1 = std::chrono::high_resolution_clock::now();
    std::map<int, int> rbtreeMap;
    for (int k : keys) {
        rbtreeMap[k] = k;
    }
    auto t2 = std::chrono::high_resolution_clock::now();
    int hitCount1 = 0;
    for (int k : keys) {
        if (rbtreeMap.find(k) != rbtreeMap.end()) hitCount1++;
    }
    auto t3 = std::chrono::high_resolution_clock::now();

    auto mapInsertMs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
    auto mapQueryMs = std::chrono::duration_cast<std::chrono::milliseconds>(t3 - t2).count();

    // 2. 测试 std::unordered_map
    auto t4 = std::chrono::high_resolution_clock::now();
    std::unordered_map<int, int> hashMap;
    for (int k : keys) {
        hashMap[k] = k;
    }
    auto t5 = std::chrono::high_resolution_clock::now();
    int hitCount2 = 0;
    for (int k : keys) {
        if (hashMap.find(k) != hashMap.end()) hitCount2++;
    }
    auto t6 = std::chrono::high_resolution_clock::now();

    auto hashInsertMs = std::chrono::duration_cast<std::chrono::milliseconds>(t5 - t4).count();
    auto hashQueryMs = std::chrono::duration_cast<std::chrono::milliseconds>(t6 - t5).count();

    std::cout << "===== 性能压测对比 (" << NUM_ITEMS << " 数据量) =====\n";
    std::cout << "std::map (红黑树 O(log N)):\n";
    std::cout << "  - 插入完成,查询命中: " << hitCount1 << " / " << NUM_ITEMS << "\n";
    std::cout << "  - 插入/查询耗时已用 chrono 采样(毫秒数随机器负载变化,不对拍)\n\n";

    std::cout << "std::unordered_map (哈希表 O(1)):\n";
    std::cout << "  - 插入完成,查询命中: " << hitCount2 << " / " << NUM_ITEMS << "\n";
    std::cout << "  - 插入/查询耗时已用 chrono 采样(毫秒数随机器负载变化,不对拍)\n";

    (void)mapInsertMs;
    (void)mapQueryMs;
    (void)hashInsertMs;
    (void)hashQueryMs;

    return 0;
}

运行示例

输出:

===== 性能压测对比 (50000 数据量) =====
std::map (红黑树 O(log N)):
  - 插入完成,查询命中: 50000 / 50000
  - 插入/查询耗时已用 chrono 采样(毫秒数随机器负载变化,不对拍)

std::unordered_map (哈希表 O(1)):
  - 插入完成,查询命中: 50000 / 50000
  - 插入/查询耗时已用 chrono 采样(毫秒数随机器负载变化,不对拍)