前言
对于结构体和指针的深层运用,建议具有一定基础食用。
单链表是一种常见的数据结构,其特点如下:
单向性:每个节点只有一个指针,指向下一个节点,不能回溯到前面的节点。
动态性:单链表的长度可以动态地改变,可以根据需要进行节点的添加和删除操作。
存储效率较低:与数组相比,单链表的存储效率较低,因为每个节点需要额外的指针空间来指向下一个节点。
随机访问效率较低:由于单链表只能从头节点开始逐个遍历,因此访问某个特定位置的节点需要线性时间,效率较低。
插入和删除操作效率较高:由于单链表的动态性,插入和删除操作只需要改变指针的指向,效率较高。
空间分配灵活:单链表的节点可以根据需要动态分配,因此可以灵活地使用内存空间。
单链表逻辑图:
每一个结构包含一个数据类型和下一个数据的地址。前面的结构体章节提到(写文章-CSDN创作中心)如何定义这样的类型:
typedef struct dlb {
int date;
struct dlb* next;
}dlb;
初始化:
单链表的初始化使用结构体指针:因为单链表基本是由结构体指针构成的,每个节点都是一个结构体,包含指向下一个节点的指针和数据域。因此,需要定义一个结构体来表示节点,然后通过结构体指针来访问和操作每个节点。
在初始化单链表时,需要创建一个头节点来代表链表的开始位置。这个头节点也是一个结构体,同样包含一个指向下一个节点的指针和数据域。由于单链表是由多个节点组成的,因此需要用一个指向头节点的结构体指针来表示整个单链表。这个指针将被用于访问链表中的每个节点,从而进行插入、删除、遍历等操作。
因此,单链表的初始化通常使用结构体指针来表示整个链表,从而方便对链表进行操作
int main() {
dlb *ptr=NULL;
}
头节点初始为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;
}
没有改变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;
}
这里我们看到x,y实现交换,改变int用的int*的指针;如果要改变int*,就用int**来进行改变,如果用int*改变就变成了传值调用,你传的是地址,其效果为传值调用;(不懂看写文章-CSDN创作中心指针总结)
前面我们初始化:
int main() {
dlb *ptr=NULL;
}
要改变就要用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;
}
}
为方便检查写一个打印函数:
要打印数据:确保循环条件
注意: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*来接收,因为不需要函数内部对该参数所指向的变量的修改会影响到原变量的值。
尾删:
情况一:有其他(至少两个)节点
如何找到节点:
我们用一个单链表指针来记录它的上一个节点
情况二:只有一个节点
实现:
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;
}
}
总结:
(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;
}
单链表的总结完成,不足欢迎补充,求三连!!!!!!!!!!!!!!!!!