好的!既然你提到了 C++ 标准库中的容器类 <list>,我将为你详细讲解它的用法,包括基本概念、特性、功能以及实用示例。<list> 是 C++ STL 中的双向链表容器,适合需要频繁插入和删除元素的场景。与 <vector> 不同,它不支持随机访问,但提供了高效的链表操作。以下是完整的内容,适合学习和实践。


C++ 标准库:<list>

<list> 是一个模板类,表示双向链表(doubly-linked list)。它的主要特点是:

  • 动态大小:可以在运行时添加或删除元素。
  • 非连续内存:元素通过指针链接,分散在内存中。
  • 高效插入/删除:在任意位置插入或删除元素的时间复杂度为 O(1)。
  • 双向迭代:支持从头到尾或从尾到头的遍历,但不支持随机访问。

包含头文件

#include <list>
#include <iostream> // 通常配合使用

定义格式

std::list<类型> 变量名;
  • 类型:元素的数据类型(如 intdoublestd::string)。
  • 可选:初始大小或初始化列表。

基本用法

1. 声明和初始化

#include <list>
#include <iostream>

int main() {
    std::list<int> lst1;               // 空链表
    std::list<int> lst2(5, 10);        // 5 个元素,每个值为 10
    std::list<int> lst3 = {1, 2, 3, 4}; // 初始化列表(C++11 起)

    std::cout << "lst3: ";
    for (int val : lst3) {
        std::cout << val << " ";
    }
    std::cout << std::endl; // 输出:1 2 3 4
    return 0;
}

2. 添加和删除元素

#include <list>
#include <iostream>

int main() {
    std::list<int> lst;
    lst.push_back(10);  // 在末尾添加
    lst.push_front(20); // 在开头添加
    lst.push_back(30);

    std::cout << "Original: ";
    for (int val : lst) {
        std::cout << val << " "; // 输出:20 10 30
    }
    std::cout << std::endl;

    lst.pop_front(); // 删除开头元素
    lst.pop_back();  // 删除末尾元素
    std::cout << "After pop: ";
    for (int val : lst) {
        std::cout << val << " "; // 输出:10
    }
    std::cout << std::endl;

    return 0;
}
  • push_back() / push_front():O(1) 复杂度。
  • pop_back() / pop_front():O(1) 复杂度。

3. 访问元素

#include <list>
#include <iostream>

int main() {
    std::list<double> lst = {1.5, 2.7, 3.9};
    std::cout << "Front: " << lst.front() << std::endl; // 第一个元素
    std::cout << "Back: " << lst.back() << std::endl;   // 最后一个元素
    return 0;
}

输出:

Front: 1.5
Back: 3.9
  • <list> 不支持 []at(),只能通过 front()back() 或迭代器访问。

4. 大小和状态

#include <list>
#include <iostream>

int main() {
    std::list<int> lst = {1, 2, 3};
    std::cout << "Size: " << lst.size() << std::endl;         // 当前元素数:3
    std::cout << "Empty? " << (lst.empty() ? "Yes" : "No") << std::endl; // 是否为空:No
    return 0;
}
  • size():O(1)(C++11 起),早期可能是 O(n)。
  • capacity(),因为链表不预分配内存。

5. 插入和删除

#include <list>
#include <iostream>

int main() {
    std::list<int> lst = {1, 2, 3, 4};
    auto it = lst.begin();
    ++it; // 移动到第二个元素
    lst.insert(it, 99); // 在第二个位置插入 99
    std::cout << "After insert: ";
    for (int val : lst) {
        std::cout << val << " "; // 输出:1 99 2 3 4
    }
    std::cout << std::endl;

    it = lst.begin(); // 重置迭代器
    lst.erase(it);    // 删除第一个元素
    std::cout << "After erase: ";
    for (int val : lst) {
        std::cout << val << " "; // 输出:99 2 3 4
    }
    std::cout << std::endl;

    return 0;
}
  • insert() / erase():O(1) 复杂度(仅操作指针,不移动元素)。

6. 迭代器

#include <list>
#include <iostream>

int main() {
    std::list<std::string> lst = {"Apple", "Banana", "Orange"};
    std::cout << "Forward: ";
    for (auto it = lst.begin(); it != lst.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl; // 输出:Apple Banana Orange

    std::cout << "Reverse: ";
    for (auto it = lst.rbegin(); it != lst.rend(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl; // 输出:Orange Banana Apple
    return 0;
}
  • 支持双向迭代器:begin()/end()(正向),rbegin()/rend()(逆向)。

7. 特殊操作

#include <list>
#include <iostream>

int main() {
    std::list<int> lst = {3, 1, 4, 1, 5};
    lst.sort(); // 排序
    std::cout << "Sorted: ";
    for (int val : lst) {
        std::cout << val << " "; // 输出:1 1 3 4 5
    }
    std::cout << std::endl;

    lst.unique(); // 删除连续重复元素
    std::cout << "Unique: ";
    for (int val : lst) {
        std::cout << val << " "; // 输出:1 3 4 5
    }
    std::cout << std::endl;

    return 0;
}
  • sort():O(n log n),链表专用排序。
  • unique():O(n),需先排序。

<vector> 的对比

特性std::liststd::vector
内存布局非连续(链表)连续(数组)
插入/删除O(1)(任意位置)O(n)(非末尾)
随机访问不支持支持(O(1))
迭代器类型双向随机访问
内存开销较高(指针)较低(仅数据)

注意事项

  1. 性能:适合频繁插入/删除,不适合需要快速随机访问的场景。
  2. 迭代器失效:插入不影响现有迭代器,删除会使指向被删元素的迭代器失效。
  3. 内存使用:每个节点有额外指针开销,比 <vector> 占更多内存。
  4. 排序<list> 自带 sort(),不能用 std::sort(需要随机访问迭代器)。

小练习

以下是一个基于 <list> 的练习题,你可以尝试:

问题:

写一个 C++ 程序:

  1. 定义一个 std::list<int>,初始化为 {1, 2, 3, 4, 5}。
  2. 在开头插入 99。
  3. 删除第 3 个元素(值为 3)。
  4. 输出所有元素和当前大小。

参考答案(你可以先尝试)

#include <list>
#include <iostream>

int main() {
    std::list<int> lst = {1, 2, 3, 4, 5};

    // 在开头插入 99
    lst.push_front(99);

    // 删除第 3 个元素(索引 2)
    auto it = lst.begin();
    std::advance(it, 2); // 移动到第 3 个元素
    lst.erase(it);

    // 输出所有元素和大小
    std::cout << "Elements: ";
    for (int val : lst) {
        std::cout << val << " ";
    }
    std::cout << "\nSize: " << lst.size() << std::endl;

    return 0;
}

输出:

Elements: 99 1 2 4 5
Size: 5

下一步

  • 如果你想试试这个练习,请写下你的代码,我会帮你检查。
  • 如果你想要更复杂的内容(比如合并链表、反转链表),或者设计 <list> 的测验题,请告诉我!
  • 你也可以提出具体问题,比如“如何用 <list> 实现队列?”或“<list><forward_list> 的区别?”。

现在,你想做什么?试试练习,还是有其他需求?