单链表——增删查改

   本节复习链表的增删查改

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

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

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

目录

单链表的全部接口:

 准备文件

建立结构体蓝图

申请链表节点函数接口

单链表的打印函数接口

单链表尾插函数接口

单链表头插函数接口

 单链表尾删函数接口

单链表的头删函数接口

 单链表查找函数接口

单链表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);
	}

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows达梦数据库(下载及使用)

解压安装包 点击最后一个文件 下一步 接受 下一步 下一步 下一步 点击初始化 开始

k8s-Istio服务网络 27

官网&#xff1a;https://istio.io/latest/zh/about/service-mesh/ Istio与k8s的区别 SpringCloud传统微服务结合k8s与Istio与k8s结合&#xff1a; Istio数据面&#xff1a;通过envoy以sidecar方式拦截svc的流量来进行治理。 Istio控制面&#xff1a;pilot list/watch APIserv…

数字孪生与智慧城市:实现城市治理现代化的新路径

随着信息技术的迅猛发展&#xff0c;智慧城市已成为城市发展的必然趋势。数字孪生技术作为智慧城市建设的重要支撑&#xff0c;以其独特的优势为城市治理现代化提供了新的路径。本文将探讨数字孪生技术在智慧城市中的应用&#xff0c;以及如何实现城市治理的现代化。 一、数字…

HADOOP完全分布式搭建(饭制版)

HADOOP完全分布式搭建&#xff08;饭制版&#xff09; 1.虚拟机安装 安装系统 点击VMware Workstation左上角文件&#xff0c;新建虚拟机 选择自定义&#xff0c;点击下一步 点击下一步 选择稍后安装操作系统&#xff08;后续我们使用的操作系统为CentOS7&#xff09;,点击…

【研发日记】,Matlab/Simulink开箱报告(十)——Requirements Toolbox

前言 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;五&#xff09;——S-Fuction模块(C MEX S-Function)》 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;六&#xff09;——S-Fuction模块&#xff08;TLC&#xff09;》 见《开…

2.案例、鼠标时间类型、事件对象参数

案例 注册事件 <!-- //disabled默认情况用户不能点击 --><input type"button" value"我已阅读用户协议(5)" disabled><script>// 分析&#xff1a;// 1.修改标签中的文字内容// 2.定时器// 3.修改标签的disabled属性// 4.清除定时器// …

构建部署_Jenkins介绍与安装

构建部署_Jenkins介绍与安装 构建部署_Jenkins介绍与安装Jenkins介绍Jenkins安装 构建部署_Jenkins介绍与安装 Jenkins介绍 Jenkins是一个可扩展的持续集成引擎。 持续集成&#xff0c;就是通常所说的CI&#xff08;Continues Integration&#xff09;&#xff0c;可以说是现…

Ajax(2)

图片上传 传图片文件不能像传文字一样用JSON格式&#xff0c;可以用form-data类型携带文件 1.获取图片文件对象 2.使用FormData&#xff08;浏览器内置的构造函数&#xff09;携带图片文件 3.提交表单数据到服务器&#xff0c;返回图片网址 这里可能用到的事件监听器&#…

[算法] 牛课题霸 - DP6 连续子数组最大和 - 动态规划

文章目录 题目链接解题过程思路一思路二 题目链接 DP6 连续子数组最大和 解题过程 思路一 两个for循环&#xff0c;遍历。 因为每个元素都要遍历两遍&#xff0c;所以时间复杂度O(n^2)。 简单的测试用例可以通过&#xff0c;但是提交时&#xff0c;一个巨大的数组用例&…

Copilot如何将word文稿一键转为PPT

背景 很多小伙伴平时经常会遇到的一个场景是&#xff0c;如何将word文稿图文转为PPT。 这个过程是既复杂而又无趣的。 现在&#xff0c;有了copilot&#xff0c;你可以一键搞定&#xff01; 使用copilot Pro来实现 比如我们想要做一个关于copilot studio的PPT展示&#xf…

Unix环境高级编程-学习-05-TCP/IP协议与套接字

目录 一、概念 二、TCP/IP参考模型 三、客户端和服务端使用TCP通信过程 1、同一以太网下 四、函数介绍 1、socket &#xff08;1&#xff09;声明 &#xff08;2&#xff09;作用 &#xff08;3&#xff09;参数 &#xff08;4&#xff09;返回值 &#xff08;5&…

【Python】新手入门学习:详细介绍单一职责原则(SRP)及其作用、代码示例

【Python】新手入门学习&#xff1a;详细介绍单一职责原则&#xff08;SRP&#xff09;及其作用、代码示例 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyT…

MySQL一些命令记录

查看数据引擎 show engines;创建数据库,并选择库 CREATE DATABASE IF NOT EXISTS test_database; USE test_database;创建表 CREATE TABLE IF NOT EXISTS test_table (id INT AUTO_INCREMENT PRIMARY KEY,field1 VARCHAR(50),field2 VARCHAR(50),field3 VARCHAR(50),field4 …

https超文本传输安全协议到底是什么?

HTTPS&#xff08;全称&#xff1a;Hyper Text Transfer Protocol over Secure Socket Layer&#xff09;是超文本传输安全协议的英文翻译缩写&#xff0c;它是以安全为目标的HTTP通道&#xff0c;在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性。HTTPS在HTTP的基…

基于SSM的协同过滤算法的电影推荐系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的协同过滤算法的电影推荐系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通…

盘点9款AI论文写作神器,轻松写出高质量论文

0. 未来百科 未来百科&#xff0c;是一个全球最大的 AI 产品导航网站 —— 为发现全球优质 AI 工具而生 。目前已 聚集全球 10000优质 AI 工具产品 &#xff0c;旨在帮助用户发现全球最好的 AI 工具&#xff0c;同时为研发 AI 垂直应用的创业公司提供展示窗口&#xff0c;迎接…

如何使用vue定义组件之——父组件调用子组件

首先&#xff0c;我们需要创建两个组件模板template&#xff1a; <template id"father"><div><h3>我是父组件</h3><h3>访问自己的数据:</h3><h3>{{ msg }}</h3></div></template><template id"…

C#多线程(5)——异步方法async与await

在上一章节中&#xff0c;为大家介绍了C#多线程&#xff08;4&#xff09;——任务并行库TPL&#xff0c;TPL是从.NetFramwork4.0后引入的基于异步操作的一组API&#xff0c;核心关注于任务【 T a s k 和 T a s k < T > \textcolor{red}{Task 和 Task<T>} Task和Ta…

HarmonyOS NEXT应用开发之下拉刷新与上滑加载案例

介绍 本示例介绍使用第三方库的PullToRefresh组件实现列表的下拉刷新数据和上滑加载后续数据。 效果图预览 使用说明 进入页面&#xff0c;下拉列表触发刷新数据事件&#xff0c;等待数据刷新完成。上滑列表到底部&#xff0c;触发加载更多数据事件&#xff0c;等待数据加载…

基于Springboot的集团门户网站(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的集团门户网站&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…