目录

数据结构单链表list

数据结构——单链表list

前言:大家好😍,本文主要介绍数据结构——单链表


一、单链表

概念:单链表逻辑上相邻,物理上不一定相邻的链式存储结构

所以一个结点不仅仅要保存自身的数据,还要保存自己下一个连接结点的地址

所以每一个结点需要俩个域: 一个数据域,一个指针域

https://i-blog.csdnimg.cn/direct/75a70a04f78c4ceebc4c8838155a55c9.png

https://i-blog.csdnimg.cn/direct/0d6be5549ba24f80a8c98d0c774fd454.png

二、使用步骤

1.结构体定义

typedef int Elem_Type;
struct Node
{
	Elem_Type data;//数据域 存储有效数据
	struct Node* next;//指针域  存下一个有效结点的地址
};

typedef struct Node Node;
typedef struct Node* pNode;

struct Node:定义了一个 结构体 Node ,它包含两个成员:

Elem_Type data;: 数据域 ,用于存储节点中的数据。由于 Elem_Type 被定义为 int,所以这里的 data 可以存储一个整数。

struct Node* next;: 指针域 ,用于存储指向下一个节点的地址。这样,每个节点都可以通过 next 指针链接到下一个节点,形成链表。

typedef struct Node Node;:为结构体 Node 创建了一个别名 Node,这样在代码中可以直接 使用 Node 来声明这种类型的变量 。

typedef struct Node* pNode;:定义了一个 pNode 类型,它实际上是 指向 Node 结构体的指针类型。这通常用于表示链表的头指针或遍历链表时的指针变量。

2.初始化

//初始化
void Init_List(struct Node* head)
{
	assert(head != NULL);
	if (NULL == head)
	{
		exit(EXIT_FAILURE);
	}
	head->next = nullptr;
}
  • 参数 head 是指向 Node 结构体的指针。
  • 初始化链表: head->next = nullptr;head 节点的 next 指针设置为 nullptr ,表示链表为空,即没有其他节点。 https://i-blog.csdnimg.cn/direct/8ac4d18a44ad46cba115ba56784e2489.png

3.插入

3.1 头插

bool Insert_head(struct Node* head, Elem_Type val)
{
	assert(head != NULL);
	if (NULL == head)
	{
		exit(EXIT_FAILURE);
	}

	Node* pnewnode=(Node*)malloc(sizeof(Node));
	if (NULL == pnewnode)
		exit(1);
	pnewnode->data = val;

	pnewnode->next = head->next;
	head->next = pnewnode;
}

Node* pnewnode = (Node*)malloc(sizeof(Node)); :使用 malloc 函数分配内存以创建一个新的 Node 结构体实例,并将其地址赋给指针 pnewnode

  1. pnewnode->data = val; :将传入的数据 val 存储在新节点的 data 成员中。
  2. pnewnode->next = head->next; :将新节点的 next 指针指向当前头节点的下一个节点,即链表的第二个节点。
  3. head->next = pnewnode; :将头节点的 next 指针指向新节点,这样新节点就成为了链表的第一个节点。 https://i-blog.csdnimg.cn/direct/5558c822af9c490ba3e124178621f888.png https://i-blog.csdnimg.cn/direct/40d5d522ab3947c89d68640e5c1be977.png

3.2 尾插

bool Insert_tail(struct Node* head, Elem_Type val)
{
	assert(head != NULL);
	if (NULL == head)
	{
		exit(EXIT_FAILURE);
	}
	Node* pnewnode = (Node*)malloc(sizeof(Node));
	if (NULL == pnewnode)
		exit(1);
	pnewnode->data = val;

	Node* p = head;
	while (p->next != NULL)
	{
		p = p->next;
	}
	 
	//pnewnode->next = head->next;
	//head->next = pnewnode;
	pnewnode->next = p->next;
	p->next = pnewnode;

	return true;
} 

https://i-blog.csdnimg.cn/direct/0c4bfbf3f1bd4141be40fc714bcfd3c4.png

3.3 按位置插

bool Insert_pos(struct Node* head, Elem_Type val, int pos)
{
	assert(head != NULL);
	assert(pos >= 0 && pos <= Get_Length(head));

	Node* pnewnode = (Node*)malloc(sizeof(Node));
	if (NULL == head)
	{
		exit(1);
	}

	pnewnode->data = val;

	Node* p = head;
	for (int i = 0; i < pos; i++)
		p = p->next;

	//pnewnode->next = head->next;
     //head->next = pnewnode;
	pnewnode->next = p->next;
	p->next = pnewnode;

	return true;
}

https://i-blog.csdnimg.cn/direct/ff8281b230d24a5d9df6d9333404ab13.png https://i-blog.csdnimg.cn/direct/0e093479c9ce4725ae61481c31415270.png

四.删除

4.1头删

bool Del_head(struct Node* head)
{
	assert(head != NULL);

	 
	if (Is_Empty(head))
		return false;

	Node* p = head->next;
	head->next = p->next;

	free(p);
	p = NULL;
	return true;
}

https://i-blog.csdnimg.cn/direct/4777a568b4ac4a4aa77cf1e3b06b1e71.png

https://i-blog.csdnimg.cn/direct/6b02409eef804f28931939b14465cb59.png

4.2 尾删

bool Del_tail(struct Node* head)
{
	assert(head != NULL);


	if (Is_Empty(head))
		return false;

	Node* p = head;
	while (p->next->next != NULL)
		p = p->next;

	Node* q = p->next;
	p->next = q->next;

	free(p);
	p = NULL;
	return true;
}

https://i-blog.csdnimg.cn/direct/6b957425b9ab49ab9e7d5b0c96b98905.png

https://i-blog.csdnimg.cn/direct/23fda4b8e4094074901d6bbd4e086525.png

4.3 按位置删

//8.按值删(删除值val出现的第一次的地方)
bool Del_val(struct Node* head, Elem_Type val)
{
	//0.assert
	assert(head != NULL);

	//0.5 判断链表是否是空链表
	if (Is_Empty(head))
		return false;

	//1.调用Search函数,查找当前值val是否存在,用临时指针q接收
	Node* q = Search(head, val);
	if (q == NULL)
	{
		return false;
	}

	//2.申请临时指针p,让指针p停留在指针q指向节点的上一个节点
	Node* p = head;
	for ( ; p->next != q; p = p->next);

	//3.跨越指向
	p->next = q->next;

	//4.释放
	free(q);
	q = NULL;

	return true;
}

https://i-blog.csdnimg.cn/direct/16394872c7fa47e1ba850415bf157656.png

https://i-blog.csdnimg.cn/direct/1993e1f9322c43d7906440b2e598f53a.png

4.4按值删

bool Del_val(struct Node* head, Elem_Type val)
{
	//0.assert
	assert(head != NULL);

	//0.5 判断链表是否是空链表
	if (Is_Empty(head))
		return false;

	//1.调用Search函数,查找当前值val是否存在,用临时指针q接收
	Node* q = Search(head, val);
	if (q == NULL)
	{
		return false;
	}

	//2.申请临时指针p,让指针p停留在指针q指向节点的上一个节点
	Node* p = head;
	for ( ; p->next != q; p = p->next);

	//3.跨越指向
	p->next = q->next;

	//4.释放
	free(q);
	q = NULL;

	return true;
}

五 统计有效值个数

int Get_Length(struct Node* head)
{
	int count = 0;
	for (Node* p = head->next; p != NULL; p = p->next)
	{
		count++;
	}

	return count;
}

https://i-blog.csdnimg.cn/direct/823b5f9246d74e5ebf2f7553a53b5181.png

六 销毁1

//11.销毁1(需要辅助节点参与进来)
void Destroy1(struct Node* head)
{
	//只要不是空链表,则头删一次,直到链表为空
	/*while (!Is_Empty(head))
	{
		Del_head(head);
	}*/

	while (head->next != NULL)
	{
		Node* q = head->next;
		head->next = q->next;
		free(q);
		q = NULL;
	}
}

https://i-blog.csdnimg.cn/direct/1c790f18987446ed8c03baa89704523b.png

销毁2

//12.销毁2(不需要辅助节点参与进来)
void Destroy2(struct Node* head)
{
	//0.assert  head  

	//1.申请指针p,让其保存辅助节点的指针域
	Node* p = head->next;//p有可能为NULL, 有可能不空

	//2.申请指针q,先不给q赋值
	Node* q = NULL; //因为p有可能是NULL,所以不能现在直接把p->next给到q

	//3.反复通过p和q打配合,去销毁后续节点
	while (p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}

	//4.节点全部销毁完毕,别忘了把辅助节点的指针域置空
	head->next = NULL;//这一行代码可以帮助,下一次启用的时候,辅助节点不用初始化了

}

https://i-blog.csdnimg.cn/direct/0ada41a0c7b04f008c0422664bb14094.png

七 查找

//9.查找(查找值val出现的第一次的地方)
struct Node* Search(struct Node* head, Elem_Type val)
{
	//0.assert

	for (Node* p = head->next; p != NULL; p = p->next)
	{
		if (p->data == val)
		{
			return p;
		}
	}

	return NULL;
}

八 打印

//15.打印
void Show(struct Node* head)
{
	//assert

	for (Node* p = head->next; p != NULL; p = p->next)
	{
		printf("%d ", p->data);
	}
	printf("\n");

}