单链表(c语言实现)

                                                             前言

对于结构体和指针的深层运用,建议具有一定基础食用。

 

单链表是一种常见的数据结构,其特点如下:

单向性:每个节点只有一个指针,指向下一个节点,不能回溯到前面的节点。

动态性:单链表的长度可以动态地改变,可以根据需要进行节点的添加和删除操作。

存储效率较低:与数组相比,单链表的存储效率较低,因为每个节点需要额外的指针空间来指向下一个节点。

随机访问效率较低:由于单链表只能从头节点开始逐个遍历,因此访问某个特定位置的节点需要线性时间,效率较低。

插入和删除操作效率较高:由于单链表的动态性,插入和删除操作只需要改变指针的指向,效率较高。

空间分配灵活:单链表的节点可以根据需要动态分配,因此可以灵活地使用内存空间。

单链表逻辑图:

f5f23d6fd8ad4dc8897cc2c5271b8a80.png

 每一个结构包含一个数据类型和下一个数据的地址。前面的结构体章节提到(写文章-CSDN创作中心)如何定义这样的类型:

typedef struct dlb {
	int date;
	struct dlb* next;
	
}dlb;

初始化:

单链表的初始化使用结构体指针:因为单链表基本是由结构体指针构成的,每个节点都是一个结构体,包含指向下一个节点的指针和数据域。因此,需要定义一个结构体来表示节点,然后通过结构体指针来访问和操作每个节点。

在初始化单链表时,需要创建一个头节点来代表链表的开始位置。这个头节点也是一个结构体,同样包含一个指向下一个节点的指针和数据域。由于单链表是由多个节点组成的,因此需要用一个指向头节点的结构体指针来表示整个单链表。这个指针将被用于访问链表中的每个节点,从而进行插入、删除、遍历等操作。

因此,单链表的初始化通常使用结构体指针来表示整个链表,从而方便对链表进行操作

int main() {
	dlb *ptr=NULL;

}

757440ffc1da4dd7ae5b7b37324f87a3.png

头节点初始为null,还没用指向a; 

对链表进行操作(尾增):

补充:传值调用与传址调用

传值调用是指将变量的值作为参数传递给函数,函数内部对该参数的修改不会影响到原变量的值,而传址调用则是指将变量的地址作为参数传递给函数,函数内部对该参数所指向的变量的修改会影响到原变量的值。

传值调用:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 1;
    int y = 2;
    swap(x, y);
    printf("x = %d, y = %d", x, y);
    return 0;
}

09fbe32aab6f4f0fbd46db6dfbc5a654.png

没有改变x,y的值

传址调用:

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 1;
    int y = 2;
    swap(&x, &y);
    printf("x = %d, y = %d", x, y);
    return 0;
}

 a07ee83621cb46e7801446fd6d1b9d87.png

 这里我们看到x,y实现交换,改变int用的int*的指针;如果要改变int*,就用int**来进行改变,如果用int*改变就变成了传值调用,你传的是地址,其效果为传值调用;(不懂看写文章-CSDN创作中心指针总结)

前面我们初始化:

int main() {
	dlb *ptr=NULL;

}

要改变就要用dlb**来;

尾增逻辑:

情况一:

957dc331c0b94b39a949d03583d12f1a.png

对其增加,就头节点指向开辟的内容:

4e2ed10f02354f738fc04020845bd52e.png 

情况二:有其他节点

aa57b0393ab34f4ab5eb8053a7e9fd71.png 

对其增加:

fd442db75788496bb94a9dd2480d0fdb.png

实现:

void endadd(dlb** ptr) {
	printf("输入你想增加的值\n");
	int num = 0;
	scanf("%d", &num);
	dlb* add = (dlb*)malloc(sizeof(dlb));
	if (add == NULL) {
		printf("增加失败\n");
	}
	printf("将尾增\n");
	add->date = num;
	add->next = NULL;
	if (*ptr == NULL) {
		*ptr = add;
	}
	else {
		dlb* pt = *ptr;
		while (pt->next!=NULL)
		{
			pt = pt->next;
		}
		pt->next = add;
	}

}

为方便检查写一个打印函数:

要打印数据:确保循环条件

ac16fb62112140f3833bf6a1388256d1.png

注意:ptr都为地址,非int 的数据

实现:

void print1(dlb* ptr) {
	dlb* prin = ptr;
	printf("开始打印\n");
	while (prin!= NULL)
	{
		printf("%d  ", prin->date);
		prin=prin->next;
	}
	printf("NULL\n");
}

 此时用dlb*来接收,因为不需要函数内部对该参数所指向的变量的修改会影响到原变量的值。

尾删:

情况一:有其他(至少两个)节点

fd442db75788496bb94a9dd2480d0fdb.png

c0944236e3d04eca801b91294aa065e6.png

如何找到节点:

我们用一个单链表指针来记录它的上一个节点

9330cbd8d7bb4c5385cb63568e12b250.png 

情况二:只有一个节点

a706a93c355c4113857e47a42ef68202.png

 7c7cc43afd5f4f078260841edcf1f6a8.png

 

 实现:


void enddele(dlb** ptr) {
	if (*ptr == NULL) {
		return;
	}
	printf("正在尾删\n");
	if ((*ptr)->next == NULL) {
		free((*ptr));
		*ptr = NULL;
	}
	else {
	dlb* pare = *ptr;
	dlb* new1 = NULL;
	while (pare->next!=NULL)
	{
		new1 = pare;
		pare = pare->next;
	
	}
	free(pare);
	pare = NULL;
	new1->next = NULL;


	}
}

头增:

51c78e59f6894c9686db4c300dfaee17.png

f9f5196cee694a57b27eef0c1b4ca5cb.png 

实现:

void headadd(dlb** ptr) {
	printf("输入你想增加的值\n");
	int num = 0;
	scanf("%d", &num);
	dlb* add = (dlb*)malloc(sizeof(dlb));
	if (add == NULL) {
		printf("增加失败\n");
	}
	printf("将头增\n");
	add->date = num;
	add->next = *ptr;
	*ptr = add;
}

 头删:

3b1c6aa773ee49fe95cd7659c1a6a351.png

 

ba36bbb9cfbe41869c76283373ca9265.png

步骤不能错:

845ce619c8d342b2bbacb1101e723ab5.png

 

void headdele(dlb** ptr) {
	if (*ptr == NULL) {
		return;
	}
	printf("将头删\n");
	dlb* receive = *ptr;
	*ptr = receive->next;
	free(receive);
	receive = NULL;

}

 除了上面几种,还可以中间增加,中间删除;实现这些功能还要写一个函数,用来提供节点位置。找:

与上面的打印逻辑相同,多了比较和返回;

实现:

dlb* find(dlb* ptr) {
	printf("输入你想查找的值\n");
	int a = 0;
	scanf("%d", &a);
	dlb* receive = ptr;
	while (receive)
	{	
		if (receive->date == a) {
			printf("找到了%d\n", receive->date);
			return receive;
		}
		receive = receive->next;
	}
	printf("没有找到\n");
	return NULL;
}

中间(记录位置前面)增加:

 情况一:中间增加:c359fabf7f734a22bcb4c0ba2e0b9b36.png

 3763f7b4be414f8a93dee6161522886d.png

 注意:不可以先

6144f6550f6042d69802d03c1676d172.png

这样的话找不到下一个节点

 

情况二;头增加bac220b201314c58989d47866732348a.png

 引用函数;

实现:

void midhadd(dlb** ptr) {
	dlb* record = find(*ptr);
	dlb* last = *ptr;
	int b = 0;
	printf("输入你想增加的值\n");
	scanf("%d", &b);
	dlb* add = (dlb*)malloc(sizeof(dlb));
	if (record == *ptr) {
		printf("正在头增\n");
		headadd(ptr);

	}
	else{
		while (last->next != record)
		{
			last = last->next;
		}
		add->date = b;
		add->next = record;
		last->next = add;
	}

}

中间增加(记录位置后):

c359fabf7f734a22bcb4c0ba2e0b9b36.png

fabbde6fc9c049d9952d24afda134184.png

逻辑差不多;

实现:

void mideadd(dlb** ptr) {
	dlb* record = find(*ptr);
	dlb* last = *ptr;
	int b = 0;
	printf("输入你想增加的值\n");
	scanf("%d", &b);
	dlb* add = (dlb*)malloc(sizeof(dlb));
	add->date = b;
	add->next = record->next;
	record->next = add;
}

 中间删除:

情况一:中间删除:

da34cfe0887f4cfbbd6d0b4fc2cbf1d1.png

与中间增加差不多

21626acd5a98487db6e4d986bde5abdd.png 

 情况二:头部删除fabbde6fc9c049d9952d24afda134184.pngddb926f3d0544fc290283738c214d113.png

实现:

void midhdele(dlb** ptr) {
	if (*ptr == NULL) {
		printf("链表为空\n");
	}
	dlb* record = find(*ptr);
	dlb* last = *ptr;
	if (record == *ptr) {
		headdele(ptr);
	}
	else{
		while (last->next != record)
		{
			last = last->next;
		}
		last->next = record->next;
		free(record);
		record = NULL;
	}

}

 总结:

(1)对于结构体和指针的深层运用

(2)判断指针所指向内容时是否为空,根据实际情况来:如1.增加时,不用判断是否内容为空2.删除时,你没有内容不可能删除......

(3)循环的判断条件,根据实际情况来,明确比较类型

(4)明确传值调用与传址调用

(5)实现逻辑,步骤不能反

(6)动态开辟,记得置空

总代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
typedef struct dlb {
	int date;
	struct dlb* next;
	
}dlb;
void endadd(dlb** ptr) {
	printf("输入你想增加的值\n");
	int num = 0;
	scanf("%d", &num);
	dlb* add = (dlb*)malloc(sizeof(dlb));
	if (add == NULL) {
		printf("增加失败\n");
	}
	printf("将尾增\n");
	add->date = num;
	add->next = NULL;
	if (*ptr == NULL) {
		*ptr = add;
	}
	else {
		dlb* pt = *ptr;
		while (pt->next!=NULL)
		{
			pt = pt->next;
		}
		pt->next = add;
	}

}
void print1(dlb* ptr) {
	dlb* prin = ptr;
	printf("开始打印\n");
	while (prin!= NULL)
	{
		printf("%d  ", prin->date);
		prin=prin->next;
	}
	printf("NULL\n");
}
void enddele(dlb** ptr) {
	if (*ptr == NULL) {
		return;
	}
	printf("正在尾删\n");
	if ((*ptr)->next == NULL) {
		free((*ptr));
		*ptr = NULL;
	}
	else {
	dlb* pare = *ptr;
	dlb* new1 = NULL;
	while (pare->next!=NULL)
	{
		new1 = pare;
		pare = pare->next;
	
	}
	free(pare);
	pare = NULL;
	new1->next = NULL;


	}
}
void headadd(dlb** ptr) {
	printf("输入你想增加的值\n");
	int num = 0;
	scanf("%d", &num);
	dlb* add = (dlb*)malloc(sizeof(dlb));
	if (add == NULL) {
		printf("增加失败\n");
	}
	printf("将头增\n");
	add->date = num;
	add->next = *ptr;
	*ptr = add;
}
void headdele(dlb** ptr) {
	if (*ptr == NULL) {
		return;
	}
	printf("将头删\n");
	dlb* receive = *ptr;
	*ptr = receive->next;
	free(receive);
	receive = NULL;

}
dlb* find(dlb* ptr) {
	printf("输入你想查找的值\n");
	int a = 0;
	scanf("%d", &a);
	dlb* receive = ptr;
	while (receive)
	{	
		if (receive->date == a) {
			printf("找到了%d\n", receive->date);
			return receive;
		}
		receive = receive->next;
	}
	printf("没有找到\n");
	return NULL;
}
void midhadd(dlb** ptr) {
	dlb* record = find(*ptr);
	dlb* last = *ptr;
	int b = 0;
	printf("输入你想增加的值\n");
	scanf("%d", &b);
	dlb* add = (dlb*)malloc(sizeof(dlb));
	if (record == *ptr) {
		printf("正在头增\n");
		headadd(ptr);

	}
	else{
		while (last->next != record)
		{
			last = last->next;
		}
		add->date = b;
		add->next = record;
		last->next = add;
	}

}
void mideadd(dlb** ptr) {
	dlb* record = find(*ptr);
	dlb* last = *ptr;
	int b = 0;
	printf("输入你想增加的值\n");
	scanf("%d", &b);
	dlb* add = (dlb*)malloc(sizeof(dlb));
	add->date = b;
	add->next = record->next;
	record->next = add;
}
void midhdele(dlb** ptr) {
	if (*ptr == NULL) {
		printf("链表为空\n");
	}
	dlb* record = find(*ptr);
	dlb* last = *ptr;
	if (record == *ptr) {
		headdele(ptr);
	}
	else{
		while (last->next != record)
		{
			last = last->next;
		}
		last->next = record->next;
		free(record);
		record = NULL;
	}

}

int main() {
	dlb* ptr = NULL;
}

 

单链表的总结完成,不足欢迎补充,求三连!!!!!!!!!!!!!!!!!

 

 

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值