当前位置: 首页 > 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.dtcms.com/a/250984.html

相关文章:

  • 【图像处理入门】8. 数学基础与优化:线性代数、概率与算法调优实战
  • 设计模式-装饰器模式
  • Python使用requests调用接口
  • React 实现九宫格抽奖游戏
  • 超高速总线CDCTL01A 芯片在机器人领域的应用解析
  • React组件通信——context(提供者/消费者)
  • NVIDIA Isaac GR00T N1.5 人形机器人强化学习入门教程(四)Lerobot、宇树 G1 等不同形态机器人微调教程
  • 一个字节一定是8比特吗?如何查看二进制文件前几个字节?计算机存储单元bit?数据类型大小?
  • 函数指针与指针函数:本质区别与高级应用
  • YOLOv4 改进点详解
  • Spring Boot多数据源切换:三种实现方式详解与实战
  • 二.干货干货!!!SpringAI入门到实战-记忆存储和会话隔离
  • Adobe 发布 Android 版 Photoshop(目前免费测试)
  • 电阻篇---下拉电阻
  • 行列式的逆序数法定义 线性代数
  • 论文笔记 <交通灯> <多智能体>DERLight双重经验回放灯机制
  • Web server failed to start. Port XXX was already in use.
  • Nginx限速配置详解
  • CountDownLatch与CyclicBarrier的区别
  • AOP +ThreadLocal实现对接口监控
  • 共享内存实现进程通信
  • C++ STL常用二分查找算法
  • 第二十六周:序列化和反序列化
  • Jenkins 全面深入学习目录
  • MFE微前端:如何捕捉远程应用的remote.js加载失败的错误?
  • 001微信小程序入门
  • sql中like and not like的优化
  • Web后端基础:数据库
  • 【1】Redis 缓存穿透原理和解决方案
  • 操作系统核心名词解释--期末简答题快速复习