链表介绍和无头单向非循环链表的C语言实现(注释超详细)

目录

链表

1、概念

2、分类

无头单向非循环链表的C语言实现

1、功能介绍

2、头文件内容

3、头文件中功能实现 

3.1、动态申请一个节点

3.2、单链表的尾插

3.3、单链表的头插

3.4、单链表的尾删

3.5、单链表的头删

3.6、单链表的查找

3.7、单链表在pos位置之后插入x

3.8、单链表删除pos位置之后的值

3.9、单链表的打印

3.10、 单链表的销毁

4、主函数(测试用程序

链表

1、概念

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

2、分类

  • 单向链表或双向链表
  • 带头链表或不带头链表
  • 循环链表或非循环链表

在我们具体操作中,常用的链表有两类,分别是无头单向非循环链表带头双向循环链表,这两者分别时结构最简单和结构最复杂的两种链表。

对于无头单向非循环链表,一般很少用于直接存储数据,因为其结构太过简单。实际中经常时用来作为其他数据结构的子结构来使用。

对于带头双向循环链表,一般会用来单独存储数据内容,因为其结构复杂,有较高的操作性和实用性。

无头单向非循环链表的C语言实现

1、功能介绍

  1. 单链表尾部插入元素
  2. 单链表头部插入元素
  3. 删除单链表尾部元素
  4. 删除单链表头部元素
  5. 查找单链表中的元素
  6. 单链表某位置插入元素
  7. 单链表删除某位后元素
  8. 打印单链表中的内容
  9. 删除单链表的内容

2、头文件内容

#pragma once
#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 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
void SListInsertAfter(SListNode* pos, SLTDateType x);

// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);

// 单链表的打印
void SListPrint(SListNode* plist);

// 单链表的销毁
void SListDestroy(SListNode* plist);

3、头文件中功能实现 

3.1、动态申请一个节点

SListNode* BuySListNode(SLTDateType x)//动态申请一个节点,链表的初始化
{
	SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));//开辟单链表存储空间
	if (NewNode == NULL)
	{
		printf("内存开辟失败\n");
		exit(0);
	}
	NewNode->data = x;
	NewNode->next = NULL;
	return NewNode;
}

3.2、单链表的尾插

// 单链表的尾插
//pplist指向的是单链表中的节点,而节点作为指针的存在,所以pplist以二级指针的形式指向节点来对节点的指向进行修改
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	assert(pplist);//判断链表是否存在
	//此时为空链表,直接将pplist指向最新插入的节点即可
	if (*pplist == NULL)
	{
		*pplist = BuySListNode(x);
	}
	//链表不为空
	else
	{
		//寻找原链表中的最后一个节点
		SListNode* cur = *pplist;
		while (cur->next)
		{
			cur = cur->next;
		}
		cur->next = BuySListNode(x);//插入新节点
	}
}

3.3、单链表的头插

//单链表的头插
void SListPushFront(SListNode **pplist, SLTDateType x)
{
	assert(pplist);
	SListNode* NewNode = BuySListNode(x);
	if (*pplist == NULL)//链表为空,直接插入
	{
		*pplist = NewNode;
	}
	else
	{
		NewNode->next = *pplist;//用新节点指向链表首位
		*pplist = NewNode;//头插节点赋值
	}
	//实际上可以不对链表进行判空处理,因为具体操作内容相同
	//未保证代码逻辑的严谨性,在此做分情况讨论,便于理解
}

3.4、单链表的尾删

// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
	assert(pplist);//保证pplist指向实参plist
	if (*pplist == NULL)//空链表
	{
		return;
	}

	else if ((*pplist)->next == NULL)//链表仅存在一个节点
	{
		free(*pplist);
		*pplist = NULL;
	}
	else//法一:链表中至少存在两个节点
	{
		SListNode* cur = *pplist;
		while (cur->next->next)//cur指向倒数第二,最后元素释放,并置空
		{
			cur = cur->next;
		}
		free(cur->next);
		cur->next = NULL;
	}
	/* 法二:
    else
    {
		SListNode* cur = *pplist;
		SListNode* pr = NULL;
		while (cur->next)   //cur指向倒数第一,并通过pr保存cur前一个节点
	    {
		    pr = cur;
		    cur = cur->next;
		}
		free(cur);
		pr->next = NULL;
    }
	*/
}

3.5、单链表的头删

// 单链表的头删
void SListPopFront(SListNode** pplist)
{
	assert(pplist);
	if (*pplist == NULL)//空链表
	{
		printf("链表为空,无法删除\n");
		return;
	}
	else
	{
		SListNode* cur = *pplist;//保存头指针
		*pplist = cur->next;//通过头指针保存第二节点内容
		free(cur);//删除头节点内容
	}
}

3.6、单链表的查找

SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* cur = plist;
	while (cur)//遍历链表进行一一比对查找
	{
		if (x == cur->data)
		{
			return cur;
		}
		cur = cur->next;
	}
	

3.7、单链表在pos位置之后插入x

// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	//新节点难以插入到pos之前,因为是单链表,无法知道pos前一位置内容
	//并且没有提供plist,则无法通过遍历得到pos前一位置内容
	SListNode* NewNode = BuySListNode(x);
	if (pos == NULL)//判空
	{
		return;
	}
	else
	{
		NewNode->next = pos->next;
		pos->next = NewNode;
	}
}

3.8、单链表删除pos位置之后的值

// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
	if (pos == NULL)//判空
	{
		return;
	}
	SListNode* cur = NULL;
	cur = pos->next;
	if (cur == NULL)//无法删除最后一位
	{
		return;
	}

	//pos->data = cur->data;//让pos位置元素等于后一位元素,可以满足删除pos位置元素
	pos->next = cur->next;//让pos指向下下一位元素,空出下一位元素
	free(cur);//让pos下一位元素制空,即删除
}

3.9、单链表的打印

void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

3.10、 单链表的销毁

void SListDestroy(SListNode** pplist)
{
	assert(pplist);
	SListNode* cur = *pplist;
	while (cur)
	{
		*pplist = cur->next;
		free(cur);
		cur = *pplist;
	}
	*pplist = NULL;
}

4、主函数(测试用程序)

int main()
{
	SListNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);
	SListPrint(plist);
	printf("-------------\n");
	SListPopBack(&plist);
	SListPrint(plist);
	printf("-------------\n");
	SListPushFront(&plist, 5);
	SListPrint(plist);
	printf("-------------\n");
	SListPopFront(&plist);
	SListPrint(plist);
	printf("-------------\n");
	SListInsertAfter(SListFind(plist,2), 5);
	SListPrint(plist);
	printf("-------------\n");
	SListEraseAfter(SListFind(plist, 2));
	SListPrint(plist);
	printf("-------------\n");
	SListDestroy(&plist);
	SListPrint(plist);
	system("pause");
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值