当前位置: 首页 > news >正文

单链表:数据结构的灵动之链

本文主要讲解链表的概念和结构以及实现单链表

目录

一、链表的概念及结构

二、单链表的实现

1.1链表的实现:

 1.2单链表的实现:

单链表尾插:

 单链表的头插:

 单链表的尾删:

 单链表头删:

 单链表查找:

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

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

 在pos的前面插入:

  删除pos位置:

  销毁:

 整体实现:



一、链表的概念及结构

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

链表的结构跟⽕⻋⻋厢相似,淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只需要将⽕⻋⾥的某节⻋厢去掉/加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的。概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
⻋厢是独⽴存在的,且每节⻋厢都有⻋⻔。想象⼀下这样的场景,假设每节⻋厢的⻋⻔都是锁上的状态,需要不同的钥匙才能解锁,每次只能携带⼀把钥匙的情况下如何从⻋头⾛到⻋尾?
最简单的做法:每节⻋厢⾥都放⼀把下⼀节⻋厢的钥匙。

其中,下一节车厢的钥匙就是,下一个节点的地址,也就是下一个节点的指针

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

 next指针就相当于是下一节车厢的钥匙

链表中每一节车厢都是malloc独立申请下来的空间,我们称之为节点,节点主要由数据以及下一个节点的地址组成,所以我们要用结构体来封装

 当然,如果我们想要保存的数据是字符型或者是浮点型,只需要写一个,这个随时可以替换

typedef int SLTDateType;
  •  链式结构在逻辑上是连续的,在物理结构上不⼀定连续
  • 节点⼀般是从堆上申请的
  • 从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

 


二、单链表的实现

1.1链表的实现:

首先,创建链表,把链表连接起来

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;
#include "slist.h"
void SListNodeTest()
{
	SListNode* node1 = (SListNode*)malloc(sizeof(SListNode));
	node1->data = 1;
	SListNode* node2 = (SListNode*)malloc(sizeof(SListNode));
	node2->data = 2;
	SListNode* node3 = (SListNode*)malloc(sizeof(SListNode));
	node3->data = 3;
	SListNode* node4 = (SListNode*)malloc(sizeof(SListNode));
	node4->data = 4;
	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = NULL;
	SListPrint(node1);
}
int main()
{
	SListNodeTest();
	return 0;
}

 打印的函数:

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

把连接起来的结果打印一下


 1.2单链表的实现:

  • 需要记住的两点:
  1. 但凡添了新数据第一步都是要申请新的空间的
  2. 插入节点,记住一定要从后往前改变

单列表相比于顺序表是没有初始化的,他一开始就是创建一个结构体指针

void SListNodeTest1()
{
	SListNode* plist = NULL;
}

单链表尾插:

  • 尾插首先需要找尾,但如果整个列表是空的的话,那就直接把newNode给*pphead,如果不是空的,就要找尾,定义一个ptail,不要动头节点*pphead,那ptail去尾插

// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	SListNode* ptail = *pplist;
	SListNode* newNode=BuySListNode(x);
	if (ptail == NULL)
	*pplist = newNode;
	else
	{
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		ptail->next=newNode;
	}
}

 运行结果:

 这里传的是二级指针,为什么要传二级指针呢?

一开始定义了一个结构体的指针置为NULL,这个指针是plist等于NULL,我给这个结构体指针给他newNode空间和nest指针以及date数据,让 phlist==newNode,相当于定义了一个数组arr ,让int *tmp去开辟malloc空间,成功了就让arr==tmp,为了真正的改变这个SlistNode *phlist我要知道它地址,相当于说我要给它赋值,由于函数结束,你的形参是不能影响实参的,所以说我要传它的地址,他是一级指针,所以我要传二级指针,为了真正能赋值,让phlist接收好这个节点的空间, 这是给一级指针赋值,想要真正改变一级指针,,你就要传他的地址,就要用二级指针接收

 

 这是一个难点,头插也是一样

 单链表的头插:

  • 头插只需要让新节点newNode的next指针指向头节点*pplist,再让头节点*pplist到新的节点newNode上
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newNode = BuySListNode(x);
	newNode->next = *pplist;
	*pplist = newNode;
}

 单链表的尾删:

  • 尾删需要注意定义两个节点,一个节点跟着另一个节点的尾巴后面ptail2跟着ptail1的后面第一步就让ptail2=ptail1,让ptail1往后走,这样就让ptail2跟在ptail1后面,当找到最后个节点,直接把最后一个节点释放掉此时ptail1就是最后个节点,ptail2就是最后一个节点的前节点,那前一个节点的nest置为空
void SListPopBack(SListNode** pplist)
{
	assert(*pplist);
	SListNode* ptail1 = *pplist;
	SListNode* ptail2 = *pplist;
	while (ptail1->next)
	{
		ptail2 = ptail1;
		ptail1 = ptail1->next;
	}
	free(ptail2->next);
	ptail2->next = NULL;
}

 单链表头删:

  • 头删只需要保存头节点的下一个支点,再让头节点释放掉,再让头节点走到刚才保存那个节点上去
void SListPopFront(SListNode** pplist)
{
	assert(*pplist);
	SListNode* pphead = (*pplist)->next;
	free(*pplist);
	*pplist = pphead;
}

 

 单链表查找:

  • 单链表的查找返回接收需要在Test.c上实现,找到了就返回,该节点找不到就返回空(NULL)
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	assert(plist);
	while (plist)
	{
		if (plist->data == x)
			return plist;
		plist = plist->next;
	}
	return NULL;
}

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

  • 在pose位置之后插入x,只需要创建一个新的节点,让新节点的下一个节点指向pos下一个节点,再让pos下一个节点指向新节点
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	SListNode* newNode = BuySListNode(x);
	newNode->next = pos->next;
	pos->next = newNode;
}

 

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

 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
	SListNode* tmp = pos->next->next;
	free(pos->next);
	pos->next = NULL;
	pos->next = tmp;
}

 在pos的前面插入:

  • 在pos的前面插入,首先,如果列表里面只有一个节点,那就相当于是头插,如果不是的话,那就循环找到pos
// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x)
{
	assert(*pplist);
	if (*pplist == pos)
	{
		SListPushFront(pplist, x);
	}
	else
	{
		SListNode* tmp = *pplist;
		while (tmp->next != pos)
		{
			tmp = tmp->next;
		}
		SListNode* newNode = BuySListNode(x);
		newNode->next = pos;
		tmp->next = newNode;
	}
}

 

  删除pos位置:

  • 删除pos位置只需要用temp1保存pos的下一个节点再让pos释放掉,循环找到pos的前一个节点,再让pos前一个节点指向tmp1
 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos)
{
	SListNode* tmp = *pplist;
	SListNode* tmp1 = pos->next;
	while (tmp->next != pos)
	{
		tmp = tmp->next;
	}
	free(pos);
	pos = NULL;
	tmp->next = tmp1;
}

  销毁:

  •  销毁需要一个一个销毁,因为每个节点都开辟了空间
void SLTDestroy(SListNode** pplist)
{
	assert(*pplist);
	while (*pplist)
	{
		SListNode* tmp = (*pplist)->next;
		free(*pplist);
		*pplist = tmp;
	}
}

 整体实现:

slist.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.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
void SListInsertAfter(SListNode* pos, SLTDateType x);
 单链表删除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
#include "slist.h"
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* Node = (SListNode*)malloc(sizeof(SListNode));
	if (Node == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	Node->data = x;
	Node->next = NULL;
	return Node;
	 
}
// 单链表打印
void SListPrint(SListNode* plist)
{
	assert(plist);
	SListNode* pcur = plist;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	SListNode* ptail = *pplist;
	SListNode* newNode=BuySListNode(x);
	if (ptail == NULL)
	*pplist = newNode;
	else
	{
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		ptail->next=newNode;
	}
}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newNode = BuySListNode(x);
	newNode->next = *pplist;
	*pplist = newNode;
}
// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
	assert(*pplist);
	SListNode* ptail1 = *pplist;
	SListNode* ptail2 = *pplist;
	while (ptail1->next)
	{
		ptail2 = ptail1;
		ptail1 = ptail1->next;
	}
	free(ptail2->next);
	ptail2->next = NULL;
}
// 单链表头删
void SListPopFront(SListNode** pplist)
{
	assert(*pplist);
	SListNode* pphead = (*pplist)->next;
	free(*pplist);
	*pplist = pphead;
}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	assert(plist);
	while (plist)
	{
		if (plist->data == x)
			return plist;
		plist = plist->next;
	}
	return NULL;
}
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	SListNode* newNode = BuySListNode(x);
	newNode->next = pos->next;
	pos->next = newNode;
}
 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
	SListNode* tmp = pos->next->next;
	free(pos->next);
	pos->next = NULL;
	pos->next = tmp;
}

// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x)
{
	assert(*pplist);
	if (*pplist == pos)
	{
		SListPushFront(pplist, x);
	}
	else
	{
		SListNode* tmp = *pplist;
		while (tmp->next != pos)
		{
			tmp = tmp->next;
		}
		SListNode* newNode = BuySListNode(x);
		newNode->next = pos;
		tmp->next = newNode;
	}
}
 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos)
{
	SListNode* tmp = *pplist;
	SListNode* tmp1 = pos->next;
	while (tmp->next != pos)
	{
		tmp = tmp->next;
	}
	free(pos);
	pos = NULL;
	tmp->next = tmp1;
}
void SLTDestroy(SListNode** pplist)
{
	assert(*pplist);
	while (*pplist)
	{
		SListNode* tmp = (*pplist)->next;
		free(*pplist);
		*pplist = tmp;
	}
}

 ——————————————————完 结——————————————————————

相关文章:

  • chokidar - chokidar 初识(初识案例演示、初识案例解读、初识案例测试)
  • 算法学习-线程池
  • 软考程序员-操作系统基本知识核心考点和知识重点总结
  • 代码随想录算法训练营第十四天|替换数字
  • 如果我没安装office,只安装了wps,python 如何通过win32com.client.Dispatch操作ppt?
  • 蓝桥杯备考:模拟题之神奇的幻方
  • 【nnUnetv2】推理+评估+测试
  • 计算机网络的分类及其性能指标
  • victoriametrics 部署
  • 【技术】外设驱动库开发笔记55:MAX31865热电阻变送器驱动
  • Pydantic Mixin:构建可组合的验证系统体系
  • Zstd(Zstandard)压缩算法
  • 数据库设计-笔记2
  • DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加导出数据功能示例9,TableView15_09带排序的导出表格示例
  • 多层感知机与反向传播
  • Qt调用Miniconda的python方法
  • 桥接模式 (Bridge Pattern)
  • Centos6配置yum源
  • 国企笔试之2025年中广核校招SHL测评笔试内容详解
  • 一文了解 threejs 中.bin 文件与 .gltf 文件 和 .glb 文件三者之间的关系
  • 可以做宣传的网站/企业seo整站优化方案
  • 哪个网站可以做店招店标轮播/app推广注册招代理
  • 在线网站/搜索引擎优化的概念是什么
  • 北京建设部网站官网/网站seo属于什么专业
  • 重生做二次元网站/搜索引擎关键词广告
  • 怎么在网上找做网站的客户/百度手机端排名如何优化