单链表专题

目录

一.引言

二.顺序表的问题及思考 

三.链表的概念及结构 

概念:

结构:

 四.定义链表节点的结构

五.打印链表 

头文件

源文件

测试

结果 

六.单链表的实现 

1.申请新节点 

2.单链表的数据插入 

2.1尾插

2.2头插

3.单链表的删除 

3.1尾删

3.2头删

 4单链表的查找

5单链表在指定位置之前插入数据

6单链表在指定位置后前插入数据

7单链表删除指定节点

8单链表删除指定位置之后的节点

9销毁链表

七.链表的分类

八.完整代码 

SList.h

SList.c

text.c

 九总结


一.引言

之前我们已经实现了顺序表的构建和基本操作,并基于顺序表实现了通讯录,不过对于顺序表来说,有些操作还是有些问题和缺陷的,这就引入了今天的专题--单链表.

二.顺序表的问题及思考 

1. 中间/头部的插⼊删除,时间复杂度为O(N)

2. 增容需要申请新空间,拷⻉数据,释放旧空间。会有不⼩的消耗。

3. 增容⼀般是呈2倍的增⻓,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到 200,我们再继续插⼊了5个数据,后⾯没有数据插⼊了,那么就浪费了95个数据空间。 

也就是说针对顺序表 中间/头部插入效率低下,增容降低运行效率,增容造成空间浪费

那么如何解决这些问题呢,很明显这必然是我们今天的主角单链表 

三.链表的概念及结构 

概念:

链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的。

链表的结构跟⽕⻋⻋厢相似,淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只 需要将⽕⻋⾥的某节⻋厢去掉/加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的。⻋厢是独⽴存在的,且每节⻋厢都有⻋⻔。想象⼀下这样的场景,假设每节⻋厢的⻋⻔都是锁上的状 态,需要不同的钥匙才能解锁,每次只能携带⼀把钥匙的情况下如何从⻋头⾛到⻋尾? 最简单的做法:每节⻋厢⾥都放⼀把下⼀节⻋厢的钥匙。

结构:

在链表⾥,每节“⻋厢”是什么样的呢?

与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“结点/节点” 节点的组成主要有两个部分:当前节点要保存的数据和保存下⼀个节点的地址(指针变量)。 图中指针变量plist保存的是第⼀个节点的地址,我们称plist此时“指向”第⼀个节点,如果我们希 望plist“指向”第⼆个节点时,只需要修改plist保存的内容为0x0012FFA0。 

补充说明:

1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续

2、节点⼀般是从堆上申请的

3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

 四.定义链表节点的结构

//定义节点的结构
//数据+指向下一个节点的指针

typedef int SLTDateNode;//方便修改数据类型

typedef struct SListNode {
	SLTDateNode data;
	struct SListNode* next;//指向下一个节点的指针
}SLTNode;

这里需要用到结构体指针指向下个节点,从而将数据连接起来.

五.打印链表 

头文件

//链表的打印
void SLTPrint(SLTNode* phead);

源文件

#include"SList.h"

void SLTPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur)//pcur!=NULL
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

重点是pcur=pcur->next ,将pcur指向下一个节点

测试

#define _CRT_SECURE_NO_WARNINGS 1

#include "SList.h"

void SListTest01()
{
	//链表是由一个一个的节点组成
	//创建几个节点
	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;

	//调用链表的打印
	SLTNode* plist = node1;
	SLTPrint(plist);
}

int main()
{
	SListTest01();
	return 0;
}

首先需要用malloc开辟空间(这里不要用realloc,不是扩容)创造四个节点,再将四个节点连接起来,最后调用链表进行打印.

结果 

成功打印了我们链表的结构

六.单链表的实现 

跟我们之前顺序表的操作和实现类似,可以先看这篇博客--顺序表的构建和基本操作

1.申请新节点 

这里跟顺序表不同,单链表是你需要就创建节点,为了后面能方便使用,我们在这里封装成一个函数.

头文件

//申请新节点
SLTNode* SLTBuyNode(SLTDateType x);

源文件 

//申请新节点
SLTNode* SLTBuyNode(SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

2.单链表的数据插入 

2.1尾插

头文件

//尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x);

源文件

//链表尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	//*pphead 就是指向第一个节点的指针
	//空链表和非空链表两种情况
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		//ptail指向的就是尾节点
		ptail->next = newnode;
	}

}

这里需要注意的是: 我们这里跟顺序表不一样的是这里我们使用了二级指针来接收一级指针的地址,因为形参的改变要影响实参就必须要传地址,而顺序表则是传入地址用一级指针来接收,可以跟这篇博客进行对比--顺序表的构建和基本操作,而且我们要判断pphead是否为NULL,因为后面我们需要对pphead解引用,空指针是无法解引用的,所以加上assert

下面是示意图

测试尾插

void SListText02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPushBack(NULL, 1);

	SLTPrint(plist);
}

int main()
{
	SListText02();
	return 0;
}

 

可以看到是没有什么问题的 

2.2头插

头插相比尾插简单很多,只需将newnode与单链表连接起来,再将newnode成为新的头节点

示意图

头文件

//头插
void SLTPushFront(SLTNode** pphead, SLTDateType x);

源文件 


//链表的头插
void SLTPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	//newnode *pphead
	newnode->next = *pphead;//指向头节点
	*pphead = newnode;//newnode成为新的头节点
	//当链表为空时也成立
}

3.单链表的删除 

3.1尾删

尾删分为两种情况:1.只有一个节点,直接删除,并将头节点指向NULL

                              2.有多个节点,我们需要两个指针,一个指向最后的节点,一个指向最后一个的前一个节点,因为单链表无法通过后面来寻找前面的节点.

示意图

头文件

//尾删
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;
	}
	
}

测试

void SListText02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPushBack(NULL, 1);
	SLTPushFront(&plist, 6);
	SLTPushFront(&plist, 7);

	//测试尾删
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
}

int main()
{
	SListText02();
	return 0;
}

 

从结果可以看到我们的代码没有什么问题

3.2头删

头删只需要将头节点的下一个节点的地址保存,并将其赋予头节点,最后删除头节点即可

示意图

头文件

//头删
void SLTPopFront(SLTNode** pphead);

源文件

//头删
void SLTPopFront(SLTNode** pphead)
{
	//链表不能为空
	assert(pphead && *pphead);

	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

测试

void SListText02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPushBack(NULL, 1);
	SLTPushFront(&plist, 6);
	SLTPushFront(&plist, 7);

	//测试尾删
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);

	//测试头删
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
}

int main()
{
	SListText02();
	return 0;
}

结果

 

 4单链表的查找

头文件

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x);

源文件

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x)
{
	SLTNode* pcur = phead;
	while (pcur)//pcur!=NULL
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

测试

	//测试查找
	SLTNode* find1 = SLTFind(plist,3);
	if (find1 == NULL)
	{
		printf("没有找到!");
	}
	else
	{
		printf("找到了!\n");
	}
	SLTNode* find2 = SLTFind(plist, 30);
	if (find2 == NULL)
	{
		printf("没有找到!");
	}
	else
	{
		printf("找到了!\n");
	}

结果

5单链表在指定位置之前插入数据

示意图 

头文件

//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x);

源文件

//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	assert(pphead && *pphead);
	assert(pos);

	SLTNode* newnode = SLTBuyNode(x);
	//若pos == *pphead;说明是头插
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);//直接调用头插
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//pre - > newnode - > pos
		newnode->next = pos;
		prev->next = newnode;
	}
}

测试

void SListText02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPushBack(NULL, 1);
	SLTPushFront(&plist, 6);
	SLTPushFront(&plist, 7);

	//测试尾删
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);

	//测试头删
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);

	//测试查找
	SLTNode* find1 = SLTFind(plist,3);
	SLTInsert(&plist, find1, 11);
	SLTPrint(plist);
	//if (find1 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
	//SLTNode* find2 = SLTFind(plist, 30);
	//if (find2 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
}

int main()
{
	SListText02();
	return 0;
}

 结果

6单链表在指定位置后前插入数据

示意图

所以这里一定要注意顺序! 

头文件

//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDateType x);

源文件

//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	pos->next = newnode;
}

测试 

void SListText02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPushBack(NULL, 1);
	SLTPushFront(&plist, 6);
	SLTPushFront(&plist, 7);

	//测试尾删
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);

	//测试头删
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);

	//测试查找
	SLTNode* find1 = SLTFind(plist,3);
	SLTInsert(&plist, find1, 11);
	SLTPrint(plist);
	SLTInsertAfter(find1, 15);
	SLTPrint(plist);
	//if (find1 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
	//SLTNode* find2 = SLTFind(plist, 30);
	//if (find2 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
}

int main()
{
	SListText02();
	return 0;
}

结果

7单链表删除指定节点

头文件

//删除pos节点
void SLTErase(SLTNode ** pphead, SLTNode * pos);

源文件

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	//pos是头节点/pos不是头节点
	if (pos == *pphead)
	{
		//头删
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//prev pos pos->next
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
	
}

测试

void SListText02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPushBack(NULL, 1);
	SLTPushFront(&plist, 6);
	SLTPushFront(&plist, 7);

	//测试尾删
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);

	//测试头删
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);

	//测试查找
	SLTNode* find1 = SLTFind(plist,3);
	SLTInsert(&plist, find1, 11);
	SLTPrint(plist);
	SLTInsertAfter(find1, 15);
	SLTPrint(plist);
	SLTErase(&plist,find1);
	SLTPrint(plist);
	//if (find1 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
	//SLTNode* find2 = SLTFind(plist, 30);
	//if (find2 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
}

int main()
{
	SListText02();
	return 0;
}

结果

8单链表删除指定位置之后的节点

示意图

头文件

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);

源文件

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	//pos del del->next
	pos->next = del->next;
	free(del);
	del = NULL;
}

测试

#include "SList.h"

void SListTest01()
{
	//链表是由一个一个的节点组成
	//创建几个节点
	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;

	//调用链表的打印
	SLTNode* plist = node1;
	SLTPrint(plist);
}

void SListText02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPushBack(NULL, 1);
	SLTPushFront(&plist, 6);
	SLTPushFront(&plist, 7);

	//测试尾删
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);

	//测试头删
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);

	//测试查找
	SLTNode* find1 = SLTFind(plist,3);
	SLTInsert(&plist, find1, 11);
	SLTPrint(plist);
	SLTInsertAfter(find1, 15);
	SLTPrint(plist);
	SLTErase(&plist,find1);
	SLTPrint(plist);
	SLTEraseAfter(plist);
	SLTPrint(plist);

	//if (find1 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
	//SLTNode* find2 = SLTFind(plist, 30);
	//if (find2 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
}

int main()
{
	SListText02();
	return 0;
}

结果 

9销毁链表

示意图

头文件

//销毁链表
void SListDesTroy(SLTNode** pphead);

源文件

//销毁链表
void SListDesTroy(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

七.链表的分类

链表的结构⾮常多样,以下情况组合起来就有8种(2x2x2)链表结构:

1.带头

2.不带头

3.单向

4.双向

5.循环

6.不循环

八.完整代码 

SList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//定义节点的结构
//数据+指向下一个节点的指针

typedef int SLTDateType;//方便修改数据类型

typedef struct SListNode {
	SLTDateType data;
	struct SListNode* next;//指向下一个节点的指针
}SLTNode;


//申请新节点
SLTNode* SLTBuyNode(SLTDateType x);
//链表的打印
void SLTPrint(SLTNode* phead);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDateType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x);
//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x);
//删除pos节点
void SLTErase(SLTNode ** pphead, SLTNode * pos);
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDateType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);

SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

//链表的打印
void SLTPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur)//pcur!=NULL
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}
//申请新节点
SLTNode* SLTBuyNode(SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

//链表尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	//*pphead 就是指向第一个节点的指针
	//空链表和非空链表两种情况
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		//ptail指向的就是尾节点
		ptail->next = newnode;
	}

}

//链表的头插
void SLTPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	//newnode *pphead
	newnode->next = *pphead;//指向头节点
	*pphead = newnode;//newnode成为新的头节点
	//当链表为空时也成立
}

//尾删
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;
	}
	
}

//头删
void SLTPopFront(SLTNode** pphead)
{
	//链表不能为空
	assert(pphead && *pphead);

	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x)
{
	SLTNode* pcur = phead;
	while (pcur)//pcur!=NULL
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	assert(pphead && *pphead);
	assert(pos);

	SLTNode* newnode = SLTBuyNode(x);
	//若pos == *pphead;说明是头插
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);//直接调用头插
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//pre - > newnode - > pos
		newnode->next = pos;
		prev->next = newnode;
	}
}
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	pos->next = newnode;
}

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	//pos是头节点/pos不是头节点
	if (pos == *pphead)
	{
		//头删
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//prev pos pos->next
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
	
}


//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	//pos del del->next
	pos->next = del->next;
	free(del);
	del = NULL;
}

//销毁链表
void SListDesTroy(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

text.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SList.h"

void SListTest01()
{
	//链表是由一个一个的节点组成
	//创建几个节点
	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;

	//调用链表的打印
	SLTNode* plist = node1;
	SLTPrint(plist);
}

void SListText02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPushBack(NULL, 1);
	SLTPushFront(&plist, 6);
	SLTPushFront(&plist, 7);

	//测试尾删
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);

	//测试头删
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);

	//测试查找
	SLTNode* find1 = SLTFind(plist,3);
	SLTInsert(&plist, find1, 11);
	SLTPrint(plist);
	SLTInsertAfter(find1, 15);
	SLTPrint(plist);
	SLTErase(&plist,find1);
	SLTPrint(plist);
	SLTEraseAfter(plist);
	SLTPrint(plist);

	//if (find1 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
	//SLTNode* find2 = SLTFind(plist, 30);
	//if (find2 == NULL)
	//{
	//	printf("没有找到!");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
}

int main()
{
	SListText02();
	return 0;
}

 九总结

 这次链表内容属实有点多,但是不要急,慢慢看,后面的双向链表,循环链表对你来说那是真的简单,后面我也会更新链表OJ算法题和双向链表,如果有兴趣的宝子们记得点赞关注哦!

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值