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

链表基本操作

文章目录

  • 1、单链表
    • 1.1 链表的创建
    • 1.2 链表的遍历
    • 1.3 链表的删除
    • 1.4 链表的插入
    • 1.5 链表和数组
  • 2、双向链表
    • 2.1 双链表的创建
    • 2.2 双链表的删除
    • 2.3 双链表的插入
    • 2.4 双向循环链表
    • 2.5 双链表优缺点

1、单链表

链表是一种物理存储单元上非连续、非顺序的存储结构,插入和删除速度快,并且不需要像数组一样预先开辟空间链表结构可以充分利用计算机内存空间,实现灵活地内存动态管理。

Pasted-image-2025040117473344f072a03e24e176.png

链表由一系列结点组成,每个结点包括两个部分:

  • 存储数据元素的数据域
  • 存储下一个结点地址的指针域

1.1 链表的创建

1.定义结构体存储结点信息

struct node {
	int data;  //存储当前结点的数据值
	node *next;  /存储下一个结点的地址
}

2.创建头尾节点

  • 使用 new 运算符来动态申请空间
  • 使用 -> 结构体指针运算符来访问内部成员
  • 头结点数据域通常不做存储(删除结点只能通过它前一个结点指向它后一个结点,头结点没有前一个结点)

*head.datahead->data 效果是一样的。

node *head, *tail;  //定义头结点和尾结点
head = new node;  //动态申请内存空间
head->next = NULL;  //指针域初始化为空
tail = head;  //此时的头结点也是尾结点

3.添加新结点

int x;
while(cin >> x) {  //会自动获取到输入内容的结束位置,再终止输入
	node *p = new node;  //定义新结点p,动态申请新的空间
	p->data = x;  //给数据域赋值
	p->next = NULL;  //指针域初始化为空
	tail->next = p;  //连到尾结点后面
	tail = p;  //尾结点更新为当前的p
}

1.2 链表的遍历

node *p = head;  //使用p作为当前结点,从头开始遍历
while(p->next != NULL) {  //判断下一个结点是否为空
	cout << p->next->data << " ";  //输出下一个结点的数据,因为头结点无数据
	p = p->next;  //p更新为下一个结点
} 

1.3 链表的删除

删除单链表中p结点的下一个结点

p->next = p->next->next;  //将p所指的下一个结点修改为下一个结点的下一个结点

PixPin_2025-04-01_18-13-0901a34a3c0f76eff8.gif

1.4 链表的插入

在p结点之后插入一个结点s

node *s = new node;  //动态申请新的空间
s->next = p->next;  //先将新结点的指针域指向p的下一个结点
p->next = s;  //再将p的下一个结点修改为s

PixPin_2025-04-01_18-13-398f2e7f5b50888dea.gif

1.5 链表和数组

数组:

  • 优点:
    随机访问性强,查找速度快。
  • 缺点:
    插入和删除效率低,,可能浪费内存,,内存空间要求高,数组大小固定,不能动态拓展。

链表:

  • 优点:
    插入删除速度快;内存利用率高,不会浪费内存;大小没有固定,拓展很灵活。
  • 缺点:
    不能随机查找,必须从第一个开始遍历,查找效率低。
查找插入删除
数组O(1)O(n)O(n)
链表O(n)O(1)O(1)

2、双向链表

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继直接前驱,所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点后继结点

Pasted-image-20250401183027dc7f17708b708341.png

双向链表每个节点有三个部分:

  • 存储数据元素的数据域
  • 存储上一个节点地址的指针域
  • 存储下一个结点地址的指针域

2.1 双链表的创建

1.定义结构体存储结点信息

struct node {
	int data;  //存储当前结点的数据值
	node *pre, *next;  /存储前驱和后继结点的地址
}

2.创建头尾节点

node *head, *tail;  //定义头结点和尾结点
head = new node;  //动态申请内存空间
head->pre = NULL;  //前驱指针初始化为空
head->next = NULL;  //后继指针初始化为空
tail = head;  //此时的头结点也是尾结点

3.添加新结点

int x;
while(cin >> x) {  //会自动获取到输入内容的结束位置,再终止输入
	node *p = new node;  //定义新结点p,动态申请新的空间
	p->data = x;  //给数据域赋值
	p->pre = tail;  //前驱指针指向前驱节点tail
	p->next = NULL;  //后继指针初始化为空
	tail->next = p;  //尾结点的后继指针指向p
	tail = p;  //尾结点更新为当前的p
}

2.2 双链表的删除

删除双链表中p结点

p->pre->next = p->next;  //将p上一个结点的后继指针指向p的下一个结点
p->next->pre = p->pre;  //将p下一个结点的前驱指针指向p的上一个结点

PixPin_2025-04-01_18-48-56578d0f7d9e7af118.gif

2.3 双链表的插入

在p结点之后插入一个结点s

node *s = new node;  //动态申请新的空间
s->next = p->next;  //新结点的后继指针域指向p的下一个结点
s->pre = p;  //新结点的前驱指针指向p
p->next->pre = s;  // p的下一个结点的前驱指针指向s
p->next = s;  //p的后继指针指向s

PixPin_2025-04-01_18-51-5051df3f5373f70761.gif

2.4 双向循环链表

双向循环链表:最后一个结点的后继指针指向头结点,且头结点的前驱指针指向最后一个结点。

head->pre = tail;
tail->next = head;

2.5 双链表优缺点

  • 优点:
    从双向链表中的任意一个结点开始,都可以很方便地访问前驱结点和后继结点
  • 缺点:
    增加删除节点复杂,需要多分配一个指针存储空间

相关文章:

  • idea中的--、-D、-X的区别
  • Docker容器深度解析:从基础概念到企业级实践
  • LSTM网络是什么?
  • Suricata配置之YAML
  • Netty的心跳机制怎么实现的?
  • 【408--考研复习笔记】操作系统----知识点速览
  • 深入解析拓扑排序:算法与实现细节
  • EL表达式与JSTL标签库实战指南:从基础到OA系统改造
  • STL新增内容
  • flutter 曲线学习 使用第三方插件实现左右滑动
  • 厘米级定位赋能智造升级:品铂科技UWB技术驱动工厂全流程自动化与效能跃升”
  • Boost库中的谓词函数
  • 基于大模型的室间隔缺损手术全流程预测与方案研究报告
  • 蹊跷的崩溃:CoreData 数据保存时提示“不可接受类型”(Unacceptable type)
  • k8s常用总结
  • C++刷题(四):vector
  • 没有数据湖?可观测性也许不再有效!
  • 透视飞鹤2024财报:如何打赢奶粉罐里的科技战?
  • deepseek对IBM MQ错误日志分析
  • java项目挂机自动重启操作指南
  • 外企聊营商|武田制药:知识产权保护助创新药研发
  • 钕铁硼永磁材料龙头瞄准人形机器人,正海磁材:已向下游客户完成小批量供货
  • 复原展出孙吴大墓,江苏首座考古博物馆将开放
  • 问责!美国海军对“杜鲁门”号航母一系列事故展开调查
  • 时隔3年俄乌直接谈判今日有望重启:谁参加,谈什么
  • 陕西河南山西等地将现“干热风”灾害,小麦产区如何防范?