C++链表从概念到代码,手把手教你实现增删查改

网安智编 厦门萤点网络科技 2025-12-16 20:33 50 0
目录 1.链表的概念 ​编辑 2.链表的雏形 ​编辑 3.链表的组成 ​编辑 4.链表代码 4.1创建节点 4.2链表的打印 4.3链表的尾插 4.4链表的头插 4.5链表的尾删 4.6链表的头删 4.7链表的查找 4.8链表在指定位置之前...

目录

1.链表的概念

​编辑 2.链表的雏形

​编辑 3.链表的组成

​编辑 4.链表代码

4.1创建节点

4.2链表的打印

4.3链表的尾插

4.4链表的头插

4.5链表的尾删

4.6链表的头删

4.7链表的查找

4.8链表在指定位置之前插⼊数据

4.9链表在指定位置之后插⼊数据

4.9-1删除pos节点

4.9-2删除pos之后的节点

4.9-3销毁链表

补充:测试文件:

1.链表的概念

【零基础学数据结构】链表

2.链表的雏形

【零基础学数据结构】链表

3.链表的组成

【零基础学数据结构】链表

4.链表代码

创建准备;

4.1创建节点

数据+指向下一个节点的指针

// 首先定义数据的类型,方便后续更改
typedef int SLTNodeDataType;
// 创建节点
typedef struct SListNode
{
	SLTNodeDataType data; // 存储数据
	struct SListNode* next;// 指向下一个节点的指针
}SLTNode;

【零基础学数据结构】链表

4.2链表的打印

头文件声明:

void SLTPrint(SLTNode* phead);

执行文件代码实现:

// 链表的打印
void SLTPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;// 保证phead指向的位置是收节点,方便后续再次遍历
	while (pcur)// 等价于 pcur != NULL;
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULLn");
}

【零基础学数据结构】链表

4.3链表的尾插

头文件声明:

void SLTPushBack(SLTNode** pphead, SLTNodeDataType x);

// 创建新的节点
SLTNode* SLTBuyNode(SLTNodeDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (NULL == newnode)
	{
		perror("SLTBuyNode malloc err!");
		exit(1);
	}
	// 创建节点成功
	newnode->data = x;
	newnode->next = NULL;
	// 返回
	return newnode;
}

执行文件代码实现:

// 链表的尾插
void SLTPushBack(SLTNode** pphead, SLTNodeDataType x)
{
	// 传参不可为空
	assert(pphead);
	// 创建一个新的节点
	SLTNode* newnode = SLTBuyNode(x);
	// 空链表和非空链表
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		// 找尾巴
		SLTNode* ptail = *pphead;
		while (ptail->next) // 等价于ptail->next !=NULL;
		{
			ptail = ptail->next;
		}
		// 找到尾巴,开始连接
		ptail->next = newnode;
	}
	
}

【零基础学数据结构】链表

4.4链表的头插

头文件声明:

// 链表的头插
void SLTPushFront(SLTNode** pphead, SLTNodeDataType x);

执行文件代码实现:

// 链表的头插
void SLTPushFront(SLTNode** pphead, SLTNodeDataType x)
{
	// 传参不可为空
	assert(pphead);
	// 创建一个新的节点
	SLTNode* newnode = SLTBuyNode(x);
	// 头插
	newnode->next = *pphead;
	*pphead = newnode;
}

头插就很简单了, 只需要创建新的节点放到链表的头部,不论是空链表和非空链表,情况都是一样的,连接好后,将头指针改指向,指向新头插的节点。

4.5链表的尾删

头文件声明:

void SLTPopBack(SLTNode** pphead);

执行文件代码实现:

// 链表的尾删
void SLTPopBack(SLTNode** pphead)
{
	// 链表不可以为空,传参不可为空
	assert(pphead && *pphead);
	// 链表有一个节点和多个节点
	// 一个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else //多个节点
	{
		// 找尾
		SLTNode* prev = *pphead;
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		free(ptail); // 释放空间
		ptail = NULL;// 防止野指针
		prev->next = NULL;
	}
	
}

尾删除需要影响原链表,所以传二级指针,同时尾删时我们需要注意是否节点是一个,如果是一个,删除后链表为空,申请的空间需要释放,防止造成空间浪费。如果是多个节点,我们需要找尾巴ptail,并且记录前一个节点prev,删除尾节点并且释放,置为空,将prev->next=NULL。

4.6链表的头删

头文件声明:

void SLTPopFront(SLTNode** pphead);

执行文件代码实现:

// 链表的头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

删除前断言所传的指针是否为空,链表是否为空,头删除前记录下一个节点,删除头节点后,将头置后。

4.7链表的查找

头文件声明:

SLTNode* SLTFind(SLTNode* phead, SLTNodeDataType x);

执行文件代码实现:

// 链表的查找
SLTNode* SLTFind(SLTNode* phead, SLTNodeDataType x)
{
	
	SLTNode* prev = phead;
	while (prev)// 等价于 prev != NULL;
	{
		if (prev->data == x)
		{
			return prev;
		}
		prev = prev->next;
	}
	// 遍历完毕没有找到,返回空。
	return NULL;
}

4.8链表在指定位置之前插⼊数据

头文件声明:

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTNodeDataType x);

执行文件代码实现:

// 链表在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTNodeDataType x)
{
	// 不可以传入NULL
	assert(pphead && *pphead);
	assert(pos);
	// 创建一个新的节点
	SLTNode* newnode = SLTBuyNode(x);
	// 如果节点只有一个,就是头插
	if (pos == *pphead)
	{

链表概念详解_c++ 链表_链表组成结构

SLTPushFront(pphead, x); } else { SLTNode* pcur = *pphead; // pcur所指向的节点不为pos前一个节点 while (pcur->next != pos) { pcur = pcur->next; } // pcur所指向的节点为pos前一个节点 newnode->next = pos; pcur->next = newnode; } }

4.9链表在指定位置之后插⼊数据

头文件声明:

void SLTInsertAfter(SLTNode* pos, SLTNodeDataType x);

执行文件代码实现:

// 链表在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTNodeDataType x)
{
	assert(pos);
	// 创建要插入的节点
	SLTNode* newnode = SLTBuyNode(x);
	// 开始插入
	newnode->next = pos->next;
	pos->next = newnode;
}

4.9-1删除pos节点

头文件声明:

void SLTErase(SLTNode** pphead, SLTNode* pos);

执行文件代码实现:

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		// prev所指向的就是pos前一个节点
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

4.9-2删除pos之后的节点

头文件声明:

void SLTEraseAfter(SLTNode* pos);

执行文件代码实现:

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next); // 传过来的参数不可以为空,pos下一个的参数也不可以是NULL
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

4.9-3销毁链表

文件声明:

void SListDesTroy(SLTNode** pphead);

执行文件代码实现:

//销毁链表
void SListDesTroy(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	// 全部销毁完后,置为空NULL;
	*pphead = NULL;
}

补充:测试文件:

//void SListNodeText01()
//{
//	// 创建节点
//	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
//	node1->data = 1;
//
//	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
//	node2->data = 2;
//
//	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
//	node3->data = 3;
//
//	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
//	node4->data = 4;
//
//	// 将四个节点连接起来
//	node1->next = node2;
//	node2->next = node3;
//	node3->next = node4;
//	node4->next = NULL;
//
//	// 打印
//	SLTPrint(node1);
//}
void SLTNodeText02()
{
	//SLTNode* plist = NULL;
	// 测试尾插
	//SLTPushBack(&plist, 1);
	//SLTPushBack(&plist, 2);
	//SLTPushBack(&plist, 3);
	//SLTPushBack(&plist, 4);
	/*SLTPrint(plist);*/// 1->2->3->4->NULL;
	// 测试头插
	/*SLTPushFront(&plist, 5);
	SLTPrint(plist);
	SLTPushFront(&plist, 6);
	SLTPrint(plist);
	SLTPushFront(&plist, 7);
	SLTPrint(plist);
	SLTPushFront(&plist, 8);
	SLTPrint(plist);*/
    // 测试尾删
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	// 测试头删
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	// 报错
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	// 测试查找
	//SLTNode* find = SLTFind(plist, NULL);
	//if (find == NULL)
	//{
	//	printf("没有找到!n");
	//}
	//else
	//{
	//	printf("找到了!n");
	//}
	// 测试在pos位置之前插⼊数据
	//SLTNode* find = SLTFind(plist, 1);
	//SLTInsert(&plist, find, 11);
	//SLTPrint(plist);
	// 测试链表在指定位置之后插⼊数据
	/*SLTNode* find = SLTFind(plist, 4);
	SLTInsertAfter(find, 11);
	SLTPrint(plist);*/
	// 测试删除pos之后的节点
	/*SLTNode* find = SLTFind(plist, 1);
	SLTEraseAfter(find);
	SLTPrint(plist);*/
	// 测试销毁
	SListDesTroy(&plist);
}
int main()
{
	//SListNodeText01();
	SLTNodeText02();
	return 0;
}