单链表的实现

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;
}
相关推荐
wwangxu27 分钟前
C语言 编译和链接
c语言·开发语言
bigjordon36 分钟前
关于使用/bin/sh -c 用于Dockerfile的Entrypoint的问题
c语言·开发语言·chrome
Beginner_bml1 小时前
Web服务器
服务器·c语言·网络协议
7yewh1 小时前
C语言刷题 LeetCode 30天挑战 (五)贪心算法
linux·c语言·开发语言·c++·算法·leetcode·贪心算法
mirevass1 小时前
【嵌入式软件-数据结构与算法】01-数据结构
c语言·数据结构·链表··队列
MikelSun1 小时前
梳理一下C语言中的格式说明符
c语言·开发语言·c++·单片机·物联网·算法
running_bug_Hu1 小时前
深度优先算法与拓扑排序之间的关系(C语言)
c语言·算法·深度优先
Orange_Gpio2 小时前
20.指针相关知识点1
c语言
wht65872 小时前
Linux--基本指令
linux·运维·服务器·c语言·开发语言·jvm·c++
Narnat3 小时前
linux项目_c语言:Makefile编写、动态库生成、添加动态库路径
linux·运维·c语言