单链表的实现

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

SList.h

#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

#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.创建单链表的单个节点的结构体

typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

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

2.单链表的功能

// 动态申请一个节点
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,功能的实现

创造一个新节点

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)//到最后一个节点即next为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;
	}
}

对于链表的插入与删除数据最主要的操作就是改变链表的next的指向。
而尾插就是找到最后一个节点,再将最后一个节点的next指向新创建的节点。

链表的释放

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

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

头插

void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newdata = BuySListNode(x);

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

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

尾删

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;  //没有找到,返回空

}

在pos位置前插入

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位置后的节点

void SListEraseAfter(SListNode* pos)
{

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

}

在pos前插入

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位置节点

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;
}
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南子北游

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值