【C++STL】list用法详解

  1. list 是一种序列式容器,支持在常数时间内在任意位置进行插入和删除操作,并可以双向迭代(即向前或向后遍历)。

  2. list 的底层采用双向链表结构。链表中每个元素都存储在一个独立的节点中,节点之间通过指针相连,分别指向其前驱节点和后继节点。

  3. listforward_list 十分相似,主要区别在于 forward_list 是单向链表,仅支持向前迭代,因此结构更简单,在某些场景下效率更高。

  4. 相较于其他序列式容器(如 array、vector、deque),list 在任意位置执行插入和删除操作通常具有更好的性能表现。

  5. 然而,与其他序列式容器相比,listforward_list 最大的不足是不支持随机访问。例如,若要访问 list 中的第 6 个元素,必须从某个已知位置(如头部或尾部)开始逐个迭代到达目标位置,所需时间为线性级。此外,list 每个节点都需要额外空间存储前后节点的指针信息,对于存储元素较小但链表规模较大的情况,这部分开销可能成为一个重要考虑因素。

一.list的构造函数

也就是4种构造函数

  1. 默认构造:创建空列表

  2. 填充构造:创建包含n个相同元素的列表

  3. 范围构造:从已有的迭代器范围创建列表(可以是数组、vector、list等)

  4. 拷贝构造:创建另一个列表的完整副本(深拷贝)

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    // 1. 默认构造
    std::list<int> list1;
    std::cout << "Default constructor - size: " << list1.size() << std::endl;
    
    // 2. 填充构造
    std::list<int> list2(4, 88);  // 4个元素,都是88
    std::cout << "Fill constructor - size: " << list2.size() << std::endl;
    
    // 3. 范围构造
    int values[] = {1, 2, 3, 4, 5};
    std::list<int> list3(values, values + 3);  // 取前3个元素
    std::cout << "Range constructor - size: " << list3.size() << std::endl;
    
    // 4. 拷贝构造
    std::list<int> list4(list2);
    std::cout << "Copy constructor - size: " << list4.size() << std::endl;
    
    return 0;
}

二.list的容量操作

  1. empty
  • 作用:判断容器是否为空。
  • 返回值:布尔值(true 表示容器为空,false 表示不为空)。
  1. size
  • 作用:返回容器中当前元素的个数。
  • 返回值:一个无符号整数(通常是 size_t 类型)。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    // 创建list的四种方法
    std::cout << "=== 创建list的四种方法 ===" << std::endl;
    
    // 1. 空list
    std::list<int> list1;
    std::cout << "1. 空list: size=" << list1.size() 
              << ", empty=" << (list1.empty() ? "true" : "false") << std::endl;
    
    // 2. 包含5个100的list
    std::list<int> list2(5, 100);
    std::cout << "2. 5个100: size=" << list2.size() 
              << ", empty=" << (list2.empty() ? "true" : "false") << std::endl;
    
    // 3. 从数组创建list
    int arr[] = {1, 2, 3};
    std::list<int> list3(arr, arr + 3);
    std::cout << "3. 从数组创建: size=" << list3.size() 
              << ", empty=" << (list3.empty() ? "true" : "false") << std::endl;
    
    // 4. 拷贝list
    std::list<int> list4(list2);
    std::cout << "4. 拷贝list: size=" << list4.size() 
              << ", empty=" << (list4.empty() ? "true" : "false") << std::endl;
    
    std::cout << "\n=== empty()方法示例 ===" << std::endl;
    
    // 检查list是否为空
    std::list<int> mylist;
    
    if (mylist.empty()) {
        std::cout << "mylist是空的" << std::endl;
    }
    
    // 添加一个元素
    mylist.push_back(10);
    
    if (!mylist.empty()) {
        std::cout << "mylist现在不是空的" << std::endl;
    }
    
    std::cout << "\n=== size()方法示例 ===" << std::endl;
    
    // 查看list大小
    std::list<int> numbers = {1, 2, 3, 4, 5};
    
    std::cout << "numbers有" << numbers.size() << "个元素" << std::endl;
    
    // 添加元素
    numbers.push_back(6);
    std::cout << "添加一个元素后,numbers有" << numbers.size() << "个元素" << std::endl;
    
    // 删除元素
    numbers.pop_front();
    std::cout << "删除一个元素后,numbers有" << numbers.size() << "个元素" << std::endl;
    
    std::cout << "\n=== 综合示例 ===" << std::endl;
    
    // 清空list
    while (!numbers.empty()) {
        std::cout << "当前有" << numbers.size() << "个元素,移除一个" << std::endl;
        numbers.pop_front();
    }
    
    std::cout << "现在numbers是空的吗?" << (numbers.empty() ? "是" : "否") << std::endl;
    std::cout << "当前大小: " << numbers.size() << std::endl;
    
    return 0;
}

三.list的成员访问操作

由于 list 是链表结构,不支持像数组或 vector 那样通过下标 ([]) 或 at() 进行随机访问,它仅提供了两个最直接的方法来获取其两端的元素。

  1. front
  • 作用:返回对 list 中第一个元素的引用。
  • 要求:容器不能为空。在调用此函数前,应使用 empty() 进行检查,否则如果对空 list 调用 front() 会导致未定义行为。
  • 使用场景:常用于获取、修改或检查链表的头部元素。
  1. back
  • 作用:返回对 list 中最后一个元素的引用。
  • 要求:容器不能为空。在调用此函数前,应使用 empty() 进行检查,否则如果对空 list 调用 back() 会导致未定义行为。
  • 使用场景:常用于获取、修改或检查链表的尾部元素。

总结与重要提醒:

  • 双向访问:front 和 back 体现了 list 作为双向链表的特性,可以高效地从两端操作数据。
  • 非随机访问:list 没有提供 operator[] 或 at() 成员函数,因为链表无法在常数时间内跳到任意位置。
  • 安全第一:使用这两个函数前,务必确保容器非空,这是防止程序崩溃的关键。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== front()和back()方法示例 ===" << std::endl;
    
    // 创建一个list
    std::list<int> numbers = {10, 20, 30, 40, 50};
    
    // 获取第一个和最后一个元素
    std::cout << "第一个元素: " << numbers.front() << std::endl;
    std::cout << "最后一个元素: " << numbers.back() << std::endl;
    
    // 修改第一个和最后一个元素
    numbers.front() = 100;
    numbers.back() = 500;
    
    std::cout << "修改后:" << std::endl;
    std::cout << "第一个元素: " << numbers.front() << std::endl;
    std::cout << "最后一个元素: " << numbers.back() << std::endl;
    
    std::cout << "\n=== 安全访问示例 ===" << std::endl;
    
    // 创建一个空的list
    std::list<int> emptyList;
    
    // 安全访问:先检查是否为空
    if (!emptyList.empty()) {
        std::cout << "第一个元素: " << emptyList.front() << std::endl;
        std::cout << "最后一个元素: " << emptyList.back() << std::endl;
    } else {
        std::cout << "list是空的,不能使用front()和back()" << std::endl;
    }
    
    std::cout << "\n=== 实际应用示例 ===" << std::endl;
    
    // 创建队列(使用list)
    std::list<int> queue;
    
    // 入队
    queue.push_back(1);
    queue.push_back(2);
    queue.push_back(3);
    
    // 出队:获取并移除第一个元素
    while (!queue.empty()) {
        std::cout << "处理任务: " << queue.front() << std::endl;
        queue.pop_front();
    }
    
    std::cout << "\n=== 创建栈(使用list)===" << std::endl;
    
    // 创建栈
    std::list<int> stack;
    
    // 压栈
    stack.push_back(100);
    stack.push_back(200);
    stack.push_back(300);
    
    // 弹栈:获取并移除最后一个元素
    while (!stack.empty()) {
        std::cout << "弹出元素: " << stack.back() << std::endl;
        stack.pop_back();
    }
    
    return 0;
}

四.list的成员修改操作

  1. assign
  • 作用:用新的内容替换整个容器的所有元素。可以指定新元素的数量和值,或者用另一个区间的元素来赋值。
  • 特点:调用后,容器原有的所有元素都会被替换/销毁。
cpp 复制代码
#include <iostream>
#include <list>
#include<vector>

int main() {
    std::cout << "=== assign方法示例 ===" << std::endl;
    
    // 创建一个初始list
    std::list<int> numbers = {1, 2, 3, 4, 5};
    
    std::cout << "初始list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 1. 用n个相同值替换所有元素
    std::cout << "\n1. 用5个100替换所有元素:" << std::endl;

    numbers.assign(5, 100);

    std::cout << "替换后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 2. 用另一个区间的元素替换
    std::cout << "\n2. 用数组元素替换:" << std::endl;
    int arr[] = {10, 20, 30};

    numbers.assign(arr, arr + 3);

    std::cout << "替换后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 3. 用另一个list的元素替换
    std::cout << "\n3. 用另一个list的元素替换:" << std::endl;
    std::list<int> otherList = {100, 200, 300, 400};

    numbers.assign(otherList.begin(), otherList.end());

    std::cout << "替换后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 4. 用vector的元素替换
    std::cout << "\n4. 用vector的元素替换:" << std::endl;
    std::vector<int> vec = {7, 8, 9, 10, 11, 12};

    numbers.assign(vec.begin(), vec.end());

    std::cout << "替换后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 5. 清空list
    std::cout << "\n5. 清空list:" << std::endl;

    numbers.assign(0, 0);  // 分配0个元素
    
    std::cout << "清空后大小: " << numbers.size() 
              << ", 是否为空: " << (numbers.empty() ? "是" : "否") << std::endl;
    
    return 0;
}
  1. emplace_front
  • 作用:在链表头部直接构造并插入一个新元素。参数将直接传递给元素的构造函数。
  • 优点:比 push_front 更高效,因为它避免了临时对象的创建和拷贝/移动操作(C++11引入)。
  1. push_front
  • 作用:在链表头部插入一个已存在元素的副本。
  • 对比: emplace_front 是"构造然后插入",push_front 是"拷贝/移动然后插入"。
  1. pop_front
  • 作用:移除链表头部的第一个元素。
  • 注意:容器不能为空。此函数不返回被移除的元素,如需获取该元素,应在调用前使用 front()。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== emplace_front、push_front、pop_front示例 ===" << std::endl;
    
    std::list<int> numbers;
    
    // 1. push_front - 在头部插入元素
    std::cout << "\n1. 使用push_front添加元素:" << std::endl;
    numbers.push_front(3);
    numbers.push_front(2);
    numbers.push_front(1);
    
    std::cout << "当前list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 2. emplace_front - 在头部直接构造元素
    std::cout << "\n2. 使用emplace_front添加元素:" << std::endl;
    numbers.emplace_front(0);
    numbers.emplace_front(-1);
    
    std::cout << "当前list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 3. pop_front - 移除头部元素
    std::cout << "\n3. 使用pop_front移除元素:" << std::endl;
    
    // 先获取头部元素
    std::cout << "移除前,头部元素: " << numbers.front() << std::endl;
    
    // 移除头部元素
    numbers.pop_front();
    std::cout << "移除后,新头部元素: " << numbers.front() << std::endl;
    
    std::cout << "当前list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 4. 连续移除所有元素
    std::cout << "\n4. 连续pop_front移除所有元素:" << std::endl;
    while (!numbers.empty()) {
        std::cout << "准备移除: " << numbers.front() << std::endl;
        numbers.pop_front();
    }
    
    std::cout << "最终list大小: " << numbers.size() << std::endl;
    
    // 5. 安全使用pop_front
    std::cout << "\n5. 安全使用pop_front:" << std::endl;
    if (!numbers.empty()) {
        numbers.pop_front();  // 不会执行,因为list为空
    } else {
        std::cout << "list为空,不能pop_front" << std::endl;
    }
    
    // 6. emplace_front与push_front对比
    std::cout << "\n6. emplace_front与push_front对比:" << std::endl;
    
    // 使用emplace_front直接构造
    numbers.emplace_front(100);
    std::cout << "添加后: " << numbers.front() << std::endl;
    
    // 使用push_front插入已存在的值
    int value = 200;
    numbers.push_front(value);
    std::cout << "添加后: " << numbers.front() << std::endl;
    
    return 0;
}
  1. emplace_back
  • 作用:在链表尾部直接构造并插入一个新元素。参数将直接传递给元素的构造函数。
  • 优点:比 push_back 更高效(C++11引入)。
  1. push_back
  • 作用:在链表尾部插入一个已存在元素的副本。最常用的添加元素的方法之一。
  1. pop_back
  • 作用:移除链表尾部的最后一个元素。
  • 注意:容器不能为空。此函数不返回被移除的元素,如需获取该元素,应在调用前使用 back()。

我们直接看例子

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== emplace_back、push_back、pop_back示例 ===" << std::endl;
    
    std::list<int> numbers;
    
    // 1. push_back - 在尾部插入元素
    std::cout << "\n1. 使用push_back添加元素:" << std::endl;
    numbers.push_back(1);
    numbers.push_back(2);
    numbers.push_back(3);
    
    std::cout << "当前list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 2. emplace_back - 在尾部直接构造元素
    std::cout << "\n2. 使用emplace_back添加元素:" << std::endl;
    numbers.emplace_back(4);
    numbers.emplace_back(5);
    
    std::cout << "当前list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 3. pop_back - 移除尾部元素
    std::cout << "\n3. 使用pop_back移除元素:" << std::endl;
    
    // 先获取尾部元素
    std::cout << "移除前,尾部元素: " << numbers.back() << std::endl;
    
    // 移除尾部元素
    numbers.pop_back();
    std::cout << "移除后,新尾部元素: " << numbers.back() << std::endl;
    
    std::cout << "当前list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 4. 连续移除所有元素
    std::cout << "\n4. 连续pop_back移除所有元素:" << std::endl;
    while (!numbers.empty()) {
        std::cout << "准备移除: " << numbers.back() << std::endl;
        numbers.pop_back();
    }
    
    std::cout << "最终list大小: " << numbers.size() << std::endl;
    
    // 5. 安全使用pop_back
    std::cout << "\n5. 安全使用pop_back:" << std::endl;
    if (!numbers.empty()) {
        numbers.pop_back();  // 不会执行,因为list为空
    } else {
        std::cout << "list为空,不能pop_back" << std::endl;
    }
    
    // 6. emplace_back与push_back对比
    std::cout << "\n6. emplace_back与push_back对比:" << std::endl;
    
    // 使用emplace_back直接构造
    numbers.emplace_back(100);
    std::cout << "添加后: " << numbers.back() << std::endl;
    
    // 使用push_back插入已存在的值
    int value = 200;
    numbers.push_back(value);
    std::cout << "添加后: " << numbers.back() << std::endl;
    
    std::cout << "\n7. 综合示例 - 模拟栈操作:" << std::endl;
    std::list<std::string> stack;
    
    // 使用push_back模拟入栈
    stack.push_back("任务1");
    stack.push_back("任务2");
    stack.push_back("任务3");
    
    std::cout << "栈顶(最后添加的): " << stack.back() << std::endl;
    
    // 使用pop_back模拟出栈
    while (!stack.empty()) {
        std::cout << "执行: " << stack.back() << std::endl;
        stack.pop_back();
    }
    
    return 0;
}
  1. emplace
  • 作用:在指定的迭代器位置之前直接构造并插入一个新元素。这是 insert 的高效版本。
  • 优点:适用于在链表中间任意位置插入,且无需创建临时对象。
  1. insert
  • 作用:在指定的迭代器位置之前插入一个或多个已存在元素的副本。可以插入单个元素、多个相同元素或另一个容器的一段元素。
  • 核心优势:对于 list,在任意位置的插入操作都是常数时间 O(1),这是其相对于 vector 和 deque 的巨大优势。

我们直接看一个例子

cpp 复制代码
#include <iostream>
#include <list>
#include <vector>

int main() {
    std::cout << "=== insert和emplace方法示例 ===" << std::endl;
    
    std::list<int> numbers = {1, 2, 3, 4, 5};
    
    std::cout << "初始list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 1. insert - 在指定位置插入单个元素
    std::cout << "\n1. insert插入单个元素:" << std::endl;
    auto it = numbers.begin();
    ++it;  // 指向第二个元素
    
    numbers.insert(it, 100);  // 在第二个位置前插入100
    std::cout << "在第二个位置前插入100后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 2. insert - 插入多个相同元素
    std::cout << "\n2. insert插入多个相同元素:" << std::endl;
    it = numbers.end();
    numbers.insert(it, 3, 999);  // 在末尾插入3个999
    std::cout << "在末尾插入3个999后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 3. insert - 插入另一个容器的元素
    std::cout << "\n3. insert插入数组元素:" << std::endl;
    int arr[] = {7, 8, 9};
    it = numbers.begin();
    std::advance(it, 3);  // 移动到第4个位置
    numbers.insert(it, arr, arr + 3);
    std::cout << "在第4个位置插入数组后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 4. emplace - 在指定位置直接构造元素
    std::cout << "\n4. emplace直接构造元素:" << std::endl;
    std::list<std::string> words = {"apple", "banana", "cherry"};
    
    std::cout << "初始字符串list: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << std::endl;
    
    auto word_it = words.begin();
    ++word_it;  // 指向第二个元素
    
    // 使用emplace直接构造字符串
    words.emplace(word_it, "orange");
    std::cout << "在第二个位置插入orange后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << std::endl;
    
    // 5. insert返回值 - 返回指向新插入元素的迭代器
    std::cout << "\n5. insert返回值示例:" << std::endl;
    std::list<int> list2 = {10, 20, 30};
    it = list2.begin();
    ++it;
    
    auto new_it = list2.insert(it, 15);  // 插入15
    std::cout << "插入的元素: " << *new_it << std::endl;
    
    // 在刚插入的元素后继续插入
    list2.insert(++new_it, 16);
    
    std::cout << "多次插入后: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    // 6. 性能对比:insert vs emplace
    std::cout << "\n6. insert和emplace对比:" << std::endl;
    std::list<std::string> list3;
    
    // 使用insert需要创建临时对象
    std::string temp = "hello";
    list3.insert(list3.begin(), temp);
    std::cout << "使用insert: " << list3.front() << std::endl;
    
    // 使用emplace直接构造,没有临时对象
    list3.emplace(list3.begin(), "world");
    std::cout << "使用emplace: " << list3.front() << std::endl;
    
    return 0;
}
  1. erase
  • 作用:移除一个指定位置的元素,或移除一个迭代器区间 [first, last) 内的所有元素。
  • 返回值:返回指向被移除元素之后那个元素的迭代器。这是一个非常重要的特性,用于在遍历中安全地删除元素。
  • 核心优势:对于 list,在任意位置的删除操作都是常数时间 O(1)。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== erase方法示例 ===" << std::endl;
    
    // 1. 删除指定位置的单个元素
    std::cout << "\n1. 删除指定位置的单个元素:" << std::endl;
    std::list<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    std::cout << "初始list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    auto it = numbers.begin();
    std::advance(it, 3);  // 指向第4个元素(下标从0开始)
    
    // 删除第4个元素
    numbers.erase(it);
    
    std::cout << "删除第4个元素后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 2. 删除一个区间内的元素
    std::cout << "\n2. 删除一个区间内的元素:" << std::endl;
    std::list<int> list2 = {10, 20, 30, 40, 50, 60, 70};
    
    std::cout << "初始list: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    auto first = list2.begin();
    std::advance(first, 2);  // 指向第3个元素
    auto last = first;
    std::advance(last, 3);   // 指向第6个元素
    
    // 删除从第3个到第5个元素([first, last)区间)
    list2.erase(first, last);
    
    std::cout << "删除第3到第5个元素后: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    // 3. 使用erase返回值
    std::cout << "\n3. 使用erase返回值:" << std::endl;
    std::list<int> list3 = {1, 2, 3, 4, 5};
    
    std::cout << "初始list: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    it = list3.begin();
    while (it != list3.end()) {
        if (*it == 3) {
            it = list3.erase(it);  // 删除3,返回下一个元素的迭代器
        } else {
            ++it;
        }
    }
    
    std::cout << "删除所有3后: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    // 4. 在循环中删除特定条件元素
    std::cout << "\n4. 在循环中删除特定条件元素:" << std::endl;
    std::list<int> list4 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    std::cout << "初始list: ";
    for (int n : list4) std::cout << n << " ";
    std::cout << std::endl;
    
    it = list4.begin();
    while (it != list4.end()) {
        if (*it % 2 == 0) {  // 删除偶数
            it = list4.erase(it);
        } else {
            ++it;
        }
    }
    
    std::cout << "删除所有偶数后: ";
    for (int n : list4) std::cout << n << " ";
    std::cout << std::endl;
    
    // 5. 删除所有元素
    std::cout << "\n5. 删除所有元素:" << std::endl;
    std::list<int> list5 = {1, 2, 3, 4, 5};
    
    std::cout << "初始大小: " << list5.size() << std::endl;
    
    // 删除从开始到结束的所有元素
    list5.erase(list5.begin(), list5.end());
    
    std::cout << "删除所有元素后大小: " << list5.size() << std::endl;
    
    return 0;
}
  1. swap
  • 作用:交换两个 list 容器的全部内容。此操作非常快,因为它只交换两个容器内部的指针(如头指针、尾指针等),而不交换每个元素。
  • 优点:常数时间复杂度 O(1),且不会使指向容器内元素的迭代器、引用和指针失效(它们会指向交换后的新容器中的同一元素)。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== swap方法示例 ===" << std::endl;
    
    // 创建两个不同的list
    std::list<int> list1 = {1, 2, 3};
    std::list<int> list2 = {10, 20, 30, 40};
    
    std::cout << "交换前:" << std::endl;
    std::cout << "list1: ";
    for (int n : list1) std::cout << n << " ";
    std::cout << "\nlist2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    // 1. 交换两个list
    list1.swap(list2);
    
    std::cout << "\n交换后:" << std::endl;
    std::cout << "list1: ";
    for (int n : list1) std::cout << n << " ";
    std::cout << "\nlist2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    // 2. 验证swap是O(1)操作
    std::cout << "\n2. 验证swap是常数时间操作:" << std::endl;
    std::cout << "交换前大小 - list1:" << list1.size() 
              << " list2:" << list2.size() << std::endl;
    
    // 再次交换
    list1.swap(list2);
    
    std::cout << "再次交换后大小 - list1:" << list1.size() 
              << " list2:" << list2.size() << std::endl;
    
    // 3. swap不会使迭代器失效
    std::cout << "\n3. swap不会使迭代器失效:" << std::endl;
    
    // 获取list1中第二个元素的迭代器
    auto it1 = list1.begin();
    ++it1;  // 指向第二个元素(2)
    
    // 获取list2中第三个元素的迭代器
    auto it2 = list2.begin();
    std::advance(it2, 2);  // 指向第三个元素(30)
    
    std::cout << "交换前:" << std::endl;
    std::cout << "it1指向: " << *it1 << " (list1的第2个元素)" << std::endl;
    std::cout << "it2指向: " << *it2 << " (list2的第3个元素)" << std::endl;
    
    // 交换list1和list2
    list1.swap(list2);
    
    std::cout << "\n交换后:" << std::endl;
    std::cout << "it1仍然指向: " << *it1 << " (但现在在list2中)" << std::endl;
    std::cout << "it2仍然指向: " << *it2 << " (但现在在list1中)" << std::endl;
    
    // 4. 使用std::swap也可以交换list
    std::cout << "\n4. 使用std::swap交换list:" << std::endl;
    
    std::list<int> a = {100, 200};
    std::list<int> b = {300, 400, 500};
    
    std::cout << "交换前: a = ";
    for (int n : a) std::cout << n << " ";
    std::cout << ", b = ";
    for (int n : b) std::cout << n << " ";
    std::cout << std::endl;
    
    std::swap(a, b);  // 标准库的swap
    
    std::cout << "交换后: a = ";
    for (int n : a) std::cout << n << " ";
    std::cout << ", b = ";
    for (int n : b) std::cout << n << " ";
    std::cout << std::endl;
    
    // 5. 性能比较:swap vs 赋值
    std::cout << "\n5. swap与赋值的性能比较:" << std::endl;
    
    std::list<int> x(1000, 1);  // 1000个1
    std::list<int> y(1000, 2);  // 1000个2
    
    std::cout << "交换前: x大小=" << x.size() 
              << ", y大小=" << y.size() << std::endl;
    
    // swap是O(1),只交换指针
    x.swap(y);
    std::cout << "swap后: x大小=" << x.size() 
              << ", y大小=" << y.size() << std::endl;
    
    // 赋值是O(n),需要复制所有元素
    x = y;  // 将y的所有元素复制到x
    std::cout << "赋值后: x大小=" << x.size() 
              << ", y大小=" << y.size() << std::endl;
    
    return 0;
}
  1. resize
  • 作用:改变容器的大小。
  • 如果新尺寸 (n) 小于当前尺寸,则容器尾部多余的元素会被销毁。
  • 如果新尺寸大于当前尺寸,则会在容器尾部添加额外数量的元素(可以指定初始化值,默认为值初始化的新元素)。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== resize方法示例 ===" << std::endl;
    
    // 1. 创建初始list
    std::list<int> numbers = {1, 2, 3, 4, 5};
    
    std::cout << "初始list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 2. 缩小list大小
    std::cout << "\n2. 缩小list大小(resize到3):" << std::endl;
    numbers.resize(3);  // 只保留前3个元素
    
    std::cout << "缩小后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 3. 扩大list大小(使用默认值0)
    std::cout << "\n3. 扩大list大小(resize到6,默认值0):" << std::endl;
    numbers.resize(6);  // 扩大,新元素为0
    
    std::cout << "扩大后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 4. 扩大list大小(指定新元素的值)
    std::cout << "\n4. 扩大list大小(resize到8,指定值99):" << std::endl;
    numbers.resize(8, 99);  // 扩大,新元素为99
    
    std::cout << "扩大后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 5. 缩小到空list
    std::cout << "\n5. 缩小到空list(resize到0):" << std::endl;
    numbers.resize(0);
    
    std::cout << "缩小后大小: " << numbers.size() << std::endl;
    std::cout << "list是否为空: " << (numbers.empty() ? "是" : "否") << std::endl;
    
    // 6. 从空list扩大
    std::cout << "\n6. 从空list扩大(resize到5,指定值7):" << std::endl;
    numbers.resize(5, 7);  // 创建5个元素,都是7
    
    std::cout << "扩大后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 7. resize与构造函数对比
    std::cout << "\n7. resize与构造函数对比:" << std::endl;
    
    // 构造函数创建10个100
    std::list<int> list1(10, 100);
    std::cout << "构造函数list(10, 100): ";
    for (int i = 0; i < 5; ++i) {  // 只显示前5个
        auto it = list1.begin();
        std::advance(it, i);
        std::cout << *it << " ";
    }
    std::cout << "... 大小: " << list1.size() << std::endl;
    
    // 使用resize达到相同效果
    std::list<int> list2;
    list2.resize(10, 100);
    std::cout << "resize(10, 100): ";
    for (int i = 0; i < 5; ++i) {
        auto it = list2.begin();
        std::advance(it, i);
        std::cout << *it << " ";
    }
    std::cout << "... 大小: " << list2.size() << std::endl;
    
    // 8. 字符串list的resize示例
    std::cout << "\n8. 字符串list的resize示例:" << std::endl;
    std::list<std::string> words = {"apple", "banana", "cherry"};
    
    std::cout << "初始字符串list: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    // 缩小
    words.resize(2);
    std::cout << "resize(2)后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    // 扩大,使用默认值""
    words.resize(4);
    std::cout << "resize(4)后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    // 扩大,指定新元素的值
    words.resize(6, "fruit");
    std::cout << "resize(6, \"fruit\")后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    return 0;
}
  1. clear
  • 作用:移除容器内的所有元素,使 size() 变为 0。容器本身(内存占用)可能不会改变,但所有元素对象都会被销毁。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== clear方法示例 ===" << std::endl;
    
    // 1. 创建并清空一个list
    std::list<int> numbers = {1, 2, 3, 4, 5};
    
    std::cout << "初始list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 使用clear清空list
    numbers.clear();
    
    std::cout << "clear()后大小: " << numbers.size() << std::endl;
    std::cout << "list是否为空: " << (numbers.empty() ? "是" : "否") << std::endl;
    
    // 2. 清空后可以重新添加元素
    std::cout << "\n2. 清空后重新添加元素:" << std::endl;
    numbers.push_back(10);
    numbers.push_back(20);
    numbers.push_back(30);
    
    std::cout << "添加后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 3. 再次清空
    numbers.clear();
    std::cout << "再次clear()后大小: " << numbers.size() << std::endl;
    
    // 4. clear与resize(0)对比
    std::cout << "\n4. clear()与resize(0)对比:" << std::endl;
    
    std::list<int> list1 = {1, 2, 3};
    std::list<int> list2 = {1, 2, 3};
    
    std::cout << "list1初始大小: " << list1.size() << std::endl;
    std::cout << "list2初始大小: " << list2.size() << std::endl;
    
    list1.clear();
    list2.resize(0);
    
    std::cout << "list1 clear()后大小: " << list1.size() << std::endl;
    std::cout << "list2 resize(0)后大小: " << list2.size() << std::endl;
    
    // 5. 字符串list的clear操作
    std::cout << "\n5. 字符串list的clear操作:" << std::endl;
    
    std::list<std::string> words = {"apple", "banana", "cherry", "date"};
    
    std::cout << "初始字符串list: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    words.clear();
    std::cout << "clear()后: 大小=" << words.size() 
              << ", 是否为空: " << (words.empty() ? "是" : "否") << std::endl;
    
    // 6. 多次clear是安全的
    std::cout << "\n6. 多次clear是安全的:" << std::endl;
    
    std::list<int> list3;
    list3.clear();  // 已经是空的,但clear仍然安全
    list3.clear();  // 再次clear也安全
    list3.clear();  // 多次clear不会出错
    
    std::cout << "多次clear后大小: " << list3.size() << std::endl;
    
    // 7. clear与其他操作结合
    std::cout << "\n7. clear与其他操作结合:" << std::endl;
    
    std::list<int> data;
    
    // 添加一些数据
    for (int i = 0; i < 5; ++i) {
        data.push_back(i * 10);
    }
    
    std::cout << "添加数据后大小: " << data.size() << std::endl;
    
    // 模拟数据处理
    if (!data.empty()) {
        std::cout << "处理数据..." << std::endl;
        data.clear();  // 处理完后清空
        std::cout << "处理完成,清空数据" << std::endl;
    }
    
    std::cout << "最终大小: " << data.size() << std::endl;
    
    return 0;
}

五.list特有的结点操作

5.1. splice(拼接)

作用:将一个 list 中的元素(或整个 list)移动到另一个 list 的指定位置。这是链表特有的高效操作,因为只修改指针,不涉及元素的拷贝或移动。

特点:

  • 非常高效(常数时间或线性时间,取决于移动的范围)。
  • 源 list 中的元素会被移除,合并到目标 list 中。
  • 有多个重载版本:可以移动单个元素、一个区间内的所有元素,或者整个 list。

我们看看splice的3种重载形式

1. 移动整个 list

cpp 复制代码
void splice(const_iterator position, list& other);
void splice(const_iterator position, list&& other);  // C++11 移动语义

参数意义:

  • position:目标 list 中的位置,新元素会插入到这个位置之前
  • other:源 list,它的所有元素会被移动到目标 list 中

示例:

cpp 复制代码
list1.splice(list1.begin(), list2);
// 把list2的所有元素移动到list1的开头
// 操作后:list2为空,list2的所有元素在list1中

2. 移动单个元素

cpp 复制代码
void splice(const_iterator position, list& other, const_iterator i);

参数意义:

  • position:目标 list 中的位置,元素会插入到这个位置之前
  • other:源 list,元素从这里移出
  • i:源 list 中的迭代器,指向要移动的单个元素

示例:

cpp 复制代码
auto it = list2.begin();  // 指向list2的第一个元素
list1.splice(list1.end(), list2, it);
// 把list2的第一个元素移动到list1的末尾
// 只移动一个元素,list2大小减1,list1大小加1

3. 移动一个区间

cpp 复制代码
void splice(const_iterator position, list& other, 
            const_iterator first, const_iterator last);

参数意义:

  • position:目标 list 中的位置,区间元素会插入到这个位置之前
  • other:源 list,区间元素从这里移出
  • first:源 list 中的起始迭代器,指向区间的开始位置(包含)
  • last:源 list 中的结束迭代器,指向区间的结束位置(不包含)

示例:

cpp 复制代码
auto first = list2.begin();      // 指向开始
auto last = list2.begin();
advance(last, 3);                // 向前移动3位

list1.splice(list1.begin(), list2, first, last);
// 把list2的前3个元素(位置0,1,2)移动到list1的开头
// list2大小减3,list1大小加3

那么,接下来我们看一个完整的例子:

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== splice(拼接)方法示例 ===" << std::endl;
    
    // 创建两个list
    std::list<int> list1 = {1, 2, 3, 4, 5};
    std::list<int> list2 = {10, 20, 30, 40, 50};
    
    std::cout << "初始状态:" << std::endl;
    std::cout << "list1: ";
    for (int n : list1) std::cout << n << " ";
    std::cout << " 大小: " << list1.size() << std::endl;
    
    std::cout << "list2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    // 1. 移动list2中的一个元素到list1
    std::cout << "\n1. 移动list2中的一个元素到list1开头:" << std::endl;
    auto it2 = list2.begin();
    ++it2;  // 指向list2的第二个元素(20)
    
    list1.splice(list1.begin(), list2, it2);  // 把元素20移动到list1开头 ------ 移动单个元素的形式
    
    std::cout << "操作后:" << std::endl;
    std::cout << "list1: ";
    for (int n : list1) std::cout << n << " ";
    std::cout << " 大小: " << list1.size() << std::endl;
    
    std::cout << "list2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    // 2. 移动list2中的一段元素到list1
    std::cout << "\n2. 移动list2中的一段元素到list1末尾:" << std::endl;
    auto first = list2.begin();
    auto last = list2.begin();
    std::advance(last, 3);  // 指向第4个元素
    
    list1.splice(list1.end(), list2, first, last);  // 移动前3个元素 ------ 移动一个区间的形式
    
    std::cout << "操作后:" << std::endl;
    std::cout << "list1: ";
    for (int n : list1) std::cout << n << " ";
    std::cout << " 大小: " << list1.size() << std::endl;
    
    std::cout << "list2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    // 3. 移动整个list2到list1
    std::cout << "\n3. 移动整个list2到list1开头:" << std::endl;
    // 首先恢复list2的内容
    list2 = {100, 200, 300};
    
    std::cout << "恢复list2后:" << std::endl;
    std::cout << "list2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    list1.splice(list1.begin(), list2);  // 移动整个list2到list1开头 ------ 移动整个链表
    
    std::cout << "操作后:" << std::endl;
    std::cout << "list1: ";
    for (int n : list1) std::cout << n << " ";
    std::cout << " 大小: " << list1.size() << std::endl;
    
    std::cout << "list2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    // 4. 同一个list内部移动元素
    std::cout << "\n4. 同一个list内部移动元素:" << std::endl;
    std::list<int> list3 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    std::cout << "初始list3: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    // 把第5个元素(5)移动到开头
    auto pos = list3.begin();
    std::advance(pos, 4);  // 指向第5个元素(5)
    auto element = pos;
    
    list3.splice(list3.begin(), list3, element);
    
    std::cout << "把5移动到开头后: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    // 5. 实际应用:合并两个有序链表的一部分
    std::cout << "\n5. 实际应用:合并有序链表的一部分:" << std::endl;
    
    std::list<int> sorted1 = {1, 3, 5, 7, 9};
    std::list<int> sorted2 = {2, 4, 6, 8, 10};
    
    std::cout << "有序链表1: ";
    for (int n : sorted1) std::cout << n << " ";
    std::cout << std::endl;
    
    std::cout << "有序链表2: ";
    for (int n : sorted2) std::cout << n << " ";
    std::cout << std::endl;
    
    // 把sorted2中小于5的元素移动到sorted1中
    auto it = sorted2.begin();
    while (it != sorted2.end() && *it < 5) {
        auto next = std::next(it);
        sorted1.splice(sorted1.end(), sorted2, it);
        it = next;
    }
    
    std::cout << "移动后:" << std::endl;
    std::cout << "sorted1: ";
    for (int n : sorted1) std::cout << n << " ";
    std::cout << std::endl;
    
    std::cout << "sorted2: ";
    for (int n : sorted2) std::cout << n << " ";
    std::cout << std::endl;
    
    return 0;
}
  1. splice只修改指针,不复制元素,非常高效
  2. 源list中的元素被移除,转移到目标list
  3. 操作后迭代器仍然有效
  4. 时间复杂度:O(1)(移动单个元素或整个list)或O(n)(移动一个区间)

5.2. remove(移除)

作用:移除容器中所有值等于指定值的元素。

注意:

  • 会遍历整个 list,删除所有匹配的元素。
  • 时间复杂度为 O(n)。
  • 与 erase 不同,它按值删除,而不是按位置。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== remove方法示例 ===" << std::endl;
    
    // 1. 移除特定值的所有元素
    std::cout << "\n1. 移除所有值为3的元素:" << std::endl;
    std::list<int> numbers = {1, 2, 3, 4, 3, 5, 3, 6};
    
    std::cout << "初始list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    numbers.remove(3);  // 移除所有值为3的元素
    
    std::cout << "remove(3)后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 2. 移除不存在的值
    std::cout << "\n2. 移除不存在的值(100):" << std::endl;
    std::list<int> list2 = {1, 2, 3, 4, 5};
    
    std::cout << "初始list: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    list2.remove(100);  // 移除100,但list中没有这个值
    
    std::cout << "remove(100)后: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    // 3. 移除所有值为0的元素
    std::cout << "\n3. 移除所有值为0的元素:" << std::endl;
    std::list<int> list3 = {0, 1, 0, 2, 0, 3, 0, 4, 0};
    
    std::cout << "初始list: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << " 大小: " << list3.size() << std::endl;
    
    list3.remove(0);
    
    std::cout << "remove(0)后: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << " 大小: " << list3.size() << std::endl;
    
    // 4. 字符串list的remove操作
    std::cout << "\n4. 字符串list的remove操作:" << std::endl;
    std::list<std::string> words = {"apple", "banana", "apple", "cherry", "apple", "date"};
    
    std::cout << "初始字符串list: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    words.remove("apple");  // 移除所有"apple"
    
    std::cout << "remove(\"apple\")后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    return 0;
}

5.3. remove_if(条件移除)

  • 作用:移除容器中所有满足特定条件的元素。需要一个谓词函数(返回 bool 的可调用对象)作为参数。
  • 示例:删除所有偶数:myList.remove_if([](int n){ return n % 2 == 0; });
  • 注意:同样需要遍历整个 list,时间复杂度为 O(n)。
cpp 复制代码
#include <iostream>
#include <list>

// 自定义判断函数
bool isEven(int n) {
    return n % 2 == 0;
}

bool isOdd(int n) {
    return n % 2 != 0;
}

bool isGreaterThan5(int n) {
    return n > 5;
}

int main() {
    std::cout << "=== remove_if方法示例 ===" << std::endl;
    
    // 1. 使用普通函数作为谓词
    std::cout << "\n1. 使用普通函数删除所有偶数:" << std::endl;
    std::list<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    std::cout << "初始list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    numbers.remove_if(isEven);  // 删除所有偶数
    
    std::cout << "删除偶数后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 2. 使用lambda表达式(C++11)
    std::cout << "\n2. 使用lambda表达式删除大于5的数:" << std::endl;
    std::list<int> list2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    std::cout << "初始list: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    // 使用lambda表达式
    list2.remove_if([](int n) { return n > 5; });
    
    std::cout << "删除大于5的数后: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    // 3. 删除字符串列表中长度小于3的字符串
    std::cout << "\n3. 删除长度小于3的字符串:" << std::endl;
    std::list<std::string> words = {"a", "ab", "abc", "abcd", "b", "bc", "bcd"};
    
    std::cout << "初始字符串list: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    words.remove_if([](const std::string& s) { return s.length() < 3; });
    
    std::cout << "删除长度小于3的字符串后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    // 4. 使用函数对象(仿函数)
    std::cout << "\n4. 使用函数对象删除特定范围的数:" << std::endl;
    
    // 定义一个函数对象
    class Between3And7 {
    public:
        bool operator()(int n) const {
            return n >= 3 && n <= 7;
        }
    };
    
    std::list<int> list3 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    std::cout << "初始list: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << " 大小: " << list3.size() << std::endl;
    
    list3.remove_if(Between3And7());  // 删除3到7之间的数
    
    std::cout << "删除3到7之间的数后: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << " 大小: " << list3.size() << std::endl;
    
    // 5. 删除所有负数
    std::cout << "\n5. 删除所有负数:" << std::endl;
    std::list<int> list4 = {-5, -2, 0, 3, -1, 4, 2, -3, 1};
    
    std::cout << "初始list: ";
    for (int n : list4) std::cout << n << " ";
    std::cout << " 大小: " << list4.size() << std::endl;
    
    list4.remove_if([](int n) { return n < 0; });
    
    std::cout << "删除负数后: ";
    for (int n : list4) std::cout << n << " ";
    std::cout << " 大小: " << list4.size() << std::endl;
    
    // 6. 删除所有重复的字母(大小写敏感)
    std::cout << "\n6. 删除所有小写字母:" << std::endl;
    std::list<char> letters = {'A', 'b', 'C', 'd', 'E', 'f', 'G', 'h'};
    
    std::cout << "初始字符list: ";
    for (char c : letters) std::cout << c << " ";
    std::cout << " 大小: " << letters.size() << std::endl;
    
    letters.remove_if([](char c) { return std::islower(c); });
    
    std::cout << "删除小写字母后: ";
    for (char c : letters) std::cout << c << " ";
    std::cout << " 大小: " << letters.size() << std::endl;
    
    // 7. 更复杂的条件:删除能被3整除且大于10的数
    std::cout << "\n7. 删除能被3整除且大于10的数:" << std::endl;
    std::list<int> list5 = {3, 6, 9, 12, 15, 18, 21, 24, 27, 30};
    
    std::cout << "初始list: ";
    for (int n : list5) std::cout << n << " ";
    std::cout << " 大小: " << list5.size() << std::endl;
    
    list5.remove_if([](int n) { return n > 10 && n % 3 == 0; });
    
    std::cout << "删除能被3整除且大于10的数后: ";
    for (int n : list5) std::cout << n << " ";
    std::cout << " 大小: " << list5.size() << std::endl;
    
    // 8. remove_if与remove的对比
    std::cout << "\n8. remove_if与remove的对比:" << std::endl;
    std::cout << "remove(value): 删除所有等于value的元素" << std::endl;
    std::cout << "remove_if(pred): 删除所有使pred返回true的元素" << std::endl;
    std::cout << "remove_if更灵活,可以实现任意条件删除" << std::endl;
    
    return 0;
}

5.4. unique(去重)

作用:移除容器中连续重复的元素,只保留每组相同元素中的第一个。

注意:

  • 通常在使用 sort() 之后调用,因为 unique 只能删除相邻的重复元素。
  • 有重载版本可以接受一个二元谓词来自定义"相等"的比较方式。
  • 时间复杂度为 O(n)。

特别注意需要是连续重复!!

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== unique方法示例 ===" << std::endl;
    
    // 1. 删除连续重复的元素
    std::cout << "\n1. 删除连续重复的元素:" << std::endl;
    std::list<int> numbers = {1, 1, 2, 3, 3, 3, 4, 5, 5, 6};
    
    std::cout << "初始list: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    numbers.unique();  // 删除连续重复的元素
    
    std::cout << "unique()后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << " 大小: " << numbers.size() << std::endl;
    
    // 2. 不连续的重复元素不会被删除
    std::cout << "\n2. 不连续的重复元素不会被删除:" << std::endl;
    std::list<int> list2 = {1, 2, 1, 3, 2, 4, 1, 5};
    
    std::cout << "初始list: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    list2.unique();
    
    std::cout << "unique()后: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    std::cout << "注意:不连续的重复元素不会被删除" << std::endl;
    
    // 3. 先排序再使用unique(常用组合)
    std::cout << "\n3. 先排序再使用unique(删除所有重复元素):" << std::endl;
    std::list<int> list3 = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
    
    std::cout << "初始list: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << " 大小: " << list3.size() << std::endl;
    
    list3.sort();  // 先排序
    std::cout << "排序后: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    list3.unique();  // 再删除重复元素
    
    std::cout << "unique()后: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << " 大小: " << list3.size() << std::endl;
    
    // 4. 使用自定义比较函数(谓词)
    std::cout << "\n4. 使用自定义比较函数:" << std::endl;
    std::list<int> list4 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    std::cout << "初始list: ";
    for (int n : list4) std::cout << n << " ";
    std::cout << " 大小: " << list4.size() << std::endl;
    
    // 自定义比较:如果两个数相差不超过1,则认为相等
    list4.unique([](int a, int b) { return std::abs(a - b) <= 1; });
    
    std::cout << "自定义比较unique后: ";
    for (int n : list4) std::cout << n << " ";
    std::cout << " 大小: " << list4.size() << std::endl;
    std::cout << "注意:相邻且相差不超过1的元素被删除了" << std::endl;
    
    // 5. 字符串list的去重
    std::cout << "\n5. 字符串list的去重:" << std::endl;
    std::list<std::string> words = {"apple", "apple", "banana", "banana", "apple", "cherry"};
    
    std::cout << "初始字符串list: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    // 先排序再去重
    words.sort();
    std::cout << "排序后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << std::endl;
    
    words.unique();
    std::cout << "unique()后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << " 大小: " << words.size() << std::endl;
    
    // 6. 自定义字符串比较(忽略大小写)
    std::cout << "\n6. 自定义字符串比较(忽略大小写):" << std::endl;
    std::list<std::string> list5 = {"Apple", "apple", "BANANA", "banana", "Cherry"};
    
    std::cout << "初始list: ";
    for (const auto& w : list5) std::cout << w << " ";
    std::cout << " 大小: " << list5.size() << std::endl;
    
    // 排序(默认区分大小写)
    list5.sort();
    std::cout << "排序后: ";
    for (const auto& w : list5) std::cout << w << " ";
    std::cout << std::endl;
    
    // 自定义比较:比较小写形式是否相等
    list5.unique([](const std::string& a, const std::string& b) {
        std::string a_lower, b_lower;
        for (char c : a) a_lower += std::tolower(c);
        for (char c : b) b_lower += std::tolower(c);
        return a_lower == b_lower;
    });
    
    std::cout << "忽略大小写unique后: ";
    for (const auto& w : list5) std::cout << w << " ";
    std::cout << " 大小: " << list5.size() << std::endl;
    
    // 7. 实际应用:删除连续相同的字符
    std::cout << "\n7. 实际应用:删除连续相同的字符:" << std::endl;
    std::list<char> letters = {'a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'e', 'e', 'e'};
    
    std::cout << "初始字符list: ";
    for (char c : letters) std::cout << c << " ";
    std::cout << " 大小: " << letters.size() << std::endl;
    
    letters.unique();
    
    std::cout << "unique()后: ";
    for (char c : letters) std::cout << c << " ";
    std::cout << " 大小: " << letters.size() << std::endl;
    std::cout << "只保留了每组相同字符的第一个" << std::endl;
    
    return 0;
}

5.5. merge(合并)

作用:将两个已排序的 list 合并成一个有序的 list。合并后,源 list 变为空。

前提条件:两个 list 都必须已经按照相同的排序规则(升序)排好序。

特点:

  • 合并操作是稳定的(相等元素的相对顺序保持不变)。
  • 非常高效,时间复杂度为 O(n),因为只需重新链接节点。
  • 有重载版本可以接受自定义的比较函数。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== merge方法示例 ===" << std::endl;
    
    // 1. 合并两个升序排列的list
    std::cout << "\n1. 合并两个升序排列的list:" << std::endl;
    std::list<int> list1 = {1, 3, 5, 7, 9};
    std::list<int> list2 = {2, 4, 6, 8, 10};
    
    std::cout << "合并前:" << std::endl;
    std::cout << "list1: ";
    for (int n : list1) std::cout << n << " ";
    std::cout << " 大小: " << list1.size() << std::endl;
    
    std::cout << "list2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    list1.merge(list2);  // 合并list2到list1
    
    std::cout << "\n合并后:" << std::endl;
    std::cout << "list1: ";
    for (int n : list1) std::cout << n << " ";
    std::cout << " 大小: " << list1.size() << std::endl;
    
    std::cout << "list2: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << " 大小: " << list2.size() << std::endl;
    
    // 2. 合并包含重复元素的list
    std::cout << "\n2. 合并包含重复元素的list:" << std::endl;
    std::list<int> list3 = {1, 2, 2, 5, 9};
    std::list<int> list4 = {2, 3, 4, 6, 7};
    
    std::cout << "合并前:" << std::endl;
    std::cout << "list3: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    std::cout << "list4: ";
    for (int n : list4) std::cout << n << " ";
    std::cout << std::endl;
    
    list3.merge(list4);
    
    std::cout << "\n合并后:" << std::endl;
    std::cout << "list3: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << " 大小: " << list3.size() << std::endl;
    
    std::cout << "list4: ";
    for (int n : list4) std::cout << n << " ";
    std::cout << " 大小: " << list4.size() << std::endl;
    
    // 3. 自定义比较函数(降序合并)
    std::cout << "\n3. 自定义比较函数(降序合并):" << std::endl;
    std::list<int> list5 = {9, 7, 5, 3, 1};      // 降序排列
    std::list<int> list6 = {10, 8, 6, 4, 2};     // 降序排列
    
    std::cout << "合并前:" << std::endl;
    std::cout << "list5(降序): ";
    for (int n : list5) std::cout << n << " ";
    std::cout << std::endl;
    
    std::cout << "list6(降序): ";
    for (int n : list6) std::cout << n << " ";
    std::cout << std::endl;
    
    // 使用自定义比较函数进行降序合并
    list5.merge(list6, std::greater<int>());
    
    std::cout << "\n降序合并后:" << std::endl;
    std::cout << "list5: ";
    for (int n : list5) std::cout << n << " ";
    std::cout << " 大小: " << list5.size() << std::endl;
    
    std::cout << "list6: ";
    for (int n : list6) std::cout << n << " ";
    std::cout << " 大小: " << list6.size() << std::endl;
    
    // 4. 字符串list的合并
    std::cout << "\n4. 字符串list的合并:" << std::endl;
    std::list<std::string> words1 = {"apple", "banana", "orange"};
    std::list<std::string> words2 = {"grape", "kiwi", "peach"};
    
    std::cout << "合并前:" << std::endl;
    std::cout << "words1: ";
    for (const auto& w : words1) std::cout << w << " ";
    std::cout << std::endl;
    
    std::cout << "words2: ";
    for (const auto& w : words2) std::cout << w << " ";
    std::cout << std::endl;
    
    // 注意:字符串默认按字典序排序
    words1.sort();
    words2.sort();
    
    std::cout << "\n排序后:" << std::endl;
    std::cout << "words1: ";
    for (const auto& w : words1) std::cout << w << " ";
    std::cout << std::endl;
    
    std::cout << "words2: ";
    for (const auto& w : words2) std::cout << w << " ";
    std::cout << std::endl;
    
    words1.merge(words2);
    
    std::cout << "\n合并后:" << std::endl;
    std::cout << "words1: ";
    for (const auto& w : words1) std::cout << w << " ";
    std::cout << " 大小: " << words1.size() << std::endl;
    
    std::cout << "words2: ";
    for (const auto& w : words2) std::cout << w << " ";
    std::cout << " 大小: " << words2.size() << std::endl;
    
    // 5. 错误的用法:合并未排序的list
    std::cout << "\n5. 错误的用法:合并未排序的list:" << std::endl;
    std::list<int> list7 = {5, 1, 9, 3, 7};      // 未排序
    std::list<int> list8 = {2, 8, 4, 6, 10};     // 未排序
    
    std::cout << "合并前:" << std::endl;
    std::cout << "list7(未排序): ";
    for (int n : list7) std::cout << n << " ";
    std::cout << std::endl;
    
    std::cout << "list8(未排序): ";
    for (int n : list8) std::cout << n << " ";
    std::cout << std::endl;
    
    list7.merge(list8);  // 合并未排序的list,结果可能不正确
    
    std::cout << "\n合并后:" << std::endl;
    std::cout << "list7: ";
    for (int n : list7) std::cout << n << " ";
    std::cout << " 大小: " << list7.size() << std::endl;
    std::cout << "注意:合并未排序的list可能导致结果不正确" << std::endl;
    
    // 6. 合并空list
    std::cout << "\n6. 合并空list:" << std::endl;
    std::list<int> list9 = {1, 2, 3, 4, 5};
    std::list<int> emptyList;
    
    std::cout << "合并前:" << std::endl;
    std::cout << "list9: ";
    for (int n : list9) std::cout << n << " ";
    std::cout << " 大小: " << list9.size() << std::endl;
    
    std::cout << "emptyList: ";
    for (int n : emptyList) std::cout << n << " ";
    std::cout << " 大小: " << emptyList.size() << std::endl;
    
    list9.merge(emptyList);  // 合并空list
    
    std::cout << "\n合并后:" << std::endl;
    std::cout << "list9: ";
    for (int n : list9) std::cout << n << " ";
    std::cout << " 大小: " << list9.size() << std::endl;
    
    std::cout << "emptyList: ";
    for (int n : emptyList) std::cout << n << " ";
    std::cout << " 大小: " << emptyList.size() << std::endl;
    std::cout << "合并空list不会改变原list" << std::endl;
    
    return 0;
}

5.6. sort(排序)

作用:对容器内的元素进行排序。由于 list 不能随机访问,它使用归并排序(或其他适合链表的算法)来实现。

特点:

  • 排序是稳定的。
  • 时间复杂度为 O(n log n)。
  • 会改变容器本身(就地排序)。
  • 有重载版本可以接受自定义的比较函数。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== list的sort方法示例 ===" << std::endl;
    
    // 1. 默认升序排序
    std::cout << "\n1. 默认升序排序:" << std::endl;
    std::list<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6, 10};
    
    std::cout << "排序前: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    numbers.sort();  // 默认升序排序
    
    std::cout << "排序后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 2. 降序排序
    std::cout << "\n2. 降序排序:" << std::endl;
    std::list<int> list2 = {5, 2, 8, 1, 9, 3, 7, 4, 6, 10};
    
    std::cout << "排序前: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    list2.sort(std::greater<int>());  // 降序排序
    
    std::cout << "降序排序后: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    // 3. 字符串排序
    std::cout << "\n3. 字符串排序:" << std::endl;
    std::list<std::string> words = {"banana", "apple", "cherry", "date", "elderberry"};
    
    std::cout << "排序前: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << std::endl;
    
    words.sort();  // 默认按字典序排序
    
    std::cout << "排序后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << std::endl;
    
    // 4. 自定义排序规则(按字符串长度排序)
    std::cout << "\n4. 按字符串长度排序:" << std::endl;
    std::list<std::string> list3 = {"apple", "banana", "cherry", "date", "fig", "grapefruit"};
    
    std::cout << "排序前: ";
    for (const auto& w : list3) std::cout << w << " ";
    std::cout << std::endl;
    
    // 自定义比较函数:按长度排序,长度相同按字典序
    list3.sort([](const std::string& a, const std::string& b) {
        if (a.length() != b.length())
            return a.length() < b.length();
        return a < b;
    });
    
    std::cout << "按长度排序后: ";
    for (const auto& w : list3) std::cout << w << " ";
    std::cout << std::endl;
    
    // 5. 结构体排序
    std::cout << "\n5. 结构体排序:" << std::endl;
    struct Person {
        std::string name;
        int age;
        double salary;
    };
    
    std::list<Person> people = {
        {"Alice", 30, 50000},
        {"Bob", 25, 45000},
        {"Charlie", 35, 60000},
        {"David", 28, 48000},
        {"Eve", 40, 70000}
    };
    
    std::cout << "排序前:" << std::endl;
    for (const auto& p : people) {
        std::cout << p.name << " (" << p.age << "岁, 工资: " << p.salary << ")" << std::endl;
    }
    
    // 按年龄排序
    people.sort([](const Person& a, const Person& b) {
        return a.age < b.age;
    });
    
    std::cout << "\n按年龄排序后:" << std::endl;
    for (const auto& p : people) {
        std::cout << p.name << " (" << p.age << "岁, 工资: " << p.salary << ")" << std::endl;
    }
    
    // 按工资降序排序
    people.sort([](const Person& a, const Person& b) {
        return a.salary > b.salary;
    });
    
    std::cout << "\n按工资降序排序后:" << std::endl;
    for (const auto& p : people) {
        std::cout << p.name << " (" << p.age << "岁, 工资: " << p.salary << ")" << std::endl;
    }
    
    return 0;
}

5.7. reverse(反转)

作用:将容器中元素的顺序反转。

特点:

  • 非常高效,时间复杂度为 O(n),因为只需将每个节点的前后指针交换。
  • 会改变容器本身。
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::cout << "=== list的reverse方法示例 ===" << std::endl;
    
    // 1. 反转整数list
    std::cout << "\n1. 反转整数list:" << std::endl;
    std::list<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    std::cout << "反转前: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    numbers.reverse();  // 反转list
    
    std::cout << "反转后: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    
    // 2. 反转字符串list
    std::cout << "\n2. 反转字符串list:" << std::endl;
    std::list<std::string> words = {"apple", "banana", "cherry", "date", "elderberry"};
    
    std::cout << "反转前: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << std::endl;
    
    words.reverse();  // 反转list
    
    std::cout << "反转后: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << std::endl;
    
    // 3. 反转单个元素的list
    std::cout << "\n3. 反转单个元素的list:" << std::endl;
    std::list<int> single = {42};
    
    std::cout << "反转前: ";
    for (int n : single) std::cout << n << " ";
    std::cout << std::endl;
    
    single.reverse();
    
    std::cout << "反转后: ";
    for (int n : single) std::cout << n << " ";
    std::cout << std::endl;
    
    // 4. 反转空list
    std::cout << "\n4. 反转空list:" << std::endl;
    std::list<int> emptyList;
    
    std::cout << "反转前: ";
    for (int n : emptyList) std::cout << n << " ";
    std::cout << "(空list)" << std::endl;
    
    emptyList.reverse();  // 反转空list是安全的
    
    std::cout << "反转后: ";
    for (int n : emptyList) std::cout << n << " ";
    std::cout << "(空list)" << std::endl;
    
    // 5. 多次反转
    std::cout << "\n5. 多次反转:" << std::endl;
    std::list<int> list2 = {1, 2, 3, 4, 5};
    
    std::cout << "原始: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    list2.reverse();
    std::cout << "第一次反转: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    list2.reverse();
    std::cout << "第二次反转: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    list2.reverse();
    std::cout << "第三次反转: ";
    for (int n : list2) std::cout << n << " ";
    std::cout << std::endl;
    
    // 6. 反转和排序结合使用
    std::cout << "\n6. 反转和排序结合使用:" << std::endl;
    std::list<int> list3 = {5, 3, 8, 1, 9, 2, 7, 4, 6, 10};
    
    std::cout << "原始: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    // 先排序
    list3.sort();
    std::cout << "排序后: ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    // 再反转(变成降序)
    list3.reverse();
    std::cout << "反转后(降序): ";
    for (int n : list3) std::cout << n << " ";
    std::cout << std::endl;
    
    // 7. 自定义对象的反转
    std::cout << "\n7. 自定义对象的反转:" << std::endl;
    struct Point {
        int x, y;
    };
    
    std::list<Point> points = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}};
    
    std::cout << "反转前:" << std::endl;
    for (const auto& p : points) {
        std::cout << "(" << p.x << ", " << p.y << ") ";
    }
    std::cout << std::endl;
    
    points.reverse();
    
    std::cout << "反转后:" << std::endl;
    for (const auto& p : points) {
        std::cout << "(" << p.x << ", " << p.y << ") ";
    }
    std::cout << std::endl;
    
    return 0;
}
相关推荐
端平入洛2 天前
delete又未完全delete
c++
端平入洛3 天前
auto有时不auto
c++
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1234 天前
matlab画图工具
开发语言·matlab
dustcell.4 天前
haproxy七层代理
java·开发语言·前端
norlan_jame4 天前
C-PHY与D-PHY差异
c语言·开发语言
哇哈哈20214 天前
信号量和信号
linux·c++
多恩Stone4 天前
【C++入门扫盲1】C++ 与 Python:类型、编译器/解释器与 CPU 的关系
开发语言·c++·人工智能·python·算法·3d·aigc
QQ4022054964 天前
Python+django+vue3预制菜半成品配菜平台
开发语言·python·django
遥遥江上月4 天前
Node.js + Stagehand + Python 部署
开发语言·python·node.js