C++ 是一种功能强大且灵活的编程语言,提供了多种数据结构来有效地组织和管理数据。掌握这些数据结构对于写出高效、可维护的代码至关重要。本文将详细讨论 C++ 中常见的数据结构,包括数组、链表、栈、队列、树、图以及 C++ STL 中提供的容器,例如向量(vector)、列表(list)、集合(set)、映射(map)、哈希表(unordered_map)、智能指针等。

一、基本数据结构

1. 数组(Array)

数组是存储相同类型元素的固定大小的集合。数组是最基本的数据结构之一,支持随机访问。

#include <iostream>  

int main() {  
    int arr[5] = {10, 20, 30, 40, 50};  
    for (int i = 0; i < 5; ++i) {  
        std::cout << arr[i] << " "; // 输出:10 20 30 40 50  
    }  
    return 0;  
}

优缺点

  • 优点:快速的随机访问(O(1)),内存开销小。
  • 缺点:固定大小,不方便插入和删除。

2. 链表(Linked List)

链表是由节点组成的线性数据结构,每个节点包含数据和指向下一个节点的指针。链表有单向链表和双向链表之分。

#include <iostream>  

struct Node {  
    int data;  
    Node* next;  
};  

void printList(Node* head) {  
    Node* temp = head;  
    while (temp) {  
        std::cout << temp->data << " "; // 输出链表节点的数据  
        temp = temp->next;  
    }  
}  

int main() {  
    Node* head = new Node{10, nullptr};  
    head->next = new Node{20, nullptr};  
    head->next->next = new Node{30, nullptr};  

    printList(head); // 输出:10 20 30  

    // 释放内存(未实现)  
    return 0;  
}

优缺点

  • 优点:动态大小,方便插入和删除(O(1))。
  • 缺点:随机访问速度慢(O(n)),额外的内存开销用于存储指针。

3. 栈(Stack)

栈是一个后进先出(LIFO,Last In First Out)数据结构。可以使用数组或链表实现。

#include <iostream>  
#include <stack>  

int main() {  
    std::stack<int> s;  
    s.push(1);  
    s.push(2);  
    s.push(3);  

    while (!s.empty()) {  
        std::cout << s.top() << " "; // 输出:3 2 1  
        s.pop();  
    }  

    return 0;  
}

优缺点

  • 优点:操作简单,适用于临时存储数据,快速(O(1))。
  • 缺点:只能访问栈顶元素,不支持随机访问。

4. 队列(Queue)

队列是一个先进先出(FIFO,First In First Out)数据结构。可以使用数组或链表实现。

#include <iostream>  
#include <queue>  

int main() {  
    std::queue<int> q;  
    q.push(1);  
    q.push(2);  
    q.push(3);  

    while (!q.empty()) {  
        std::cout << q.front() << " "; // 输出:1 2 3  
        q.pop();  
    }  

    return 0;  
}

优缺点

  • 优点:适合处理顺序性的数据。
  • 缺点:同样不允许随机访问。

二、树(Tree)

1. 二叉树(Binary Tree)

每个节点最多有两个子节点的树结构。常用于实现搜索树、堆等数据结构。

#include <iostream>  

struct Node {  
    int data;  
    Node* left;  
    Node* right;  
};  

void inorderTraversal(Node* root) {  
    if (root) {  
        inorderTraversal(root->left);  
        std::cout << root->data << " "; // 中序遍历  
        inorderTraversal(root->right);  
    }  
}  

int main() {  
    Node* root = new Node{1, nullptr, nullptr};  
    root->left = new Node{2, nullptr, nullptr};  
    root->right = new Node{3, nullptr, nullptr};  

    inorderTraversal(root); // 输出:2 1 3  

    // 释放内存(未实现)  
    return 0;  
}

2. 二叉搜索树(Binary Search Tree)

满足左子树小于根节点,右子树大于根节点的二叉树,用于高效的查找操作。

#include <iostream>  

struct Node {  
    int data;  
    Node* left;  
    Node* right;  

    Node(int value) : data(value), left(nullptr), right(nullptr) {}  
};  

Node* insert(Node* root, int value) {  
    if (!root) return new Node(value);  
    if (value < root->data)  
        root->left = insert(root->left, value);  
    else  
        root->right = insert(root->right, value);  
    return root;  
}  

void inorderTraversal(Node* root) {  
    if (root) {  
        inorderTraversal(root->left);  
        std::cout << root->data << " ";  
        inorderTraversal(root->right);  
    }  
}  

int main() {  
    Node* root = nullptr;  
    root = insert(root, 3);  
    insert(root, 1);  
    insert(root, 2);  
    insert(root, 4);  
    inorderTraversal(root); // 输出:1 2 3 4  

    return 0;  
}

三、图(Graph)

图是一种非线性数据结构,包含节点和连接节点的边。图有有向图和无向图之分。

#include <iostream>  
#include <vector>  

class Graph {  
    int V; // 节点数  
    std::vector<std::vector<int>> adj; // 邻接表  

public:  
    Graph(int v) : V(v), adj(v) {}  

    void addEdge(int u, int v) {  
        adj[u].push_back(v); // 有向图,将 v 添加到 u 的邻接表  
    }  

    void printGraph() {  
        for (int i = 0; i < V; ++i) {  
            std::cout << i << ": ";  
            for (int j : adj[i]) {  
                std::cout << j << " ";  
            }  
            std::cout << std::endl;  
        }  
    }  
};  

int main() {  
    Graph g(5);  
    g.addEdge(0, 1);  
    g.addEdge(0, 4);  
    g.addEdge(1, 2);  
    g.addEdge(1, 3);  
    g.addEdge(1, 4);  
    g.addEdge(2, 3);  
    g.addEdge(3, 4);  

    g.printGraph();  

    return 0;  
}

四、C++ STL 中的容器

C++ 标准库 STL (Standard Template Library) 提供了多个容器类,使得数据结构的实现和管理更为简便。以下是常用的 STL 容器:

1. 向量(std::vector

动态数组,支持快速随机访问和动态扩展。

#include <iostream>  
#include <vector>  

int main() {  
    std::vector<int> vec = {1, 2, 3, 4, 5};  
    vec.push_back(6); // 添加元素  
    for (int i : vec) {  
        std::cout << i << " "; // 输出:1 2 3 4 5 6  
    }  
    return 0;  
}

2. 列表(std::list

双向链表的实现,支持快速插入和删除操作,但不支持随机访问。

#include <iostream>  
#include <list>  

int main() {  
    std::list<int> lst = {1, 2, 3};  
    lst.push_back(4);  
    lst.push_front(0);  
    for (int i : lst) {  
        std::cout << i << " "; // 输出:0 1 2 3 4  
    }  
    return 0;  
}

3. 集合(std::set

按顺序存储唯一元素的集合,基于红黑树实现。

#include <iostream>  
#include <set>  

int main() {  
    std::set<int> s = {1, 2, 3};  
    s.insert(2); // 不会插入第二个 2  
    s.insert(4);  
    for (int i : s) {  
        std::cout << i << " "; // 输出:1 2 3 4  
    }  
    return 0;  
}

4. 映射(std::map

基于键值对存储的有序容器,适合快速查找和删除。

#include <iostream>  
#include <map>  

int main() {  
    std::map<std::string, int> m;  
    m["apple"] = 1;  
    m["banana"] = 2;  
    m["orange"] = 3;  

    for (const auto& pair : m) {  
        std::cout << pair.first << ": " << pair.second << std::endl; // 输出键值对  
    }  
    return 0;  
}

5. 哈希表(std::unordered_map

基于哈希表实现的,无序容器,支持快速的键值对存取操作。

#include <iostream>  
#include <unordered_map>  

int main() {  
    std::unordered_map<std::string, int> umap;  
    umap["apple"] = 1;  
    umap["banana"] = 2;  

    for (const auto& pair : umap) {  
        std::cout << pair.first << ": " << pair.second << std::endl; // 输出键值对  
    }  
    return 0;  
}

五、智能指针

智能指针是 C++11 引入的一种管理动态内存的方法,减少内存泄漏的风险。常见的智能指针有 std::unique_ptr 和 std::shared_ptr

#include <iostream>  
#include <memory>  

int main() {  
    std::unique_ptr<int> uptr(new int(10)); // 创建 unique_ptr  
    std::cout << *uptr << std::endl; // 输出:10  

    // std::shared_ptr<int> sptr = std::make_shared<int>(20); // 创建 shared_ptr  
    // std::cout << *sptr << std::endl; // 输出:20  

    return 0;  
}