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

C++——list链表


在这里插入图片描述

❀保持低旋律节奏->个人主页

专栏链接:《C++学习》、《Linux学习》


文章目录

    • 一、list介绍
    • 二、list使用
        • 2.1list构造
        • 2.2list iterator的使用
        • 2.3list capacity
        • 2.4 ❀list element access
        • 2.5list modifiers
        • 迭代器失效
    • 三、list和vector对比
    • 四、对list的一些补充

一、list介绍

list官方文档介绍
在这里插入图片描述

list容器使用双链表来实现,每个元素指针通过前驱和后续相关联。元素可以存储在不连续的不同位置。

  • 核心优势
    1.任意位置的插入 / 删除操作是常数时间(O (1)):只要有对应位置的迭代器,在列表的任何位置插入或删除元素都很高效
    2.支持双向迭代:可以向前或向后遍历元素。
  • 主要劣势
    1.不支持随机访问:无法像数组、vector 那样通过下标 [] 直接访问元素(例如访问第 6 个元素,必须从已知位置遍历,时间复杂度为 O (n))。
    2.内存开销大:每个元素需要额外存储前驱和后继指针,对于小元素、大规模列表,内存消耗会比较明显
  • 与其他容器相比
    1.对比 vector/array/deque:在 “任意位置的插入 / 删除” 场景下表现更好,适合排序等频繁操作元素位置的算法。
    2.对比 forward_list:forward_list 是单向链表,更小巧高效,但仅支持单向遍历;std::list 是双向链表,支持双向遍历。

双链表介绍

在这里插入图片描述

哨兵节点的介绍

在链表数据结构中,哨兵节点(Sentinel Node) 是一个不存储实际业务数据的虚拟节点,主要作用是统一链表操作的逻辑,消除边界条件的特殊处理,让代码更简洁、更不易出错。

在这里插入图片描述

场景无哨兵实现有哨兵实现
链表为空时需要单独判断 head == nullptr无需判断(哨兵的 next 自然是 nullptr)
删除头节点时需要单独处理(头节点无前驱)无需特殊处理(头节点的前驱是哨兵)
代码复杂度多分支判断,逻辑分散逻辑统一,无额外边界判断
出错概率高(容易遗漏边界情况)低(所有节点处理逻辑一致)

有无哨兵节点的对比——底层!

//存在哨兵的代码
#include <list>
#include<iostream>
using namespace std;
template<class T>
class ListNode
{
public://using T* = ListNode*; //T是一个具体的类ListNode是一个结构体//using node = ListNode<T>;  //ListNode<T> 是一个结构体 node 也是一个结构体T val;ListNode<T>* prev;ListNode<T>* next;ListNode(T val) : val(val), prev(nullptr), next(nullptr) {}
};template<class T>ListNode<T>* del(ListNode<T>*head,T target)
{//using node = ListNode<T>;//边界一:链表为空if (head == nullptr)return nullptr;//边界二:删除头节点if (head->val == target){ListNode<T>* newHead = head->next;delete head;return newHead;}//情况三:处理非头节点ListNode<T>* prev = head;while (prev->next != nullptr){if (prev->next->val == target){ListNode<T>* toDelete = prev->next;// 同步更新后继节点的prev指针(双向链表关键)if (toDelete->next != nullptr) {toDelete->next->prev = prev;}prev->next = toDelete->next;delete toDelete;break;}prev = prev->next;}return head;
}
int main() {// 创建链表:1 <-> 2 <-> 3ListNode<int>* head = new ListNode<int>(1);head->next = new ListNode<int>(2);head->next->prev = head;  // 2的prev指向1head->next->next = new ListNode<int>(3);head->next->next->prev = head->next;  // 3的prev指向2// 删除值为2的节点head = del(head, 2);// 验证结果:1 <-> 3(正向和反向遍历)cout << "正向遍历:";ListNode<int>* cur = head;while (cur != nullptr) {cout << cur->val << " ";cur = cur->next;}// 输出:1 3cout << "\n反向遍历:";cur = head->next;  // 此时cur指向3while (cur != nullptr) {cout << cur->val << " ";cur = cur->prev;  // 反向遍历(依赖prev指针)}// 输出:3 1return 0;
}
//无哨兵的代码
#include <list>
#include<iostream>
using namespace std;template<class T>
class ListNode
{
public:T val;ListNode<T>* prev;ListNode<T>* next;ListNode(T val) : val(val), prev(nullptr), next(nullptr) {}
};template<class T>
ListNode<T>* del(ListNode<T>* head, T target)
{// 创建哨兵节点(值无意义,仅作虚拟头节点)ListNode<T>* sentinel = new ListNode<T>(0);sentinel->next = head;  // 哨兵指向原头节点if (head != nullptr) {head->prev = sentinel;  // 原头节点的prev指向哨兵(双向链表维护)}// 从哨兵开始遍历(统一所有节点的前驱逻辑)ListNode<T>* prev = sentinel;while (prev->next != nullptr){if (prev->next->val == target){ListNode<T>* toDelete = prev->next;// 同步更新后继节点的prev指针if (toDelete->next != nullptr) {toDelete->next->prev = prev;}prev->next = toDelete->next;delete toDelete;break;}prev = prev->next;}// 恢复新头节点,释放哨兵ListNode<T>* newHead = sentinel->next;if (newHead != nullptr) {newHead->prev = nullptr;  // 新头节点prev置空(脱离哨兵)}delete sentinel;  // 哨兵完成使命,释放内存return newHead;  // 返回新头节点
}int main() {// 创建链表:1 <-> 2 <-> 3ListNode<int>* head = new ListNode<int>(1);head->next = new ListNode<int>(2);head->next->prev = head;  // 2的prev指向1head->next->next = new ListNode<int>(3);head->next->next->prev = head->next;  // 3的prev指向2// 删除值为2的节点head = del(head, 2);// 验证结果:1 <-> 3(正向和反向遍历)cout << "正向遍历:";ListNode<int>* cur = head;while (cur != nullptr) {cout << cur->val << " ";cur = cur->next;}// 输出:1 3cout << "\n反向遍历:";cur = head->next;  // 此时cur指向3while (cur != nullptr) {cout << cur->val << " ";cur = cur->prev;  // 反向遍历(依赖prev指针)}// 输出:3 1return 0;
}

二、list使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已
达到可扩展的能力。以下为list中一些常见的重要接口。

2.1list构造

常见的构造 list构造

构造函数( (constructor))接口说明
list (size_type n, const value_type& val =value_type())造的list中包含n个值为val的元素
list()构建空list
list(const list&x)拷贝构造
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造
2.2list iterator的使用

list官方文档介绍
此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。

在这里插入图片描述

1.begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
2.rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

2.3list capacity

list官方文档介绍
在这里插入图片描述

2.4 ❀list element access
函数声明接口使用
front返回list的第一个节点中值的引用
back返回list最后一个节点中值的引用
2.5list modifiers

list官方文档介绍
在这里插入图片描述

迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

类型inserterase
string1.若插入后未触发扩容:插入位置及之后的迭代器全部失效(元素后移,位置改变)。2.若插入后触发扩容:所有迭代器、指针、引用全部失效(内存地址整体改变)。删除位置及之后的迭代器全部失效(元素前移,位置改变)。未删除的元素(删除位置之前)的迭代器仍然有效。
vector与string 同理与string同理
list所有迭代器都不会失效(插入的新元素不会改变原有元素的地址和指针关系)。只有被删除元素的迭代器失效,其他所有迭代器(包括前驱和后继)仍然有效。

三、list和vector对比

vectorlist
底层结构动态顺序表,一段连续空间带头结点的双向循环链表
随机访问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素效率O(N)
插入和删除任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生态指针对原生态指针(节点指针)进行封装
迭代器失效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随机访问

四、对list的一些补充

list::sort

list库里面存在一个单独的sortlist::sort
algorithm 里面也有sortalgorithm::sort

  • 那么两者有什么区别?
    algorithm里面的sort使用的是快速排序,我们都知道快速排序在排序算法里面已经是最快的了
    而list里面的sort使用的是归并排序

在进行大量数据排序比较时,使用list::sort就会花费很大的代价。因此不建议使用list::sort

list 底层迭代器的实现
在这里插入图片描述

http://www.dtcms.com/a/503057.html

相关文章:

  • 泉州网站关键词推广费用泉州网站建设优化公司
  • 动画基础:动画里的18种基本相机角度
  • 上海做高端网站建设wordpress自动分享
  • 【含文档+PPT+源码】基于小程序开发的宠物寄养平台管理系统
  • 【LeetCode】81. 搜索旋转排序数组 II
  • 力扣21:合并两个有序链表
  • FastAPI之 HTTP响应
  • 中国建设银行广西分行网站首页wordpress 数字商城
  • UE5 材质-5:砖缝不透明材质系列,掩码节点 ComponentMask,材质函数 CustomRotator 旋转UV,
  • 2510C++,rest_rpc
  • [Power BI] 卡片图与多行卡
  • 大模型理论概述
  • 做风险投资网站商城网站建设策划书
  • 【STM32项目开源】STM32单片机智能家居控制系统
  • 【设计模式】Java规则树重构复杂业务逻辑
  • 网络:传输层协议UDP和TCP
  • 从Excel姓名匹配案例学Python:由点及面的系统化学习指南
  • 建网站的专业公司家教网站制作
  • 赋能金融科技:基于AWS的云上量化交易解决方案,让策略研发与部署效率倍增
  • 洛谷 P1012 [NOIP 1998 提高组] 拼数
  • P12954 [GCJ Farewell Round #2] Railroad Maintenance【题解】
  • 虚幻引擎虚拟制片入门教程 之 Sequencer基础
  • 考研408--计算机网络--day1-概念组成功能三种交换技术分类
  • 网站标签怎么做跳转页面网站的建设ppt模板
  • Laravel下载和安装图解(非常详细)
  • Python机器学习---3.分类模型评估
  • Rust Tokio vs Go net/http:云原生与嵌入式生态选型指南
  • 【Java 详解】Mysql 索引从入门到精通
  • Vue收集表单数据
  • 正点原子RK3568学习日志10-向系统条件一个系统调用