C++(链表二)

分享内容

  1. 循环链表
  2. 双向链表
  3. 操作双向链表

虚拟内存空间

进程的虚拟内存空间通常分为两部分:用户空间:这是普通程序可以访问的内存区域,包括代码段、数据区、栈区、堆区等。内核空间:这是操作系统内核专用的内存区域,普通程序无法直接访问。内核空间用于存储内核代码、内核数据结构以及驱动程序等。用户空间和内核空间的划分是为了保护内核的稳定性,防止用户程序直接操作内核数据。
在这里插入图片描述

循环链表

上节课学习的链表也称为单向链表。单向链表的尾节点指针指向空地址NULL。
在这里插入图片描述
如果把尾节点指针是指向链表的头节点,就得到了循环链表(也叫环形链表)
在这里插入图片描述

循环链表的突现

循环链表的创建步骤和单向链表相同

①定义节点

struct node{
	int data;
	node *next;
}

在这里插入图片描述
②声明、初始化节点变量

node a = {1, NULL} ;
node b = {2, NULL} ;
node c = {3, NULL} ;

在这里插入图片描述
③ 构建节点之间的关系,建立循环

a.next = &b;
b.next = &c;
c. next = &a;

和单向链表的唯一区别在于要把尾节点指针指向头节点
在这里插入图片描述
在这里插入图片描述

循环链表-练习

循环链表与单链表相比,最大的区别在于?B

A、循环链表的头节点存储了链表的长度信息。

B、循环链表的最后一个节点的指针指向头节点。

C、循环链表只能单向遍历。

D、循环链表的头节点不存储数据。

循环链表的遍历

在循环链表中,可以从任意节点出发进行(正向)遍历。

遍历结束的条件是回到最初出发的节点。

node *P = &b;
do {

	cout << P->data << " ";

	P = P->next;

} while (P != &b) ;
cout << endl;

循环链表的插入、删除

在循环链表中插入、删除节点和单向链表相同。除了以下情况:
①循环链表初始为空,插入一个节点。

这个节点的next指针需要指向它自己。
在这里插入图片描述

②要删除的是循环链表中唯一的节点。

这个节点的next指针指向NULL即可。
在这里插入图片描述

循环链表的应用

● 循环链表由于其环形结构的特点,在需要模拟环形顺序、频繁插入和删除节

点的场景中具有独特的优势。

● 常见的应用包括约瑟夫环问题、环形队列、多线程调度、音乐播放列表和游

戏角色轮换等。

循环链表-练习1

1、在不为空的循环链表中插入一个新节点时,以下说法正确的是?B

A、必须找到链表的头节点才能插入。

B、可以在任意已知节点后插入新节点,无需找到头节点。

C、插入操作的时间复杂度为O(n)。

D、插入操作必须从头节点开始遍历。
解析:在循环链表中,插入一个新节点时,只需要找到一个已知节点,然后在该节点后插
入新节点即可,无需找到头节点。选项A和D错误;选项c错误,和单向链表一样,插入操
作的时间复杂度为O(1)。
2、在至少有2个节点的循环链表中,如果要删除一个节点,以下步骤正
确的是?B
A、只需要将该节点的指针指向NULL即可。

B、需要找到该节点的前驱节点,然后调整指针。

C、只需要将该节点的指针指向下一个节点即可。

D、需要重新调整整个链表的指针。
解析:和单向链表一样,在循环链表中,删除一个节点需要找到该节点的前驱节点,然
后将前驱节点的指针指向被删除节点的下一个节点。选项A和c都忽略了前驱节点的调整;

双向链表

单向链表,每个节点只能访问后继节点。
在这里插入图片描述
有时候只知每个元素后面的元素是不够的,还需要知道前面的元素是什么。
在单向链表的每个节点添加一个指向前驱节点的指针,就得到双向链表。
在这里插入图片描述
单向链表变成双向链表只需要两步:
在这里插入图片描述

1 每个节点添加 prev指针

2 prev指针指向前一个节点
在这里插入图片描述
在这里插入图片描述

建立双向链表



1定义双向链表的节点(结构体)

struct node{
	int data;// 数据
	node *next;// 指向后继节点的指针
	node *prev;// 指向前驱节点的指针
}
  1. 创建节点
node a = {1, NULL, NULL} ;
node b = {2, NULL, NULL} ;
node c = {3, NULL, NULL} ;
  1. 通过指针实现双向链接
a.next = &b;

b.next = &c;连接后继节点
c.prev = &b;
b.prev = &a;连接前驱节点

每个节点都可以访问前驱节点和后继节点
在这里插入图片描述

遍历双向链表

  1. 正向遍历:
node *P = &a;从头节点开始

while (P != NULL) {直到后继节点为空
	cout << P->data << " ";
	P = P->next;找后继节点
}

  1. 反向遍历:
node *P = &C;从尾节点开始
while(P != NULL){直到前驱节点为空
	cout << P->data << " ";
	P=P->prev; 找前驱节点找前驱节点
}

在这里插入图片描述

与单向链表相比

● 双向链表的节点可以方便的访问前驱(前一个)节点和后继节点。
● 双向链表可以双向遍历(单向链表只能单向遍历)。
● 每个节点增加了一个指针,所以双向链表内存占用比单链表多。

适用场景

● 如果你的应用场景主要集中在单向顺序处理数据,且对内存占用敏感,单向链表是更好的选择。
● 如果你需要频繁双向遍历数据,或者需要快速定位节点的前驱和后继,双向链表更适合。

双向链表(插八节点)

插入节点分:在双向链表的头部、中间、尾部插入节点

  1. 在头部插入(e插入到a前面)

在这里插入图片描述
在这里插入图片描述
2. 在中间插入新节点(e插入到a后面)
在这里插入图片描述
3. 在尾部插入新节点(e插入到c后面)
在这里插入图片描述

插入节点-练习1

【真题】以下哪组操作能完成在双向链表中,在p指向的节点之后插入s
指向的节点。(其中,next为节点的直接后继,prev为节点的直接前驱)D

A
p->next->prev =s;
s->prev = p;
p->next =s;
s->next = p->next;
B
p->next->prev = s;
p->next = s;
s->prev = p;
s->next = p->next;

C,

s->prev = p;
s->next = p->next;
p->next =s;
p->next->prev = s;

D.

s->next = p->next;
p->next->prev = s;
s->prev = p;
p->next = s;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2、【真题】在一个双向链表中,p指向链表中的一个节点,s指向一待插入
节点,现要求在p之前插入s,则正确的操作是?D

A,

p->prev = s;
s->next = p;
p->prev->next =s;
s->prev = p->prev;
在这里插入图片描述

B.
s->prev = p->prev;
p->prev->next =s;
s->next = p;
p->prev = s->next;
在这里插入图片描述

C.

s->next = p;
p->next = s;
p->prev->next =s;
s->next = p;
在这里插入图片描述

D.

s->prev = p->prev;
p->prev->next = s;
s->next = p;
p->prev =s;
在这里插入图片描述

双向链表(删除节点)

删除节点分:删除头节点、删除中间节点、删除尾节点

  1. 删除头节点a
    在这里插入图片描述2. 删除中间节点b

在这里插入图片描述
3. 删除尾节点c
在这里插入图片描述

删除节点-练习

【真题】设p指向双向链表中的一个节点,它的左右节点均为非空。现要
求删除节点p,则下列语句序列中不正确的是?D

A,

p->prev->next = p->next;
p->prev->next->prev = p->prev;
在这里插入图片描述

B,

p->prev->next = p->next;
p->next->prev = p->prev;
在这里插入图片描述

C.

p->next->prev = p->prev;
p->next->prev->next = p->next;
在这里插入图片描述

D,

p->next->prev = p->next;
p->prev->next = p->prev;
在这里插入图片描述

倒背如流

乐乐想要编写一个程序,实现持续输入数字,直到输入数字0时停止。在输入结束后,
需要将输入的数字按顺序从头到尾和从尾到头各背诵一次。
【输入格式】
一行包含多个整数数字,其中最后一个数字为0,每个整数的值均小于2^31。
【输出格式】
两行,第一行将按照正序输出这些数字,第二行倒序输出这些数字,数字之间用空格分隔。
【输入样例】1 3 3 1 4 6 2 0
【输出样例】1 3 3 1 4 6 2
2 6 4 1 3 3 1
题目要求正序、倒序输出,推荐使用双向链表,方便双向遍历

struct node {
	int data;// 数据
	node *next;// 指向后继节点的指针
	node *prev;// 指向前驱节点的指针
}

正序、倒序输出需要知道头节点和尾节点在哪。
node *head, *tail;
在这里插入图片描述

在这里插入图片描述

创建节点的两种方式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完整代码

#include<iostream>
using namespace std;

struct node{
	int data;// 数据
	node *next;// 指向后继节点的指针
	node *prev;// 指向前驱节点的指针
}
int main(){
	int n;
	cin >> n;
	//1、读入第一个数,创建第一个节点,头指针指向这个节点
	node *head= new node;//动态分配内存
	head->data = n;//新结点的数据
	head->prev= head->next=NULL;//初始化指针

	// 2、重复插入新节点直到读到o
	node *p=head;//新节点总是插入p后一个位置
	while (1) {
		cin >> n;
		if (n == 0)
		break;//读入的数据为0时,停止输入数据
		node *s=new node;//分配新结点s
		s->data = n;// 新结点存储数字n
		s->prev= s->next=NULL;//初始化指针
		// 把s插入p后面
		s->prev = p;
		p->next = s;
		// p后移1
		p = p->next;
	}
	//3、保存指向尾节点的指针
	node *tail = p;
	
	// 4、正序遍历输出
	p=head;//p指向头节点
	while (p != NULL) {
		cout << p->data << '';
		p=p->next;// 指向后继节点
	}
	cout << endl;
	
	// 5、逆序遍历输出
	p= tail;//p指向尾节点
	while (p != NULL) {
		cout << p->data << '';
		p=p->prev;//指向前驱节点
	}
	cout << endl;
	return 0;
}

本次课程的知识点

  1. 循环链表的概念

  2. 循环链表与单向链表的对比

  3. 双向链表的概念

  4. 双向链表与单向链表的对比

  5. 双向链表的操作:插入节点、删除节点
    1、以下关于双向链表的说法错误的是?C

A、双向链表可以方便地实现从任意节点向前或向后遍历

B、在双向链表中,删除一个节点需要修改两个节点的指针

C、相同数据量时,双向链表比单链表少占内存空间

D、双向链表的头节点的前驱指针通常为空
2、在双向链表中,每个节点包含的指针域指向的内容是?A

A、前驱节点的地址和后继节点的地址

B、前驱节点的地址和数据域

C、后继节点的地址和数据域

D、只有后继节点的地址

动态创建链表

创建n个节点的单向链表,按顺序存储1~n。请用动态分配内存的方式
创建节点。遍历输出每个节点的值。
【输入格式】一个正整数n
【输出格式】输出单向链表每个节点的值,数值之间用空格隔开。
【输入样例】10
【输出样例】1 2 3 4 5 6 7 8 9 10

#include<iostream>
using namespace std;

struct node{
	int data;// 数据
	node *next;	// 指向后继节点的指针
};
int main() {
	int n;
	cin >> n;
	//1、创建第一个节点,头指针指向这个节点
	node *head= new node;//动态分配内存
	head->data = 1;//新结点的数据
	head->next = NULL;//初始化指针
	
	// 2、插入值为2~n的新节点(在尾部插入)
	int i = 2;
	node *p=head;//新节点总是插入p后一个位置
	for(int i = 2; i < n ; i++) {
		// 分配新结点s
		node *s = new node;
		s->data = i;// 新结点存储数字i
		s->next = NULL;	//初始化指针
		// 把s插入p后面
		p->next = s;
		// p后移1
		p = p->next;
	}

	// 正序遍历输出
	p=head;//p指向头节点
	while (p != NULL) {
		cout << p->data << '';
		p=p->next;//指向后继节点
	}
	cout << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有点。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值