单链表——增删查改

news2024/10/6 20:30:04

   本节复习链表的增删查改

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

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

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

目录

单链表的全部接口:

 准备文件

建立结构体蓝图

申请链表节点函数接口

单链表的打印函数接口

单链表尾插函数接口

单链表头插函数接口

 单链表尾删函数接口

单链表的头删函数接口

 单链表查找函数接口

单链表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.coloradmin.cn/o/1514509.html

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

相关文章

JVM之调优(一)

背景&#xff1a;生产环境由于堆内存较大&#xff0c;fullgc 垃圾回收导致程序卡顿问题&#xff08;假死&#xff09; 目录 一、程序卡顿导致的影响 前端页面空白后端数据重复 二、解决方法 降低堆内存大小使用合适的垃圾回收器&#xff08;可以尝试&#xff0c;还未进行测试…

【毕设级项目】基于嵌入式的智能家居控制板(完整工程资料源码)

基于嵌入式的智能家居控制板演示效果 基于嵌入式的智能家居控制板 前言&#xff1a; 随着科技的不断进步&#xff0c;物联网技术得到了突飞猛进的发展。智能家居是物联网技术的典型应用领域之一。智能家居系统将独立家用电器、安防设备连接成一个具有思想的整体&#xff0c;实现…

Android Studio入门——页面跳转

1.工程目录 2.MainActivity package com.example.demo01;import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCo…

数据集成工具 ---- datax 3.0

1、datax: 是一个异构数据源离线同步工具&#xff0c;致力于实现关系型数据库&#xff08;mysql、oracle等&#xff09;hdfs、hive、hbase等各种异构数据源之间的数据同步 2、参考网址文献&#xff1a; https://github.com/alibaba/DataX/blob/master/introduction.mdhttps:/…

pycharm配置解释器

pycharm配置解释器 1.mac配置解释器 1.mac配置解释器

【C++庖丁解牛】STL简介 | string容器初次见面

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. 什么是STL2. STL的版本…

扫描全能王发布2023“绿色数据”:减碳超12万吨,相当于多种700万棵树

近年来&#xff0c;“绿色转型”“低碳生活”成为全民热议话题。从全国覆盖率越来越高的“垃圾分类”&#xff0c;到蓬勃发展的“无纸化办公”&#xff0c;低碳生活、绿色消费的环保风尚不断兴起。植树节将至&#xff0c;合合信息旗下扫描全能王发布了年度用户文档扫描数据&…

详细分析Mysql中的LOCATE函数(附Demo)

目录 1. 基本概念2. Demo3. 实战 1. 基本概念 LOCATE()函数在SQL中用于在字符串中查找子字符串的位置 它的一般语法如下&#xff1a; LOCATE(substring, string, start)LOCATE()函数返回子字符串在主字符串中第一次出现的位置 如果未找到子字符串&#xff0c;则返回0 具体的…

如何在群晖用Docker本地搭建Vocechat聊天服务并无公网ip远程交流协作

文章目录 1. 拉取Vocechat2. 运行Vocechat3. 本地局域网访问4. 群晖安装Cpolar5. 配置公网地址6. 公网访问小结 7. 固定公网地址 如何拥有自己的一个聊天软件服务? 本例介绍一个自己本地即可搭建的聊天工具,不仅轻量,占用小,且功能也停强大,它就是Vocechat. Vocechat是一套支持…

【BFS二叉树】113路径总和II

113路径总和 II 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 思路&#xff1a; 题目最终输出的是路径&#xff0c;因此用BFS遍历的时候&#xff0c;需要记录走到每个节点的路径&#xff1…

ISIS单区域实验简述

ISIS 中间系统到中间系统&#xff0c;也是链路状态协议&#xff0c;工作在数据链路层&#xff0c;不依赖IP地址&#xff1b;与OSPF一样采用最短路径SPF算法&#xff0c;收敛速度快。 实验基础配置&#xff1a; r1: sys sysname r1 undo info enable int g0/0/0 ip add 12.1.1.1…

基于XMind的E-R图制作【笔记】

基于XMind的E-R图制作【笔记】 前言版权基于XMind的E-R图制作1.打开XMind2.选择模板3.插入一个自由主题4.为它插入子主题5.快速插入子主题6. 统一设置子主题样式 最后 前言 2024-3-11 10:36:33 以下内容源自《【创作模板】》 仅供学习交流使用 版权 禁止其他平台发布时删除…

js【详解】ajax (含XMLHttpRequest、 同源策略、跨域)

ajax 的核心API – XMLHttpRequest get 请求 // 新建 XMLHttpRequest 对象的实例 const xhr new XMLHttpRequest(); // 发起 get 请求&#xff0c;open 的三个参数为&#xff1a;请求类型&#xff0c;请求地址&#xff0c;是否异步请求&#xff08; true 为异步&#xff0c;f…

程序语言设计

一、程序设计语言及其构成 1.程序设计语言 2.高级程序设计语言划分 3.常见的高级程序语言 4.标记语言 5.程序设计语言的构成 二、表达式 表达式的类型及转换规则 三、传值和传址调用 1.数据类型 2.传值和传址调用 四、语言处理程序 1.语言处理程序 语言处理程序&#xff1…

kangle一键安装脚本

Kangle一键脚本&#xff0c;是一款可以一键安装KangleEasypanelMySQLPHP集合的Linux脚本。 脚本本身集成&#xff1a;PHP5.38.2、MYSQL5.68.0&#xff0c;支持极速安装和编译安装2种模式&#xff0c;支持CDN专属安装模式。同时也对Easypanel面板进行了大量优化。 脚本特点 ◎…

FreeRTOS 的任务挂起和恢复

1. 任务挂起和恢复的 API 函数 API函数描述vTaskSuspend()挂起任务vTaskResume()恢复被挂起的任务xTaskResumeFromISR()在中断中恢复被挂起的任务 挂起&#xff1a;挂起任务类似暂停&#xff0c;可恢复&#xff1b; 删除任务&#xff0c;堆栈都给释放掉了&#xff0c;无法恢复…

某电信公司组织结构优化咨询项目成功案例纪实

——构建前后端组织结构&#xff0c;提升组织运营效率 随着企业的不断发展&#xff0c;行业的竞争也越来越激烈&#xff0c;企业只能不断调整自身的战略才能更好的适应这样的大环境。在战略调整的过程中&#xff0c;企业往往会面临这样的问题&#xff1a;管理层的经营理念各不…

从0到1快速搭建一个jeecg 企业级应用管理后台

一. 基本介绍 官网地址&#xff1a;https://jeecg.com/ JeecgBoot 是一款企业级的低代码平台&#xff01;前后端分离架构 SpringBoot2.x&#xff0c;SpringCloud&#xff0c;Ant Design&Vue3&#xff0c;Mybatis-plus&#xff0c;Shiro&#xff0c;JWT 支持微服务。强大的…

Linux内核编译(版本6.0以及版本v0.01)并用qemu驱动

系统环境&#xff1a; ubuntu-22.04.1-desktop-amd64 目标平台: x86 i386 内核版本: linux-6.0.1 linux-0.0.1 环境配置 修改root密码 sudo passwd 修改软件源&#xff08;非必要&#xff09; vmtools安装&#xff08;实现win-linux软件互传&#xff09; 安装一些必须的软件&…

day02vue学习

day02 一、今日学习目标 1.指令补充 指令修饰符v-bind对样式增强的操作v-model应用于其他表单元素 2.computed计算属性 基础语法计算属性vs方法计算属性的完整写法成绩案例 3.watch侦听器 基础写法完整写法 4.综合案例 &#xff08;演示&#xff09; 渲染 / 删除 / 修…