分享内容

  1. 链表的概念
  2. 建立链表
  3. 链表的基本操作

虚拟内存空间

操作系统为每个正在运行的进程分配独立的虚拟内存空间。这种机制的优点包括:
隔离性:一个进程无法直接访问另一个进程的内存空间,从而提高了系统的安全性和稳定性。
简化编程模型:每个进程都感觉自己拥有整个计算机的内存资源。

链表的概念

数组的概念

· 在内存中,数组是一块连续的区域。

· 数组大小固定,不利于扩展(当数组定义的空间不够时要重新定义数组)。
在这里插入图片描述

链表

新的数据结构一链表:
在这里插入图片描述

●链表(linked list)是一种线性数据结构。
● 链表是由多个独立的“节点”组成的,节点之间通过“指针”连接。整体构成一个链表!
● 每个节点都包含两项数据:节点的“数据”和指向下一节点的“指针”。
在这里插入图片描述
在这里插入图片描述
● 链表的首个节点被称为“头节点”,最后一个节点被称为“尾节点”。
● 尾节点指向的是“空”。

这种链表也称为单向链表,因为节点只能访问后续节点。

链表的内存特点

在这里插入图片描述

链表的概念-练习

1)链表的节点由几部分组成()?A

A、两部分,一部分存放节点值,另一部分存放指针

B、只有一部分,存放节点值

C、只有一部分,存放指针

D、两部分,一部分存放节点值,另一部分存放节点所占内存

插入节点

插入节点:在链表中插入节点非常容易。
例如,在节点1、2之间插入“节点4”。分两步完成:
①“节点4”指向“节点2”
②“节点1”指向“节点4”
在这里插入图片描述

删除节点

删除节点:在链表中删除节点也非常方便
例如,删除下面链表中的“节点4”。只需要1步:把“节点1”的指针指向“节点2”
在这里插入图片描述
【注意】尽管在删除操作完成后“节点4”仍然指向“节点2”,但实际上遍历此链表已经
无法访问到“节点4”,这意味着“节点4”已经不再属于该链表了。

查找节点

**查找节点:**在链表中查找节点的效率较低。
例如,在下面链表中访问(查找)数据部分=7的节点。需要从头节点出发,逐个向后遍历,直至找到目标节点。

在这里插入图片描述
同样,访问第i个节点,也只能从头节点出发,逐个找下一个并记数,没有办法像数组一
样直接用下标访问。

链表(总结)

① 链表(Linked List)是一种线性数据结构,其中每个节点存储一个数据和一个指向
下一个节点的指针。
②链表的优点
● 插入和删除操作无需移动其他元素,时间复杂度为O(1)。
● 链表不需要预先分配固定大小的内存空间,链表的大小可以根据需要动态扩展或缩减。
③ 链表的缺点
● 访问任意节点需要从头节点开始遍历,时间复杂度为O(n)。
● 链表节点除了包含值,还需额外保存一个指针。因此在相同数据量下,链表比数组占用更多的内存空间。

链表的概念-练习

2)以下关于链表的描述,哪一项是错误的?D

A、链表是一种线性数据结构,其中每个节点存储一个数据元素和一个指向下一个节

点的指针。

B、链表的优点是插入和删除操作的时间复杂度为O(1),无需移动其他元素。

C、链表的缺点是访问任意节点的时间复杂度为O(n),因为需要从头节点开始遍历。

D、链表必须预先分配固定大小的内存空间。

链表的典型应用

链表通常用于实现栈、队列、哈希表和图等数据结构。

· 当插入和删除操作都在链表的一端进行时,它表现的特性为先进后出,对应栈;
在这里插入图片描述
·当插入操作在链表的一端进行,删除操作在链表的另一端进行,它表现的特性为先进先出,对应队列。
在这里插入图片描述

建立链表

一般把链表节点定义为结构体

①定义节点

struct node{
	int data;// 数据
	node *next;// 指向下一个节点的指针
};

② 声明、初始化节点变量

【注意】a、b、c三个变量的内存地址不一定连续

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

· 在大括号里为节点的每个成员指定初始值
· NULL表示指针为空(指向空地址)
③构建节点之间的链接关系

a.next = &b;

b.next = &c;
&:取地址运算符

(注意】结构体变量访问成员时用.符号

得到一个有三个节点的链表:
在这里插入图片描述
通常将头节点当作链表的代称,比如以上链表可记作链表a。
在这里插入图片描述
验证一下,节点是否真的链接成功?

从头节点开始遍历:

node *head = &a;
while (head != NULL) {//循环终止条件
	cout << head->data << " ";
	head = head->next;
}
cout << endl;

【注意】结构体指针变量访问成员时用->符号
在这里插入图片描述

插入节点

插入节点分:插入成为头节点和其他情况(插入在某节点后)

①把P节点插入链表,成为新的头节点。
在这里插入图片描述
【思考】如果P和head都是指针,代码怎么写?
在这里插入图片描述
② 在节点n0后面插入一个新节点P
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

插入节点-练习

3)假设已有一个链表。请你创建一个新的数值为4的节点,调用insert函数,把新节点插入到节点b后面。

/*在链表的节点 n0 之后插入节点 P */
void insert (node *n0, node *P) {
	P->next = n0->next;	
	n0->next = P;

}

第一步,创建新节点

node d = {4, NULL} ;

第二步,调用insert函数

insert (&b, &d);
4)假设一个链表,head指向头节点。哪个代码可以实现把新节点P,插入成为新的头节点?D
A, head.next = p;

B. p.next = head;

C. head= p;

D. p->next = head; head = p;
在这里插入图片描述

删除节点

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

①删除头节点
在这里插入图片描述
在这里插入图片描述

步骤:

node *n1 = n0->next;
if (n1 	!= NULL) {
	node *n2 = n1->next;
	n0->next =n2;

}

实现删除n0后面的节点的函数remove()

/*删除链表的节点 n0 之后的首个节点 */
void remove (node *n0) {
	node *n1 = n0->next;
	if (n1 != NULL) {
		node *n2 = n1->next;
		n0->next = n2;

	}
}

时间复杂度O(1)

查找节点

第一种情况:查找链表中数值等于value的节点
在这里插入图片描述



/*访问链表中数值为 value 的节点,head 是头节点 */
node*access (node *head, int value) {

	while ( head != NULL && head->data != value)

		head = head->next;

	return head;

}

找到,则返回找到的节点指针,否则返回NULL。
第二种情况:查找链表中数值等于value的节点的前导节点
在这里插入图片描述

/*访问链表中数值为 value 的节点的前导节点 */
node* access (node *head, int value) {
	node *pre = NULL;
	while ( head != NULL&& head->data != value) {
		pre = head;
		head = head->next;
	
	}
	return pre;
}

找到,则返回找到的节点指针,否则返回NULL。
第三种情况:查找链表中索引为index的节点

/*访问链表中索引为 index 的节点,head 是头节点 */
node* access (node *head, int index) {
	for (int i = 0; i < index; i++) {
		if (head == NULL)
			return NULL;
	head = head->next;
	}
	return head;
}

找到,则返回非NULL的节点指针,否则返回NULL。
时间复杂度O(n)

链表操作

创建一个链表,按顺序存储数字1~5。然后:
1、创建节点P,存储数字9。
2、在链表中寻找数字m(1≤m≤5),在数字m的节点后面插入节点P。
3、删除头节点。
4、依次输出链表中的数据。
【输入描述】输入m(1≤m≤5)。
【输出描述】链表中的数据(空格隔开)
【输入样例】3
输出样例】 2 3 9 4 5
实现链表功能

①定义节点(结构体)
②实现链表的基本函数:insert、remove、access

主程序流程
①声明、初始化节点变量,存储数字1~5
② 按顺序建立链表连接
③创建节点P,存储数字9。
④在链表中寻找数字m(1≤m≤5),在数字m的节点后面插入节点P。
⑤ 删除头节点。
⑥ 依次输出链表中的数据

创建一个链表,按顺序存储数字1~5。

1.定义节点

struct node{
	int data;// 数据
	node *next;// 指向下一个节点的指针
}

2.实现insert()函数:在节点n0后面插入一个新节点P



void insert (node *n0, node *P) {
	P->next = n0->next;
	n0->next = P;

}

实现remove()函数:删除链表的节点n0之后的首个节点

void remove (node *n0) {
	node *n1 = n0->next;
	if (n1 != NULL) {
		node *n2 = n1->next;
		n0->next = n2;
	}
}

实现 access()函数:查找链表中数值等于value的节点

node*access (node *head, int value) {
	while ( head != NULL && head->data != value)

		head = head->next;

	return head;
}

创建一个链表,按顺序存储数字1~5

1 声明、初始化节点变量

2 按顺序建立链表连接

node n1 = {1, NULL} ;
node n2 = {2, NULL} ;

node n3 = {3, NULL} ;

node n4 = {4, NULL} ;
node n5 = {5, NULL} ;

n1.next =&n2;

n2.next =&n3;

n3.next = &n4;

n4.next = &n5;

在这里插入图片描述

完整代码

#include<iostream>
using namespace std;

struct node{
int data;// 数据
node *next;// 指针

/*在链表的节点 n0 之后插入节点 P*/
void insert (node *n0, node *P) {
	P->next = n0->next;
	n0->next = P;
}
/* 删除链表的节点 n0 之后的首个节点*/
void remove (node *n0) {
	node *n1 = n0->next;
	if(n1 != NULL) {// n0 -> n1 -> n2
		node *n2 = n1->next;
		n0->next = n2;
	}
}

/*访问链表中数值为 value 的节点,head 是头节点 */
node* access (node *head, int value) {
	while ( head != NULL && head->data != value)
		head = head->next;
	return head;
}
int main() {
	// 创建节点
	node n1 = {1, NULL} ;
	node n2 = {2, NULL} ;
	node n3 = {3, NULL} ;
	node n4 = {4, NULL} ;
	node n5 = {5, NULL} ;
	// 建立连接 n1->n2->n3->n4->n5
	n1.next = &n2;
	n2.next = &n3;
	n3.next = &n4;
	n4.next = &n5;
	
	node *head= &n1;//指向头节点
	int m;
	cin >> m;
	
	//1、创建结点P,存储数字9。
	node P = {9,NULL} ;
	//2、在链表中寻找数字m(1≤m≤5),在数字m的后面插入结点P。
	node *mNode = access (head,m) ;
	insert (mNode, &P) ;
	
	// 3、删除头节点
	head = head->next;
	
	// 4、依次输出链表中的数据。
	node *temp= head;// 从头节点开始
	while( temp != NULL) {
		cout << temp->data << " ";
		temp = temp->next;
	}
	cout << endl;
	
	return 0;
}

在这里插入图片描述

本次课程的知识点

  1. 链表的概念

  2. 链表和数组的对比

  3. 建立链表的步骤

  4. 链表的操作:插入节点、删除节点、访问节点
    1、以下关于链表的描述,哪一项是错误的?D

A、链表是一种线性数据结构,其中每个节点存储一个数据元素和一个指向下一
个节点的指针。

B、链表的优点是插入和删除操作的时间复杂度为O(1),无需移动其他元素。

C、链表的缺点是访问任意节点的时间复杂度为O(n),因为需要从头节点开始
遍历。

D、链表必须预先分配固定大小的内存空间。
在这里插入图片描述

字母链表

创建一个链表,按顺序存储字母A’~‘E’。从第一个节点依次输出链表中的
数据。
【输入】无
【输出】ABCDE

#include<iostream>
using namespace std;

struct node{
	char data;// 数据
	node *next;// 指针
}
int main(){
	// 创建节点
	node n1 = {'A', NULL} ;
	node n2 = {'B', NULL} ;
	node n3 = {'C', NULL} ;
	node n4 = {'D', NULL} ;
	node n5 = {'E', NULL} ;
	// 建立连接 n1->n2->n3->n4->n5
	n1.next = &n2;
	n2.next = &n3;
	n3.next = &n4;
	n4.next = &n5;
	
	node *head= &n1;// 指向头节点
	while ( head != NULL) {
		cout << head->data;
		head = head->next;
	}
	cout << endl;
	
	return 0;

}

更多推荐