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

 本节复习链表的增删查改

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

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

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

目录

单链表的全部接口:

 准备文件

建立结构体蓝图

申请链表节点函数接口

单链表的打印函数接口

单链表尾插函数接口

单链表头插函数接口

 单链表尾删函数接口

单链表的头删函数接口

 单链表查找函数接口

单链表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.hqwc.cn/news/496812.html

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

相关文章

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的水果识别系统(Python+PySide6界面+训练代码)

摘要&#xff1a;本篇博客详尽介绍了一套基于深度学习的水果识别系统及其实现代码。系统采用了尖端的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等前代算法进行了详细的性能对比分析&#xff0c;提供在识别图像、视频、实时视频流和批量文件中水果方面的高效准确性。文…

Rocky Linux 运维工具 mv

一、mv的简介 ​​mv​是Linux系统中的命令&#xff0c;用于移动文件或重命名文件。它可以在同一文件系统内将文件从一个目录移动到另一个目录&#xff0c;也可以修改文件的名称。 二、mv的参数说明 1、 三、mv的实战示例 1、重命名 ###查看目录/root/下的文件列表 [rootloc…

selenium-激活pycharm,以及在pycharm中使用selenium时标红报错问题处理

激活pycharm&#xff1a;http://idea.955code.com/ 01 pycharm中导入selenium报错 现象: pycharm中输入from selenium import webdriver, selenium标红 原因1: pycharm使用的虚拟环境中没有安装selenium&#xff1a; 解决方法: 在pycharm中通过设置或terminal面板重新安装s…

代码随想录算法训练营第三十六天|背包理论基础,416. 分割等和子集

系列文章目录 代码随想录算法训练营第一天|数组理论基础&#xff0c;704. 二分查找&#xff0c;27. 移除元素 代码随想录算法训练营第二天|977.有序数组的平方 &#xff0c;209.长度最小的子数组 &#xff0c;59.螺旋矩阵II 代码随想录算法训练营第三天|链表理论基础&#xff…

鸿运(通天星CMSV6车载)主动安全监控云平台敏感信息泄露漏洞

文章目录 前言声明一、系统简介二、漏洞描述三、影响版本四、漏洞复现五、修复建议 前言 鸿运主动安全监控云平台实现对计算资源、存储资源、网络资源、云应用服务进行7*24小时全时区、多地域、全方位、立体式、智能化的IT运维监控&#xff0c;保障IT系统安全、稳定、可靠运行…

35岁了,还能转行做鸿蒙开发吗?

随着互联网行业的蓬勃发展时&#xff0c;不止从何时网上开始就有了&#xff1a;“程序员30岁危机、35岁中年危机”这种类似的话题&#xff0c;可以说影响了不少程序员。 人们一般常说的是三十而立&#xff0c;一个人应该对生活、职业、个人信仰等方面有了明确的认识和规划&…

creo怎么更改公制单位

打开软件-文件-选项-配置编辑器-里面有三项分别是1.装配体的&#xff1b;2.钣金的&#xff1b;3.零件 将三个后面的值进行更改&#xff0c;文件位置在&#xff08;桌面creo图标右键属性目标里的链接里&#xff09; 选中mmns的模版。 全部改完后选中左下角的导出配置&#xff0…

自然语言:信息抽取技术在CRM系统中的应用与成效

一、引言 在当今快速变化的商业环境中&#xff0c;客户关系管理&#xff08;CRM&#xff09;已成为企业成功的关键因素。CRM系统的核心在于有效地管理客户信息&#xff0c;跟踪与客户的每一次互动&#xff0c;以及深入分析这些数据以提升客户满意度和忠诚度。在我最近参与的一个…

2.23作业

1.自己实现单向循环链表的功能 //loop_list.c#include"loop_list.h" //创建单向循环链表 loop_p create_head() {loop_p H(loop_p)malloc(sizeof(loop_list));if(HNULL){printf("空间申请失败\n");return NULL;}H->len0;H->nextH;return H; }//创建…

项目解决方案:海外门店视频汇聚方案(全球性的连锁店、国外连锁店视频接入和汇聚方案)

目 录 一、概述 二、建设目标及需求 2.1 建设目标 2.2 需求描述 2.3 需求分析 三、建设方案设计 3.1 系统方案拓扑图 3.2 方案描述 3.3 服务器配置推荐 四、产品功能 4.1 资源管理平台 &#xff08;1&#xff09;用户权限管理 &#xff08;2&#xff09…

Web前端---图层嵌套与层叠三行三列效果

1.图层的嵌套设计 <!doctype html> <html> <head> <meta charset"utf-8"> <title>图层嵌套</title><style type"text/css">.inline_div{display:inline-block;}#wrap{width400px;height250px;border:2px solid…

Git实用命令

撤销操作 # 撤销暂存&#xff0c;保留修改内容 git reset HEAD <file># 撤销修改 git checkout -- <file>远程仓库操作 #添加远程仓库git remote add demo https://gitee.com/dwp1216/demo.git拉取指定文件 git init git remote add -f origin https://172.16.0.…