0
点赞
收藏
分享

微信扫一扫

数据结构——单链表的增加、删除、查找、修改,详细解析

他说Python 2022-04-07 阅读 53


 

目录

一、单链表的处理

1、节点的设置

2、节点(结构体)内存空间的开辟

二、单链表功能的实现

1、整体框架

2、主菜单

3、功能的实现

3.1 打印单链表数据功能的实现

 3.2 从单链表尾部插入节点功能的实现

 3.3 从单链表头部插入节点功能的实现

  3.4 从单链表头部删除节点功能的实现

 3.5 从单链表尾部删除节点功能的实现

 3.6 查找数据函数:给一个数据,返回这个数据所在节点的地址的函数

 3.7 随即插入节点:在返回的节点地址前插入一个节点的函数

 3.7 随即删除节点:给数据返回其所在节点地址,删除这个节点的函数

 3.8 修改数据函数:给一个数据,找到这个数据所在的节点,并用新数据修改

 三、总代码

         1、源函数代码test.c

2、头文件SList.h代码

3、函数功能实现源文件 SList.c

四、代码运行实例展示 


一、单链表的处理

1、节点的设置

typedef int SLdatetype;//重定义类型名,这样可以修改想要的类型
typedef struct SListNode SLNode;//重定义结构体类型的名字

struct SListNode //单链表节点
{
SLdatetype date;
SLNode* next;
};

2、节点(结构体)内存空间的开辟

SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
newnode->date = x;//将需要的数据x放入节点里
newnode->next = NULL;//创建一个新的节点

 

 

二、单链表功能的实现

1、整体框架

int main()
{
menu();
int input = 0;
do
{
fprintf(stdout, "请输入:>");
fscanf(stdin,"%d",
switch (input)
{
case 1:
testSList1();//进入尾插数据操作
fprintf(stdout, "尾插数据成功\n");
break;
case 2:
testSList2();//进入头插数据操作
fprintf(stdout, "头插数据成功\n");
break;
case 3:
testSList3();//进入头删数据操作
fprintf(stdout, "头删数据成功\n");
break;
case 4:
testSList4();//进入尾删数据操作
fprintf(stdout, "尾删数据成功\n");
break;
case 5:
testSList5();//进入随机插入数据操作
fprintf(stdout, "随机插入数据成功\n");
break;
//testSList6();
case 6:
testSList7();//进入随机删除数据操作
fprintf(stdout, "随机删除数据成功\n");
break;
case 7:
testSList8();//进入修改数据操作
break;
case 0:
printf("退出\n");
break;
default:
fprintf(stdout, "选择错误,请重新选择\n");
}
} while (input);
}

2、主菜单

void menu()
{
printf("*************************************\n");
printf("***** 1、尾插 2、头插 *****\n");
printf("***** 3、头删 4、尾删 *****\n");
printf("***** 5、随机插 6、随机删 *****\n");
printf("***** 7、修改 0、退出 *****\n");
printf("*************************************\n");
}

 

3、功能的实现

3.1 打印单链表数据功能的实现

//打印单链表
void SListPrint(SLNode* phead)//实参是单链表的第一个节点的地址
{
while (phead != NULL)
{
printf("%d->", phead->date);
phead = phead->next;
}
printf("NULL\n");
}

 3.2 从单链表尾部插入节点功能的实现

//尾插函数
void SListPushBack(SLNode** pphead, SLdatetype x)
{
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
newnode->date = x;
newnode->next = NULL;//创建一个新的节点

if (*pphead == NULL)//当没有节点的时候,这时候开辟的内存就给plist
{
*pphead = newnode;
}
else
{
SLNode* cru = *pphead;
while (cru->next != NULL)//这里必须改变节点里面的指针next存放的地址
{
cru = cru->next;
}
cru->next = newnode;//所以这里必须用cru->next访问这个指针才可以改变next存放的地址
}
}

 

 3.3 从单链表头部插入节点功能的实现

//头插函数
void SListPushFront(SLNode** pphead, SLdatetype x)
{
SLNode* newcode = (SLNode*)malloc(sizeof(SLNode));
newcode->date = x;
newcode->next = NULL;

//if (*pphead == NULL)//当单链表里没有节点的时候
//{
// *pphead = newcode;
//}
//else
//{
newcode->next = *pphead;//这里已经包括了单链表没有节点的时候,所以可以不用上面if
*pphead = newcode;
//}
}

  3.4 从单链表头部删除节点功能的实现

//头删函数 因为节点是动态开辟的,所以直接用free函数就能删除,
//一般来说释放空间后,指向这块空间的指针要置为空指针
void SListPopFront(SLNode** pphead)
{
if (*pphead != NULL)//如果空指针,即没有节点,那么就不需要删除
{
SLNode* next0 = (*pphead)->next;//这里也满足只有一个节点的情况
free(*pphead);
*pphead = next0;
}
}

 

 3.5 从单链表尾部删除节点功能的实现

//尾删函数 因为同样节点是动态开辟的,所以直接用free函数释放空间
void SListPopBack(SLNode** pphead)
{
if (*pphead)//如果单链表没有节点,即plist是空指针,就不进入尾删
{
SLNode* cru = *pphead;
SLNode* prev = NULL;
if((*pphead)->next==NULL)
{
free(*pphead);
*pphead = NULL;
}
while (cru->next != NULL)//新定义一个指针,得到尾删节点的前一个节点的地址
{
prev = cru;
cru = cru->next;
}
prev->next = NULL;
free(cru);
cru = NULL;
}
}

 

 3.6 查找数据函数:给一个数据,返回这个数据所在节点的地址的函数

//给一个数据,返回这个数据所在节点的地址的函数
SLNode* SListFindDate(SLNode* phead, SLdatetype x)
//因为不需要改变指向第一个节点的指针的值,
//即不需要改变这个指针存放的地址
{
while (phead != NULL)//如果没有节点,根本不会进入循环去找
{
if (phead->date == x)
{
return phead;
}
else
{
phead = phead->next;
}
}
return NULL;
}

 

 3.7 随即插入节点:在返回的节点地址前插入一个节点的函数

//在返回的节点地址pos前插入一个数据的函数
void SListPushpos(SLNode** pphead,SLdatetype x,SLdatetype y)
{
SLNode* prev = NULL;
SLNode* cru = *pphead;
SLNode* pos = SListFindDate(*pphead, x);

SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
newnode->date = y;
newnode->next = NULL;

if (pos != NULL)//如果是没找到这个数据,或者没有节点,就不随机插
{
while (cru != pos)
{
prev = cru;
cru = cru->next;
}
if (prev != NULL)
{
newnode->next = pos;
prev->next = newnode;
}
else
{
newnode->next = pos;
*pphead = newnode;
}
}
}

 

 3.7 随即删除节点:给数据返回其所在节点地址,删除这个节点的函数

//给数据返回其所在节点地址,删除这个节点的函数
void SListPopdate(SLNode** pphead, SLdatetype x)
{
SLNode* cru = *pphead;
SLNode* prev = NULL;
SLNode* pos = SListFindDate(*pphead, x);

if (pos != NULL)//如果是没找到这个数据,或者没有节点,就不随机删
{
while (cru != pos)
{
prev = cru;
cru = cru->next;
}
if (prev != NULL)
{
prev->next = pos->next;
free(pos);
pos = NULL;
}
else
{
*pphead = pos->next;
free(cru);
cru = NULL;
}
}
}

 3.8 修改数据函数:给一个数据,找到这个数据所在的节点,并用新数据修改

//给一个数据,找到这个数据所在的节点,并用新数据修改
void SListChangeDate(SLNode* phead, SLdatetype x,SLdatetype y)
//因为不需要改变节点的地址,所以值传递即可
//x是查找的数据,y是新数据,用来修改查找的数据
{
SLNode* cru = phead;
while (cru != NULL)//如果没有节点,根本不会进入循环去找
{
if (cru->date == x)
{
cru->date = y;
break;//修改完数据后,就跳出循环
}
else
{
cru = cru->next;
}
}

if (cru == NULL)//如果循环完单链表,没有找到要修改的那个数据
{
fprintf(stdout, "要修改的数据不存在,请重新修改数据\n");
}
else
{
fprintf(stdout, "修改成功\n");
}
}

 

 

 三、总代码

1、源函数代码test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"SList.h"

//主菜单
void menu()
{
printf("*************************************\n");
printf("***** 1、尾插 2、头插 *****\n");
printf("***** 3、头删 4、尾删 *****\n");
printf("***** 5、随机插 6、随机删 *****\n");
printf("***** 7、修改 0、退出 *****\n");
printf("*************************************\n");
}

//验证尾插函数
void testSList1()
{
SLNode* plist = NULL;//指针一定要初始化,不然会变成野指针
SListPushBack(
SListPushBack(
SListPushBack(
SListPushBack(
SListPrint(plist);
}

//验证头插函数
void testSList2()
{
SLNode* plist = NULL;
SListPushFront(
SListPushFront(
SListPushFront(
SListPushFront(
SListPrint(plist);
}

//验证头删函数
void testSList3()
{
SLNode* plist = NULL;
SListPushBack(
SListPushBack(
SListPushBack(
SListPushBack(
SListPopFront(
SListPopFront(
SListPrint(plist);
}

//验证尾删函数
void testSList4()
{
SLNode* plist = NULL;
SListPushFront(
SListPushFront(
SListPushFront(
SListPushFront(
SListPopFront(
SListPopFront(
SListPrint(plist);

}

//验证给数据返回其所在节点地址的函数,和删除数据节点函数或插入数据节点之前的函数配合使用
void testSList5()
{
SLNode* plist = NULL;
SListPushBack(
SListPushBack(
SListPushBack(
SListPushBack(
SListFindDate(plist,2);
}

//验证在返回的节点地址pos前插入一个数据的函数
void testSList6()
{
SLNode* plist = NULL;
SListPushFront(
SListPushFront(
SListPushFront(
SListPushFront(
SListPushpos(&plist, 8, 1);
SListPrint(plist);
}

//验证给数据返回其所在节点地址,删除这个节点的函数
void testSList7()
{
SLNode* plist = NULL;
SListPushBack(
SListPushBack(
SListPushBack(
SListPushBack(
SListPopdate(
SListPopdate(
SListPrint(plist);
}

//验证给一个数据,找到这个数据所在的节点,并用新数据修改的函数
void testSList8()
{
SLNode* plist = NULL;
SListPushFront(
SListPushFront(
SListPushFront(
SListPushFront(
SListChangeDate(plist, 6, 9);
SListPrint(plist);
}

int main()
{
menu();
int input = 0;
do
{
fprintf(stdout, "请输入:>");
fscanf(stdin,"%d",
switch (input)
{
case 1:
testSList1();//进入尾插数据操作
fprintf(stdout, "尾插数据成功\n");
break;
case 2:
testSList2();//进入头插数据操作
fprintf(stdout, "头插数据成功\n");
break;
case 3:
testSList3();//进入头删数据操作
fprintf(stdout, "头删数据成功\n");
break;
case 4:
testSList4();//进入尾删数据操作
fprintf(stdout, "尾删数据成功\n");
break;
case 5:
testSList6();//进入随机插入数据操作
fprintf(stdout, "随机插入数据成功\n");
break;
case 6:
testSList7();//进入随机删除数据操作
fprintf(stdout, "随机删除数据成功\n");
break;
case 7:
testSList8();//进入修改数据操作
break;
case 0:
printf("退出\n");
break;
default:
fprintf(stdout, "选择错误,请重新选择\n");
}
} while (input);
}

 

2、头文件SList.h代码

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

typedef int SLdatetype;//重定义类型名,这样可以修改想要的类型
typedef struct SListNode SLNode;//重定义结构体类型的名字

struct SListNode //单链表节点
{
SLdatetype date;
SLNode* next;
};

void SListPrint(SLNode* phead);//打印单链表

void SListPushBack(SLNode** pphead, SLdatetype x);//尾插函数

void SListPushFront(SLNode** pphead, SLdatetype x);//头插函数

void SListPopFront(SLNode** pphead);//头删函数

void SListPopBack(SLNode** pphead);//尾删函数

SLNode* SListFindDate(SLNode* phead, SLdatetype x);//验证给数据返回其所在节点地址的函数

void SListPushpos(SLNode** pphead, SLdatetype x, SLdatetype y);//在返回的节点地址pos前插入一个数据的函数,x是找的数据,y是插入的数据

void SListPopdate(SLNode** pphead, SLdatetype x);//给数据返回其所在节点地址,删除这个节点的函数

void SListChangeDate(SLNode* phead, SLdatetype x,SLdatetype y);//因为不需要改变节点的地址,所以值传递即可
//x是查找的数据,y是新数据,用来修改查找的数据

 

3、函数功能实现源文件 SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"


//打印单链表
void SListPrint(SLNode* phead)//实参是单链表的第一个节点的地址
{
while (phead != NULL)
{
printf("%d->", phead->date);
phead = phead->next;
}
printf("NULL\n");
}

//尾插函数
void SListPushBack(SLNode** pphead, SLdatetype x)
{
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
newnode->date = x;
newnode->next = NULL;//创建一个新的节点

if (*pphead == NULL)//当没有节点的时候,这时候开辟的内存就给plist
{
*pphead = newnode;
}
else
{
SLNode* cru = *pphead;
while (cru->next != NULL)//这里必须改变节点里面的指针next存放的地址
{
cru = cru->next;
}
cru->next = newnode;//所以这里必须用cru->next访问这个指针才可以改变next存放的地址
}
}


//头插函数
void SListPushFront(SLNode** pphead, SLdatetype x)
{
SLNode* newcode = (SLNode*)malloc(sizeof(SLNode));
newcode->date = x;
newcode->next = NULL;

//if (*pphead == NULL)//当单链表里没有节点的时候
//{
// *pphead = newcode;
//}
//else
//{
newcode->next = *pphead;//这里已经包括了单链表没有节点的时候,所以可以不用上面if
*pphead = newcode;
//}
}


//头删函数 因为节点是动态开辟的,所以直接用free函数就能删除,
//一般来说释放空间后,指向这块空间的指针要置为空指针
void SListPopFront(SLNode** pphead)
{
if (*pphead != NULL)//如果空指针,即没有节点,那么就不需要删除
{
SLNode* next0 = (*pphead)->next;//这里也满足只有一个节点的情况
free(*pphead);
*pphead = next0;
}
}



//尾删函数 因为同样节点是动态开辟的,所以直接用free函数释放空间
void SListPopBack(SLNode** pphead)
{
if (*pphead)//如果单链表没有节点,即plist是空指针,就不进入尾删
{
SLNode* cru = *pphead;
SLNode* prev = NULL;
if((*pphead)->next==NULL)
{
free(*pphead);
*pphead = NULL;
}
while (cru->next != NULL)//新定义一个指针,得到尾删节点的前一个节点的地址
{
prev = cru;
cru = cru->next;
}
prev->next = NULL;
free(cru);
cru = NULL;
}
}


//给一个数据,返回这个数据所在节点的地址的函数
SLNode* SListFindDate(SLNode* phead, SLdatetype x)
//因为不需要改变指向第一个节点的指针的值,
//即不需要改变这个指针存放的地址
{
while (phead != NULL)//如果没有节点,根本不会进入循环去找
{
if (phead->date == x)
{
return phead;
}
else
{
phead = phead->next;
}
}
return NULL;
}


//在返回的节点地址pos前插入一个数据的函数
void SListPushpos(SLNode** pphead,SLdatetype x,SLdatetype y)
{
SLNode* prev = NULL;
SLNode* cru = *pphead;
SLNode* pos = SListFindDate(*pphead, x);

SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
newnode->date = y;
newnode->next = NULL;

if (pos != NULL)//如果是没找到这个数据,或者没有节点,就不随机插
{
while (cru != pos)
{
prev = cru;
cru = cru->next;
}
if (prev != NULL)
{
newnode->next = pos;
prev->next = newnode;
}
else
{
newnode->next = pos;
*pphead = newnode;
}
}
}


//给数据返回其所在节点地址,删除这个节点的函数
void SListPopdate(SLNode** pphead, SLdatetype x)
{
SLNode* cru = *pphead;
SLNode* prev = NULL;
SLNode* pos = SListFindDate(*pphead, x);

if (pos != NULL)//如果是没找到这个数据,或者没有节点,就不随机删
{
while (cru != pos)
{
prev = cru;
cru = cru->next;
}
if (prev != NULL)
{
prev->next = pos->next;
free(pos);
pos = NULL;
}
else
{
*pphead = pos->next;
free(cru);
cru = NULL;
}
}
}


//给一个数据,找到这个数据所在的节点,并用新数据修改
void SListChangeDate(SLNode* phead, SLdatetype x,SLdatetype y)
//因为不需要改变节点的地址,所以值传递即可
//x是查找的数据,y是新数据,用来修改查找的数据
{
SLNode* cru = phead;
while (cru != NULL)//如果没有节点,根本不会进入循环去找
{
if (cru->date == x)
{
cru->date = y;
break;//修改完数据后,就跳出循环
}
else
{
cru = cru->next;
}
}

if (cru == NULL)//如果循环完单链表,没有找到要修改的那个数据
{
fprintf(stdout, "要修改的数据不存在,请重新修改数据\n");
}
else
{
fprintf(stdout, "修改成功\n");
}
}

 

四、代码运行实例展示 

 

举报

相关推荐

0 条评论