[C/C++]数据结构 链表(单向链表,双向链表)

前言:

        上一文中我们介绍了顺序表的特点及实现,但是顺序表由于每次扩容都是呈二倍增长(扩容大小是自己定义的),可能会造成空间的大量浪费,但是链表却可以解决这个问题.

概念及结构:

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

注意:

  1. 链式结构在逻辑上是连续的,但是在物理上不一定连续
  2. 结点一般都是从堆上申请出来的
  3. 从堆上申请的空间是按照一定的策略来分配的,两次申请的空间可能连续也可能不连续

    分类:

  1. 单向或双向
  2. 带头或不带头
  3. 循环或不循环

    虽然链表有这么多种结构,但是在实际中最常用的还是两种结构:

  • 无头单向非循环链表:

          结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈       希桶、图的邻接表等等。       

  •  带头双向循环链表:

       结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单

无头单向非循环链表

接口:

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** pphead,SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode** pphead,SListNode* pos);
// 在pos的前面插入
SListNode* SListInsertFront(SListNode** pphead, SListNode* pos, SLTDateType x);
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos);
//单链表摧毁
void SLTDestroy(SLNode** pphead);

1.动态申请结点

SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
}

2.单链表打印

void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;

	 while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

3.单链表尾插

void SListPushBack(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
   
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

4.单链表头插

void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	newnode->next = *pplist;
	*pplist = newnode;
}

5.单链表尾删

void SListPopBack(SListNode** pplist)
{
	assert(*pplist);


	SListNode* pre = NULL;
	SListNode* tail = *pplist;


	if ((*pplist)->next == NULL)
	{
		*pplist = NULL;
	}
	else
	{
	   /* while (tail->next)
	   {
		 pre = tail; 
		 tail = tail->next;
	   }
	     free(tail);
	     tail = NULL;
	     pre->next = NULL;*/
		SListNode* tail = *pplist;
		while (tail->next->next)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	} 
}

6.单链表头删

void SListPopFront(SListNode** pplist)
{
	assert(pplist);


	if ((*pplist)->next == NULL)
	{
		*pplist = NULL;
	}
	else
	{
		SListNode* ret = ((*pplist)->next);
		free(*pplist);
		*pplist = ret;
	}
}

7.单链表查找

SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	assert(plist);


	SListNode* ret = plist;
	while (ret->data != x&&ret->next)
	{
		ret=ret->next;
	}
	if (ret->next == NULL && ret->data != x)
		return NULL;
	else
		return ret; 
}

8.摧毁单链表

void SLTDestroy(SListNode** pphead)
{
	SListNode* cur = *pphead;
	SListNode* ret = NULL;


	while (cur)
	{
		ret = cur->next;
		free(cur);
		cur = ret;
	}
	*pphead = NULL;
}

9.在pos位置之后插入x

// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode** pphead,SListNode* pos, SLTDateType x)
{
	assert(pphead);
	//检测插入位置是否存在
	assert(pos); 
	assert(*pphead);

	SListNode* newnode=BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

10.删除pos位置之后的值

// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode** pphead,SListNode* pos)
{
	assert(pphead);
	assert(pos);
	assert(pos->next);
	assert(*pphead);

	SListNode* tmp = pos->next;
	pos->next = pos->next->next;
	free(tmp);
	tmp = NULL;
}

11.在pos位置之前插入x

SListNode* SListInsertFront(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);

	SListNode* newnode = BuySListNode(x);
	SListNode* pre = *pphead;

	if (*pphead == pos)
	{
		SListPushFront(pphead,x);
	}
	else
	{
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		newnode->next = pos;
		pre->next = newnode;
	}
	

}

12.删除pos位置

void SLTErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SListNode* pre = *pphead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		pre->next = pos->next;
		free(pos);
		pos = NULL;
	}

}

 

带头双向循环链表

接口:

// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

1.创建返回链表的头节点

ListNode* ListCreate(LTDataType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	node->_data = x;
	node->_next = NULL;
	node->_prev = NULL;
	return node;
}

2.双向链表销毁

void ListDestory(ListNode* pHead)
{
	assert(pHead);


	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		ListNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	free(pHead);
}

3.双向链表打印

void ListPrint(ListNode* pHead)
{
	assert(pHead);
	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		printf("%d <-> ", cur->_data);
		cur = cur->_next;
	}
}

4.双向链表尾插

void ListPushBack(ListNode* pHead, LTDataType x)
{
	ListNode* newnode = ListCreate(x);
	ListNode* tail = pHead->_prev;     //尾指针


	tail->_next = newnode;
	newnode->_next = pHead;
	newnode->_prev = tail;
	pHead->_prev = newnode;
}

5.双向链表尾删

void ListPopBack(ListNode* pHead)
{
	assert(pHead);
	assert(pHead->_next!=pHead);


	ListNode* tail = pHead->_prev;


	pHead->_prev = tail->_prev;
	tail->_prev->_next = pHead;
	free(tail);
	tail = NULL;
}

6.双向链表头插

void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead);


	ListNode* newnode = ListCreate(x);


	newnode->_next = pHead->_next;
	pHead->_next->_prev = newnode;
	pHead->_next = newnode;
	newnode->_prev = pHead;
}

7.双向链表头删

void ListPopFront(ListNode* pHead)
{
	ListNode* pHeadNext = pHead->_next;


	pHeadNext->_next->_prev = pHead;
	pHead->_next = pHeadNext->_next;


	free(pHeadNext);
	pHeadNext = NULL;
}

8.双向链表查找


ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		if (cur->_data == x)
			return cur;


		cur = cur->_next;
	}
	return NULL;
}

9.双向链表在pos的前面进行插入

void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);


	ListNode* posprev = pos->_prev;
	ListNode* newnode = ListCreate(x);
	newnode->_next = pos;
	pos->_prev = newnode;
	posprev->_next = newnode;
	newnode->_prev = pos->_prev;
}

10.双向链表删除pos位置的节点

void ListErase(ListNode* pos)
{
	assert(pos);
	
	ListNode* posprev = pos->_prev;
	ListNode* posnext = pos->_next;


	posprev->_next = posnext;
	posnext->_prev = posprev;
	free(pos);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/160751.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

halcon识别验证码,先训练后识别

识别验证码图片&#xff0c;使用halcon 21.05 下面代码识别准确率100% 目录 训练&#xff0c;图片打标签使用代码创建分类器&#xff1b;识别验证码&#xff0c;检验识别效果使用“助手”加载训练文件&#xff0c;加载训练分类器&#xff0c;察看收集的字符&#xff0c;训练识别…

Theory behind GAN

假如要生成一些人脸图&#xff0c;实际上就是想要找到一个分布&#xff0c;从这个分布内sample出来的图片像是人脸&#xff0c;分布之外生成的就不像人脸。而GAN要做的就是找到这个distribution。 在GAN之前用的是Maximum Likelihood Estimation。 Maximum Likelihood Estimat…

【C++】类和对象(5)--运算符重载

目录 一 概念 二 运算符重载的实现 三 关于时间的所有运算符重载 四 默认赋值运算符 五 const取地址操作符重载 一 概念 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载是具有特殊函数名的函数&#xff0c;也具有其返回值类型&#xff0c;函数名字以及参数…

Android 13.0 Launcher3仿ios长按app图标实现抖动动画开始拖拽停止动画

1.概述 在13.0的系统rom定制化开发中,在对系统原生Launcher3的定制需求中,也有好多功能定制的,在ios等电子产品中 的一些好用的功能,也是可以被拿来借用的,所以在最近的产品开发需求中,需求要求模仿ios的 功能实现长按app图标实现抖动动画,接下来看如何分析该功能的实现…

基于静电放电算法优化概率神经网络PNN的分类预测 - 附代码

基于静电放电算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于静电放电算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于静电放电优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

【数据结构与算法】JavaScript实现双向链表

文章目录 一、双向链表简介二、封装双向链表类2.0.创建双向链表类2.1.append(element)2.2.toString()汇总2.3.insert(position,element)2.4.get(position)2.5.indexOf(element)2.7.update(position,element)2.8.removeAt(position)2.9.其他方法2.10.完整实现 三、链表结构总结3…

苍穹外卖--员工分页查询

请求参数封装&#xff1a; Data public class EmployeePageQueryDTO implements Serializable {//员工姓名private String name;//页码private int page;//每页显示记录数private int pageSize;}请求结果封装&#xff1a; public class PageResult implements Serializable {…

青岛数字孪生赋能工业制造,加速推进制造业数字化转型

随着企业数字化进程的推进&#xff0c;数字孪生技术逐渐在汽车行业得到广泛应用。5G与数字孪生、工业互联网的融合将加速数字中国、智慧社会建设&#xff0c;加速中国新型工业化进程&#xff0c;为中国经济发展注入新动能。数字孪生、工业物联网、工业互联网等新一代信息通信技…

Pattern Recognition投稿经验

文章目录 ManuscriptTitle PageHighlightsAuthor BiographyDeclarationSubmit 合作推广&#xff0c;分享一个人工智能学习网站。计划系统性学习的同学可以了解下&#xff0c;点击助力博主脱贫( •̀ ω •́ )✧ 停更了大半年&#xff0c;近期终于完成了论文投稿&#xff0c;趁…

基于人工电场算法优化概率神经网络PNN的分类预测 - 附代码

基于人工电场算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于人工电场算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于人工电场优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

什么是PWA(Progressive Web App)?它有哪些特点和优势?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

【算法训练营】参数解析+跳石板

&#x1f308;欢迎来到Python专栏 &#x1f64b;&#x1f3fe;‍♀️作者介绍&#xff1a;前PLA队员 目前是一名普通本科大三的软件工程专业学生 &#x1f30f;IP坐标&#xff1a;湖北武汉 &#x1f349; 目前技术栈&#xff1a;C/C、Linux系统编程、计算机网络、数据结构、Mys…

跟我一起来做一个音视频产品功能!

前言&#xff1a; 大家好&#xff0c;上来和大家汇报一下h264翻译进度&#xff0c;目前翻译完了第六章&#xff0c;第七章快翻译完了&#xff0c;马上可以翻译第八章。 在第七章翻译完了之后&#xff0c;我会做一个知识点总结出来&#xff0c;一起学习&#xff0c;一起进步&…

小程序授权获取昵称

wxml: <form bindsubmit"formsubmit"><view style"width: 90%;display: flex;margin-left: 5%;"><view class"text1">昵称&#xff1a;</view><input style"width: 150px;margin-left: 30px;margin-top: 30px;…

操作系统秋招面试题

自己在秋招过程中遇到的高频操作系统相关的面试题 内存管理 虚拟内存 虚拟内存的⽬的是为了让物理内存扩充成更⼤的逻辑内存&#xff0c;从⽽让程序获得更多的可⽤内存。 为了更好的管理内存&#xff0c;操作系统将内存抽象成地址空间。每个程序拥有⾃⼰的地址空间&#xff…

【具身智能评估1】具身视觉语言规划(EVLP)仿真环境汇总

参考论文&#xff1a;Core Challenges in Embodied Vision-Language Planning 论文作者&#xff1a;Jonathan Francis, Nariaki Kitamura, Felix Labelle, Xiaopeng Lu, Ingrid Navarro, Jean Oh 论文原文&#xff1a;https://arxiv.org/abs/2106.13948 论文出处&#xff1a;Jo…

恕我直言,大模型对齐可能无法解决安全问题,我们都被表象误导了

是否听说过“伪对齐”这一概念&#xff1f; 在大型语言模型&#xff08;LLM&#xff09;的评估中&#xff0c;研究者发现了一个引人注目的现象&#xff1a;当面对多项选择题和开放式问题时&#xff0c;模型的表现存在显著差异。这一差异根源在于模型对复杂概念的理解不够全面&…

SpringCloud 之Feign的性能优化

Feign底层默认是JDK自带的HttpURLConnection&#xff0c;它是单线程发送HTTP请求的&#xff0c;不能配置线程池&#xff0c;我们使用Okhttp或者HttpClien 朵发送http请求&#xff0c;并且它们两个都支持线程池。 常见HTTP客户端 HttpClient HttpClient 是 Apache Jakarta Comm…

数位和相等数对的最大和【教3妹学编程-算法题】数位和相等数对的最大和

3妹&#xff1a;2哥&#xff0c;你有没有看到新闻“18岁父亲为4岁儿子落户现身亲子鉴定” 2哥 : 啥&#xff1f;18岁就当爹啦&#xff1f; 3妹&#xff1a;确切的说是14岁好吧。 2哥 : 哎&#xff0c;想我30了&#xff0c; 还是个单身狗。 3妹&#xff1a;别急啊&#xff0c; 2…
最新文章