链表——删除元素或插入元素(头插法及尾插法)

目录

链表的结点由一个结构体构成

判断链表是否为空

键盘输入链表中的数据

输出链表中的数据

返回链表的元素个数

清空链表

 返回指定位置的元素值

查找数据所在位置

删除链表的元素

插入元素

建立无头结点的单链表

建立有头结点的单链表(头插法)

建立有头节点的单链表(尾插法)

总代码如下:


链表:

是一种在存储单元上非连续,非顺序的存储结构,由一系列结点组成,结点可以在运行的过程中动态生成,每个结点包括两部分:存储数据的数据域;存储下一节点地址的指针域

指针的理解:

将某个变量(例如 int a)赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

链表的结点由一个结构体构成

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

判断链表是否为空

如果链表头结点的指针域不为空,如果定义链表头结点是 struct node *L,也就是 L->next !=NULL.

(因为在有头结点的链表中,头结点数据域中不存储数据,它的指针域存放的是链表第一个元素的地址)

//判断链表是否为空
bool isEmptyList(struct node* L)
{
	//判断头结点指向的下一指针域不为空,说明该链表不为空
	if (L->next != NULL)
		return true;//不为空
	else
		return false;//为空
}

建立头结点:

创建头结点,为头结点分配内存(用到 malloc 函数,使用头文件),令链表的头指针为空指针(有头节点的链表)。

键盘输入链表中的数据

//输入n个元素
void putList(struct node *L, int n)
{
	struct node* p, * q;
	L->next = (struct node*)malloc(sizeof(struct node));
	q = L->next;
	printf("输入%d个元素放入链表:",n);
	for (int i = 0; i < n; i++)
	{
		int x;
		scanf("%d", &x);
		p = (struct node*)malloc(sizeof(struct node));
		p->data = x;
		p->next = NULL;
		q->next = p;
		q = p;
	}
}

输出链表中的数据

//输出链表中的数据
void printList(struct node* L)
{
	struct node* p;
	p = L->next;
	printf("当前链表元素:");
	while (p->next != NULL)
	{
		p = p->next;//工作指针后移
		printf("%d ", p->data);//输出当前结点的数据
	}
	printf("\n");
}

返回链表的元素个数

因为链表是动态分配内存,所以没有记录结点个数,此时可以从头结点开始,通过指针后移找到后面的结点,当指针域为空时,就结束循环,循环的次数就是结点个数。

//返回链表元素个数
int numList(struct node* L)
{
	int sum = 0;//计数器
	struct node* p;//定义一个工作指针,因为如果直接用L指针后移的话,这个链表就找不到了
	p = L->next;//将头指针指针域指向的地址赋给p,也就是链表的第一个元素(头指针中不存放数据)
	while (p != NULL)
	{
		p=p->next;
		sum++;
	}
	return sum;
}

清空链表

对于清空链表,是要把链表中每一个结点的空间都释放,而不是只释放头结点的空间。而且在释放空间之前,要记录它的指针域。

//清空链表
void clearList(struct node** L)
{
	struct node* p, * q;
	p = (*L)->next;//工作指针
	while (p!=NULL)
	{
		//记录将要被释放空间的结点的指针域,如果不记录,那么链表后面的结点就找不到了
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
}

 返回指定位置的元素值

用一个工作指针后移找结点,循环时计数器自增,找到指定位置,然后返回。

//返回链表的指定位置的元素
int elementList(struct node* L, int x)//x为指定位置
{
	int i = 0;//计数器
	struct node* p;
	p = L->next;
	//找到指定位置的前面那个位置,因为当刚好i==x-1时,此时进入循环,p=p->next,刚好到达结点为x的位置
	while (p != NULL && i < x-1)
	{
		p = p->next;
		i++;
	}
	if (p)
		return p->data;
	else
		return 0;
}

查找数据所在位置

用一个工作指针后移找结点,循环中计数器自增,如果找到一个结点的数据与目标值相等就返回该位置,直到循环结束,若是没有返回,就说明链表里面没有 x 元素。

//查找数据所在位置
int findList(struct node* L, int x)
{
	int i = 0;
	struct node* p;
	p = L->next;
	while (p != NULL)
	{
		if (p->data == x)//找到了直接输出该节点的序号
			return i;
		i++;
		p = p->next;
	}
	return 0;//代表没有找到数据为x的结点
}

也有更好的方法,比如说如果有多个结点的元素与 x 相等,上面这个代码就解决不了了,此时可以用一个数组记录找到的位置,结束时返回该数组的地址。

删除链表的元素

先搞清有头结点的链表和无头结点的链表之间的区别:

无头结点的单链表_没有头结点的循环单链表-CSDN博客

对于有头结点的链表就相对来说方便一些,不用考虑删除头指针的情况。

将要删除的结点赋给q,然后将 p->next 赋给 p->next.

//删除元素
bool deleteList(struct node* L, int x, int* e)//x是删除元素的结点序号,传入的地址e是用来记录删除结点的数据的
{
	struct node* p, * q;
	p = L->next;
	int i = 0;
	while (i < x - 2 && p->next != NULL)//这里循环到i<x-1,因为找到当前的结点后,是删除它后面那个元素
	{
		p = p->next;
		i++;
	}
	if (p->next == NULL)
		return false;//删除失败
	q = p->next;
	*e = q->data;
	p->next = q->next;
	return true;//删除成功
}

插入元素

首先插入键盘输入的数字作为插入的数据:

先开辟一块空间,再将值放入 p 的数据域,将其指针域赋为 NULL,每次循环都进行此操作(到了最后一个结点的时候,它的指针域就是 NULL),也可以在输入完数据后将最后一个结点的指针域赋为 NULL。

每次循环将 p 结点赋给 q 结点,下一轮循环时就将 q->next=p.

//插入元素(输入)
void PutList(struct node* L,int x,int y)
{
	struct node* q,* p,* s;
	int i=1;
	p = L;
	while (i < x && p != NULL)//找到要插入元素的位置
	{
		p = p->next;
		i++;
	}
	s = (struct node*)malloc(sizeof(struct node));
	s->data = y;//赋值
	//这两步不能反
	s->next = p->next;
	p->next = s;
}

也可以生成随机数据作为插入的数据:此时用到 rand 函数和 srand 函数(用到 #include<stdlib.h> 函数和 #include<time.h> 头文件)

//插入元素(随机)
void PutRandList(struct node** L,int x)
{
	struct node* p, * q;
	q = NULL;
	int i = 1;
	p = (*L);
	while (i < x && p != NULL)
	{
		p = p->next;
		i++;
	}
	srand(time(0));
	int s = rand()%100;
	q = (struct node*)malloc(sizeof(struct node));
	q->data = s;
	q->next = p->next;
	p->next = q;
}

建立无头结点的单链表

链表的每一个结点都有数据域,说明首元结点前没有头结点。

//创建链表(无头结点)
void creatList(struct node** L, int n)
{
	printf("输入%d个元素:",n);
	struct node* p, * q;
	*L = NULL;
	for (int i = 0; i < n; i++)
	{
		int x;
		scanf("%d", &x);
		p = (struct node*)malloc(sizeof(struct node));
		p->data = x;
		p->next = NULL;
		if (*L == NULL)//如果头结点是空的就将第一个结点赋值给头结点(因为这是无头结点)
			*L = p;
		else
			q->next = p;
		q = p;
	}
}

建立有头结点的单链表(头插法)

建立一个带头结点的链表,为结点 p 开辟一块空间,然后将生成的随机数赋值给 p 的数据域,将 p 插入到表头,循环执行。

//建立有头结点的单链表(头插法)
void CreateListHead(struct node* L, int n)//n为元素个数
{
	srand(time(0));
	struct node* p;
	for(int i=0;i<n;i++)
	{
		p = (struct node*)malloc(sizeof(struct node));
		int s = rand() % 100;//随机数
		p->data = s;
		p->next = L->next;
		L->next = p;
	}
}

建立有头节点的单链表(尾插法)

与头插法不同的是:尾插法需要另外一个指向尾部的结点 r ,在链表中插入元素时,只需要将 r 的指针指向 p 即可,然后将 p 赋值给 r ,这样可以使 r 始终在链表尾部,并且将要插入的元素置于 r 的后方,也就是链表的尾部。插入结束后,将链表尾部的元素的指针指向NULL。

//建立有头结点的单链表(尾插法)
void CreateListTail(struct node* L, int n)
{
	srand(time(0));
	struct node* p, * q;
	q = L;
	for (int i = 0; i < n; i++)
	{
		p = (struct node*)malloc(sizeof(struct node));
		int s = rand() % 100;
		p->data = s;
		q->next = p;
		q = p;
	}
	q->next = NULL;
}

总代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
struct node
{
	int data;
	struct node* next;
};
//清空链表
void clearList(struct node** L)
{
	struct node* p, * q;
	p = (*L)->next;//工作指针
	while (p != NULL)
	{
		//记录将要被释放空间的结点的指针域,如果不记录,那么链表后面的结点就找不到了
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
}
//输入n个元素
void putList(struct node* L, int n)
{
	struct node* p, * q;
	L->next = (struct node*)malloc(sizeof(struct node));
	q = L;
	printf("输入%d个元素放入链表:", n);
	for (int i = 0; i < n; i++)
	{
		int x;
		scanf("%d", &x);
		p = (struct node*)malloc(sizeof(struct node));
		p->data = x;
		p->next = NULL;
		q->next = p;
		q = p;
	}
}
//输出链表中的数据
void printList(struct node* L)
{
	struct node* p;
	p = L->next;
	printf("当前链表元素:");
	while (p != NULL)
	{
		printf("%d ", p->data);//输出当前结点的数据
		p = p->next;//工作指针后移
	}
	printf("\n");
}
//返回链表元素个数
int numList(struct node* L)
{
	int sum = 0;//计数器
	struct node* p;//定义一个工作指针,因为如果直接用L指针后移的话,这个链表就找不到了
	p = L->next;//将头指针指针域指向的地址赋给p,也就是链表的第一个元素(头指针中不存放数据)
	while (p != NULL)
	{
		p = p->next;
		sum++;
	}
	return sum;
}
//判断链表是否为空
bool isEmptyList(struct node* L)
{
	//判断头结点指向的下一指针域不为空,说明该链表不为空
	if (L->next != NULL)
		return true;//不为空
	else
		return false;//为空
}
//初始化
void InitList(struct node** L)
{
	*L = (struct node*)malloc(sizeof(struct node));
	(*L)->next = NULL;
}
//返回链表的指定位置的元素
int elementList(struct node* L, int x)//x为指定位置
{
	int i = 0;//计数器
	struct node* p;
	p = L->next;
	//找到指定位置的前面那个位置,因为当刚好i==x-1时,此时进入循环,p=p->next,刚好到达结点为x的位置
	while (p != NULL && i < x-1)
	{
		p = p->next;
		i++;
	}
	if (p)
		return p->data;
	else
		return 0;
}
//查找数据所在位置
int findList(struct node* L, int x)
{
	int i = 0;
	struct node* p;
	p = L;
	while (p != NULL)
	{
		if (p->data == x)//找到了直接输出该节点的序号
			return i;
		i++;
		p = p->next;
	}
	return 0;//代表没有找到数据为x的结点
}
//删除元素
bool deleteList(struct node* L, int x, int* e)//x是删除元素的结点序号,传入的地址e是用来记录删除结点的数据的
{
	struct node* p, * q;
	p = L->next;
	int i = 0;
	while (i < x - 2 && p->next != NULL)//这里循环到i<x-1,因为找到当前的结点后,是删除它后面那个元素
	{
		p = p->next;
		i++;
	}
	if (p->next == NULL)
		return false;//删除失败
	q = p->next;
	*e = q->data;
	p->next = q->next;
	return true;//删除成功
}
//插入元素(输入)
void PutList(struct node* L, int x, int y)
{
	struct node* q, * p, * s;
	int i = 1;
	p = L;
	while (i < x && p != NULL)//找到要插入元素的位置
	{
		p = p->next;
		i++;
	}
	s = (struct node*)malloc(sizeof(struct node));
	s->data = y;//赋值
	//这两步不能反
	s->next = p->next;
	p->next = s;
}
//插入元素(随机)
void PutRandList(struct node** L, int x)
{
	struct node* p, * q;
	q = NULL;
	int i = 1;
	p = (*L);
	while (i < x && p != NULL)
	{
		p = p->next;
		i++;
	}
	srand(time(0));
	int s = rand() % 100;
	q = (struct node*)malloc(sizeof(struct node));
	q->data = s;
	q->next = p->next;
	p->next = q;
}
//创建链表(无头结点)
void creatList(struct node** L, int n)
{
	printf("输入%d个元素:", n);
	struct node* p, * q;
	*L = NULL;
	for (int i = 0; i < n; i++)
	{
		int x;
		scanf("%d", &x);
		p = (struct node*)malloc(sizeof(struct node));
		p->data = x;
		p->next = NULL;
		if (*L == NULL)//如果头结点是空的就将第一个结点赋值给头结点(因为这是无头结点)
			*L = p;
		else
			q->next = p;
		q = p;
	}
}
//建立有头结点的单链表(头插法)
void CreateListHead(struct node* L, int n)//n为元素个数
{
	srand(time(0));
	struct node* p;
	for (int i = 0; i < n; i++)
	{
		p = (struct node*)malloc(sizeof(struct node));
		int s = rand() % 100;//随机数
		p->data = s;
		p->next = L->next;
		L->next = p;
	}
}
//建立有头结点的单链表(尾插法)
void CreateListTail(struct node* L, int n)
{
	srand(time(0));
	struct node* p, * q;
	q = L;
	for (int i = 0; i < n; i++)
	{
		p = (struct node*)malloc(sizeof(struct node));
		int s = rand() % 100;
		p->data = s;
		q->next = p;
		q = p;
	}
	//这个地方不能省,因为输出的时候是以最后一个结点的指针域为NULL判断结束的
	q->next = NULL;
}

int main()
{
	int n, x;
	struct node* L;
	//初始化后判断链表是否为空
	InitList(&L);//初始化链表
	if (isEmptyList(L))
		printf("当前链表不为空\n");
	else
		printf("当前链表为空\n");


	//输入n个元素存入链表
	printf("输入元素个数:");
	scanf("%d", &n);//输入元素个数
	putList(L, n);//键盘输入n个元素进入链表
	printList(L);
	if (isEmptyList(L))
		printf("当前链表不为空\n");
	else
		printf("当前链表为空\n");


	//统计链表中元素个数
	printf("链表中元素个数:%d\n", numList(L));


	//输入链表的结点位置,找对应位置的元素
	printf("输入要查找的地址:");
	scanf("%d", &x);
	printf("%d位置上的元素是:%d\n", x, elementList(L, x));


	//输入元素,找链表中对应的位置
	printf("输入要查找的数据:");
	scanf("%d", &x);
	printf("%d在链表的第%d个\n", x, findList(L, x));


	//输入要删除的位置,删除对应的元素
	printf("输入要删除的位置:");
	scanf("%d", &x);
	int y;//记录删除的那个元素是什么
	if (deleteList(L, x, &y))
	{
		printList(L);//打印出链表
		printf("删除的元素是:%d\n", y);
	}
	else
		printf("删除失败\n");


	//插入元素
	printf("输入插入的元素的位置和元素:");
	scanf("%d%d", &x, &y);
	PutList(L, x, y);
	printList(L);//打印出链表


	//插入元素(随机生成数)
	printf("输入要插入随机数字的位置:");
	scanf("%d", &x);
	PutRandList(&L, x);
	printList(L);//打印出链表
	clearList(&L);//清空链表
	if (isEmptyList(L))//判断链表是否为空
		printf("当前链表不为空\n");
	else
		printf("当前链表为空\n");


	//建立无头结点的链表
	printf("建立无头结点的单链表,输入结点个数:");
	scanf("%d", &x);
	creatList(&L, x);//建立无头结点的单链表
	struct node* t = L;
	while (t != NULL)//打印无头结点单链表(打印时不能用上面的printList函数,因为这个是无头结点的链表)
	{
		printf("%d ", t->data);
		t = t->next;
	}
	printf("\n");
	clearList(&L);//清空


	//建立有头结点的单链表
	printf("输入头插法的要插入的元素个数:");
	scanf("%d", &x);
	CreateListHead(L, x);
	printList(L);
	clearList(&L);//清空


	//建立有头结点的单链表(随机生成数)
	printf("输入尾插法的要插入的元素个数:");
	scanf("%d", &x);
	CreateListTail(L, x);
	printList(L);
	clearList(&L);//清空
}

在C语言中,线性链表是一种常见的数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。对于线性链表插入和删除操作,需要特别注意指针的正确操作,以保持链表的连续性。 以下是线性链表插入和删除操作的C语言示例代码: 插入操作: ```c // 定义链表节点结构体 typedef struct Node { int data; // 数据域 struct Node* next; // 指针域,指向下一个节点 } Node; // 在链表的第pos个位置插入新节点 void insert(Node** head, int data, int pos) { Node* newNode = (Node*)malloc(sizeof(Node)); // 创建新节点 newNode->data = data; newNode->next = NULL; if (pos == 0) { // 插入链表头部 newNode->next = *head; *head = newNode; } else { // 插入链表中间或尾部 Node* temp = *head; for (int i = 0; temp != NULL && i < pos - 1; i++) { temp = temp->next; } if (temp != NULL) { newNode->next = temp->next; temp->next = newNode; } else { // 如果pos大于链表长度,则不插入 free(newNode); } } } ``` 删除操作: ```c // 删除链表中第pos个位置的节点 void delete(Node** head, int pos) { Node* temp = *head; Node* prev = NULL; if (temp != NULL && pos == 0) { // 删除头节点 *head = temp->next; free(temp); } else { // 遍历到要删除的节点的前一个节点 for (int i = 0; temp != NULL && i < pos; i++) { prev = temp; temp = temp->next; } if (temp == NULL || prev == NULL) { // 如果位置不合法,不进行删除 return; } // 删除节点 prev->next = temp->next; free(temp); } } ``` 这两个函数分别实现了在链表插入和删除节点的功能。在使用时需要注意传入正确的链表指针以及要操作的位置。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

明里灰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值