数据结构篇2—《单链表(不带头单向不循环链表)》

在这里插入图片描述

🚩前言

在数据结构中主要的就是对数据的增删查改操作,在前面讲的顺序表就是其中一种结构,顺序表前身的数组(静态和动态)。而该结构是叫单链表,从逻辑结构上看是连续的,而在物理结构上是不连续存储的。下面就来对单链表的具体实现:

1、单链表的内涵

(1) 逻辑结构

什么叫作逻辑结构上是连续的,而物理结构上是不连续的。首先,在一个连续的空间当中就是连续存储,反之就不是,但是从我们思维结构来看可以当做是连续的,下面是单链表的逻辑结构:
在这里插入图片描述

(2) 物理结构

物理结构上就得从计算机内存中来看,就是在一个内存块中,每个节点所开辟的空间位置不是紧挨着的,存在一定距离,如下简图:
在这里插入图片描述

2、链表的分类

链表的种类比起顺序表就多了,顺序表就是静态和动态。而链表主要从带不带头、单向还是双向、循不循环三个点来分类,如下:
在这里插入图片描述

比如 :不带头单向不循环->单链表
不带头双向循环链表
带头单向循环链表等等。最常见的就是单链表和带头双向循环链表。

3、单链表的具体实现

(1) 框架结构

在这里插入图片描述

(2) SingleLinkList.h头文件的实现

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

//创建单链表结构体
typedef int DataType;
typedef struct SingLinkListNode
{
	DataType data;
	struct SingLinkListNode *next;
}SLTNode;

//打印函数
void PrintLinkList(SLTNode *phead);

//尾插
void SLTPushBack(SLTNode **pphead,DataType data);

//头插
void SLTPushFront(SLTNode **pphead,DataType data);

//尾删
void SLTPopBack(SLTNode **pphead);

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

//查找
SLTNode* SLTFind(SLTNode *phead,DataType data);

//指定位置之前插入数据
void SLTInsert(SLTNode **pphead,SLTNode *pos,DataType data);

//指定位置之后插入数据
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, DataType data);

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

//销毁链表函数
void SLTDestroy(SLTNode** pphead);

(3)SingleLinkList.c源文件的实现

①SLTPushBack()尾插函数
SLTNode* BuyNode(DataType x)
{
	SLTNode *NewNode = (SLTNode*)malloc(sizeof(SLTNode));
	if (NewNode == NULL)
	{
		perror("BuyNode:: malloc fail!");
		exit(1);
	}
	else
	{
		NewNode->data = x;
		NewNode->next = NULL;
	}
	return NewNode;
}

void SLTPushBack(SLTNode **pphead, DataType data)
{
	assert(pphead);
	SLTNode *NewNode=BuyNode(data);
	if ((*pphead) == NULL)
	{
		*pphead = NewNode;
	}
	else
	{
		//找尾
		SLTNode *ptail = *pphead;
		while (ptail->next!=NULL)
		{
			ptail = ptail->next;
		}
		ptail->next = NewNode;
	}
}

图解:尾插数据得申请新节点BuyNode();
在这里插入图片描述

②SLTPushFront()头插函数
void SLTPushFront(SLTNode **pphead, DataType data)
{
	assert(pphead);
	SLTNode *NewNode = BuyNode(data);
	if (*pphead == NULL)
	{
		*pphead = NewNode;
	}
	else
	{
		NewNode->next = *pphead;
		*pphead = NewNode;
	}
}

图解:
在这里插入图片描述

③SLTPopBack()尾删函数
void SLTPopBack(SLTNode **pphead)
{
	assert(pphead&&(*pphead)->data);
	SLTNode* prev = NULL;
	SLTNode* ptail = *pphead;
	//当只有一个节点的时候
	if ((*pphead)->next==NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找尾节点
		while ((ptail->next)!=NULL)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		ptail = NULL;
		prev->next = NULL;
	}
}

图解:
在这里插入图片描述

④SLTPopFront()头删函数
void SLTPopFront(SLTNode **pphead)
{
	assert(pphead && *pphead);
	SLTNode *pcur = (*pphead)->next;
	free(*pphead);
	*pphead = pcur;
}

图解:头删很简单。
在这里插入图片描述

⑤SLTInsert()和SLTInsertAfter()指定位置之前和之后插入数据函数
void SLTInsert(SLTNode** pphead, SLTNode* pos, DataType data)
{
	assert(pphead && pos);
	SLTNode* NewNode = BuyNode(data);
	SLTNode* prev = *pphead;
	//当插入的数据节点是头节点时就调用头插
	if (pos == *pphead)
	{
		SLTPushFront(pphead,data);
	}
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = NewNode;
		NewNode->next = pos;
	}
}

void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, DataType data)
{
	assert(pphead && pos);
	//SLTNode* pcur = *pphead;
	SLTNode* NewNode = BuyNode(data);
	//pcur = pos->next;
	//pos->next = NewNode;
	//NewNode->next = pcur;
	NewNode->next = pos->next;
	pos->next = NewNode;
}

图解:
在这里插入图片描述

⑥SLTErase()和SLTDestroy()删除和销毁函数
//删除
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && pos);
	SLTNode* prev = *pphead;
	if (pos == *pphead)
	{
		//头删
		SLTPopFront(pphead);
	}
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
//销毁
void SLTDestroy(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

在销毁的时候得注意,因为节点是一个一个创建的,所以也得一个一个的销毁。

4、完整代码

1、头文件

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

//创建单链表结构体
typedef int DataType;
typedef struct SingLinkListNode
{
	DataType data;
	struct SingLinkListNode *next;
}SLTNode;

//打印函数
void PrintLinkList(SLTNode *phead);

//尾插
void SLTPushBack(SLTNode **pphead,DataType data);

//头插
void SLTPushFront(SLTNode **pphead,DataType data);

//尾删
void SLTPopBack(SLTNode **pphead);

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

//查找
SLTNode* SLTFind(SLTNode *phead,DataType data);

//指定位置之前插入数据
void SLTInsert(SLTNode **pphead,SLTNode *pos,DataType data);

//指定位置之后插入数据
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, DataType data);

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

//销毁链表函数
void SLTDestroy(SLTNode** pphead);

2、源文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"SingleLinkList.h"


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

SLTNode* BuyNode(DataType x)
{
	SLTNode *NewNode = (SLTNode*)malloc(sizeof(SLTNode));
	if (NewNode == NULL)
	{
		perror("BuyNode:: malloc fail!");
		exit(1);
	}
	else
	{
		NewNode->data = x;
		NewNode->next = NULL;
	}
	return NewNode;
}

void SLTPushBack(SLTNode **pphead, DataType data)
{
	assert(pphead);
	SLTNode *NewNode=BuyNode(data);
	if ((*pphead) == NULL)
	{
		*pphead = NewNode;
	}
	else
	{
		//找尾
		SLTNode *ptail = *pphead;
		while (ptail->next!=NULL)
		{
			ptail = ptail->next;
		}
		ptail->next = NewNode;
	}
}

void SLTPushFront(SLTNode **pphead, DataType data)
{
	assert(pphead);
	SLTNode *NewNode = BuyNode(data);
	if (*pphead == NULL)
	{
		*pphead = NewNode;
	}
	else
	{
		NewNode->next = *pphead;
		*pphead = NewNode;
	}
}

void SLTPopBack(SLTNode **pphead)
{
	assert(pphead&&(*pphead)->data);
	SLTNode* prev = NULL;
	SLTNode* ptail = *pphead;
	//当只有一个节点的时候
	if ((*pphead)->next==NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找尾节点
		while ((ptail->next)!=NULL)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		ptail = NULL;
		prev->next = NULL;
	}
}

void SLTPopFront(SLTNode **pphead)
{
	assert(pphead && *pphead);
	SLTNode *pcur = (*pphead)->next;
	free(*pphead);
	*pphead = pcur;
}

SLTNode* SLTFind(SLTNode *phead, DataType data)
{
	assert(phead);
	SLTNode *pcur = phead;
	while (pcur)
	{
		if (pcur->data == data)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

void SLTInsert(SLTNode** pphead, SLTNode* pos, DataType data)
{
	assert(pphead && pos);
	SLTNode* NewNode = BuyNode(data);
	SLTNode* prev = *pphead;
	//当插入的数据节点是头节点时就调用头插
	if (pos == *pphead)
	{
		SLTPushFront(pphead,data);
	}
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = NewNode;
		NewNode->next = pos;
	}
}

void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, DataType data)
{
	assert(pphead && pos);
	//SLTNode* pcur = *pphead;
	SLTNode* NewNode = BuyNode(data);
	//pcur = pos->next;
	//pos->next = NewNode;
	//NewNode->next = pcur;
	NewNode->next = pos->next;
	pos->next = NewNode;
}

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

void SLTDestroy(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

3、测试代码文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"SingleLinkList.h"

void test()
{
	SLTNode* sl=NULL;

	//尾插函数测试
	SLTPushBack(&sl, 1);
	SLTPushBack(&sl, 2);
	SLTPushBack(&sl, 3);
	SLTPushBack(&sl, 4);
	//打印
	PrintLinkList(sl);
	printf("\n");

	//头插函数测试
	SLTPushFront(&sl, 10);
	SLTPushFront(&sl, 20);
	SLTPushFront(&sl, 30);
	//打印
	PrintLinkList(sl);
	printf("\n");

	//尾删函数测试
	SLTPopBack(&sl);
	SLTPopBack(&sl);
	//打印
	PrintLinkList(sl);
	printf("\n");


	//头删函数测试
	SLTPopFront(&sl);
	SLTPopFront(&sl);
	//打印
	PrintLinkList(sl);

	//查找函数测试
	int number = 0;
	printf("\n请输入要查找的数:");
	scanf("%d",&number);
	if (SLTFind(sl, number) == NULL)
	{
		printf("没找到!\n");
	}
	else
	{
		printf("找到了!%p\n", SLTFind(sl, number));
	}

	//在一节点之前插入数据函数测试
	int number1 = 0;
	int number2 = 0;
	printf("请输入要在那个节点之前插入数据:");
	scanf("%d",&number1);
	printf("请输入要插入的数据:");
	scanf("%d",&number2);
	SLTInsert(&sl,SLTFind(sl,number1),number2);
	//打印
	PrintLinkList(sl);
	printf("\n");

	//在一节点之后插入数据函数测试
	int number3 = 0;
	int number4 = 0;
	printf("请输入要在那个节点之后插入数据:");
	scanf("%d", &number3);
	printf("请输入要插入的数据:");
	scanf("%d", &number4);
	SLTInsertAfter(&sl, SLTFind(sl, number3), number4);
	//打印
	PrintLinkList(sl);
	printf("\n");

	//删除节点函数测试
	int number5 = 0;
	printf("请输入要删除的节点的数据:");
	scanf("%d",&number5);
	SLTErase(&sl,SLTFind(sl,number5));
	PrintLinkList(sl);
	printf("\n");

	//销毁函数测试
	SLTDestroy(&sl);
}

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

5、效果展示

在这里插入图片描述
在这里插入图片描述

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
带头结点的单向循环链表可以通过以下步骤进行建立: 1. 定义链表节点结构体,包含数据域和指向下一个节点的指针; 2. 定义一个指向链表首节点的指针,初始值为NULL; 3. 读入第一个节点的数据,创建一个节点,将其作为链表首节点,指向自身,将其指针赋值给链表首指针; 4. 读入下一个节点的数据,创建一个节点,将其插入到链表的尾部,即将链表尾节点的指针指向新节点,新节点的指针指向首节点,完成插入操作; 5. 重复执行步骤4,直到读取完所有节点的数据。 具体实现可以参考以下代码: ```c typedef struct Node { int data; // 数据域 struct Node *next; // 指向下一个节点的指针 } Node; int main() { Node *head = NULL; // 链表首指针初始化为NULL Node *tail = NULL; // 链表尾指针初始化为NULL int n; // 节点个数 int data; // 数据 scanf("%d", &n); // 读入节点个数 for (int i = 0; i < n; i++) { scanf("%d", &data); // 读入节点数据 Node *node = (Node*)malloc(sizeof(Node)); // 创建新节点 node->data = data; // 赋值数据域 node->next = NULL; // 新节点的next指针初始化为NULL if (head == NULL) { // 如果链表为空,将新节点作为首节点 head = node; node->next = head; // 新节点的next指针指向自身 tail = head; // 将尾指针指向首节点 } else { // 如果链表不为空,将新节点插入到尾部 node->next = head; // 新节点的next指针指向首节点 tail->next = node; // 将尾节点的next指针指向新节点 tail = node; // 将尾指针指向新节点 } } return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值