【数据结构与算法】单链表的增删查改(代码+图解)
创始人
2024-01-21 02:16:35
0

目录

顺序表和链表的特点:

时间复杂度:

分析:

单链表结构体和数据类型:

开辟一个节点和存储数据:

打印

尾插

         尾删

头插

头删:

查找单链表中的元素

在pos后插入x

在pos前插入x

删除pos后的一个元素:

删除pos位置的元素:

摧毁单链表:

完整代码:


80f3084b30fd4543b4719418da39509b.png

顺序表和链表的特点:

 顺序表使用数组存储线形的元素,其特点是可以随机存取,但是,因为逻辑上相邻的元素物理上也相邻,所以插入删除需要移动元素.

链表使用指针链表示线形表元素的逻辑关系,插入和删除只需修改指针,不能随机存取.

单向链表增加删除节点简单。 遍历时候不会死循环;

时间复杂度:

链表中数据元素之间的逻辑关系靠的是节点之间的指针,当需要在链表中某处插入或删除节点时,只需改变相应节点的指针指向即可,无需大量移动元素,因此链表中插入、删除或移动数据所耗费的时间复杂度为 O(1)

而顺序表中,插入、删除和移动数据可能会牵涉到大量元素的整体移动,因此时间复杂度至少为 O(n);

链表存储数据一次只开辟存储一个节点的物理空间,如果后期不够还可以再申请。

分析:

单链表结构体和数据类型:

typedef int SLTDataType;
typedef struct SLlistNode
{SLTDataType data;struct SListNode* next;
}SLTNode;

 

开辟一个节点和存储数据:

SLTNode* BuySLTNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}

malloc函数开辟一个大小为sizeof(SLTNode)字节即一个结构体大小的空间,原本malloc返回值是void类型的指针,但是代码里面的(SLTNode*)将malloc的返回值强制类型转换为SLTNode类型,这样方便了后面数据的存储;

打印


void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}

调用SLTPrint()函数使,用*phead接收传进来的参数

尾插

void SLTPushBack(SLTNode** phead,SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);if (*phead == NULL){*phead = newnode;}else{SLTNode* ptail = *phead;while (ptail->next)     //找尾{ptail = ptail->next;}ptail->next = newnode;}
}

图解:

66eba0284d364d38b8a51ccb6ee89624.png

首先用ptail记住链表头部的位置,再ptail=ptail->next寻找最后一个节点 

尾删

void SLTPopBack(SLTNode** phead)
{assert(*phead);//只有一个指针if ((*phead)->next==NULL){free(*phead);*phead = NULL;}else{SLTNode* pre = NULL;                //记录倒数第二个指针,SLTNode* ptail = *phead;while (ptail->next){pre = ptail;ptail = ptail->next;}free(ptail);pre->next = NULL;                     //将被释放的那个指针置空,避免出现野指针}
}

 assert(*phead)实现对*phead判空;这里解释一下为什么传参要用双指针,因为改变的是指针,而不是指针的值;

例如:

//单指针传参交换指针指向的值
void Swap1(int *p1,int *p2)
{int tmp= *p1;   //这里p1解引用之后就是p1指针指向的值*P1 = *p2;*p2= tmp;
}//双指针传参交换指针
void Swap2(int ** pp1,int **pp2)
{int *tmp=*pp1;*pp1 = *pp2;*pp2 = tmp;
}int main()
{int a=0,b=1;Swap1(&a,&b);int *ptr1  = &a,ptr2 = &b;Swap2(&ptr1,&ptr2); 
}

Swap2(&ptr1,&ptr2)通过交换a、b的地址来交换值,而Swap1通过改变指针指向的值来交换值;

总结:1、改变int,传递int *给形参,*形参进行交换改变

           2、改变int*,传递int**给形参,*形参进行交换改变

头插

void SLTPushFront(SLTNode** phead, SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);newnode->next = *phead;                      //*phead = newnode;                           //换头
}

33881d44edb9473aa8caab5882b2a364.png

先将newnode->next指向phead(头节点),再phead=newnode; 

头删:

void SLTPopFront(SLTNode** phead)
{assert(*phead);SLTNode* newnode = NULL;newnode = (*phead)->next;          //存储第二个指针free(*phead);                            //释放第一个指针空间*phead = newnode;                        //换头
}

查找单链表中的元素

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

在pos后插入x

void SLTInsertAfter(SLTNode** phead, SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* cur = *phead;while (cur != pos){cur = cur->next;}SLTNode* newnode = BuySLTNode(x);newnode->next = cur->next;cur->next = newnode;}

在pos前插入x

void SLTInsert(SLTNode**phead,SLTNode* pos, SLTDataType x)
{assert(pos);if (*phead == pos){SLTPushFront(phead,x);           //头插}else{SLTNode* pre = *phead;while (pre->next != pos){pre = pre->next;}SLTNode* newnode = BuySLTNode(x);pre->next = newnode;newnode->next = pos;}
}

删除pos后的一个元素:

void SLTEraseAfter(SLTNode* pos)
{assert(pos);if (pos->next = NULL){return;}else{SLTNode* next = pos->next;pos->next = next->next;free(next);}}

图解: 

f9164a69043f4ba59d4d05166642001b.png

删除pos位置的元素:


void SLTErase(SLTNode** phead, SLTNode* pos)
{assert(pos);assert(*phead);if (*phead == pos){SLTPopFront(phead);                   //这里不用*phead,因为传过去的是**phead;解引用的时候改变的是*phead;}else{SLTNode* pre = *phead;while (pre->next != pos){pre = pre->next;}pre->next = pos->next;free(pos);}}

摧毁单链表:

void SLTDestroy(SLTNode** phead)
{SLTNode* cur = *phead;while (cur){SLTNode* next = cur->next;free(cur);cur = next;}*phead = NULL;
}

完整代码:

我用SList.h存放函数的声明和一些头文件:

#pragma once
#include
#include
#includetypedef int SLTDataType;
typedef struct SLlistNode
{SLTDataType data;struct SListNode* next;
}SLTNode;SLTNode* BuySLTNode(SLTDataType x);
SLTNode* CreateSList(int n);
void SLTPrint(SLTNode* phead);void SLTPushBack(SLTNode** phead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPushFront(SLTNode** phead, SLTDataType x);
void SLTPopFront(SLTNode** phead);SLTNode* SLTFind(SLTNode* phead, SLTDataType x);// 查找链表中的元素
void SLTInsertAfter(SLTNode**phead,SLTNode* pos,SLTDataType x);//在pos位置之后插入x
void SLTInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);//在pos位置前面插入xvoid SLTEraseAfter(SLTNode* pos);//删除pos后面的一个元素void SLTErase(SLTNode** phead, SLTNode* pos);//删除pos位置的元素
void SLTDestroy(SLTNode** phead);

用SList.c定义函数:

#define  _CRT_SECURE_NO_WARNI
#include"SList.h"SLTNode* BuySLTNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}
//SLTNode* CreateSList(int n)
//{
//	SLTNode* phead = NULL;
//	SLTNode* ptail = NULL;
//	int x;
//	for (int i = 0; i < n; i++)
//	{
//		SLTNode* newnode = BuySLTNode(i);
//		if (phead == NULL)
//		{
//			ptail = phead = newnode;
//		}
//		else
//		{
//			ptail->next = newnode;
//			ptail = newnode;
//		}
//	}
//	return phead;
//}void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}void SLTPushBack(SLTNode** phead,SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);if (*phead == NULL){*phead = newnode;}else{SLTNode* ptail = *phead;while (ptail->next)     //找尾{ptail = ptail->next;}ptail->next = newnode;}
}
void SLTPopBack(SLTNode** phead)
{assert(*phead);//只有一个指针if ((*phead)->next==NULL){free(*phead);*phead = NULL;}else{SLTNode* pre = NULL;                //记录倒数第二个指针,SLTNode* ptail = *phead;while (ptail->next){pre = ptail;ptail = ptail->next;}free(ptail);pre->next = NULL;                     //将被释放的那个指针置空,避免出现野指针}
}void SLTPushFront(SLTNode** phead, SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);newnode->next = *phead;                      //*phead = newnode;                           //换头
}void SLTPopFront(SLTNode** phead)
{assert(*phead);SLTNode* newnode = NULL;newnode = (*phead)->next;          //存储第二个指针free(*phead);                            //释放第一个指针空间*phead = newnode;                        //换头
}SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}void SLTInsertAfter(SLTNode** phead, SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* cur = *phead;while (cur != pos){cur = cur->next;}SLTNode* newnode = BuySLTNode(x);newnode->next = cur->next;cur->next = newnode;}void SLTInsert(SLTNode**phead,SLTNode* pos, SLTDataType x)
{assert(pos);if (*phead == pos){SLTPushFront(phead,x);           //头插}else{SLTNode* pre = *phead;while (pre->next != pos){pre = pre->next;}SLTNode* newnode = BuySLTNode(x);pre->next = newnode;newnode->next = pos;}
}void SLTEraseAfter(SLTNode* pos)
{assert(pos);if (pos->next = NULL){return;}else{SLTNode* next = pos->next;pos->next = next->next;free(next);}}void SLTErase(SLTNode** phead, SLTNode* pos)
{assert(pos);assert(*phead);if (*phead == pos){SLTPopFront(phead);                   //这里不用*phead,因为传过去的是**phead;解引用的时候改变的是*phead;}else{SLTNode* pre = *phead;while (pre->next != pos){pre = pre->next;}pre->next = pos->next;free(pos);}}void SLTDestroy(SLTNode** phead)
{SLTNode* cur = *phead;while (cur){SLTNode* next = cur->next;free(cur);cur = next;}*phead = NULL;
}

用test.c写主函数和函数调用:

#define  _CRT_SECURE_NO_WARNINGS
#include"SList.h"
//void test1()
//{
//	SLTNode* plist = NULL;
//	plist=CreateSList(3);
//	SLTPrint(plist);
//}
//
//void testSLTNode2()
//{
//	SLTNode* plist = NULL;
//	SLTPushBack(&plist, 1);
//	SLTPushBack(&plist, 2);
//	SLTPushBack(&plist, 3);
//	SLTPushBack(&plist, 4);
//	
//	SLTPopBack(&plist);
//	
//	SLTPrint(plist);
//}
//
//void testSTLNode3()
//{
//	SLTNode* plist = NULL;
//	SLTPushFront(&plist,1);
//	SLTPushFront(&plist,2);
//	SLTPushFront(&plist,3);
//	SLTPushFront(&plist,4);
//	SLTPushFront(&plist,5);
//
//	SLTPrint(plist);
//
//	SLTPopFront(&plist);
//	SLTPopFront(&plist);
//
//	printf("\n");
//	SLTPrint(plist);
//}
//void testSLTNode4()
//{
//	SLTNode* plist = NULL;
//	SLTPushFront(&plist, 2);
//	SLTPushFront(&plist, 3);
//	SLTPushFront(&plist, 5);
//
//	SLTNode *p = SLTFind(plist, 5);
//	SLTInsertAfter(p, 99);
//
//	SLTPrint(plist);
//}//void testSLTNode5()
//{
//	SLTNode* plist = NULL;
//	SLTPushBack(&plist, 1);
//	SLTPushBack(&plist, 2);
//	SLTPushBack(&plist, 5);
//
//	SLTNode* p = SLTFind(plist, 1);
//	SLTInsertAfter(&plist,p , 6);
//
//	SLTPrint(plist);
//}void testSLTNode6()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushFront(&plist, 4);SLTPushFront(&plist, 5);SLTPushFront(&plist, 6);SLTNode* p = SLTFind(plist, 4);SLTInsert(&plist,p, 100);SLTPrint(plist);p = SLTFind(plist, 5);SLTInsertAfter(&plist,p, 200);SLTPrint(plist);p = SLTFind(plist, 100);SLTErase(&plist, p);SLTPrint(plist);p = NULL;SLTDestroy(&plist);SLTPrint(plist);
}int main()
{//test1();//testSLTNode2();//testSTLNode3();//testSLTNode4();//testSLTNode5();testSLTNode6();return 0;
}

707ac00b85a147ffa61c5dc06a2fe02f.png

 运行结果:

261e991e051e47cc9825ea58ce16e0f7.png

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...