数据结构(链表)-总结

目录

前言

        1. typedef 语句的作用:

        2.判空操作

        3.为什么插入,删除函数返回值都为指针类型

1.不带头节点的单链表

        (1)定义数据结构

        (2)函数列表

                1.尾插法建立单链表

                2.头插法建立单链表

                3.在第一个值为 x 的结点前面添加值为 a 的结点(若未找到,则添加到链表末尾)

                4.删除第一个值为 x 的结点

                5.删除值为 x 的所有结点

                6.销毁链表

                7.递归返回结点个数

                8.创建升序链表

                9.递归升序插入

                10.就地逆序单链表

                11.打印链表

        (3)完整代码      

2.带头节点的单链表

        (1)定义数据结构

                1.结点类型

                2.头节点类型

        (2)函数列表

                1.创建头节点

                2.尾插法建立链表

                3.头插法建立链表

                4.升序插入元素

                5.删除全部值为 x 的结点

                6.升序排序

                7.查找值为 x 的结点,将 x 改为 a

                8.销毁链表

                9.打印链表

        (3)完整代码

3.双向链表

        (1)数据结构

        (2)函数列表

                1.初始化头节点

                2.打印双向链表

                3.尾插法插入数据

                4.头插法插入数据

                5.有序插入

                6.函数指针创建链表

                7.删除所有值为 x 的结点

                8.销毁链表

        (3)完整代码

        (4)运行结果

4.带头结点的单循环链表

        (1)数据结构

        (2)函数列表

                1.初始化头节点

                2.尾插函数

                3.头插函数

                4.升序插入函数

                5.删除所有值为 x 的结点

                6.销毁链表

                7.打印链表

        (3)完整代码

        (4)运行结果


前言

这篇文章以及代码都是在某培训机构学完数据结构之后,自己根据课程重新写了一点代码总结。

文章和代码中如有谬误,敬请指出!

这里对文章中可能有疑虑的一些内容进行了综述:

1. typedef 语句的作用:

        (1)简化定义语句,提高可读性

在使用 typedef 后,结构体变量的定义由原先的 struct Node newNode 简化为 Node newNode,尤其在结构体名字较长时,可以使代码更为简洁,提高可读性。

        (2)增强代码的可维护性和复用性

通过使用 typedef 定义数据类型的别名,代码在变更数据类型时变得更具灵活性。例如,若需将数据域的类型修改为 char,使用 typedef 的情况下只需修改一处宏定义即可实现全局变更,而不必修改代码中所有使用了该类型的地方。这有助于提升代码的可维护性和复用性。

2.判空操作

在带头结点的链表判空时,我先对头节点进行判断 然后使用或运算 对首结点进行判空处理。这两个操作不能互换位置,因为当链表没有被创建时,链表是没有头节点的,这个时候通过头节点去判断首结点,会造成空指针的使用,引发段错误。

3.为什么插入,删除函数返回值都为指针类型

        (1).在进行插入删除时,会对链表进行空值判断,若链表为空,则会创建新的头结点或者数据结点,所以需要使用返回值,来返回创建的表头地址。

        (2).在不带头节点的链表中进行插入和删除时,可能会遇到插入到第一个结点前面,或者是删除第一个结点的情况,此时,头指针的指向就会发生变更,需要通过返回值,更新头指针,防止出现非法访问。

        (3).可以通过返回值更新头指针,或者使用二级指针解决这个问题。

        (4)本文的代码是在VS Studio中实现的,在输入时使用的是 scanf_s() 函数,在其他编译器中大概率不能使用,建议改成 scanf() 函数进行使用。


1.不带头节点的单链表

(1)定义数据结构

typedef int Elemtype;
typedef struct Node
{
	Elemtype data;
	struct Node* next;
}Node;

(2)函数列表

1.尾插法建立单链表
//尾插法创建一个单链表
Node* Create_List_Tail()
{
	/*1.输入链表元素 */
	printf("请输入链表的数据元素\n");
	int value;

	/*2.建立头指针 和 尾指针*/
	Node* head = NULL;
	Node* rear = NULL;

	while (1)
	{
		scanf_s("%d", &value);
		if (value == 0)
		{	//输入 0 退出
			break;
		}

		/* 2.创建结点,并初始化*/
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->data = value;
		newNode->next = NULL;

		/*3.将结点插入到链表*/
		if (head == NULL)
		{	//1.链表为空,新节点作为第一个结点
			head = newNode;
			rear = newNode;
		}
		else
		{	//2.链表不为空,新节点插入到链表末尾
			rear->next = newNode;
			rear = newNode;
		}
	}
	return head;
}
2.头插法建立单链表
//头插法建立一个单链表
Node* Create_List_Head()
{
	/*1.输入链表元素 */
	printf("请输入链表的数据元素\n");
	int value;

	/*2.建立头指针 */
	Node* head = NULL;

	while (1)
	{
		scanf_s("%d", &value);
		if (value == 0)
		{	//输入 0 退出
			break;
		}

		/* 2.创建结点,并初始化*/
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->data = value;
		newNode->next = NULL;

		/*3.将结点插入到链表*/
		if (head == NULL)
		{	//1.链表为空,新节点作为第一个结点
			head = newNode;
		}
		else
		{	//2.链表不为空,新节点插入到第一个结点的位置
			newNode->next = head;
			head = newNode;
		}
	}
	return head;
}
3.在第一个值为 x 的结点前面添加值为 a 的结点(若未找到,则添加到链表末尾)
/*
* 在链表中查找值为x的结点,在x的前面添加一个值为a的结点
* 若没有找到x,则把a加入到链表的最后,并且将新链表的
* 第一个结点返回
*/
Node* ADD_a_Insert(Node* head,Elemtype x,Elemtype a)
{
	/*1.建立结点*/
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = a;
	newNode->next = NULL;

	/*2.判空*/
	if (head == NULL)
	{	//链表为空,将新节点作为第一个结点也是最后一个结点
		head = newNode;
		return head;
	}

	/*3.链表不为空,需要查找 x 结点,建立遍历指针*/
	Node* p = head;
	Node* pre = NULL;

	/*4.遍历链表,查找值为 x 的结点*/
	while (p)
	{
		if (p->data == x)
			break;
		else
		{
			pre = p;	//保存当前节点的上一个结点
			p = p->next;	//查找下一个结点
		}
	}

	/*5.结果判断*/
	if (p == head)
	{	//1.第一个结点值为 x ,插入结点到第一个结点的位置
		newNode->next = head;
		head = newNode;
	}
	else
	{	//2.插入到第一个结点后面的位置
		pre->next = newNode;
		newNode->next = p;
	}
	return head;
}
4.删除第一个值为 x 的结点
//在链表中中找到值为x的结点,将其删除,如果有多个值为x的结点,只删除第一个
Node* Delete_Frist_Value_Is_x(Node* head,Elemtype x)
{
	/*1.判空*/
	if (head == NULL)
		return NULL;

	/*2.链表不为空,建立遍历指针*/
	Node* p = head;
	Node* pre = NULL;

	/*3.遍历链表,查找值为 x 的结点*/
	while (p)
	{	//找到了
		if (p->data == x)
			break;
		else
		{	//没找到,查找下一个元素
			pre = p;	//保存前一个结点
			p = p->next;
		}
	}

	/*4.查找结束,删除判断*/
	if (p != NULL)
	{	//存在值为 x 的结点
		if (p == head)
			head = head->next;
		else
			pre->next = p->next;
		free(p);
		p = NULL;
	}
	return head;
}
5.删除值为 x 的所有结点

注意:删除结点时需要考虑结点的位置,若需要删除的节点为头节点,则需要更换头结点,若需要删除的结点是尾结点,则需要将尾结点的上一个结点的 next 指针置空。

//在链表中删除全部值为 x 的结点
Node* Delete_Value_Is_x(Node* head, Elemtype x)
{
	/*1.判空*/
	if (head == NULL)
		return NULL;

	/*2.链表不为空,建立遍历指针*/
	Node* p = head;
	Node* pre = NULL;

	/*3.遍历链表,查找值为 x 的结点*/
	while (p)
	{	//找到了,删除结点
		if (p->data == x)
		{
			if (p == head)
				head = head->next;
			else
				pre->next = p->next;
			free(p);

			//遍历下一个 结点
			if (pre == NULL)
				p = head;
			else
				p = pre->next;
		}
		else
		{	//没找到,查找下一个元素
			pre = p;	//保存前一个结点
			p = p->next;
		}
	}

	return head;
}
6.销毁链表

注意:销毁链表之后,需要将空值 NULL 返回给头指针,或者将头指针置空。防止销毁链表后,头指针仍然指向了原来的内存,导致非法访问。

//销毁一个链表
Node* Destory_List(Node* head)
{
	/*1.判空*/
	if (head == NULL)
	{
		printf("\n空链表!\n");
		return NULL;
	}
		

	/*2.链表不为空,开始删除*/
	Node* p = head;
	while (p)
	{
		head = head->next;
		free(p);
		p = head;
	}
	printf("\n删除完成!\n");
	return NULL;
}
7.递归返回结点个数
//递归返回单链表的数目
int Get_List_Length(Node* head)
{
	if (head == NULL)
		return 0;
	return 1 + Get_List_Length(head->next);
}
8.创建升序链表

注意:这里其实就是一个简单的插入,只不过在每次插入前都查找了一边插入的位置。

//创建一个升序链表
Node* Create_Sort_List()
{
	/*1.创建头指针*/
	Node* head = NULL;

	/*2.输入数据*/
	int value;
	printf("请输入链表:\n");
	while (1)
	{
		scanf_s("%d", &value);
		if (value == 0)
			break;

		/*3.创建结点*/
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->next = NULL;
		newNode->data = value;

		/*4.创建遍历指针*/
		Node* p = head;
		Node* pre = NULL;

		/*4.插入判断*/
		if (head == NULL)
		{	//链表为空,新节点作为链表的头结点
			head = newNode;
		}
		else
		{	//链表不为空,遍历链表,插入新节点到链表中
			while (p)
			{
				if (p->data > newNode->data)	//找到了,插入到 p 前面
				{
					if (p == head)
					{	//插入到 第一个元素的位置
						newNode->next = head;
						head = newNode;
					}
					else
					{	//插入到 pre 的后面,p 的前面
						pre->next = newNode;
						newNode->next = p;
					}
					break;
				}
				else
				{	//没有找到,后面还有结点
					if (p->next != NULL)
					{
						pre = p;
						p = p->next;
					}
					else
					{	//后面没有结点
						p->next = newNode;
						break;
					}
					
				}
			}
		}
	}
	return head;
}
9.递归升序插入

注意:这里使用了递归进行升序插入,在每次进行递归之前进行判断,查看是否在当前位置插入结点。每次传入的结点为当前结点的下一个结点,并将递归结果返回。用 当前节点的下一个结点接受即: head->next = Sort_Insert_List(head->next, x); 相当于每次进行的是一个头插法插入。

//递归升序插入
Node* Sort_Insert_List(Node* head, Elemtype x)
{
	/*1.判空,若链表为空,则直接将新节点作为头节点返回*/
	if (head == NULL)
	{	//链表为空
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->data = x;
		newNode->next = NULL;
		head = newNode;
		return head;
	}
	
	/*2.插入位置寻找*/
	if (head->data < x)
	{
		head->next = Sort_Insert_List(head->next, x);
		return head;
	}
	else
	{
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->data = x;
		newNode->next = NULL;

		/*3.使用头插法将 新节点插入到链表中*/
		newNode->next = head;
		head = newNode;
		return head;
	}
}
10.就地逆序单链表

注意:这段代码的实质就是逐个摘下链表的第一个结点,放入新的链表中,每次都使用头插法插入,并更新新链表的头结点的位置。

//就地逆序一个单链表 (不申请额外的空间)
Node* Reverse_List(Node* head)
{
	/*1.判空*/
	if (head == NULL)
		return NULL;

	/*2.建立遍历指针和标志指针*/
	Node* p = head;
	Node* h = NULL;

	/*3.反转链表*/
	while (p)
	{
		/*1.将链表的第一个元素摘下来*/
		head = head->next;
		p->next = NULL;

		/*2.按照头插法,将摘下来的结点加入到新的链表中*/
		p->next = h;
		h = p;

		/*3.摘下一个结点*/
		p = head;
	}
	return h;
}
11.打印链表
//打印链表
void Print_List(Node* head)
{
	printf("\n打印链表:\n");
	printf("---------------------------\n");
	
	/*1.判空*/
	if (head == NULL)
	{
		printf("空链表!");
	}
	else
	{
		/*2.链表不为空,遍历链表*/
		Node* p = head;
		while (p)
		{
			printf("%d ", p->data);
			p = p->next;
		}
	}
	printf("\n---------------------------\n");
}

(3)完整代码      

#include<stdio.h>
#include<stdlib.h>

typedef int Elemtype;
typedef struct Node
{
	Elemtype data;
	struct Node* next;
}Node;

//尾插法创建一个单链表
Node* Create_List_Tail()
{
	/*1.输入链表元素 */
	printf("请输入链表的数据元素\n");
	int value;

	/*2.建立头指针 和 尾指针*/
	Node* head = NULL;
	Node* rear = NULL;

	while (1)
	{
		scanf_s("%d", &value);
		if (value == 0)
		{	//输入 0 退出
			break;
		}

		/* 2.创建结点,并初始化*/
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->data = value;
		newNode->next = NULL;

		/*3.将结点插入到链表*/
		if (head == NULL)
		{	//1.链表为空,新节点作为第一个结点
			head = newNode;
			rear = newNode;
		}
		else
		{	//2.链表不为空,新节点插入到链表末尾
			rear->next = newNode;
			rear = newNode;
		}
	}
	return head;
}

//头插法建立一个单链表
Node* Create_List_Head()
{
	/*1.输入链表元素 */
	printf("请输入链表的数据元素\n");
	int value;

	/*2.建立头指针 */
	Node* head = NULL;

	while (1)
	{
		scanf_s("%d", &value);
		if (value == 0)
		{	//输入 0 退出
			break;
		}

		/* 2.创建结点,并初始化*/
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->data = value;
		newNode->next = NULL;

		/*3.将结点插入到链表*/
		if (head == NULL)
		{	//1.链表为空,新节点作为第一个结点
			head = newNode;
		}
		else
		{	//2.链表不为空,新节点插入到第一个结点的位置
			newNode->next = head;
			head = newNode;
		}
	}
	return head;
}

/*
* 在链表中查找值为x的结点,在x的前面添加一个值为a的结点
* 若没有找到x,则把a加入到链表的最后,并且将新链表的
* 第一个结点返回
*/
Node* ADD_a_Insert(Node* head,Elemtype x,Elemtype a)
{
	/*1.建立结点*/
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = a;
	newNode->next = NULL;

	/*2.判空*/
	if (head == NULL)
	{	//链表为空,将新节点作为第一个结点也是最后一个结点
		head = newNode;
		return head;
	}

	/*3.链表不为空,需要查找 x 结点,建立遍历指针*/
	Node* p = head;
	Node* pre = NULL;

	/*4.遍历链表,查找值为 x 的结点*/
	while (p)
	{
		if (p->data == x)
			break;
		else
		{
			pre = p;	//保存当前节点的上一个结点
			p = p->next;	//查找下一个结点
		}
	}

	/*5.结果判断*/
	if (p == head)
	{	//1.第一个结点值为 x ,插入结点到第一个结点的位置
		newNode->next = head;
		head = newNode;
	}
	else
	{	//2.插入到第一个结点后面的位置
		pre->next = newNode;
		newNode->next = p;
	}
	return head;
}

//在链表中中找到值为x的结点,将其删除,如果有多个值为x的结点,只删除第一个
Node* Delete_Frist_Value_Is_x(Node* head,Elemtype x)
{
	/*1.判空*/
	if (head == NULL)
		return NULL;

	/*2.链表不为空,建立遍历指针*/
	Node* p = head;
	Node* pre = NULL;

	/*3.遍历链表,查找值为 x 的结点*/
	while (p)
	{	//找到了
		if (p->data == x)
			break;
		else
		{	//没找到,查找下一个元素
			pre = p;	//保存前一个结点
			p = p->next;
		}
	}

	/*4.查找结束,删除判断*/
	if (p != NULL)
	{	//存在值为 x 的结点
		if (p == head)
			head = head->next;
		else
			pre->next = p->next;
		free(p);
		p = NULL;
	}
	return head;
}

//在链表中删除全部值为 x 的结点
Node* Delete_Value_Is_x(Node* head, Elemtype x)
{
	/*1.判空*/
	if (head == NULL)
		return NULL;

	/*2.链表不为空,建立遍历指针*/
	Node* p = head;
	Node* pre = NULL;

	/*3.遍历链表,查找值为 x 的结点*/
	while (p)
	{	//找到了,删除结点
		if (p->data == x)
		{
			if (p == head)
				head = head->next;
			else
				pre->next = p->next;
			free(p);

			//遍历下一个 结点
			if (pre == NULL)
				p = head;
			else
				p = pre->next;
		}
		else
		{	//没找到,查找下一个元素
			pre = p;	//保存前一个结点
			p = p->next;
		}
	}

	return head;
}

//销毁一个链表
Node* Destory_List(Node* head)
{
	/*1.判空*/
	if (head == NULL)
	{
		printf("\n空链表!\n");
		return NULL;
	}
		

	/*2.链表不为空,开始删除*/
	Node* p = head;
	while (p)
	{
		head = head->next;
		free(p);
		p = head;
	}
	printf("\n删除完成!\n");
	return NULL;
}

//递归返回单链表的数目
int Get_List_Length(Node* head)
{
	if (head == NULL)
		return 0;
	return 1 + Get_List_Length(head->next);
}

//创建一个升序链表
Node* Create_Sort_List()
{
	/*1.创建头指针*/
	Node* head = NULL;

	/*2.输入数据*/
	int value;
	printf("请输入链表:\n");
	while (1)
	{
		scanf_s("%d", &value);
		if (value == 0)
			break;

		/*3.创建结点*/
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->next = NULL;
		newNode->data = value;

		/*4.创建遍历指针*/
		Node* p = head;
		Node* pre = NULL;

		/*4.插入判断*/
		if (head == NULL)
		{	//链表为空,新节点作为链表的头结点
			head = newNode;
		}
		else
		{	//链表不为空,遍历链表,插入新节点到链表中
			while (p)
			{
				if (p->data > newNode->data)	//找到了,插入到 p 前面
				{
					if (p == head)
					{	//插入到 第一个元素的位置
						newNode->next = head;
						head = newNode;
					}
					else
					{	//插入到 pre 的后面,p 的前面
						pre->next = newNode;
						newNode->next = p;
					}
					break;
				}
				else
				{	//没有找到,后面还有结点
					if (p->next != NULL)
					{
						pre = p;
						p = p->next;
					}
					else
					{	//后面没有结点
						p->next = newNode;
						break;
					}
					
				}
			}
		}
	}
	return head;
}

//递归升序插入
Node* Sort_Insert_List(Node* head, Elemtype x)
{
	/*1.判空,若链表为空,则直接将新节点作为头节点返回*/
	if (head == NULL)
	{	//链表为空
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->data = x;
		newNode->next = NULL;
		head = newNode;
		return head;
	}
	
	/*2.插入位置寻找*/
	if (head->data < x)
	{
		head->next = Sort_Insert_List(head->next, x);
		return head;
	}
	else
	{
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->data = x;
		newNode->next = NULL;

		/*3.使用头插法将 新节点插入到链表中*/
		newNode->next = head;
		head = newNode;
		return head;
	}
}

//就地逆序一个单链表 (不申请额外的空间)
Node* Reverse_List(Node* head)
{
	/*1.判空*/
	if (head == NULL)
		return NULL;

	/*2.建立遍历指针和标志指针*/
	Node* p = head;
	Node* h = NULL;

	/*3.反转链表*/
	while (p)
	{
		/*1.将链表的第一个元素摘下来*/
		head = head->next;
		p->next = NULL;

		/*2.按照头插法,将摘下来的结点加入到新的链表中*/
		p->next = h;
		h = p;

		/*3.摘下一个结点*/
		p = head;
	}
	return h;
}

//打印链表
void Print_List(Node* head)
{
	printf("\n打印链表:\n");
	printf("---------------------------\n");
	
	/*1.判空*/
	if (head == NULL)
	{
		printf("空链表!");
	}
	else
	{
		/*2.链表不为空,遍历链表*/
		Node* p = head;
		while (p)
		{
			printf("%d ", p->data);
			p = p->next;
		}
	}
	printf("\n---------------------------\n");
}

int main()
{
	Node* head = NULL;
	int x;
	int a;
	printf("使用头插法建立链表\n");
	head = Create_List_Head();
	Print_List(head);

	printf("\n销毁这个链表");
	head = Destory_List(head);
	Print_List(head);

	printf("\n使用尾插法建立链表");
	head = Create_List_Tail(head);
	Print_List(head);

	
	printf("\n请输入您需要删除的结点:");
	scanf_s("%d", &x);
	printf("\n删除第一个值为 %d 的结点", x);
	head = Delete_Frist_Value_Is_x(head, x);
	Print_List(head);
	
	printf("\n删除所有值为 %d 的结点", x);
	head = Delete_Value_Is_x(head, x);
	Print_List(head);

	int length = Get_List_Length(head);
	printf("此时链表中的元素个数为:%d",length);
	printf("\n销毁这个链表");
	head = Destory_List(head);
	Print_List(head);

	printf("\n创建一个升序链表");
	head = Create_Sort_List();
	Print_List(head);
	printf("\n请输入您需要插入的结点:");
	scanf_s("%d", &x);
	printf("\n升序插入结点: %d", x);
	head = Sort_Insert_List(head, x);
	Print_List(head);
	
	printf("\n请输入您需要插入的结点位置:");
	scanf_s("%d", &a);
	printf("\n请输入您需要插入的结点:");
	scanf_s("%d", &x);
	printf("\n在 %d 的前面插入结点 %d ", a, x);
	head = ADD_a_Insert(head, a, x);
	Print_List(head);

	printf("\n销毁这个链表");
	head = Destory_List(head);
	Print_List(head);
	return 0;
}

2.带头节点的单链表

(1)定义数据结构

1.结点类型
typedef int Elemtype;
typedef struct Node
{
	Elemtype data;	//数据结点,存储数据
	struct Node* next;	//指针域,指向下一个结点
}Node;
  2.头节点类型
typedef struct ListNode
{
	Node* frist;	//指向链表的第一个结点
	Node* last;	//指向链表的最后一个结点
	int length;	//保存链表长度
}List;
        注意:

头节点的定义有多种类型

(1)数据结点做头节点,头节点数据域不保存数据

(2)数据节点做头节点,头节点数据域存储链表长度

(3)建立新的结点类型,保存指向链表第一个元素的 frist 指针,指向链表最后一个节点的 last 指针,保存链表长度的 length 变量。

我都代码使用的是第三种类型。使用什么样的头节点无关紧要,能够达到要求就可以。

(2)函数列表

1.创建头节点
List* Create_List_With_Head()
{
	List* L = (List*)malloc(sizeof(List));
	//初始化参数
	L->frist = NULL;
	L->last = NULL;
	L->length = 0;
}
2.尾插法建立链表
//尾插法建立链表
List* Tail_Insert_List(List*L,Elemtype x)
{
	//1.判空
	if (L == NULL)
	{	//头节点未初始化
		L = Create_List_With_Head();
	}

	/*2.创建新节点,并初始化*/
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = x;
	newNode->next = NULL;

	/*3.使用尾插法插入新结点到链表中*/
	if (L->frist == NULL)
	{	//链表为空时
		L->frist = newNode;
		L->last = newNode;
	}
	else
	{	//链表不为空
		L->last->next = newNode;
		L->last = newNode;
	}
	//更新结点个数
	L->length++;
}
3.头插法建立链表
//使用头插法建立链表
List* Head_Insert_List(List* L, Elemtype x)
{
	/*1.判空*/
	if (L == NULL)
		L = Create_List_With_Head();

	/*2.创建新节点,并初始化*/
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->next = NULL;
	newNode->data = x;

	/*3.使用头插法插入结点*/
	if (L->frist == NULL)
	{
		L->frist = newNode;
		L->last = newNode;
	}
	else
	{
		newNode->next = L->frist;
		L->frist = newNode;
	}
	L->length++;
	return L;
}
4.升序插入元素
//有序--升序插入元素到链表
List* Sort_Insert_List(List* L, Elemtype x)
{
	/*1.判空*/
	if (L == NULL)
		return;

	/*2.创建新结点并,并初始化*/
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = x;
	newNode->next = NULL;

	/*3.寻找插入位置*/
	Node* p = L->frist;
	Node* pre = NULL;
	while (p)
	{
		if (p->data > x)	//插入到 p 前面的位置
			break;
		else
		{	//没有找到,更新 p 和 pre 的位置
			pre = p;
			p = p->next;
		}
	}

	/*4.插入结点到链表中*/
	if (p == L->frist)
	{	//要插入到 第一个元素的位置
		p->next = L->frist;
		L->frist = newNode;
	}
	else if(p!=NULL)
	{	//要插入到 中间节点的位置
		newNode->next = p;
		pre->next = newNode;
	}
	else
	{
		//没有找到比 x 大的元素,插入到链表的末尾
		pre->next = newNode;
	}
	return L;
}
5.删除全部值为 x 的结点
//在链表中查找值为 x 的结点,并将其全部删除,若没找到就不删除
void Delete_Value_Is_x(List* L, Elemtype x)
{
	/*1.判空*/
	if (L == NULL || L->frist == NULL)
		return;

	/*2.创建两个个遍历链表的指针,用于寻找删除结点*/
	Node* p = L->frist;	//指向当前结点
	Node* pre = NULL;	//指向 p 的上一个结点

	/*3.遍历链表*/
	while (p)
	{
		if (p->data == x)
		{	//找到了,p为需要删除的结点
			/*判断结点的位置*/
			if (p == L->frist)
			{	//要删除的是 头节点
				L->frist = p->next;
			}
			else if (p->next == NULL)
			{	//要删除的是 尾结点
				L->last = pre;
				pre->next = NULL;
			}
			else
			{	//要删除的是中间结点
				pre->next = p->next;
			}
			//释放 p
			free(p);
			p = NULL;
		}
		else
		{	//没有找到
			pre = p;
			p = p->next;
		}
	}
}
 6.升序排序
//对链表进行排序--升序
void Sort_List(List* L)
{
	/*1.判空*/
	if (L == NULL || L->frist == NULL)
		return;

	/*2.排序*/
	Node* pnew = L->frist;	//有序链表
	Node* pold = L->frist->next;	//无序链表
	Node* p = NULL;
	Node* pre = NULL;
	

	while (pold!=NULL)
	{
		/*3.复位*/
		p = pold;
		pold = pold->next;
		pnew = L->frist;
		pre = L->frist;

		/*4.查找插入位置*/
		while (pnew != p)
		{
			if (pnew->data > p->data)
				break;
			else
			{
				pre = pnew;
				pnew = pnew->next;
			}
		}

		/*5.插入节点*/
		if (pnew == L->frist)
		{	//插入到有序表的表头
			pnew->next = pold;
			p->next = pnew;
			L->frist = p;
		}
		else if (pnew != p)
		{
			pnew->next = pold;
			p->next = pnew;
			pre->next = p;
		}
		else;
	}
}
7.查找值为 x 的结点,将 x 改为 a
//在链表中查找值为 x 的结点,将其所有的值都改为 a ,若没有找到,则不修改
void Update_Value_Is_x(List* L,Elemtype x,Elemtype a)
{
	/*1.判空*/
	if (L == NULL || L->frist == NULL)
		return;

	/*2.寻找值为 x 的结点*/
	Node* p = L->frist;
	while (p)
	{
		if (p->data == x)
			p->data = a;
		else
			p = p->next;
	}
}
8.销毁链表
//销毁链表
List* Destory_List(List* L)
{
	if (L == NULL)
		return NULL;
	Node* p;
	while (L->frist)
	{
		p = L->frist;
		L->frist = p->next;
		free(p);
		p = NULL;
	}
	free(L);
	L = NULL;
	return NULL;
}
9.打印链表
//循环打印链表
void Print_List(List* L)
{	
	printf("\n打印链表:");
	
	/*1.判空*/
	if (L == NULL || L->frist == NULL)
	{
		printf("空链表!\n");
		return;
	}

	/*2.遍历输出*/
	Node* p = L->frist;
	printf("\n----------------------------\n");
	while (p!=NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n----------------------------\n");
}

(3)完整代码

#include<stdio.h>

typedef int Elemtype;
typedef struct Node
{
	Elemtype data;	//数据结点,存储数据
	struct Node* next;	//指针域,指向下一个结点
}Node;

typedef struct ListNode
{
	Node* frist;	//指向链表的第一个结点
	Node* last;	//指向链表的最后一个结点
	int length;	//保存链表长度
}List;

//创建头节点
List* Create_List_With_Head()
{
	List* L = (List*)malloc(sizeof(List));
	//初始化参数
	L->frist = NULL;
	L->last = NULL;
	L->length = 0;
}

//尾插法建立链表
List* Tail_Insert_List(List*L,Elemtype x)
{
	//1.判空
	if (L == NULL)
	{	//头节点未初始化
		L = Create_List_With_Head();
	}

	/*2.创建新节点,并初始化*/
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = x;
	newNode->next = NULL;

	/*3.使用尾插法插入新结点到链表中*/
	if (L->frist == NULL)
	{	//链表为空时
		L->frist = newNode;
		L->last = newNode;
	}
	else
	{	//链表不为空
		L->last->next = newNode;
		L->last = newNode;
	}
	//更新结点个数
	L->length++;
}

//使用头插法建立链表
List* Head_Insert_List(List* L, Elemtype x)
{
	/*1.判空*/
	if (L == NULL)
		L = Create_List_With_Head();

	/*2.创建新节点,并初始化*/
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->next = NULL;
	newNode->data = x;

	/*3.使用头插法插入结点*/
	if (L->frist == NULL)
	{
		L->frist = newNode;
		L->last = newNode;
	}
	else
	{
		newNode->next = L->frist;
		L->frist = newNode;
	}
	L->length++;
	return L;
}

//有序--升序插入元素到链表
List* Sort_Insert_List(List* L, Elemtype x)
{
	/*1.判空*/
	if (L == NULL)
		return;

	/*2.创建新结点并,并初始化*/
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = x;
	newNode->next = NULL;

	/*3.寻找插入位置*/
	Node* p = L->frist;
	Node* pre = NULL;
	while (p)
	{
		if (p->data > x)	//插入到 p 前面的位置
			break;
		else
		{	//没有找到,更新 p 和 pre 的位置
			pre = p;
			p = p->next;
		}
	}

	/*4.插入结点到链表中*/
	if (p == L->frist)
	{	//要插入到 第一个元素的位置
		p->next = L->frist;
		L->frist = newNode;
	}
	else if(p!=NULL)
	{	//要插入到 中间节点的位置
		newNode->next = p;
		pre->next = newNode;
	}
	else
	{
		//没有找到比 x 大的元素,插入到链表的末尾
		pre->next = newNode;
	}
	return L;
}

//在链表中查找值为 x 的结点,并将其全部删除,若没找到就不删除
void Delete_Value_Is_x(List* L, Elemtype x)
{
	/*1.判空*/
	if (L == NULL || L->frist == NULL)
		return;

	/*2.创建两个个遍历链表的指针,用于寻找删除结点*/
	Node* p = L->frist;	//指向当前结点
	Node* pre = NULL;	//指向 p 的上一个结点

	/*3.遍历链表*/
	while (p)
	{
		if (p->data == x)
		{	//找到了,p为需要删除的结点
			/*判断结点的位置*/
			if (p == L->frist)
			{	//要删除的是 头节点
				L->frist = p->next;
			}
			else if (p->next == NULL)
			{	//要删除的是 尾结点
				L->last = pre;
				pre->next = NULL;
			}
			else
			{	//要删除的是中间结点
				pre->next = p->next;
			}
			//释放 p
			free(p);
			p = NULL;
		}
		else
		{	//没有找到
			pre = p;
			p = p->next;
		}
	}
}

//对链表进行排序--升序
void Sort_List(List* L)
{
	/*1.判空*/
	if (L == NULL || L->frist == NULL)
		return;

	/*2.排序*/
	Node* pnew = L->frist;	//有序链表
	Node* pold = L->frist->next;	//无序链表
	Node* p = NULL;
	Node* pre = NULL;
	

	while (pold!=NULL)
	{
		/*3.复位*/
		p = pold;
		pold = pold->next;
		pnew = L->frist;
		pre = L->frist;

		/*4.查找插入位置*/
		while (pnew != p)
		{
			if (pnew->data > p->data)
				break;
			else
			{
				pre = pnew;
				pnew = pnew->next;
			}
		}

		/*5.插入节点*/
		if (pnew == L->frist)
		{	//插入到有序表的表头
			pnew->next = pold;
			p->next = pnew;
			L->frist = p;
		}
		else if (pnew != p)
		{
			pnew->next = pold;
			p->next = pnew;
			pre->next = p;
		}
		else;
	}
}

//在链表中查找值为 x 的结点,将其所有的值都改为 a ,若没有找到,则不修改
void Update_Value_Is_x(List* L,Elemtype x,Elemtype a)
{
	/*1.判空*/
	if (L == NULL || L->frist == NULL)
		return;

	/*2.寻找值为 x 的结点*/
	Node* p = L->frist;
	while (p)
	{
		if (p->data == x)
			p->data = a;
		else
			p = p->next;
	}
}

//销毁链表
List* Destory_List(List* L)
{
	if (L == NULL)
		return NULL;
	Node* p;
	while (L->frist)
	{
		p = L->frist;
		L->frist = p->next;
		free(p);
		p = NULL;
	}
	free(L);
	L = NULL;
	return NULL;
}

//循环打印链表
void Print_List(List* L)
{	
	printf("\n打印链表:");
	
	/*1.判空*/
	if (L == NULL || L->frist == NULL)
	{
		printf("空链表!\n");
		return;
	}

	/*2.遍历输出*/
	Node* p = L->frist;
	printf("\n----------------------------\n");
	while (p!=NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n----------------------------\n");
}

int main()
{
	int x, a;
	//创建头节点
	List* L = Create_List_With_Head();

	//尾插法建立链表
	printf("请输入结点的值:\n");
	while (1)
	{
		int x;
		scanf_s("%d", &x);
		if (x == 0)
			break;
		L = Tail_Insert_List(L, x);
	}
	//打印链表
	Print_List(L);

	printf("头插法插入结点\n请输入你需要插入到结点的值:");
	scanf_s("%d", &x);
	L = Head_Insert_List(L, x);
	//打印链表
	Print_List(L);

	//升序排序链表
	printf("\n对链表进行升序排序");
	Sort_List(L);
	//打印链表
	Print_List(L);

	printf("\n使用升序插入,插入一个结点\n请输入您需要插入到节点的值:\n");
	scanf_s("%d", &x);
	Sort_Insert_List(L, x);
	//打印链表
	Print_List(L);

	//查找值
	printf("\n请输入要更改的元素值:");
	scanf_s("%d", &x);
	printf("\n请输入替换后的元素的值:");
	scanf_s("%d", &a);
	Update_Value_Is_x(L, x, a);
	//打印链表
	Print_List(L);

	//删除值
	printf("\n请输入需要删除的元素的值:");
	scanf_s("%d", &x);
	Delete_Value_Is_x(L, x);
	//打印链表
	Print_List(L);

	printf("\n销毁链表");
	//销毁链表
	L = Destory_List(L);
	//打印链表
	Print_List(L);
}

3.双向链表

(1)数据结构

注意:我才用的双向链表是带头节点的双向链表,包含了指向 第一个元素的指针 frist 和指向最后一个元素的 last 指针。

typedef int Elemtype;
typedef struct Node
{
	Elemtype data;
	struct Node* next;	//指向上一个结点
	struct Node* prev;	//指向下一个结点
}DNode;

typedef struct DList
{
	DNode* first;
	DNode* last;
	int length;
}DList;

(2)函数列表

1.初始化头节点
//初始化头结点
DList* Init_DList()
{
	DList* DL = (DList*)malloc(sizeof(DList));
	DL->first = NULL;
	DL->last = NULL;
	DL->length = 0;
	return DL;
}
2.打印双向链表

注意:这里我使用的是双向打印,这样可以查看双向链表是否准确连接。

//打印双向链表
void Print_DList(DList* DL)
{
	printf("\n打印双向链表:\n");
	printf("---------------------\n");
	if (DL == NULL || DL->first == NULL)
	{
		printf("空链表!");
	}
	else
	{
		DNode* p = DL->first;
		while (p)
		{
			printf("%d ", p->data);
			p = p->next;
		}
		p = DL->last;
		printf("\n");
		while (p)
		{
			printf("%d ", p->data);
			p = p->prev;
		}
	}
	printf("\n---------------------\n");
}
3.尾插法插入数据
//尾插法建立链表
DList* Tail_Insert_DList(DList* DL,Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
	{
		DL = Init_DList();
	}


	/*3.创建节点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;

	/*4.插入结点到链表中*/
	if (DL->first == NULL)
	{	//空链表,新节点作为第一个元素
		DL->first = newNode;
		DL->last = newNode;
	}
	else
	{	//链表不为空,使用尾插法插入结点
		DL->last->next = newNode;
		newNode->prev = DL->last;
		DL->last = newNode;
	}
	DL->length++;
	return DL;
}
4.头插法插入数据
//头插法插入数据
DList* Head_Insert_DList(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
		DL = Init_DList();

	/*2.创建新结点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;

	if (DL->first == NULL)
	{	//空链表
		DL->first = newNode;
		DL->last = newNode;
	}
	else
	{
		DL->first->prev = newNode;
		newNode->next = DL->first;
		DL->first = newNode;
	}
	DL->length++;
	return DL;
}
5.有序插入

注意:有序插入需要考虑插入位置,分别为:头插、中间插、尾插。在进行头插时,需要更新原链表的第一个结点的 prev 指针指向新结点。在进行尾插时,要更新原链表的尾结点的 next 指针指向新节点。

//有序插入
DList* Sort_Insert_DList(DList* DL, Elemtype x)
{
	if (DL == NULL)
	{	//空链表
		DL = Init_DList();
	}

	/*创建新节点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;

	/*寻找插入位置*/
	DNode* p = DL->first;
	
	/*开始遍历*/
	while (p)
	{
		if (p->data > x)
			break;
		else
			p = p->next;
	}

	//查找结束,将新节点插入到 p 的前面
	if (p == DL->first)
	{	//插入到头节点后面
		DL->first->prev = newNode;
		newNode->next = DL->first;
		DL->first = newNode;
	}
	else if(p)
	{	//插入到中间结点的位置
		p->prev->next = newNode;
		newNode->prev = p->prev;
		newNode->next = p;
		p->prev = newNode;
	}
	else
	{	//p == null 或则 没有找打比他大的结点,插入到末尾的位置
		newNode->prev = DL->last;
		DL->last->next = newNode;
		DL->last = newNode;
	}
	DL->length++;
	return DL;
}
6.函数指针创建链表

注意:这里其实没有必要使用函数指针,只是有意练习。

使用函数指针,将一个指向返回值为 DList*  参数列表为:DList*,Elemtype 类型的指针变量作为参数,可以通过一段代码,更改函数调用,来达到一段代码实现头插和尾插两个功能,减少了代码的冗余。

//创建链表	函数指针
DList* Create_DList(DList* (*func)(DList*, Elemtype))
{
	DList* DL = Init_DList();
	Elemtype value;
	printf("\n请输入链表:\n");
	while(1)
	{
		scanf_s("%d", &value);
		if (value == 0)
			break;
		DL = func(DL, value);
	}
	return DL;
}
7.删除所有值为 x 的结点

注意:删除结点和插入结点时一样,在找到删除位置之后,需要对删除的结点进行判断,如果是首位结点,则需要更新 prev next 指针,防止指针指向不可访问的内存。

//删除所有值为 x 的结点
void Delete_Value_Is_x(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL || DL->first == NULL)
		return;
	
	/*2.创建遍历指针*/
	DNode* p = DL->first;

	/*3.开始遍历链表*/
	while (p)
	{
		if (p->data == x)	//找到了,删除当前结点
		{
			/*4.判断需要删除的结点的位置*/
			if (p == DL->first)
			{	//删除第一个结点
				DL->first = p->next;
				DL->first->prev = NULL;
				free(p);
				p = NULL;
				//将 p 指向下一个需要遍历的结点
				p = DL->first;
			}
			else if (p->next != NULL)
			{	//要删除的是中间结点
				DNode* pre = p->next;
				p->next->prev = p->prev;
				p->prev->next = p->next;
				free(p);
				p = NULL;
				p = pre;
			}
			else
			{	//要删除的是尾结点
				p->prev->next = NULL;
				DL->last = p->prev;
				free(p);
				p = NULL;

			}
			DL->length--;
		}
		else
		{	//没有找到,遍历下一个结点
			p = p->next;
		}
	}
}
8.销毁链表
//销毁链表
DList* Destory_DList(DList* DL)
{
	if (DL == NULL)
	{
		printf("\n空链表!\n");
		return;
	}
	DNode* p = DL->first;
	while (p)
	{
		p = DL->first;
		DL->first = p->next;
		free(p);
		p = NULL;
		DL->length--;
	}
	free(DL);
	DL = NULL;
	return DL;
}

(3)完整代码

#include <stdio.h>
typedef int Elemtype;
typedef struct Node
{
	Elemtype data;
	struct Node* next;	//指向上一个结点
	struct Node* prev;	//指向下一个结点
}DNode;

typedef struct DList
{
	DNode* first;
	DNode* last;
	int length;
}DList;

//初始化头结点
DList* Init_DList()
{
	DList* DL = (DList*)malloc(sizeof(DList));
	DL->first = NULL;
	DL->last = NULL;
	DL->length = 0;
	return DL;
}

//打印双向链表
void Print_DList(DList* DL)
{
	printf("\n打印双向链表:\n");
	printf("---------------------\n");
	if (DL == NULL || DL->first == NULL)
	{
		printf("空链表!");
	}
	else
	{
		DNode* p = DL->first;
		while (p)
		{
			printf("%d ", p->data);
			p = p->next;
		}
		p = DL->last;
		printf("\n");
		while (p)
		{
			printf("%d ", p->data);
			p = p->prev;
		}
	}
	printf("\n---------------------\n");
}

//尾插法建立链表
DList* Tail_Insert_DList(DList* DL,Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
	{
		DL = Init_DList();
	}


	/*3.创建节点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;

	/*4.插入结点到链表中*/
	if (DL->first == NULL)
	{	//空链表,新节点作为第一个元素
		DL->first = newNode;
		DL->last = newNode;
	}
	else
	{	//链表不为空,使用尾插法插入结点
		DL->last->next = newNode;
		newNode->prev = DL->last;
		DL->last = newNode;
	}
	DL->length++;
	return DL;
}

//头插法插入数据
DList* Head_Insert_DList(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
		DL = Init_DList();

	/*2.创建新结点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;

	if (DL->first == NULL)
	{	//空链表
		DL->first = newNode;
		DL->last = newNode;
	}
	else
	{
		DL->first->prev = newNode;
		newNode->next = DL->first;
		DL->first = newNode;
	}
	DL->length++;
	return DL;
}

//有序插入
DList* Sort_Insert_DList(DList* DL, Elemtype x)
{
	if (DL == NULL)
	{	//空链表
		DL = Init_DList();
	}

	/*创建新节点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;

	/*寻找插入位置*/
	DNode* p = DL->first;
	
	/*开始遍历*/
	while (p)
	{
		if (p->data > x)
			break;
		else
			p = p->next;
	}

	//查找结束,将新节点插入到 p 的前面
	if (p == DL->first)
	{	//插入到头节点后面
		DL->first->prev = newNode;
		newNode->next = DL->first;
		DL->first = newNode;
	}
	else if(p)
	{	//插入到中间结点的位置
		p->prev->next = newNode;
		newNode->prev = p->prev;
		newNode->next = p;
		p->prev = newNode;
	}
	else
	{	//p == null 或则 没有找打比他大的结点,插入到末尾的位置
		newNode->prev = DL->last;
		DL->last->next = newNode;
		DL->last = newNode;
	}
	DL->length++;
	return DL;
}

//创建链表	函数指针
DList* Create_DList(DList* (*func)(DList*, Elemtype))
{
	DList* DL = Init_DList();
	Elemtype value;
	printf("\n请输入链表:\n");
	while(1)
	{
		scanf_s("%d", &value);
		if (value == 0)
			break;
		DL = func(DL, value);
	}
	return DL;
}

//删除所有值为 x 的结点
void Delete_Value_Is_x(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL || DL->first == NULL)
		return;
	
	/*2.创建遍历指针*/
	DNode* p = DL->first;

	/*3.开始遍历链表*/
	while (p)
	{
		if (p->data == x)	//找到了,删除当前结点
		{
			/*4.判断需要删除的结点的位置*/
			if (p == DL->first)
			{	//删除第一个结点
				DL->first = p->next;
				DL->first->prev = NULL;
				free(p);
				p = NULL;
				//将 p 指向下一个需要遍历的结点
				p = DL->first;
			}
			else if (p->next != NULL)
			{	//要删除的是中间结点
				DNode* pre = p->next;
				p->next->prev = p->prev;
				p->prev->next = p->next;
				free(p);
				p = NULL;
				p = pre;
			}
			else
			{	//要删除的是尾结点
				p->prev->next = NULL;
				DL->last = p->prev;
				free(p);
				p = NULL;

			}
			DL->length--;
		}
		else
		{	//没有找到,遍历下一个结点
			p = p->next;
		}
	}
}

//销毁链表
DList* Destory_DList(DList* DL)
{
	if (DL == NULL)
	{
		printf("\n空链表!\n");
		return;
	}
	DNode* p = DL->first;
	while (p)
	{
		p = DL->first;
		DL->first = p->next;
		free(p);
		p = NULL;
		DL->length--;
	}
	free(DL);
	DL = NULL;
	return DL;
}

int main() {
	DList* myDList = NULL;  // 初始化一个空的双向链表

	// 测试尾插法建立链表
	printf("插入数据:1,2,3\n");
	myDList = Tail_Insert_DList(myDList, 1);
	myDList = Tail_Insert_DList(myDList, 2);
	myDList = Tail_Insert_DList(myDList, 3);
	Print_DList(myDList);

	// 测试头插法插入数据
	printf("头插法插入数据:0\n");
	myDList = Head_Insert_DList(myDList, 0);
	Print_DList(myDList);

	// 测试有序插入
	printf("有序插入数据:5,4,6\n");
	myDList = Sort_Insert_DList(myDList, 5);
	myDList = Sort_Insert_DList(myDList, 4);
	myDList = Sort_Insert_DList(myDList, 6);
	Print_DList(myDList);

	// 测试删除值为 x 的结点
	printf("删除结点0,3,6\n");
	Delete_Value_Is_x(myDList, 0);
	Delete_Value_Is_x(myDList, 3);
	Delete_Value_Is_x(myDList, 6);
	Print_DList(myDList);

	// 销毁链表
	printf("销毁链表\n");
	myDList = Destory_DList(myDList);

	// 再次测试空链表情况
	Print_DList(myDList);

	// 测试创建链表函数指针
	printf("使用函数指针,用尾插法创建链表");
	myDList = Create_DList(Tail_Insert_DList);
	Print_DList(myDList);

	// 测试创建链表后销毁
	printf("销毁链表\n");
	myDList = Destory_DList(myDList);
	Print_DList(myDList);
	return 0;
}

(4)运行结果


4.带头结点的单循环链表

(1)数据结构

//定义数据结构
typedef int Elemtype;
typedef struct DNode
{
	Elemtype data;
	struct DNode* next;
}DNode;

//创建头节点
typedef struct List
{
	DNode* frist;
	DNode* last;
	int length;
}DList;

(2)函数列表

1.初始化头节点

注意:本段代码采用的是带头节点的实现,在链表为空且头结点存在时,认为 frist 指针和 last 指针都指向空,这两个指针都默认初始化为空值。

//初始化头节点
DList* Init_List_Head()
{
	DList* DL = (DList*)malloc(sizeof(DList));
	DL->frist = NULL;
	DL->last = NULL;
	DL->length = 0;
	return DL;
}
2.尾插函数

注意:在第一次插入时,首结点的 next 指针指向自己本身,在链表不为空时,需要更新尾指针,以及尾指针重新指向首结点。

//尾插
DList* Tail_Insert_List(DList* DL,Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
		DL = Init_List_Head();

	/*2.创建新节点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;

	/*3.尾插法插入结点*/
	if (DL->frist == NULL)
	{	//链表为空,新节点作为首尾结点
		DL->frist = newNode;
		DL->last = newNode;
		DL->frist->next = newNode;
	}
	else
	{	//链表不为空
		DL->last->next = newNode;
		newNode->next = DL->frist;
		DL->last = newNode;
	}
	DL->length++;
	return DL;
}
 3.头插函数
//头插
DList* Head_Insert_List(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
		DL = Init_List_Head();

	/*2.创建新节点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;

	/*3.头插法插入结点*/
	if (DL->frist == NULL)
	{
		//链表为空,新节点作为首尾结点
		DL->frist = newNode;
		DL->last = newNode;
		DL->frist->next = newNode;
	}
	else
	{
		//链表不为空
		newNode->next = DL->frist;
		DL->last->next = newNode;
		DL->frist = newNode;
	}
	DL->length++;
	return DL;
}
  4.升序插入函数

注意:因为是循环链表,所以当链表不为空时,一般不能将 p == NULL 作为循环结束条件,这会造成死循环。也不能将 p == DL->frist 作为循环结束条件,因为 p 的初值为 DL->frist ,这会导致循环刚开始就结束。

我的思路是先将循环链表断开,变成熟悉的单链表模型,再进行操作

也可以使用 DL->length 因为链表长度是已知的。可以使用循环,控制循环次数为 length 来遍历链表

//升序插入
DList* Sort_Insert_List(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
		DL = Init_List_Head();

	/*2.建立结点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;

	/*3.建立遍历结点*/
	DNode* p = DL->frist;
	DNode* pre = NULL;

	/*4.断开循环链表首尾结点的连接*/
	DL->last->next = NULL;

	/*5.查找插入位置*/
	while (p)
	{
		if (p->data > x)
		{	//找到了,插入到 p 的前面
			break;
		}
		else
		{
			pre = p;
			p = p->next;
		}
	}
	
	/*6.判断插入位置*/
	if (p == DL->frist)
	{	//头插
		newNode->next = DL->frist;
		DL->frist = newNode;
	}
	else if (p != NULL)
	{	//插入到中间结点
		pre->next = newNode;
		newNode->next = p;
	}
	else
	{	//没有找到,newNode值 为最大的结点,插入到链表末端
		DL->last->next = newNode;
	}
	DL->length++;

	/*7.重新连接断开的链表*/
	DL->last->next = DL->frist;
	return DL;
}
5.删除所有值为 x 的结点

注意:删除结点后,重新连接 last 指针的 next 指向时,需要对链表进行判空操作,若链表为空,使用 DL->frist->next 时会造成空指针的使用,会发生段错误,所以,删除后需要对链表进行判空。

//删除所有值为 x 的结点
void Delete_All_Value_Is_x(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL || DL->frist == NULL)
		return;
	
	/*2.设置遍历指针*/
	DNode* p = DL->frist;
	DNode* pre = NULL;

	/*3.断开循环链表*/
	DL->last->next = NULL;

	/*4.遍历链表*/
	while (p)
	{
		if (p->data == x)
		{	//找到了,删除 p 结点
			/*5.删除位置判断*/
			if (p == DL->frist)
			{	//头删
				DL->frist = p->next;
				free(p);
				p = NULL;
				p = DL->frist;
			}
			else
			{	//中间删除
				pre->next = p->next;
				free(p);
				p = NULL;
				p = pre->next;
			}
			DL->length--;
		}
		else
		{	//没有找到,遍历下一个结点
			pre = p;
			p = p->next;
		}
	}

	/*6.重新连接循环链表*/
	if (DL->frist != NULL)
	{	//链表不为空
		DL->last->next = DL->frist;
	}
	else
	{
		DL->frist = NULL;
		DL->last = NULL;
	}
}
6.销毁链表
//销毁链表
DList* Destory_DList(DList* DL)
{
	/*1.判空*/
	if (DL == NULL)
		return NULL;

	/*2.判空,防止使用空指针*/
	if (DL->last != NULL)
	{
		DL->last->next = NULL;
	}

	/*3.建立遍历指针*/
	DNode* p = DL->frist;

	/*4.遍历链表,逐个释放结点*/
	while (p)
	{
		p = DL->frist;
		DL->frist = p->next;
		free(p);
		p = NULL;
	}
	
	/*5.释放头节点*/
	free(DL);
	DL = NULL;
	return DL;
}
    7.打印链表
//打印链表
void Print_DList(DList* DL)
{
	printf("\n打印链表\n");
	printf("----------------------\n");
	if (DL == NULL || DL->frist == NULL)
		printf("空链表!");
	else
	{
		DNode* p = DL->frist;
		int len = DL->length;
		for (int i = 0; i < len; i++)
		{
			printf("%d ",p->data);
			p = p->next;
		}
	}
	printf("\n----------------------\n");
}

(3)完整代码

#include<stdio.h>

//定义数据结构
typedef int Elemtype;
typedef struct DNode
{
	Elemtype data;
	struct DNode* next;
}DNode;

//创建头节点
typedef struct List
{
	DNode* frist;
	DNode* last;
	int length;
}DList;

//初始化头节点
DList* Init_List_Head()
{
	DList* DL = (DList*)malloc(sizeof(DList));
	DL->frist = NULL;
	DL->last = NULL;
	DL->length = 0;
	return DL;
}

//尾插
DList* Tail_Insert_List(DList* DL,Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
		DL = Init_List_Head();

	/*2.创建新节点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;

	/*3.尾插法插入结点*/
	if (DL->frist == NULL)
	{	//链表为空,新节点作为首尾结点
		DL->frist = newNode;
		DL->last = newNode;
		DL->frist->next = newNode;
	}
	else
	{	//链表不为空
		DL->last->next = newNode;
		newNode->next = DL->frist;
		DL->last = newNode;
	}
	DL->length++;
	return DL;
}

//头插
DList* Head_Insert_List(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
		DL = Init_List_Head();

	/*2.创建新节点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;

	/*3.头插法插入结点*/
	if (DL->frist == NULL)
	{
		//链表为空,新节点作为首尾结点
		DL->frist = newNode;
		DL->last = newNode;
		DL->frist->next = newNode;
	}
	else
	{
		//链表不为空
		newNode->next = DL->frist;
		DL->last->next = newNode;
		DL->frist = newNode;
	}
	DL->length++;
	return DL;
}

//升序插入
DList* Sort_Insert_List(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL)
		DL = Init_List_Head();

	/*2.建立结点*/
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = x;
	newNode->next = NULL;

	/*3.建立遍历结点*/
	DNode* p = DL->frist;
	DNode* pre = NULL;

	/*4.断开循环链表首尾结点的连接*/
	DL->last->next = NULL;

	/*5.查找插入位置*/
	while (p)
	{
		if (p->data > x)
		{	//找到了,插入到 p 的前面
			break;
		}
		else
		{
			pre = p;
			p = p->next;
		}
	}
	
	/*6.判断插入位置*/
	if (p == DL->frist)
	{	//头插
		newNode->next = DL->frist;
		DL->frist = newNode;
	}
	else if (p != NULL)
	{	//插入到中间结点
		pre->next = newNode;
		newNode->next = p;
	}
	else
	{	//没有找到,newNode值 为最大的结点,插入到链表末端
		DL->last->next = newNode;
	}
	DL->length++;

	/*7.重新连接断开的链表*/
	DL->last->next = DL->frist;
	return DL;
}

//删除所有值为 x 的结点
void Delete_All_Value_Is_x(DList* DL, Elemtype x)
{
	/*1.判空*/
	if (DL == NULL || DL->frist == NULL)
		return;
	
	/*2.设置遍历指针*/
	DNode* p = DL->frist;
	DNode* pre = NULL;

	/*3.断开循环链表*/
	DL->last->next = NULL;

	/*4.遍历链表*/
	while (p)
	{
		if (p->data == x)
		{	//找到了,删除 p 结点
			/*5.删除位置判断*/
			if (p == DL->frist)
			{	//头删
				DL->frist = p->next;
				free(p);
				p = NULL;
				p = DL->frist;
			}
			else
			{	//中间删除
				pre->next = p->next;
				free(p);
				p = NULL;
				p = pre->next;
			}
			DL->length--;
		}
		else
		{	//没有找到,遍历下一个结点
			pre = p;
			p = p->next;
		}
	}

	/*6.重新连接循环链表*/
	if (DL->frist != NULL)
	{	//链表不为空
		DL->last->next = DL->frist;
	}
	else
	{
		DL->frist = NULL;
		DL->last = NULL;
	}
}

//销毁链表
DList* Destory_DList(DList* DL)
{
	/*1.判空*/
	if (DL == NULL)
		return NULL;

	/*2.判空,防止使用空指针*/
	if (DL->last != NULL)
	{
		DL->last->next = NULL;
	}

	/*3.建立遍历指针*/
	DNode* p = DL->frist;

	/*4.遍历链表,逐个释放结点*/
	while (p)
	{
		p = DL->frist;
		DL->frist = p->next;
		free(p);
		p = NULL;
	}
	
	/*5.释放头节点*/
	free(DL);
	DL = NULL;
	return DL;
}

//打印链表
void Print_DList(DList* DL)
{
	printf("\n打印链表\n");
	printf("----------------------\n");
	if (DL == NULL || DL->frist == NULL)
		printf("空链表!");
	else
	{
		DNode* p = DL->frist;
		int len = DL->length;
		for (int i = 0; i < len; i++)
		{
			printf("%d ",p->data);
			p = p->next;
		}
	}
	printf("\n----------------------\n");
}

int main() {
	// 创建带头结点的循环链表
	DList* DL = Init_List_Head();

	printf("tail insert 1,3,5\n");
	// 尾插法插入一些数据
	DL = Tail_Insert_List(DL, 1);
	DL = Tail_Insert_List(DL, 3);
	DL = Tail_Insert_List(DL, 5);
	// 打印链表
	Print_DList(DL);

	printf("head insert 0,-2\n");
	// 头插法插入一些数据
	DL = Head_Insert_List(DL, 0);
	DL = Head_Insert_List(DL, -2);
	// 打印链表
	Print_DList(DL);

	printf("sort insert 4,2\n");
	// 升序插入一些数据
	DL = Sort_Insert_List(DL, 4);
	DL = Sort_Insert_List(DL, 2);

	// 打印链表
	Print_DList(DL);

	printf("delete value is 3\n");
	// 删除值为 3 的所有结点
	Delete_All_Value_Is_x(DL, 3);
	// 打印链表
	Print_DList(DL);

	// 打印删除后的链表
	Print_DList(DL);

	// 极端测试
	printf("delete value is -2\n");
	// 删除值为 0 的所有结点
	Delete_All_Value_Is_x(DL, -2);

	printf("delete value is 5\n");
	// 删除值为 4 的所有结点
	Delete_All_Value_Is_x(DL, 5);

	// 打印删除后的链表
	Print_DList(DL);

	printf("destory list\n");
	// 销毁链表
	DL = Destory_DList(DL);
	Print_DList(DL);

	return 0;
}

(4)运行结果

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值