当前位置: 首页 > news >正文

c++中list的使用

在实际工程中,list特别适合以下情况:

  • 高频插入删除:如实时交易系统中的订单管理

  • 大型对象存储:避免vector扩容时的大规模复制

  • 迭代器稳定性要求高:需要长期保持有效的元素引用

  • 中间位置操作频繁:如文本编辑器的撤销操作链


前言

在C++标准模板库(STL)中,list容器就像是一位低调而技艺高超的工匠,它以独特的双向链表结构,在特定的应用场景中展现出无可替代的价值。本文将带领读者深入探索list的设计哲学、实现原理以及高效使用方法,通过丰富的代码示例和原理图解,帮助开发者真正掌握这一重要容器

list的底层实现揭秘

list的每个节点都是一个精妙设计的结构体,通常实现为:

template <typename T>
struct __list_node {__list_node* prev;  // 指向前驱节点__list_node* next;  // 指向后继节点T data;             // 存储实际数据
};

一、list容器基本操作大全

1. 创建和初始化list

list提供了多种灵活的初始化方式:

#include <list>
#include <iostream>
using namespace std;// 1. 创建空list
list<int> list1;// 2. 创建包含n个默认值的list
list<string> list2(5);  // 5个空字符串// 3. 创建包含n个指定值的list
list<double> list3(3, 3.14);  // [3.14, 3.14, 3.14]// 4. 通过初始化列表创建
list<char> list4 = {'a', 'b', 'c', 'd'};// 5. 通过迭代器范围创建
int arr[] = {1, 3, 5, 7, 9};
list<int> list5(arr, arr + sizeof(arr)/sizeof(arr[0]));// 6. 拷贝构造函数
list<int> list6(list5);

2. 元素访问操作

虽然list不支持随机访问,但仍提供了一些访问方法:

#include<list>
#include<iostream>
int main()
{
list<int> nums = {10, 20, 30, 40};
// 访问第一个元素
cout << "第一个元素: " << nums.front() << endl;  // 10
// 访问最后一个元素
cout << "最后一个元素: " << nums.back() << endl;  // 40
// 遍历访问所有元素
cout << "所有元素: ";
for(int num : nums) {cout << num << " ";
}
cout << endl;
// 使用迭代器访问
cout << "第二个元素: ";
auto it = nums.begin();
advance(it, 1);
cout << *it << endl;  // 20
}

3. 修改list内容

添加元素
list<string> fruits;
// 在末尾添加元素
fruits.push_back("apple");
fruits.push_back("banana");
// 在开头添加元素
fruits.push_front("orange");
fruits.push_front("grape");// 在指定位置插入单个元素
auto it = fruits.begin();
advance(it, 2);
fruits.insert(it, "pear");// 在指定位置插入多个相同元素
fruits.insert(it, 3, "peach");
// 在指定位置插入另一个容器的元素范围
vector<string> berries = {"strawberry", "blueberry", "raspberry"};
fruits.insert(it, berries.begin(), berries.end());
// 在第一个 "peach" 前插入 berries 的所有元素
// fruits: ["grape", "orange", "strawberry", "blueberry", "raspberry", "peach", "peach", "peach", "pear", "apple", "banana"]
// 使用emplace高效构造并插入
fruits.emplace_back("watermelon");
fruits.emplace_front("kiwi");
it = fruits.begin();
advance(it, 3);
fruits.emplace(it, "mango");
删除元素
list<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 删除末尾元素
numbers.pop_back();// 删除开头元素
numbers.pop_front();// 删除指定位置的单个元素
auto it = numbers.begin();
advance(it, 3);
numbers.erase(it);// 删除指定范围的元素
it = numbers.begin();
auto it2 = numbers.begin();
advance(it, 2);
advance(it2, 5);
numbers.erase(it, it2);// 删除所有等于特定值的元素
numbers.remove(8);// 删除满足条件的元素
numbers.remove_if([](int n) { return n % 2 == 0; });  // 删除所有偶数// 清空整个list
numbers.clear();

4. 容量查询

list<int> primes = {2, 3, 5, 7, 11};// 检查是否为空
if (primes.empty()) {cout << "list为空" << endl;
} else {cout << "list不为空" << endl;
}// 获取元素数量
cout << "元素个数: " << primes.size() << endl;// 获取可能的最大元素数量
cout << "最大容量: " << primes.max_size() << endl;

5. list特殊操作

排序和去重
list<int> data = {5, 3, 7, 1, 9, 2, 5, 3, 8};// 默认升序排序
data.sort();// 自定义排序规则
data.sort([](int a, int b) {return a > b;  // 降序排序
});// 去重(必须先排序)
data.unique();// 自定义去重条件
data.unique([](int a, int b) {return abs(a - b) <= 1;  // 去除相邻且差值≤1的元素
});

里面的包含拉姆达表达式可以看看可以缩减代码量捕获变量是可以稍微轻松点

二、list操作性能分析

时间复杂度对比表

操作时间复杂度说明
push_back/push_frontO(1)在首尾添加元素非常高效
insertO(1)插入本身是常数时间,但找到位置需要O(n)
eraseO(1)删除本身是常数时间,但找到位置需要O(n)
pop_back/pop_frontO(1)删除首尾元素非常高效
size实现依赖可能是O(1)或O(n),取决于具体实现
emptyO(1)检查是否为空总是很快
front/backO(1)访问首尾元素非常高效
sortO(n log n)使用归并排序算法
mergeO(n)合并两个已排序的list
reverseO(n)反转整个list
uniqueO(n)去除相邻的重复元素
spliceO(1)拼接操作非常高效,只是指针调整
remove/remove_ifO(n)需要遍历整个list

 常见错误避免

// 错误1:随机访问
list<int> lst = {1,2,3};
// int x = lst[1];  // 错误!list不支持[]操作符// 错误2:无效的迭代器使用
auto iter = lst.begin();
lst.erase(iter);
// *iter;  // 错误!iter已失效// 错误3:未排序直接unique
list<int> dup = {3,1,2,2,3,1};
// dup.unique();  // 不会去重所有重复元素,必须先排序// 错误4:合并未排序的list
list<int> a = {3,1,2};
list<int> b = {6,4,5};
// a.merge(b);  // 未定义行为,必须先排序

性能优化建议

  1. 批量操作:尽量使用范围插入/删除而非单个操作

  2. 避免频繁size():某些实现中size()可能是O(n)操作

  3. 优先使用emplace:避免不必要的临时对象创建


总结

C++中的list容器是一个功能强大且特性独特的序列容器,它通过双向链表实现提供了高效的插入删除操作。掌握list的关键在于:

  1. 理解其链表结构的本质特性 熟练运用各种插入删除方法 合理利用特有的sort、merge、splice

  2. 注意与其他容器的区别和适用场景 遵循最佳实践以获得最佳性能

list虽然在随机访问方面不如vector高效,但在需要频繁修改中间元素的场景下,它的性能优势无可替代。

http://www.xdnf.cn/news/1047925.html

相关文章:

  • Java基础复习之继承
  • 【鸿蒙初级】
  • 从零开始掌握 Docker:核心命令与实践指南
  • CSP 2024 入门级第一轮(88.5)
  • WebSocket网络通信架构设计详解
  • Linux系统编程 | IPC对象---共享内存
  • 【JS-2】JavaScript基础语法完全指南:从入门到精通
  • 【系统设计【2】】粗略估算
  • 量化面试绿皮书:14. 钟表零件
  • 【人工智能数学基础】实变函数与泛函分析
  • Rokid AR交互开发工具对比
  • 不同conda 不同cuda版本方法
  • 使用存储型 XSS 窃取 cookie 并发送到你控制的服务器
  • Seelen UI 是Windows 桌面开发
  • 安卓9.0系统修改定制化____深入解析安卓 9.0 各手机分区:功能、作用与差异 基础篇二
  • 防火墙技术、模型、发展趋势、局限性及安全体系相关分析
  • 【LangChain】5 评估
  • 第20篇:数据库中间件的热点 Key 缓存一致性策略与分布式协调机制
  • JavaScript 与 Vue 键盘事件全面指南(Composition API + <script setup>)
  • 【微服务】134:SpringCloud
  • 个人AI助理智能体之tool_calling_agent实战指南
  • 61、数据访问-自定义方式整合druid数据源
  • 计算机网络学习笔记:TCP三报文握手、四报文挥手
  • Ubuntu 安装并使用 Elasticsearch
  • ROS2中,在工作空间根目录下执行source ./install/setup.bash的作用?
  • Java里ArrayList和LinkedList有什么区别?
  • 第二十九场 蓝桥算法赛
  • 基于MediaPipe的手指目标跟踪与手势识别+人体姿态识别估计:MediaPipe与OpenPose算法对比
  • 【iReport】实际开发中,解决iReport中打印图片不显示问题
  • LangChain框架:AI应用开发利器