C++所有11种数据结构概览

这篇文章我们来全面地梳理一下 C++ 中所有主要的数据结构。我将按照它们的分类进行介绍,并对每一个结构提供其简介底层实现时间复杂度核心使用场景以及一个简明的 C++ 代码示例

在 C++ 中,绝大多数数据结构都是通过标准模板库 (Standard Template Library, STL) 提供的。熟练使用 STL 是 C++ 开发的关键。


数据结构分类

C++ STL 中的数据结构主要可以分为以下几类:

  1. 顺序容器 (Sequence Containers):元素按线性顺序排列。

    • std::vector
    • std::array
    • std::list
    • std::deque
    • std::forward_list
  2. 关联容器 (Associative Containers):根据键值自动排序,查找速度快。

    • std::map
    • std::set
    • std::multimap
    • std::multiset
  3. 无序关联容器 (Unordered Associative Containers):使用哈希表实现,提供平均 O(1) 的查找速度。

    • std::unordered_map
    • std::unordered_set
    • std::unordered_multimap
    • std::unordered_multiset
  4. 容器适配器 (Container Adaptors):提供特定接口的容器,底层由其他容器实现。

    • std::stack
    • std::queue
    • std::priority_queue

1. 顺序容器 (Sequence Containers)

std::vector三种数组的辨析与使用)
  • 简介:动态数组。是在 C++ 中使用最广泛的容器。它能像数组一样快速随机访问,同时也能在末尾高效地添加或删除元素。

  • 底层实现:一块连续的内存空间。当空间不足时,它会重新分配一块更大的内存(通常是原大小的 1.5 或 2 倍),并将所有旧元素复制到新空间,然后释放旧空间。

  • 时间复杂度

    • 随机访问 (通过 [].at()): O(1)
    • 末尾添加/删除 (push_back, pop_back): 均摊 O(1)
    • 中间或开头插入/删除: O(n) (因为需要移动后续所有元素)
  • 使用场景

    • 需要快速随机访问元素。
    • 主要在容器末尾进行添加或删除操作。
    • 当你需要一个动态数组时,vector 应该是你的默认首选
  • 代码示例

    #include <iostream>
    #include <vector>
    
    int main() {
        std::vector<int> vec;
        vec.push_back(10); // [10]
        vec.push_back(20); // [10, 20]
        vec.push_back(30); // [10, 20, 30]
    
        std::cout << "第一个元素: " << vec[0] << std::endl; // 快速随机访问
    
        vec.insert(vec.begin() + 1, 15); // 在中间插入 [10, 15, 20, 30]
    
        for (int val : vec) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
        return 0;
    }
    
std::list
  • 简介:双向链表。每个元素都存储了指向前一个和后一个元素的指针。

  • 底层实现:非连续的节点,每个节点包含数据以及前后指针。

  • 时间复杂度

    • 随机访问: O(n) (需要从头或尾遍历)

    • 任意位置插入/删除: O(1) (前提是已持有指向该位置的迭代器)

  • 使用场景

    • 在序列的任何位置都需要频繁地插入和删除操作。

    • 不需要随机访问元素。

  • 代码示例

    #include <iostream>
    #include <list>
    
    int main() {
        std::list<int> myList;
        myList.push_back(10);
        myList.push_front(5); // 在头部添加
        myList.push_back(20); // [5, 10, 20]
    
        auto it = myList.begin();
        it++; // 移动到 10
        myList.insert(it, 8); // 在 10 前面插入 8, O(1) 操作 [5, 8, 10, 20]
    
        for (int val : myList) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
        return 0;
    }
    
std::deque (双端队列)
  • 简介:Double-ended queue。结合了 vectorlist 的部分优点。支持快速随机访问,并且在序列的头部和尾部都能高效地添加或删除元素。

  • 底层实现:分块的连续内存(一个指向多个内存块的指针数组)。它不是一块完整的连续内存,但每个块内部是连续的。

  • 时间复杂度

    • 随机访问: O(1) (但常数时间比 vector 稍大)

    • 头部/末尾添加/删除 (push_front, pop_front, push_back, pop_back): 均摊 O(1)

    • 中间插入/删除: O(n)

  • 使用场景

    • 需要在容器的头部和尾部都进行频繁的插入和删除操作。

    • 例如,实现一个工作窃取队列。

std::array (C++11)
  • 简介:固定大小的数组。是对 C 风格数组的封装,提供了更安全、更方便的接口(如获取大小、迭代器等)。

  • 底层实现:与 C 风格数组完全一样,一块固定大小的连续内存,分配在栈上(如果作为局部变量)或静态存储区。

  • 时间复杂度

    • 所有操作都与 C 风格数组相同。随机访问 O(1)。
  • 使用场景

    • 当你明确知道需要一个固定大小的数组时。

    • 需要 C 风格数组的性能,但又想要 STL 容器的便利性(如迭代器)。


2. 关联容器 (Associative Containers)

这类容器的共同特点是自动排序

std::map
  • 简介:存储键-值对 (key-value) 的集合,键是唯一的,并根据键自动排序。

  • 底层实现:通常是红黑树 (Red-Black Tree),一种自平衡的二叉搜索树。

  • 时间复杂度

    • 插入、删除、查找: O(logn)
  • 使用场景

    • 需要存储键值对,并按键排序。

    • 需要快速根据键查找值。

    • 例如,存储学生姓名和分数的映射,并可以按姓名字典序输出。

  • 代码示例

    #include <iostream>
    #include <map>
    #include <string>
    
    int main() {
        std::map<std::string, int> ageMap;
        ageMap["Alice"] = 30;
        ageMap["Bob"] = 25;
        ageMap.insert({"Charlie", 35});
    
        std::cout << "Bob's age: " << ageMap["Bob"] << std::endl;
    
        // map 会自动按键排序
        for (const auto& pair : ageMap) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
        // 输出会是 Alice, Bob, Charlie 的顺序
        return 0;
    }
    
std::set
  • 简介:存储唯一元素的集合,并自动排序。

  • 底层实现:与 std::map 类似,通常是红黑树

  • 时间复杂度

    • 插入、删除、查找: O(logn)
  • 使用场景

    • 需要存储一组不重复的元素,并保持它们有序。

    • 快速判断一个元素是否存在于集合中。

  • 代码示例

    #include <iostream>
    #include <set>
    
    int main() {
        std::set<int> unique_numbers;
        unique_numbers.insert(10);
        unique_numbers.insert(5);
        unique_numbers.insert(10); // 重复的 10 不会被插入
    
        if (unique_numbers.count(5)) {
            std::cout << "5 is in the set." << std::endl;
        }
    
        // set 会自动排序
        for (int num : unique_numbers) {
            std::cout << num << " "; // 输出: 5 10
        }
        std::cout << std::endl;
        return 0;
    }
    
  • std::multimapstd::multiset:分别是 mapset 的多键版本,允许存储重复的键。


3. 无序关联容器 (Unordered Associative Containers)

这类容器的共同特点是不排序,但提供更快的平均查找速度。

std::unordered_map
  • 简介:存储键-值对的集合,键是唯一的,但元素是无序的。

  • 底层实现哈希表 (Hash Table)

  • 时间复杂度

    • 插入、删除、查找: 平均 O(1),最坏情况 O(n) (哈希冲突导致)
  • 使用场景

    • 当你需要 map 的功能(键值对查找),但不关心元素的顺序,并且追求极致的查找性能时。这是 map的高性能替代品。

    • 例如,统计一个文本文件中每个单词出现的频率。

  • 代码示例

    #include <iostream>
    #include <unordered_map>
    #include <string>
    
    int main() {
        std::unordered_map<std::string, int> word_count;
        word_count["hello"]++;
        word_count["world"]++;
        word_count["hello"]++;
    
        std::cout << "Count of 'hello': " << word_count["hello"] << std::endl;
    
        // 遍历顺序是不确定的
        for (const auto& pair : word_count) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
        return 0;
    }
    
std::unordered_set
  • 简介:存储唯一元素的集合,元素是无序的。

  • 底层实现哈希表 (Hash Table)

  • 时间复杂度

    • 插入、删除、查找: 平均 O(1),最坏情况 O(n)
  • 使用场景

    • 当你需要 set 的功能(存储唯一元素),但不关心顺序,并且想要最快的查找速度。

    • 例如,快速检查一个元素是否已经处理过。

  • std::unordered_multimapstd::unordered_multiset:允许存储重复键的哈希版本。


4. 容器适配器 (Container Adaptors)

它们不是真正的数据结构,而是对现有容器的接口进行封装,以提供特定的行为模式。

std::stack (栈)
  • 简介:后进先出 (Last-In, First-Out, LIFO) 的数据结构。

  • 底层实现:默认使用 std::deque,也可以用 std::vectorstd::list

  • 核心操作push() (入栈), pop() (出栈), top() (查看栈顶元素)。所有操作都是 O(1)。

  • 使用场景:函数调用栈、括号匹配、表达式求值、深度优先搜索 (DFS)。

  • 代码示例

    #include <iostream>
    #include <stack>
    
    int main() {
        std::stack<int> s;
        s.push(1); // [1]
        s.push(2); // [1, 2]
        s.push(3); // [1, 2, 3]
    
        std::cout << "Top element: " << s.top() << std::endl; // 3
        s.pop(); // 3 出栈
        std::cout << "New top element: " << s.top() << std::endl; // 2
        return 0;
    }
    
std::queue (队列)
  • 简介:先进先出 (First-In, First-Out, FIFO) 的数据结构。

  • 底层实现:默认使用 std::deque,也可以用 std::list

  • 核心操作push() (入队), pop() (出队), front() (查看队首), back() (查看队尾)。所有操作都是 O(1)。

  • 使用场景:任务队列、消息队列、广度优先搜索 (BFS)。

  • 代码示例

    #include <iostream>
    #include <queue>
    
    int main() {
        std::queue<int> q;
        q.push(1); // [1]
        q.push(2); // [1, 2]
        q.push(3); // [1, 2, 3]
    
        std::cout << "Front element: " << q.front() << std::endl; // 1
        q.pop(); // 1 出队
        std::cout << "New front element: " << q.front() << std::endl; // 2
        return 0;
    }
    
std::priority_queue (优先队列)
  • 简介:一种特殊的队列,每次出队的总是当前队列中优先级最高(值最大或最小)的元素。

  • 底层实现:默认使用 std::vector 作为底层容器,并以堆 (Heap) 的数据结构进行组织。

  • 核心操作push() (入队), pop() (出队), top() (查看优先级最高的元素)。pushpop 都是 O(logn)。

  • 使用场景:Dijkstra 算法、堆排序、任何需要动态找出最大/最小元素的场景。

  • 代码示例

    #include <iostream>
    #include <queue>
    
    int main() {
        // 默认是最大堆
        std::priority_queue<int> pq;
        pq.push(10);
        pq.push(30);
        pq.push(20);
    
        std::cout << "Top element (max): " << pq.top() << std::endl; // 30
        pq.pop();
        std::cout << "New top element: " << pq.top() << std::endl; // 20
    
        // 创建最小堆
        std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq;
        min_pq.push(10);
        min_pq.push(30);
        min_pq.push(20);
        std::cout << "Top element (min): " << min_pq.top() << std::endl; // 10
        return 0;
    }
    

如何选择?一张速查表

需求最佳选择备选方案为什么?
序列存储(默认)std::vectorstd::deque性能均衡,支持快速随机访问和尾部操作。
频繁在任意位置插入/删除std::listO(1) 的插入/删除代价。
固定大小的数组std::arrayC 风格数组更安全,接口更友好。
需要键值对映射,且按键排序std::map自动排序,查找速度 O(logn)。
需要键值对映射,追求最快速度std::unordered_mapstd::map平均 O(1) 查找,但无序。
需要存储唯一元素,并排序std::set自动去重并排序。
需要存储唯一元素,追求最快速度std::unordered_setstd::set平均 O(1) 查找,用于快速判断存在性。
后进先出 (LIFO)std::stack接口清晰,专为栈操作设计。
先进先出 (FIFO)std::queue接口清晰,专为队列操作设计。
需要动态获取最大/最小元素std::priority_queue底层为堆,高效实现。

STL 之外的数据结构

除了 STL 提供的这些,还有一些重要的数据结构需要你自己实现或使用第三方库:

  • 树 (Tree):如二叉树、AVL 树、B 树等。std::mapstd::set 内部使用了红黑树,但没有直接提供树的容器。

  • 图 (Graph):由顶点和边构成,用于表示网络关系。通常用邻接矩阵或邻接表实现。

  • 字典树 (Trie):用于高效的字符串查找和前缀匹配。

  • 布隆过滤器 (Bloom Filter):一种概率型数据结构,用于快速判断一个元素可能存在或绝对不存在于一个集合中。

以上是对所有数据结构进行的概括,具体每个数据结构的使用和语法会在后续文章中进一步描述。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值