【数据结构手册007】集合结构 - set与unordered_set的专精解析

数据结构手册007:集合结构 - set与unordered_set的专精解析

集合的数学本质:从康托尔到计算机

集合是数学中最基础的概念之一,描述了确定的、互不相同的对象的整体。在计算机科学中,集合结构继承了这一数学思想,成为处理唯一性元素的强大工具。

cpp 复制代码
// 数学集合与计算机集合的对应关系
// 数学: A = {1, 2, 3, 4, 5}
// C++: std::set<int> A = {1, 2, 3, 4, 5};

// 数学: B = {x | x 是偶数}
// C++: 通过条件判断动态构建集合

集合的基本特性:理解唯一性与无序性

集合的核心特征

集合结构具有三个基本特性:

  • 唯一性:集合中不允许重复元素
  • 确定性:元素要么属于集合,要么不属于集合
  • 无序性:集合中的元素没有特定顺序(数学定义)
cpp 复制代码
#include <set>
#include <unordered_set>
#include <iostream>

void setBasicProperties() {
    // 唯一性演示
    std::set<int> uniqueNumbers;
    uniqueNumbers.insert(1);
    uniqueNumbers.insert(2);
    uniqueNumbers.insert(2);  // 重复元素,不会被插入
    uniqueNumbers.insert(3);
    
    std::cout << "集合大小: " << uniqueNumbers.size() << std::endl;  // 3
    std::cout << "集合内容: ";
    for (int num : uniqueNumbers) {
        std::cout << num << " ";  // 1 2 3
    }
    std::cout << std::endl;
    
    // 确定性演示
    if (uniqueNumbers.find(2) != uniqueNumbers.end()) {
        std::cout << "2 属于集合" << std::endl;
    }
    if (uniqueNumbers.find(4) == uniqueNumbers.end()) {
        std::cout << "4 不属于集合" << std::endl;
    }
}

std::set:基于红黑树的有序集合

set的底层实现与特性

set的底层使用红黑树实现,保证元素的有序性和操作的对数时间复杂度。

cpp 复制代码
void setOrderedCharacteristics() {
    std::set<std::string> sortedSet;
    
    // 插入元素(自动排序)
    sortedSet.insert("orange");
    sortedSet.insert("apple");
    sortedSet.insert("banana");
    sortedSet.insert("grape");
    
    std::cout << "有序集合内容:" << std::endl;
    for (const auto& fruit : sortedSet) {
        std::cout << fruit << " ";  // apple banana grape orange
    }
    std::cout << std::endl;
    
    // 查找操作 O(log n)
    auto it = sortedSet.find("banana");
    if (it != sortedSet.end()) {
        std::cout << "找到: " << *it << std::endl;
    }
    
    // 范围查找
    auto lower = sortedSet.lower_bound("b");
    auto upper = sortedSet.upper_bound("g");
    std::cout << "b到g之间的元素: ";
    for (auto iter = lower; iter != upper; ++iter) {
        std::cout << *iter << " ";  // banana grape
    }
    std::cout << std::endl;
}

set的迭代器稳定性

cpp 复制代码
void setIteratorStability() {
    std::set<int> stableSet = {1, 2, 3, 4, 5};
    
    auto it = stableSet.find(3);
    std::cout << "初始迭代器指向: " << *it << std::endl;
    
    // 插入和删除其他元素不会使迭代器失效
    stableSet.insert(6);
    stableSet.erase(1);
    
    // 迭代器仍然有效
    std::cout << "操作后迭代器指向: " << *it << std::endl;  // 3
    
    // 但删除当前元素会使迭代器失效
    it = stableSet.find(2);
    stableSet.erase(2);
    // 此时 it 失效,不能再使用
}

std::unordered_set:基于哈希表的无序集合

unordered_set的哈希实现

unordered_set使用哈希表实现,提供平均O(1)时间复杂度的查找操作。

cpp 复制代码
void unorderedSetHashCharacteristics() {
    std::unordered_set<std::string> hashSet;
    
    // 插入元素(无序存储)
    hashSet.insert("dog");
    hashSet.insert("cat");
    hashSet.insert("elephant");
    hashSet.insert("bird");
    
    std::cout << "哈希集合内容:" << std::endl;
    for (const auto& animal : hashSet) {
        std::cout << animal << " ";  // 顺序不确定
    }
    std::cout << std::endl;
    
    // 快速查找 O(1) 平均情况
    if (hashSet.contains("cat")) {  // C++20
        std::cout << "集合包含猫" << std::endl;
    }
    
    // 哈希表统计信息
    std::cout << "桶数量: " << hashSet.bucket_count() << std::endl;
    std::cout << "负载因子: " << hashSet.load_factor() << std::endl;
}

自定义类型的哈希集合

cpp 复制代码
class Product {
public:
    std::string name;
    int id;
    double price;
    
    Product(const std::string& n, int i, double p) 
        : name(n), id(i), price(p) {}
    
    // 相等比较运算符
    bool operator==(const Product& other) const {
        return id == other.id;  // 根据ID判断相等
    }
};

// 自定义哈希函数
struct ProductHash {
    std::size_t operator()(const Product& p) const {
        return std::hash<int>{}(p.id);  // 基于ID计算哈希
    }
};

void customTypeSet() {
    std::unordered_set<Product, ProductHash> productSet;
    
    Product p1("Laptop", 1001, 999.99);
    Product p2("Mouse", 1002, 29.99);
    Product p3("Keyboard", 1003, 79.99);
    
    productSet.insert(p1);
    productSet.insert(p2);
    productSet.insert(p3);
    
    // 查找测试
    Product searchProduct("", 1002, 0.0);
    if (productSet.find(searchProduct) != productSet.end()) {
        std::cout << "找到ID为1002的产品" << std::endl;
    }
}

集合运算:数学操作的代码实现

基本集合运算

cpp 复制代码
template<typename T>
std::set<T> setUnion(const std::set<T>& A, const std::set<T>& B) {
    std::set<T> result = A;
    result.insert(B.begin(), B.end());  // 并集
    return result;
}

template<typename T>
std::set<T> setIntersection(const std::set<T>& A, const std::set<T>& B) {
    std::set<T> result;
    std::set_intersection(A.begin(), A.end(),
                         B.begin(), B.end(),
                         std::inserter(result, result.begin()));  // 交集
    return result;
}

template<typename T>
std::set<T> setDifference(const std::set<T>& A, const std::set<T>& B) {
    std::set<T> result;
    std::set_difference(A.begin(), A.end(),
                       B.begin(), B.end(),
                       std::inserter(result, result.begin()));  // 差集
    return result;
}

template<typename T>
bool isSubset(const std::set<T>& A, const std::set<T>& B) {
    // A 是 B 的子集
    return std::includes(B.begin(), B.end(), A.begin(), A.end());
}

void setOperationsDemo() {
    std::set<int> A = {1, 2, 3, 4, 5};
    std::set<int> B = {3, 4, 5, 6, 7};
    
    auto unionSet = setUnion(A, B);
    auto intersectionSet = setIntersection(A, B);
    auto differenceSet = setDifference(A, B);
    
    std::cout << "A: "; for (int x : A) std::cout << x << " "; std::cout << std::endl;
    std::cout << "B: "; for (int x : B) std::cout << x << " "; std::cout << std::endl;
    std::cout << "A ∪ B: "; for (int x : unionSet) std::cout << x << " "; std::cout << std::endl;
    std::cout << "A ∩ B: "; for (int x : intersectionSet) std::cout << x << " "; std::cout << std::endl;
    std::cout << "A - B: "; for (int x : differenceSet) std::cout << x << " "; std::cout << std::endl;
    std::cout << "A ⊆ B: " << isSubset(A, B) << std::endl;
}

高效集合运算算法

cpp 复制代码
class EfficientSetOperations {
public:
    // 使用归并思想求交集
    template<typename T>
    static std::vector<T> sortedIntersection(const std::vector<T>& sortedA, 
                                           const std::vector<T>& sortedB) {
        std::vector<T> result;
        size_t i = 0, j = 0;
        
        while (i < sortedA.size() && j < sortedB.size()) {
            if (sortedA[i] < sortedB[j]) {
                ++i;
            } else if (sortedA[i] > sortedB[j]) {
                ++j;
            } else {
                result.push_back(sortedA[i]);
                ++i;
                ++j;
            }
        }
        return result;
    }
    
    // 布隆过滤器模拟(用于大数据量去重)
    class BloomFilter {
    private:
        std::vector<bool> bits;
        size_t size;
        
    public:
        BloomFilter(size_t n) : size(n), bits(n, false) {}
        
        void add(const std::string& element) {
            size_t hash1 = std::hash<std::string>{}(element) % size;
            size_t hash2 = std::hash<std::string>{}(element + "salt") % size;
            bits[hash1] = true;
            bits[hash2] = true;
        }
        
        bool mightContain(const std::string& element) const {
            size_t hash1 = std::hash<std::string>{}(element) % size;
            size_t hash2 = std::hash<std::string>{}(element + "salt") % size;
            return bits[hash1] && bits[hash2];  // 可能有假阳性
        }
    };
};

void efficientOperationsDemo() {
    // 有序数组求交集
    std::vector<int> sortedA = {1, 2, 3, 4, 5};
    std::vector<int> sortedB = {3, 4, 5, 6, 7};
    auto intersection = EfficientSetOperations::sortedIntersection(sortedA, sortedB);
    
    std::cout << "高效交集: ";
    for (int x : intersection) std::cout << x << " ";  // 3 4 5
    std::cout << std::endl;
    
    // 布隆过滤器演示
    EfficientSetOperations::BloomFilter bloom(1000);
    bloom.add("hello");
    bloom.add("world");
    
    std::cout << "布隆过滤器检查:" << std::endl;
    std::cout << "包含'hello': " << bloom.mightContain("hello") << std::endl;
    std::cout << "包含'test': " << bloom.mightContain("test") << std::endl;
}

性能深度分析:set vs unordered_set

时间复杂度对比

cpp 复制代码
#include <chrono>
#include <random>

void performanceBenchmark() {
    const int ELEMENT_COUNT = 100000;
    std::vector<int> elements;
    
    // 生成测试数据
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, ELEMENT_COUNT * 10);
    
    for (int i = 0; i < ELEMENT_COUNT; ++i) {
        elements.push_back(dis(gen));
    }
    
    // 测试set插入性能
    auto start = std::chrono::high_resolution_clock::now();
    std::set<int> orderedSet;
    for (int elem : elements) {
        orderedSet.insert(elem);
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto setInsertTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    // 测试unordered_set插入性能
    start = std::chrono::high_resolution_clock::now();
    std::unordered_set<int> unorderedSet;
    for (int elem : elements) {
        unorderedSet.insert(elem);
    }
    end = std::chrono::high_resolution_clock::now();
    auto unorderedSetInsertTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    // 测试查找性能
    start = std::chrono::high_resolution_clock::now();
    for (int elem : elements) {
        orderedSet.find(elem);
    }
    end = std::chrono::high_resolution_clock::now();
    auto setLookupTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    start = std::chrono::high_resolution_clock::now();
    for (int elem : elements) {
        unorderedSet.find(elem);
    }
    end = std::chrono::high_resolution_clock::now();
    auto unorderedSetLookupTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    std::cout << "性能测试结果 (" << ELEMENT_COUNT << " 个元素):" << std::endl;
    std::cout << "set 插入时间: " << setInsertTime.count() << "ms" << std::endl;
    std::cout << "unordered_set 插入时间: " << unorderedSetInsertTime.count() << "ms" << std::endl;
    std::cout << "set 查找时间: " << setLookupTime.count() << "ms" << std::endl;
    std::cout << "unordered_set 查找时间: " << unorderedSetLookupTime.count() << "ms" << std::endl;
    std::cout << "set 最终大小: " << orderedSet.size() << std::endl;
    std::cout << "unordered_set 最终大小: " << unorderedSet.size() << std::endl;
}

内存使用分析

cpp 复制代码
void memoryUsageAnalysis() {
    std::set<int> orderedSet;
    std::unordered_set<int> unorderedSet;
    
    const int COUNT = 1000;
    for (int i = 0; i < COUNT; ++i) {
        orderedSet.insert(i);
        unorderedSet.insert(i);
    }
    
    std::cout << "内存使用分析:" << std::endl;
    std::cout << "set 大小: " << orderedSet.size() << std::endl;
    std::cout << "unordered_set 大小: " << unorderedSet.size() << std::endl;
    std::cout << "unordered_set 桶数量: " << unorderedSet.bucket_count() << std::endl;
    std::cout << "unordered_set 负载因子: " << unorderedSet.load_factor() << std::endl;
    
    // 内存开销分析:
    // set: 每个节点需要存储左右指针、颜色标志等,内存开销相对固定
    // unordered_set: 需要维护桶数组和链表节点,存在未使用桶的内存浪费
}

实战应用场景

场景1:数据去重与统计

cpp 复制代码
class DataDeduplicator {
public:
    // 基本去重功能
    static std::vector<int> removeDuplicates(const std::vector<int>& data) {
        std::unordered_set<int> seen;
        std::vector<int> result;
        
        for (int value : data) {
            if (seen.insert(value).second) {  // 插入成功说明是首次出现
                result.push_back(value);
            }
        }
        return result;
    }
    
    // 统计唯一元素数量
    static size_t countUniqueElements(const std::vector<std::string>& elements) {
        return std::unordered_set<std::string>(elements.begin(), elements.end()).size();
    }
    
    // 查找第一个重复元素
    static std::optional<std::string> findFirstDuplicate(const std::vector<std::string>& elements) {
        std::unordered_set<std::string> seen;
        
        for (const auto& elem : elements) {
            if (!seen.insert(elem).second) {  // 插入失败说明已存在
                return elem;
            }
        }
        return std::nullopt;
    }
};

void deduplicationDemo() {
    std::vector<int> numbers = {1, 2, 2, 3, 4, 4, 4, 5};
    std::vector<std::string> words = {"apple", "banana", "apple", "orange", "banana"};
    
    auto uniqueNumbers = DataDeduplicator::removeDuplicates(numbers);
    auto uniqueCount = DataDeduplicator::countUniqueElements(words);
    auto firstDuplicate = DataDeduplicator::findFirstDuplicate(words);
    
    std::cout << "去重后数字: ";
    for (int num : uniqueNumbers) std::cout << num << " ";  // 1 2 3 4 5
    std::cout << std::endl;
    
    std::cout << "唯一单词数量: " << uniqueCount << std::endl;  // 3
    if (firstDuplicate) {
        std::cout << "第一个重复单词: " << *firstDuplicate << std::endl;  // apple
    }
}

场景2:访问控制与权限管理

cpp 复制代码
class AccessControlSystem {
private:
    std::unordered_set<std::string> adminUsers;
    std::unordered_set<std::string> bannedIPs;
    std::set<std::string> privilegedResources;  // 有序,便于显示
    
public:
    AccessControlSystem() {
        // 初始化管理员
        adminUsers = {"alice", "bob", "charlie"};
        
        // 初始化特权资源
        privilegedResources = {"/admin", "/config", "/logs", "/backup"};
    }
    
    bool isAdmin(const std::string& username) const {
        return adminUsers.find(username) != adminUsers.end();
    }
    
    bool isIPBanned(const std::string& ip) const {
        return bannedIPs.find(ip) != bannedIPs.end();
    }
    
    bool canAccessResource(const std::string& username, const std::string& resource) const {
        if (privilegedResources.find(resource) != privilegedResources.end()) {
            return isAdmin(username);
        }
        return true;  // 非特权资源所有人都可以访问
    }
    
    void banIP(const std::string& ip) {
        bannedIPs.insert(ip);
    }
    
    void unbanIP(const std::string& ip) {
        bannedIPs.erase(ip);
    }
    
    void displayPrivilegedResources() const {
        std::cout << "特权资源列表:" << std::endl;
        for (const auto& resource : privilegedResources) {
            std::cout << " - " << resource << std::endl;
        }
    }
};

void accessControlDemo() {
    AccessControlSystem acs;
    
    std::cout << "Alice 是管理员: " << acs.isAdmin("alice") << std::endl;  // true
    std::cout << "Dave 是管理员: " << acs.isAdmin("dave") << std::endl;    // false
    
    std::cout << "Alice 访问 /admin: " << acs.canAccessResource("alice", "/admin") << std::endl;  // true
    std::cout << "Dave 访问 /admin: " << acs.canAccessResource("dave", "/admin") << std::endl;    // false
    
    acs.banIP("192.168.1.100");
    std::cout << "IP 192.168.1.100 是否被禁: " << acs.isIPBanned("192.168.1.100") << std::endl;  // true
    
    acs.displayPrivilegedResources();
}

场景3:图论中的邻接表表示

cpp 复制代码
class Graph {
private:
    std::unordered_map<int, std::unordered_set<int>> adjacencyList;
    
public:
    void addEdge(int from, int to) {
        adjacencyList[from].insert(to);
        adjacencyList[to].insert(from);  // 无向图
    }
    
    void removeEdge(int from, int to) {
        adjacencyList[from].erase(to);
        adjacencyList[to].erase(from);
    }
    
    bool hasEdge(int from, int to) const {
        auto it = adjacencyList.find(from);
        return it != adjacencyList.end() && it->second.find(to) != it->second.end();
    }
    
    const std::unordered_set<int>& getNeighbors(int vertex) const {
        static const std::unordered_set<int> emptySet;
        auto it = adjacencyList.find(vertex);
        return it != adjacencyList.end() ? it->second : emptySet;
    }
    
    void displayGraph() const {
        for (const auto& [vertex, neighbors] : adjacencyList) {
            std::cout << vertex << " -> ";
            for (int neighbor : neighbors) {
                std::cout << neighbor << " ";
            }
            std::cout << std::endl;
        }
    }
};

void graphDemo() {
    Graph socialNetwork;
    
    // 添加朋友关系
    socialNetwork.addEdge(1, 2);
    socialNetwork.addEdge(1, 3);
    socialNetwork.addEdge(2, 4);
    socialNetwork.addEdge(3, 4);
    socialNetwork.addEdge(4, 5);
    
    std::cout << "社交网络图:" << std::endl;
    socialNetwork.displayGraph();
    
    std::cout << "用户1的朋友: ";
    for (int friendId : socialNetwork.getNeighbors(1)) {
        std::cout << friendId << " ";  // 2 3
    }
    std::cout << std::endl;
    
    std::cout << "用户1和用户4是否是朋友: " << socialNetwork.hasEdge(1, 4) << std::endl;  // false
}

特殊变体:multiset与unordered_multiset

允许重复元素的集合

cpp 复制代码
void multiSetDemo() {
    // multiset:允许重复元素的有序集合
    std::multiset<int> multiNumbers;
    multiNumbers.insert(3);
    multiNumbers.insert(1);
    multiNumbers.insert(4);
    multiNumbers.insert(1);  // 允许重复
    multiNumbers.insert(2);
    
    std::cout << "multiset 内容: ";
    for (int num : multiNumbers) {
        std::cout << num << " ";  // 1 1 2 3 4
    }
    std::cout << std::endl;
    
    // 统计特定元素的出现次数
    std::cout << "1 出现次数: " << multiNumbers.count(1) << std::endl;  // 2
    
    // 查找所有等于某个值的元素
    auto range = multiNumbers.equal_range(1);
    std::cout << "所有等于1的元素: ";
    for (auto it = range.first; it != range.second; ++it) {
        std::cout << *it << " ";  // 1 1
    }
    std::cout << std::endl;
    
    // unordered_multiset:允许重复元素的无序集合
    std::unordered_multiset<std::string> multiWords;
    multiWords.insert("apple");
    multiWords.insert("banana");
    multiWords.insert("apple");
    
    std::cout << "'apple' 出现次数: " << multiWords.count("apple") << std::endl;  // 2
}

性能优化与最佳实践

1. 预留空间优化

cpp 复制代码
void reserveOptimization() {
    // 对于unordered_set,预先分配空间可以避免重新哈希
    std::unordered_set<int> optimizedSet;
    optimizedSet.reserve(10000);  // 预先分配空间
    
    for (int i = 0; i < 10000; ++i) {
        optimizedSet.insert(i);
    }
    
    std::cout << "优化后桶数量: " << optimizedSet.bucket_count() << std::endl;
    std::cout << "负载因子: " << optimizedSet.load_factor() << std::endl;
}

2. 使用emplace避免拷贝

cpp 复制代码
void emplaceOptimization() {
    std::set<std::string> stringSet;
    
    // 传统insert - 可能产生临时对象
    stringSet.insert("hello world");  // 构造临时string
    
    // emplace - 直接在集合中构造
    stringSet.emplace("hello world");  // 更高效
    
    // 对于复杂类型,优势更明显
    struct ComplexData {
        int id;
        std::string name;
        std::vector<double> values;
        
        ComplexData(int i, const std::string& n, std::vector<double> v)
            : id(i), name(n), values(std::move(v)) {}
        
        // 定义比较运算符用于set排序
        bool operator<(const ComplexData& other) const {
            return id < other.id;
        }
    };
    
    std::set<ComplexData> complexSet;
    // emplace直接构造,避免拷贝
    complexSet.emplace(1, "test", std::vector<double>{1.0, 2.0, 3.0});
}

3. 键类型选择优化

cpp 复制代码
void keyTypeOptimization() {
    // 使用小且简单的键类型
    std::unordered_set<int> good;        // 好:整型键
    std::unordered_set<std::string> ok;  // 可接受:字符串键
    
    // 避免使用复杂键类型,或提供高效哈希函数
    class EfficientKey {
    private:
        int id;
        std::string name;
        
    public:
        // 提供高效的哈希函数
        std::size_t hash() const {
            return std::hash<int>{}(id) ^ 
                   (std::hash<std::string>{}(name) << 1);
        }
        
        bool operator==(const EfficientKey& other) const {
            return id == other.id && name == other.name;
        }
    };
    
    struct EfficientKeyHash {
        std::size_t operator()(const EfficientKey& key) const {
            return key.hash();
        }
    };
    
    std::unordered_set<EfficientKey, EfficientKeyHash> efficientSet;
}

现代C++特性应用

C++17结构化绑定

cpp 复制代码
void structuredBindingDemo() {
    std::set<std::pair<int, std::string>> data = {
        {1, "Alice"},
        {2, "Bob"},
        {3, "Charlie"}
    };
    
    // C++17 结构化绑定
    for (const auto& [id, name] : data) {
        std::cout << "ID: " << id << ", Name: " << name << std::endl;
    }
    
    // 传统方式
    for (const auto& pair : data) {
        std::cout << "ID: " << pair.first << ", Name: " << pair.second << std::endl;
    }
}

C++20新特性

cpp 复制代码
void cpp20Features() {
    std::set<int> numbers = {1, 2, 3, 4, 5};
    
    // C++20 contains 方法
    if (numbers.contains(3)) {
        std::cout << "集合包含3" << std::endl;
    }
    
    // C++20 范围适配器
    std::vector<int> vec = {1, 2, 2, 3, 3, 3, 4, 5};
    
    // 使用 ranges 去重
    #if __has_include(<ranges>)
    #include <ranges>
    auto uniqueView = vec | std::views::common | std::ranges::to<std::set<int>>();
    std::cout << "去重后大小: " << uniqueView.size() << std::endl;
    #endif
}

选择指南:set vs unordered_set

决策流程图

复制代码
需要元素有序遍历?
    ↓是
选择 std::set
    ↓
    ↓否
需要范围查询或最近邻查找?
    ↓是
选择 std::set
    ↓
    ↓否
性能是关键因素,且哈希质量好?
    ↓是
选择 std::unordered_set
    ↓
    ↓否
需要稳定的性能表现?
    ↓是
选择 std::set (避免哈希冲突的最坏情况)
    ↓
    ↓否
内存使用是关键因素?
    ↓是
测试两种结构,选择更优者
    ↓
    ↓否
选择 std::unordered_set (通常更快)

具体场景建议

cpp 复制代码
void usageRecommendations() {
    // 使用set的场景:
    // 1. 需要有序遍历或范围查询
    std::set<int> sortedUniqueIds;
    
    // 2. 元素比较便宜,但哈希计算昂贵
    std::set<std::tuple<int, int, int>> coordinateSet;
    
    // 3. 需要稳定的性能表现
    std::set<std::string> sortedDictionary;
    
    // 使用unordered_set的场景:
    // 1. 只需要快速存在性检查
    std::unordered_set<std::string> wordLookup;
    
    // 2. 哈希质量好且计算快速
    std::unordered_set<int> cacheKeys;
    
    // 3. 数据量很大且主要进行查找操作
    std::unordered_set<uint64_t> largeDataset;
}

与映射结构的对比

set vs map 的关系

cpp 复制代码
void setVsMapComparison() {
    // set 可以看作是只有键的map
    std::set<int> numberSet = {1, 2, 3, 4, 5};
    std::map<int, bool> numberMap = {{1, true}, {2, true}, {3, true}, {4, true}, {5, true}};
    
    // 两者在查找性能上相似
    // 但set内存开销更小,因为不需要存储值
    
    // 选择原则:
    // - 只需要检查存在性 → set
    // - 需要存储关联数据 → map
    // - 内存敏感 → set
    // - 需要键值对 → map
}

总结

set和unordered_set作为集合抽象的具体实现,在不同的应用场景中展现了各自的优势:

std::set的优势

  • 保证元素有序,支持范围查询
  • 性能稳定可预测
  • 内存使用相对高效
  • 迭代器稳定性好

std::unordered_set的优势

  • 平均情况下更快的查找性能
  • 适合纯存在性检查场景
  • 大数据量时性能优势明显
  • 接口更简单直观

核心设计原则

  • 唯一性需求 → 考虑集合结构
  • 有序性重要 → 选择set
  • 纯性能追求 → 选择unordered_set
  • 内存敏感 → 测试两种结构

理解集合结构的特性和适用场景,能够帮助我们在面对去重、存在性检查、集合运算等问题时,选择最合适的数据结构,编写出高效可靠的代码。


下一章预告:《数据结构手册008:高级数据结构实战 - 综合应用与性能优化》

我们将综合运用所学数据结构,解决复杂的实际问题,探索数据结构组合使用的艺术,并深入性能优化技巧,构建高效可靠的系统架构。

相关推荐
-To be number.wan17 小时前
C++ 赋值运算符重载:深拷贝 vs 浅拷贝的生死线!
前端·c++
TheSumSt18 小时前
Python丨课程笔记Part3:语法进阶部分(控制结构与基础数据结构)
数据结构·笔记·python
XXYBMOOO19 小时前
内核驱动开发与用户级驱动开发:深度对比与应用场景解析
linux·c++·驱动开发·嵌入式硬件·fpga开发·硬件工程
长安er21 小时前
LeetCode 20/155/394/739/84/42/单调栈核心原理与经典题型全解析
数据结构·算法·leetcode·动态规划·
SoveTingღ1 天前
【问题解析】我的客户端与服务器交互无响应了?
服务器·c++·qt·tcp
温宇飞1 天前
内存异常
c++
!停1 天前
c语言动态申请内存
c语言·开发语言·数据结构
悟能不能悟1 天前
list<string> 和String[],转化为jsonstr是不是一样的
数据结构·windows·list
TechNomad1 天前
二叉堆&大根堆&小根堆的介绍和使用
数据结构
挖矿大亨1 天前
C++中深拷贝与浅拷贝的原理
开发语言·c++·算法