线性表——单链表的增删查改

 本节复习链表的增删查改

首先, 链表不是连续的, 而是通过指针联系起来的。 如图:

这四个节点不是连续的内存空间, 但是彼此之间使用了一个指针来连接。 这就是链表。 

现在我们来实现链表的增删查改。

目录

单链表的全部接口:

 准备文件

建立结构体蓝图

申请链表节点函数接口

单链表的打印函数接口

单链表尾插函数接口

单链表头插函数接口

 单链表尾删函数接口

单链表的头删函数接口

 单链表查找函数接口

单链表pos位置之后插入数据接口

单链表删除pos之后位置的数据

单链表在pos位置之前插入数据接口

单链表删除pos位置数据接口

单链表的销毁


单链表的全部接口:

//申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);
//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x);
//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x);
//单链表在pos之后的位置删除数据
void SListPopAfter(SLNode* pos);
//单链表在pos位置之前插入数据接口
void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
//单链表在pos位置删除数据接口
void SListPop(SLNode** pphead, SLNode* pos);
//单链表的销毁函数接口
void SLTDestory(SLNode** pphead);
 

---------------------------------------------------------------------------------------------------------------------------------

 准备文件

首先准备好三个文件夹, 一个main.c文件夹, 一个.h文件夹用来声明链表的接口以及定义结构体等。 一个.c文件夹用来实现单链表。

---------------------------------------------------------------------------------------------------------------------------------

建立结构体蓝图

首先包含一下头文件, 定义一下数据类型。

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;

接着再建立一个链表的结构体

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;

---------------------------------------------------------------------------------------------------------------------------------

申请链表节点函数接口

申请链表的节点操作, 在尾插, 头插, 或者特定位置插入的时候都需要, 所以可以封装成一个函数。 后续直接进行复用就可以。

.h函数声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode //创建结构体
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);

.c函数实现

单链表函数实现函数接口//单链表申请节点函数接口
SLNode* BuySListNode(SLTDataType x) 
{SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));if (NewNode == NULL) {printf("申请节点失败\n");return;}//NewNode->data = x;NewNode->next = NULL;
}

 在实现的过程中,可以将数据直接储存到新节点中。 然后让新节点指向NULL, 然后返回该节点。 然后将链表直接连接到这个节点就可以。

---------------------------------------------------------------------------------------------------------------------------------

单链表的打印函数接口

为了便于后续的函数接口的调试, 我们先实现单链表的打印操作。

.h函数声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);

.c函数实现

单链表函数实现函数接口//单链表申请节点函数接口
SLNode* BuySListNode(SLTDataType x) 
{SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));if (NewNode == NULL) {printf("申请节点失败\n");return;}//NewNode->data = x;NewNode->next = NULL;
}//单链表的节点打印操作
void SListPrint(SLNode* phead) 
{SLNode* cur = phead;while (cur != NULL) {printf("%d->", cur->data);cur = cur->next;}if (cur == NULL)//最后打印一个NULL{printf("NULL");}}

---------------------------------------------------------------------------------------------------------------------------------

单链表尾插函数接口

.h函数声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);

.c函数实现

单链表函数实现函数接口//单链表申请节点函数接口
SLNode* BuySListNode(SLTDataType x) 
{SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));if (NewNode == NULL) {printf("申请节点失败\n");return;}//NewNode->data = x;NewNode->next = NULL;
}//单链表的节点打印操作
void SListPrint(SLNode* phead) 
{SLNode* cur = phead;while (cur != NULL) {printf("%d->", cur->data);cur = cur->next;}if (cur == NULL)//最后打印一个NULL{printf("NULL");}}//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点SLNode* cur = *pphead; //让cur指向phead所指向空间if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是{                                              //要改变phead的指向。*pphead = newnode;//}else {while (cur->next != NULL) //让cur遍历到最后一个节点{cur = cur->next;}cur->next = newnode;//最后}}

尾插接口时传送phead的指针的原因是因为phead可能改变指向,从空指针变为指向一个节点。要改变phead的指向那就是意味着形参要相对于phead传址调用,  而phead本身就是一个一级指针, phead取地址就是一个二级指针, 所以形参是二级指针。

---------------------------------------------------------------------------------------------------------------------------------

单链表头插函数接口

.h函数接口


链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);

.c函数实现

单链表函数实现函数接口//单链表申请节点函数接口
SLNode* BuySListNode(SLTDataType x) 
{SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));if (NewNode == NULL) {printf("申请节点失败\n");return;}//NewNode->data = x;NewNode->next = NULL;
}//单链表的节点打印操作
void SListPrint(SLNode* phead) 
{SLNode* cur = phead;while (cur != NULL) {printf("%d->", cur->data);cur = cur->next;}if (cur == NULL)//最后打印一个NULL{printf("NULL");}}//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点SLNode* cur = *pphead; //让cur指向phead所指向空间if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是{                                              //要改变phead的指向。*pphead = newnode;//}else {while (cur->next != NULL) //让cur遍历到最后一个节点{cur = cur->next;}cur->next = newnode;//最后}}//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//SLNode* cur = *pphead;newnode->next = cur;*pphead = newnode;}

现在我们来利用打印接口调试一下我们写的是否存在问题。 

在main.c中输入如下代码

void TestSListNode()
{SLNode* phead = NULL;SListPushBack(&phead, 1);SListPushBack(&phead, 2);SListPushBack(&phead, 3);SListPushBack(&phead, 4);SListPushBack(&phead, 5);SListPushFront(&phead, 0);SListPrint(phead);printf("\n");/*SListPopFront(&phead);SListPopFront(&phead);SListPopFront(&phead);SListPopBack(&phead);SListPrint(phead);printf("\n");SLTDestory(&phead);SListPrint(phead);printf("\n");*/}int main() 
{
//	TestTree();
//	TestStack();
//	TestQueue();
//	TestSeqList();TestSListNode();
//	TestDSLNode();
//	TestOJ();return 0;
}

运行图如下: 

 通过检验,没有问题。 继续往下走。 

---------------------------------------------------------------------------------------------------------------------------------

 单链表尾删函数接口

.h文件声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);

.c函数实现

  首先pphead不能为空, 如果phead指向空的话就直接返回。 然后定义cur和prev两个指针, 遍历寻找尾节点。 cur领先prev一个节点, cur指向尾节点的时候, 就释放掉这个节点。 然后prev指向空节点。 寻找尾节点的过程是这样的:

代码实现

单链表函数实现函数接口//单链表申请节点函数接口
SLNode* BuySListNode(SLTDataType x) 
{SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));if (NewNode == NULL) {printf("申请节点失败\n");return;}//NewNode->data = x;NewNode->next = NULL;
}//单链表的节点打印操作
void SListPrint(SLNode* phead) 
{SLNode* cur = phead;while (cur != NULL) {printf("%d->", cur->data);cur = cur->next;}if (cur == NULL)//最后打印一个NULL{printf("NULL");}}//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点SLNode* cur = *pphead; //让cur指向phead所指向空间if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是{                                              //要改变phead的指向。*pphead = newnode;//}else {while (cur->next != NULL) //让cur遍历到最后一个节点{cur = cur->next;}cur->next = newnode;//最后}}//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//SLNode* cur = *pphead;newnode->next = cur;*pphead = newnode;}//单链表尾删函数接口
void SListPopBack(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL)return;//SLNode* cur = *pphead;SLNode* prev = *pphead;while (cur->next != NULL) //对链表进行遍历。 cur最终会指向尾节点。 prev用来维护链表{prev = cur;cur = cur->next;}if (prev == cur) {free(cur);*pphead = NULL;}else {free(cur);prev = NULL;}}

---------------------------------------------------------------------------------------------------------------------------------

单链表的头删函数接口

.h函数声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);

.c函数实现 

单链表函数实现函数接口//单链表申请节点函数接口
SLNode* BuySListNode(SLTDataType x) 
{SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));if (NewNode == NULL) {printf("申请节点失败\n");return;}//NewNode->data = x;NewNode->next = NULL;
}//单链表的节点打印操作
void SListPrint(SLNode* phead) 
{SLNode* cur = phead;while (cur != NULL) {printf("%d->", cur->data);cur = cur->next;}if (cur == NULL)//最后打印一个NULL{printf("NULL");}}//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点SLNode* cur = *pphead; //让cur指向phead所指向空间if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是{                                              //要改变phead的指向。*pphead = newnode;//}else {while (cur->next != NULL) //让cur遍历到最后一个节点{cur = cur->next;}cur->next = newnode;//最后}}//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//SLNode* cur = *pphead;newnode->next = cur;*pphead = newnode;}//单链表尾删函数接口
void SListPopBack(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL)return;//SLNode* cur = *pphead;SLNode* prev = *pphead;while (cur->next != NULL) //对链表进行遍历。 cur最终会指向尾节点。 prev用来维护链表{prev = cur;cur = cur->next;}if (prev == cur) {free(cur);*pphead = NULL;}else {free(cur);prev = NULL;}}//单链表的头删函数接口
void SListPopFront(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL)return;//SLNode* cur = *pphead;*pphead = cur->next;free(cur);
}

代码的意思是, 首先pphead不能为空, 然后phead不能指向空。 然后让一个cur指针指向头节点。 然后修改phead的指向, 使其指向第二个节点(当第二个节点是空的时候, 就是指向空)。然后释放cur指向的节点也就是头节点。 如图为过程:

---------------------------------------------------------------------------------------------------------------------------------

 单链表查找函数接口

.h接口声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);
//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x);

.c接口实现

单链表函数实现函数接口//单链表申请节点函数接口
SLNode* BuySListNode(SLTDataType x) 
{SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));if (NewNode == NULL) {printf("申请节点失败\n");return;}//NewNode->data = x;NewNode->next = NULL;
}//单链表的节点打印操作
void SListPrint(SLNode* phead) 
{SLNode* cur = phead;while (cur != NULL) {printf("%d->", cur->data);cur = cur->next;}if (cur == NULL)//最后打印一个NULL{printf("NULL");}}//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点SLNode* cur = *pphead; //让cur指向phead所指向空间if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是{                                              //要改变phead的指向。*pphead = newnode;//}else {while (cur->next != NULL) //让cur遍历到最后一个节点{cur = cur->next;}cur->next = newnode;//最后}}//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//SLNode* cur = *pphead;newnode->next = cur;*pphead = newnode;}//单链表尾删函数接口
void SListPopBack(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL)return;//SLNode* cur = *pphead;SLNode* prev = *pphead;while (cur->next != NULL) //对链表进行遍历。 cur最终会指向尾节点。 prev用来维护链表{prev = cur;cur = cur->next;}if (prev == cur) {free(cur);*pphead = NULL;}else {free(cur);prev = NULL;}}//单链表的头删函数接口
void SListPopFront(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL)return;//SLNode* cur = *pphead;*pphead = cur->next;free(cur);
}//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x) 
{SLNode* cur = phead;//定义一个指向头节点的指针, 用于遍历while (cur != NULL) //向后遍历{if (cur->data == x) //找到节点后就返回节点的地址。{return cur;}cur = cur->next;}return NULL;
}

 代码太长, 之后.c文件的代码只展示相应接口的代码

---------------------------------------------------------------------------------------------------------------------------------

单链表pos位置之后插入数据接口

.h接口声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);
//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x);
//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x);

.c接口实现 


//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x) 
{assert(pos);SLNode* newnode = BuySListNode(x);//SLNode* cur = pos->next;newnode->next = cur;pos->next = newnode;
}

 该接口的实现过程如下:

令指针cur指向pos的下一个节点, newnode的next指向cur, pos的next指向newnode

---------------------------------------------------------------------------------------------------------------------------------

单链表删除pos之后位置的数据

.h接口声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);
//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x);
//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x);
//单链表在pos之后的位置删除数据
void SListPopAfter(SLNode* pos);

.c接口实现


//单链表在pos之后的位置删除数据
void SListPopAfter(SLNode* pos) 
{assert(pos);//SLNode* cur = pos->next;pos->next = pos->next->next;free(cur);
}

该接口实现和单链表在pos位置之后插入数据接口实现方式类似, 都是利用一个cur指针指向pos之后的位置作为记忆保存,然后进行插入或者删除操作。 

---------------------------------------------------------------------------------------------------------------------------------

单链表在pos位置之前插入数据接口

该接口的实现有点复杂, 但是实现该接口之后, 对于尾插还有头插就很好实现了, 尾插和头插是该接口的两个特殊情况。 假如pos是头节点,就是头插, 假如pos是尾节点, 就是尾插。

.h接口声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);
//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x);
//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x);
//单链表在pos之后的位置删除数据
void SListPopAfter(SLNode* pos);
//单链表在pos位置之前插入数据接口
void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);

.c接口实现


//单链表在pos位置之前插入数据接口
void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x) 
{assert(pphead);//SLNode* newnode = BuySListNode(x);//if (*pphead == NULL) {*pphead = newnode;return;}//if (*pphead == pos) {newnode->next = pos;*pphead = newnode;return;}SLNode* cur = *pphead;while (cur != NULL && cur->next != pos) {cur = cur->next;}newnode->next = pos;cur->next = newnode;
}

该接口分为三种情况:

第一种是链表为空, 这个时候直接插入节点。

第二种情况是pos的位置在第一个节点的位置, 这个时候需要改变phead的指向。 

第三种情况就是最普通的情况, 在除头节点, 链表的任意节点前插入。如图:

 

---------------------------------------------------------------------------------------------------------------------------------

单链表删除pos位置数据接口

和pos位置插入数据接口一样, 实现了该接口对于尾删, 头删接口就很简单了。 头删, 尾删都是单链表删除pos位置数据接口的特殊情况。 pos是头节点, 就是头删, pos是尾节点。 就是尾删。 

.h接口声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);
//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x);
//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x);
//单链表在pos之后的位置删除数据
void SListPopAfter(SLNode* pos);
//单链表在pos位置之前插入数据接口
void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
//单链表在pos位置删除数据接口
void SListPop(SLNode** pphead, SLNode* pos);

.c接口实现


//单链表在pos位置删除数据接口
void SListPop(SLNode** pphead, SLNode* pos) 
{assert(pphead);//if (*pphead == pos) {*pphead = (*pphead)->next;free(pos);}else {SLNode* cur = *pphead;while (cur->next != pos) {cur->next = pos->next;free(pos);}}
}

pos位置删除分两种情况

一种是头删, 需要phead改变指向。

 一种是其他位置删除节点

单链表的销毁

 链表使用完之后应该销毁, 放置内存泄漏

.h接口声明

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);
//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x);
//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x);
//单链表在pos之后的位置删除数据
void SListPopAfter(SLNode* pos);
//单链表在pos位置之前插入数据接口
void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
//单链表在pos位置删除数据接口
void SListPop(SLNode** pphead, SLNode* pos);
//单链表的销毁函数接口
void SLTDestory(SLNode** pphead);

.c接口实现 


//单链表的销毁函数接口
void SLTDestory(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL) {return;}SLNode* prev = (*pphead);SLNode* cur = (*pphead)->next;if (cur == NULL) {*pphead = NULL;free(prev);}else {*pphead = NULL;while(cur != NULL){free(prev);prev = cur;cur = cur->next;}free(prev);}}

销毁需要一步一步的进行, 如下图:

现在来看一下总体代码:

.h文件

链表的增删查改///
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLTDataType;typedef struct SListNode 
{struct SListNode* next;SLTDataType data;
}SLNode;//链表接口声明///申请链表节点函数接口
SLNode* BuySListNode(SLTDataType x);
//单链表的打印函数接口
void SListPrint(SLNode* phead);
//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x);
//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x);
//单链表尾删函数接口
void SListPopBack(SLNode** pphead);
//单链表的头删函数接口
void SListPopFront(SLNode** pphead);
//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x);
//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x);
//单链表在pos之后的位置删除数据
void SListPopAfter(SLNode* pos);
//单链表在pos位置之前插入数据接口
void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
//单链表在pos位置删除数据接口
void SListPop(SLNode** pphead, SLNode* pos);
//单链表的销毁函数接口
void SLTDestory(SLNode** pphead);

.c文件

单链表函数实现函数接口//单链表申请节点函数接口
SLNode* BuySListNode(SLTDataType x) 
{SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));if (NewNode == NULL) {printf("申请节点失败\n");return;}//NewNode->data = x;NewNode->next = NULL;
}//单链表的节点打印操作
void SListPrint(SLNode* phead) 
{SLNode* cur = phead;while (cur != NULL) {printf("%d->", cur->data);cur = cur->next;}if (cur == NULL)//最后打印一个NULL{printf("NULL");}}//单链表尾插函数接口
void SListPushBack(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点SLNode* cur = *pphead; //让cur指向phead所指向空间if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是{                                              //要改变phead的指向。*pphead = newnode;//}else {while (cur->next != NULL) //让cur遍历到最后一个节点{cur = cur->next;}cur->next = newnode;//最后}}//单链表头插函数接口
void SListPushFront(SLNode** pphead, SLTDataType x) 
{assert(pphead);SLNode* newnode = BuySListNode(x);//SLNode* cur = *pphead;newnode->next = cur;*pphead = newnode;}//单链表尾删函数接口
void SListPopBack(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL)return;//SLNode* cur = *pphead;SLNode* prev = *pphead;while (cur->next != NULL) //对链表进行遍历。 cur最终会指向尾节点。 prev用来维护链表{prev = cur;cur = cur->next;}if (prev == cur) {free(cur);*pphead = NULL;}else {free(cur);prev = NULL;}}//单链表的头删函数接口
void SListPopFront(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL)return;//SLNode* cur = *pphead;*pphead = cur->next;free(cur);
}//单链表查找函数接口
SLNode* SListFind(SLNode* phead, SLTDataType x) 
{SLNode* cur = phead;//定义一个指向头节点的指针, 用于遍历while (cur != NULL) //向后遍历{if (cur->data == x) //找到节点后就返回节点的地址。{return cur;}cur = cur->next;}return NULL;
}//单链表在pos位置之后插入数据接口
void SListInsertAfter(SLNode* pos, SLTDataType x) 
{assert(pos);SLNode* newnode = BuySListNode(x);//SLNode* cur = pos->next;newnode->next = cur;pos->next = newnode;
}//单链表在pos之后的位置删除数据
void SListPopAfter(SLNode* pos) 
{assert(pos);//SLNode* cur = pos->next;pos->next = pos->next->next;free(cur);
}//单链表在pos位置之前插入数据接口
void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x) 
{assert(pphead);//SLNode* newnode = BuySListNode(x);//if (*pphead == NULL) {*pphead = newnode;return;}//if (*pphead == pos) {newnode->next = pos;*pphead = newnode;return;}SLNode* cur = *pphead;while (cur != NULL && cur->next != pos) {cur = cur->next;}newnode->next = pos;cur->next = newnode;
}//单链表在pos位置删除数据接口
void SListPop(SLNode** pphead, SLNode* pos) 
{assert(pphead);//if (*pphead == pos) {*pphead = (*pphead)->next;free(pos);}else {SLNode* cur = *pphead;while (cur->next != pos) {cur->next = pos->next;free(pos);}}
}//单链表的销毁函数接口
void SLTDestory(SLNode** pphead) 
{assert(pphead);if (*pphead == NULL) {return;}SLNode* prev = (*pphead);SLNode* cur = (*pphead)->next;if (cur == NULL) {*pphead = NULL;free(prev);}else {*pphead = NULL;while(cur != NULL){free(prev);prev = cur;cur = cur->next;}free(prev);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/717532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

位运算---求n的二进制表示中第k位是1还是0 (lowbit)

操作&#xff1a; 先把第k位移到最后一位&#xff08;右边第一位&#xff09; 看个位是1还是0 lowbit(x)&#xff1a;返回x的最右边的1。 原理&#xff1a; 其中 &#xff0c;意思是 是 的补码。 就可以求出最右边的一位1。 应用&#xff1a; 当中 的个数。 int re…

AI-数学-高中-33概率-事件的关系与运算

原作者视频&#xff1a;【概率】【一数辞典】2事件的关系与运算_哔哩哔哩_bilibili 事件&#xff1a; 和/并事件&#xff1b;积/交事件&#xff1b;互诉事件&#xff1b;对立(补集)事件&#xff1b;

【详识JAVA语言】面向对象程序三大特性之二:继承

继承 为什么需要继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是 现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程序是就需要考虑。 比如&…

04.其他方案

其他方案 1.事务状态表调⽤⽅重试接收⽅幂等 介绍 调⽤⽅维护⼀张事务状态表&#xff08;或者说事务⽇志、⽇志流⽔&#xff09;&#xff0c;在每次调⽤之前&#xff0c;落盘⼀条事务流⽔&#xff0c;⽣成⼀个全局的事务ID 事务开始之前的状态是Begin&#xff0c;全部结束之…

码垛工作站:食品生产企业的转型助推器

在当今高度自动化的工业生产中&#xff0c;码垛工作站的应用正逐渐成为一种趋势。某食品生产企业在面临市场竞争加剧、人工成本上升等多重压力下&#xff0c;决定引入码垛工作站&#xff0c;以期实现生产流程的升级与变革。 一、码垛工作站引入背景 该企业主要从事休闲食品的…

【贪心算法】Leetcode 455.分发饼干 376. 摆动序列 53. 最大子数组和

【贪心算法】Leetcode 455 分发饼干 376. 摆动序列【规律很多】53. 最大子数组和 455 分发饼干局部最优推全局最优&#xff1a;尽量用大饼干去满足大胃口的小朋友 376. 摆动序列【规律很多】思想&#xff1a;注意考虑一个坡度留首尾两个点、平坡、首尾 53. 最大子数组和【好思想…

15.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-发送通信数据包至分析工具

上一个内容&#xff1a;14.数据包分析工具界面与通信设计 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;2d6491e3c51a1a7ab4da0ee6dc4cf566a80fd6e1 代码下载地址&#xff0c;在 titan 目录下&…

模版进阶C++

非类型模版 之前我们写的模版都是在不知道模版&#xff08;类&#xff09;中有的变量的类型是什么的时候&#xff0c;我们先用模版参数定义&#xff0c;当类实例化的时候在传参确认 非类型模版&#xff1a;模版参数定义的时候也可以定义整型类型&#xff08;c20之后才支持其…

奇点云:SAFe框架下,我们对平台软件工程生产线做了4项改造

导读&#xff1a; 客户规模扩大&#xff0c;如何保证大数据软件产品和服务质量始终如一&#xff1f;几乎所有成长中的软件厂商&#xff0c;尤其是需要通过私有化部署交付的厂商&#xff0c;都会面临这个问题。正如《人月神话》中多次表明的&#xff0c;单纯地增加人手、扩大团队…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的植物病害检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发高效的植物病害检测系统对于提升农业生产效率和作物健康管理意义重大。本篇博客详细阐述了如何运用深度学习技术构建一个植物病害检测系统&#xff0c;并提供了完整的实现代码。该系统基于先进的YOLOv8算法&#xff0c;对YOLOv7、YOLOv6、YOLOv5进行了性能…

考研数学——高数:微分方程

一、一阶线性微分方程 两种形式&#xff1a; 非齐次&#xff1a; 齐次&#xff1a; 推导过程 推导公式的过程一般由特殊到一般&#xff1a;所以先求解齐次方程的解 &#xff08;然后对等式两边同时积分&#xff09; 再来求非齐次方程的解&#xff0c;由…

03-grafana的下拉列表选项制作-grafana的变量

一、准备环境 为了实现下拉列表筛选的样例&#xff0c;我们监控两个linux节点&#xff1b; 目前&#xff0c;我们已经有了一个节点了&#xff0c;再添加一个&#xff1b; 二、grafana的仪表盘变量 如果想给仪表盘自定义下拉列表&#xff0c;那么&#xff0c;需要设置变量&#…

线上问题——2021-12-27 父子线程共用线程池导致死锁故障

一、事故现象 从早上6点开始edu-wings-admin的timer-task和mq就开始报警任务堆积&#xff0c;且数量持续上升&#xff0c;到6点50左右mq也开始告警&#xff0c;8点左右发现问题&#xff0c;开始排查&#xff0c;直到11点才找到问题&#xff0c;任务开始正常消费。 二、事故影响…

基于springboot+vue的疾病防控综合系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

计算机设计大赛 深度学习猫狗分类 - python opencv cnn

文章目录 0 前言1 课题背景2 使用CNN进行猫狗分类3 数据集处理4 神经网络的编写5 Tensorflow计算图的构建6 模型的训练和测试7 预测效果8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习猫狗分类 ** 该项目较为新颖&a…

Python测试框架pytest介绍用法

1、介绍 pytest是python的一种单元测试框架&#xff0c;同自带的unittest测试框架类似&#xff0c;相比于unittest框架使用起来更简洁、效率更高 pip install -U pytest 特点&#xff1a; 1.非常容易上手,入门简单,文档丰富&#xff0c;文档中有很多实例可以参考 2.支持简单的单…

C++内存模型与内存序

写在前面 在真正了解Memory Order的作用之前&#xff0c;曾经简单地将Memory Order等同于mutex和atomic来进行线程间数据同步&#xff0c;或者用来限制线程间的执行顺序&#xff0c;其实这是一个错误的理解。直到后来仔细研究了Memory Order之后&#xff0c;才发现无论是功能还…

力扣706:设计哈希映射

题目&#xff1a; 不使用任何内建的哈希表库设计一个哈希映射&#xff08;HashMap&#xff09;。 实现 MyHashMap 类&#xff1a; MyHashMap() 用空映射初始化对象void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中&#x…

【GPU驱动开发】- mesa编译与链接过程详细分析

前言 不必害怕未知&#xff0c;无需恐惧犯错&#xff0c;做一个Creator&#xff01; 一、总体框架图 暂时无法在飞书文档外展示此内容 二、Mesa API 处理 OpenGL 函数调用 Mesa API 负责实现 OpenGL 和其他图形 API 的函数接口。Mesa API 表是一个重要的数据结构&#xf…

数据中台的演进与实践——构建企业的数字核心_光点科技

数据中台&#xff0c;一个在近年来被频繁提及的概念&#xff0c;已经成为众多企业数字化转型的核心组成部分。然而&#xff0c;尽管它的重要性被业界广泛认可&#xff0c;对于数据中台的深入理解和有效实践仍然是许多企业面临的挑战。在本文中&#xff0c;我们将从数据中台的演…