单链表的实现

1. 首先是对单链表代码的实现

SList.h

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);

// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x);
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos);
void SLTDestroy(SListNode** pphead);

SList.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newdata = (SListNode*)malloc(sizeof(SListNode));
	if (newdata==NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newdata->data = x;
	newdata->next = NULL;
	return newdata;
}

void SListPrint(SListNode* plist)
{
	SListNode* tem = plist;
	while (tem!=NULL)
	{
		printf("%d-> ",tem->data);
		tem = tem->next;
	}
	printf("NULL");
	printf("\n");
}
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	
	SListNode* newdata = BuySListNode(x);

	if (*pplist == NULL)
	{
		*pplist = newdata;

	}

	else
	{
		SListNode* tail = *pplist;

		while (tail->next !=NULL)
		{
			tail = tail->next;

		}
		tail->next = newdata;
	}
}
void SLTDestroy(SListNode** pphead)
{
	assert(*pphead); 
	SListNode* cur=*pphead;

	while (cur!=NULL)
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

//头插   对于头插没有必要判断*pplist是否为空
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newdata = BuySListNode(x);

		newdata->next = *pplist;
		*pplist = newdata;

	

}
//单链表尾删
void SListPopBack(SListNode** pplist)
{
	assert(*pplist);
	assert(pplist);

	SListNode* cur = *pplist;
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}


	while (cur->next->next)
	{
		cur = cur->next;
	}
	free(cur->next);
	cur->next = NULL;

}
//单链表头删
void SListPopFront(SListNode** pplist)
{
	assert(*pplist);

	if (*pplist == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* cur = *pplist;
		SListNode* tail = (*pplist)->next;
		*pplist = tail;
		free(cur);
		cur = NULL;
	}
}

// 单链表查找

SListNode* SListFind(SListNode* plist, SLTDateType x)
{

	SListNode* tail = plist;
	while (tail)
	{
		if (tail->data == x)
		{
			return tail;
		}
		else
		{
			tail = tail->next;
		}
	}
	return NULL;

}
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newdata = BuySListNode(x);
	if (pos->next == NULL)
	{
		pos->next = newdata;
	}
	else
	{
		newdata->next = pos->next;
		pos->next = newdata;
	}

}
void SListEraseAfter(SListNode* pos)
{

	assert(pos);
	assert(pos->next);
	SListNode* tem = pos->next;
	pos->next = pos->next->next;
	free(tem);
	tem = NULL;

}
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);
	assert((*pphead)->next);
	SListNode* newdata = BuySListNode(x);

	if (*pphead == pos)
	{
		SListPushFront(pphead,x);

	}
	
	SListNode* tem = *pphead;
	while (tem->next != pos)
	{

		tem = tem->next;
	}
	tem->next = newdata;
	newdata->next = pos;
}
void SLTErase(SListNode** pphead, SListNode* pos)
{
	assert(pos);
	assert(pphead);
	assert(*pphead);
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	SListNode* tem = *pphead;
	while (tem->next != pos)
	{
		tem = tem->next;
	}
	tem->next = pos->next;
	free(pos);
	pos = NULL;
}

2.链表的概念和结构

概念:链表是一种物理存储结构上非连续,非顺序的储存结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的

下面通过一个图来稍微介绍一下链表 :这里以没有哨兵位的一个链表为例

(哨兵位:第一个节点只储存下一个节点的地址,其他值为空,比没有哨兵的链表多一个节点)

这里每一个框框代表一个节点,每一个节点相互之间是无关的,是随机的,位置实际是东一个,西一个的。每一个节点包含了两个主要组成,一个是下一个节点的地址,二是每一个节点包含一个自己定义的值,像图中的数就是每一个链表代表的值。一般将第一个节点叫做phead。最后一个链表指向的地址为空。

可以从下面图中更加好的看出链表的结构 :上面的红色字符代表这个节点的地址,节点中的蓝色字符的位置实质是一个指针,储存着下一个节点的地址。而黑色字体的数字则代表这个节点存的数据。

注意:

1.现实中的节点一般是从堆上申请过来的。

2.从堆上申请的空间,是按照一定策略来申请的两次申请的空间可能连续也 可能不连续。

3.链表的分类

实际上链表的类型也有很多,有8种。由下面几种情况组合而来。


4.单链表的主要实现过程

1.创建单链表的单个节点的结构体

c 复制代码
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

这是一个节点的结构体,data为存储数据的地方,next则是存的下一个地址的指针。

2.单链表的功能

c 复制代码
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);

// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x);
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos);
void SLTDestroy(SListNode** pphead);

要注意这里许多地方都用了二级指针,像数据的插入和删除这样的操作就必须要二级指针,因为如果不传二级指针那么传来的指针就不会被改变(形参的改变不会影响实参)。

3,功能的实现

创造一个新节点

c 复制代码
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newdata = (SListNode*)malloc(sizeof(SListNode));
	if (newdata==NULL)//判断节点是否创造成功
	{
		perror("malloc fail");
		exit(-1);
	}
	newdata->data = x;
	newdata->next = NULL;
	return newdata;
}

打印单链表

c 复制代码
void SListPrint(SListNode* plist)
{
	SListNode* tem = plist;
	while (tem!=NULL)//到最后一个节点即next为NULL的节点停止。
	{
		printf("%d-> ",tem->data);
		tem = tem->next;
	}
	printf("NULL");
	printf("\n");
}

尾插

c 复制代码
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	
	SListNode* newdata = BuySListNode(x);

	if (*pplist == NULL)
	{
		*pplist = newdata;

	}

	else
	{
		SListNode* tail = *pplist;

		while (tail->next !=NULL)
		{
			tail = tail->next;

		}
		tail->next = newdata;
	}
}

对于链表的插入与删除数据最主要的操作就是改变链表的next的指向。

而尾插就是找到最后一个节点,再将最后一个节点的next指向新创建的节点。

链表的释放

c 复制代码
void SLTDestroy(SListNode** pphead)
{
	assert(*pphead);    //检查链表是否存在
	SListNode* cur=*pphead;

	while (cur!=NULL)   //利用循环将每一个节点都删除
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

头插

c 复制代码
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newdata = BuySListNode(x);

		newdata->next = *pplist;
		*pplist = newdata;
}

头插没必要判断*pplist是否为空

尾删

c 复制代码
void SListPopBack(SListNode** pplist)
{
	assert(*pplist);
	assert(pplist);

	SListNode* cur = *pplist;
	if ((*pplist)->next == NULL)  //只有一个节点的情况
	{
		free(*pplist);
		*pplist = NULL;
	}


	while (cur->next->next)   //当有多个节点得到时候,先找到最后一个节点,然后释放
	{
		cur = cur->next;
	}
	free(cur->next);
	cur->next = NULL;

}

头删

c 复制代码
void SListPopFront(SListNode** pplist)
{
	assert(*pplist);

	if (*pplist == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* cur = *pplist;
		SListNode* tail = (*pplist)->next;
		*pplist = tail;
		free(cur);
		cur = NULL;
	}
}

单链表的查找

c 复制代码
SListNode* SListFind(SListNode* plist, SLTDateType x)
{

	SListNode* tail = plist;
	while (tail)
	{
		if (tail->data == x)
		{
			return tail;   //找到所需查找的值
		}
		else
		{
			tail = tail->next;
		}
	}
	return NULL;  //没有找到,返回空

}

在pos位置前插入

c 复制代码
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newdata = BuySListNode(x);
	if (pos->next == NULL)   //pos位置就是最后一个位置,相当于尾插
	{
		pos->next = newdata;
	}
	else
	{
		newdata->next = pos->next;
		pos->next = newdata;
	}

}

删除pos位置后的节点

c 复制代码
void SListEraseAfter(SListNode* pos)
{

	assert(pos);
	assert(pos->next);
	SListNode* tem = pos->next;
	pos->next = pos->next->next;
	free(tem);
	tem = NULL;

}

在pos前插入

c 复制代码
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);
	assert((*pphead)->next);
	SListNode* newdata = BuySListNode(x);

	if (*pphead == pos)     //当只有一个节点时,相当于头插
	{
		SListPushFront(pphead,x);

	}
	
	SListNode* tem = *pphead;
	while (tem->next != pos)
	{

		tem = tem->next;
	}
	tem->next = newdata;
	newdata->next = pos;
}

删除pos位置节点

c 复制代码
void SLTErase(SListNode** pphead, SListNode* pos)
{
	assert(pos);
	assert(pphead);
	assert(*pphead);
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	SListNode* tem = *pphead;
	while (tem->next != pos)
	{
		tem = tem->next;
	}
	tem->next = pos->next;
	free(pos);
	pos = NULL;
}
相关推荐
励志成为大佬的小杨2 小时前
c语言中的枚举类型
java·c语言·前端
叫我阿呆就好了2 小时前
C 进阶 — 文件操作
c语言·开发语言
阳光开朗大男孩 = ̄ω ̄=3 小时前
指针之矢:C 语言内存幽境的精准飞梭
c语言
Michael_Good3 小时前
【C/C++】C语言编程规范
c语言·开发语言·c++
Willliam_william4 小时前
Python学习之路(5)— 使用C扩展
c语言·python·学习
TANGLONG2225 小时前
【初阶数据结构与算法】八大排序之非递归系列( 快排(使用栈或队列实现)、归并排序)
java·c语言·数据结构·c++·算法·蓝桥杯·排序算法
sunny-ll6 小时前
【C++】explicit关键字详解(explicit关键字是什么? 为什么需要explicit关键字? 如何使用explicit 关键字)
c语言·开发语言·c++·算法·面试
半盏茶香8 小时前
C语言勘破之路-最终篇 —— 预处理(下)
c语言·开发语言·c++·算法
TDengine (老段)10 小时前
TDengine 新功能 VARBINARY 数据类型
大数据·c语言·数据库·时序数据库·tdengine·涛思数据
No0d1es18 小时前
2024年12月青少年软件编程(C语言/C++)等级考试试卷(三级)
c语言·开发语言·青少年编程·电子学会·三级