【数据结构】线性表——带头双向循环链表

news2024/11/15 8:38:33

文章目录

  • 带头双向循环链表
  • 带头双向循环链表主体结构
  • 带头双向循环链表操作函数介绍
  • 带头双向循环链表操作函数实现
    • 带头双向循环链表的初始化函数:
    • 打印函数
    • 带头双向循环链表插入函数:
      • 指定结点后插入和查找函数
      • 头插
      • 尾插
    • 带头双向循环链表删除函数
      • 指定结点删除
      • 头删
      • 尾删
    • 带头双向循环链表修改函数
    • 销毁带头双向循环链表
  • 源代码文件
    • test.c
    • DList.c
    • DLlist.h
  • 撒花


带头双向循环链表

  • 带头双向循环链表的优点

1.支持任意位置时间复杂度为O(1)的插入和删除。

2.按照需求申请释放空间,无需担心空间不够用,无需担心浪费。

3.带头可以省去链表为空时的判断,可以使代码更加简约

  • 带头双向循环链表的缺点

1.不可以进行下标随机访问。

2.缓存利用率低

带头双向循环链表是线性表的一种,带头双向循环链表是链式存储的线性表,不同于顺序表,链表在内存空间中不连续

  • 带头:带头就是带哨兵位,可以省链表为空时进行的判断。

  • 双向:由结构体内的next指针下一条数据进行链接,由prev对前一条数据进行链接🧐。

  • 循环:以循环方式进行链接,头的(前一个)prev是尾,尾的next(后一个)是头。

PS:需要源码直接通过目录跳转到最后

带头双向循环链表主体结构

默认大小与扩容大小还有类型都可以是任意大小或类型

typedef int DListDataType;		//数据类型

typedef struct ListNode
{
	DListDataType data;	
	struct ListNode* prev;		//指针指向前一个结点
	struct ListNode* next;		//指针指向后一个结点
}LTNode;

在这里插入图片描述

带头双向循环链表操作函数介绍

  1. LTNode* InitLTNode(); //链表初始化
  2. void ListInsert(LTNode* pos, DListDataType x); //任意位置插入
  3. void ListPushBack(LTNode* phead, DListDataType x); //尾插
  4. void ListPushFront(LTNode* phead, DListDataType x); //头插
  5. void print(LTNode* phead); //输出链表
  6. void ListErase(LTNode* pos); //任意位置删除
  7. void ListPopBack(LTNode* phead); //尾删
  8. void ListPopFront(LTNode* phead); //头删
  9. LTNode* LTModify(LTNode* phead, DListDataType x); //修改指定结点
  10. LTNode* LTFind(LTNode* phead, DListDataType x); //查找指定结点
  11. void LTDestory(LTNode* phead); //销毁链表

为了代码方便阅读,将带头双向循环链表操作函数全部放在LinkedList.c文件中,将头文件放在LinkedList.h,测试文件test.c 😮

带头双向循环链表操作函数实现

为了方便调试,建议每写完1-2个函数就进行测试,初始化之后,首先实现print函数可以方便我们进行调试。

带头双向循环链表的初始化函数:

LTNode* Phead = InitLTNode();	//初始化哨兵位

LTNode* BuyNewNode(DListDataType x)		//开辟一个新结点
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return NULL;
	}
	newnode->next = NULL;
	newnode->prev = NULL;
	newnode->data = x;
	return newnode;
}

LTNode* InitLTNode()
{
	LTNode* phead = BuyNewNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

必须先进行初始化哨兵位,将哨兵位指向自己形成循环

打印函数

先写出打印函数,方便进行调试

void print(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	printf("head");
	while (cur!=phead)
	{
		printf("->%d", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

带头双向循环链表插入函数:

指定结点后插入和查找函数

两个函数可以配合使用,使用LTFind查找到需要插入的位置,然后使用ListInsert进行更改

LTNode* LTFind(LTNode* phead, DListDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

LTNode* BuyNewNode(DListDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return NULL;
	}
	newnode->next = NULL;
	newnode->prev = NULL;
	newnode->data = x;
	return newnode;
}

void ListInsert(LTNode* pos,DListDataType x)
{
	assert(pos);
	LTNode* newnode = BuyNewNode(x);
	LTNode* prev = pos->prev;

	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

在这里插入图片描述

头插

头插将新结点插入到哨兵位后面的结点

这里面有一种简单的写法,就是直接复用ListInsert,将哨兵位后面的结点传过去,也是头插的效果

void ListPushFront(LTNode* phead, DListDataType x)
{
	assert(phead);
    //第一种方法
	//ListInsert(phead->next,x);
    //第二种方法
	LTNode* newnode = BuyNewNode(x);
	LTNode* secound = phead->next;
	newnode->next = secound;
	secound->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}

尾插

从最后一个结点后面插入新结点

尾插也可以复用ListInsert函数

  • 尾插也会用到申请新结点的函数,这里不重复了
void ListPushBack(LTNode* phead, DListDataType x)
{
	assert(phead);
    //第一种办法
	//ListInsert(phead,x);
    //第二种办法
	LTNode* newnode = BuyNewNode(x);
	LTNode* tail = phead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

带头双向循环链表删除函数

指定结点删除

删除指定结点,配合STFInd使用,将指定节点前一个结点的next连接到指定结点后一个结点,再将指定结点后面结点的prev连接到指定指定结点前一个结点。

void ListErase(LTNode* pos)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	prev->next = next;
	next->prev = prev;
    free(pos);
}

头删

记得进行断言,当只有一个哨兵位时与头结点为空都无法进行删除

可以对STErase进行复用

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead != phead->next);
    //第一种方法
	//ListErase(phead->next);
    //第二种
	LTNode* secound = phead->next;
	phead->next = secound->next;
	secound->next->prev = phead;
	free(secound);
}

尾删

void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead != phead->next);
    //第一种方法
    //ListErase(phead->prev);
    //第二种方法
	LTNode* tail = phead->prev;
	phead->prev = tail->prev;
	tail->prev->next = phead;
	free(tail);
}

带头双向循环链表修改函数

配合LTFind函数使用

LTNode* LTFind(LTNode* phead, DListDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

LTNode* LTModify(LTNode* phead, DListDataType x)
{
	LTNode* pos = LTFind(phead, x);
	int y;
	scanf("%d", &y);
	pos->data = y;
}

销毁带头双向循环链表

将每个结点依次释放。最后将哨兵位释放。

void LTDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur!=phead)
	{
		LTNode* next = cur->next;
		free(cur);	
		cur = next;
	}
	free(phead);
}

源代码文件

🌞🌞为了使代码更有阅读性,我们不建议把所有函数写在一个文件里,所以这里分成三个文件,模块化管理

test.c

测试文件

#include "DList.h"

void test1()
{
	LTNode* Phead = InitLTNode();
	ListPushBack(Phead, 666);
	ListPushBack(Phead, 777);
	ListPushBack(Phead, 9999);
	ListPushBack(Phead, 888);
	print(Phead);
	ListPopBack(Phead);
	print(Phead);
	ListPopFront(Phead);
	print(Phead);
	ListPopFront(Phead);
	ListPopFront(Phead);
	print(Phead);
	ListPushFront(Phead,111);
	print(Phead);
	ListPushFront(Phead, 112);
	print(Phead);
	LTDestory(Phead);
	Phead = NULL;

}

int main()
{
	test1();
}

DList.c

i将所有函数封装在此文件下

#include "DList.h"


LTNode* BuyNewNode(DListDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return NULL;
	}
	newnode->next = NULL;
	newnode->prev = NULL;
	newnode->data = x;
	return newnode;
}
LTNode* InitLTNode()
{
	LTNode* phead = BuyNewNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

void ListInsert(LTNode* pos,DListDataType x)
{
	assert(pos);
	LTNode* newnode = BuyNewNode(x);
	LTNode* prev = pos->prev;

	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

void ListPushBack(LTNode* phead, DListDataType x)
{
	//ListInsert(phead,x);
	assert(phead);
	LTNode* newnode = BuyNewNode(x);
	LTNode* tail = phead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

void ListPushFront(LTNode* phead, DListDataType x)
{
	//ListInsert(phead->next,x);
	assert(phead);
	LTNode* newnode = BuyNewNode(x);
	LTNode* secound = phead->next;
	newnode->next = secound;
	secound->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}

void ListErase(LTNode* pos)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	prev->next = next;
	next->prev = prev;
	free(pos);
}

void ListPopBack(LTNode* phead)
{
	//ListErase(phead->prev);
	assert(phead);
	assert(phead != phead->next);
	LTNode* tail = phead->prev;
	phead->prev = tail->prev;
	tail->prev->next = phead;
	free(tail);
}

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead != phead->next);
	//ListErase(phead->next);
	LTNode* secound = phead->next;
	phead->next = secound->next;
	secound->next->prev = phead;
	free(secound);
}

void print(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	printf("head");
	while (cur!=phead)
	{
		printf("->%d", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

LTNode* LTFind(LTNode* phead, DListDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

LTNode* LTModify(LTNode* phead, DListDataType x)
{
	LTNode* pos = LTFind(phead, x);
	int y;
	scanf("%d", &y);
	pos->data = y;
}

void LTDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur!=phead)
	{
		LTNode* next = cur->next;
		free(cur);	
		cur = next;
	}
	free(phead);
}

DLlist.h

将主程序所需要的函数全部在头文件中声明,增加代码阅读性

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <malloc.h>
#include <assert.h>

typedef int DListDataType;

typedef struct ListNode
{
	DListDataType data;
	struct ListNode* prev;
	struct ListNode* next;
}LTNode;

LTNode* InitLTNode();
void ListInsert(LTNode* pos, DListDataType x);
void ListPushBack(LTNode* phead, DListDataType x);
void ListPushFront(LTNode* phead, DListDataType x);
void print(LTNode* phead);
void ListErase(LTNode* pos);
void ListPopBack(LTNode* phead);
void ListPopFront(LTNode* phead);
LTNode* LTModify(LTNode* phead, DListDataType x);
LTNode* LTFind(LTNode* phead, DListDataType x);
void LTDestory(LTNode* phead);

撒花

这就是实现带头双向循环链表的全部内容了,创作不易,还请各位小伙伴多多点赞👍关注收藏⭐,以后也会更新各种关于c语言,数据结构的博客,撒花!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022东南大学网安916专硕上岸经验帖

本文目录 第一部分简单介绍我的一些选择 第二部分寒假大三下学期小学期暑假及大四上学期考前准备及考试过程考后估分与真实分数复试准备与复试过程复试结果导师选择经验对自己考研情况的评价一些建议 第一部分 简单介绍 最近忙完了毕业设计论文和教师资格证面试&#xff0c;终…

pc端项目的h5页面运行在手机浏览器使用vconsole查看页面元素、控制台、请求等信息

文章目录 一、vconsole介绍1. 作用2. 优势 二、使用1、jq项目和js项目2、vue项目 三、使用介绍1. 使用成功&#xff0c;在页面右下角会出现如下图的vConsole2. 常用功能&#xff08;控制台、请求、元素、存储器&#xff09; 一、vconsole介绍 1. 作用 使用vconsole来查看h5页…

怎么自学python?为什么选择python

自然是因为Python简单易学且应用领域广 Python近段时间一直涨势迅猛&#xff0c;在各大编程排行榜中崭露头角&#xff0c;得益于它多功能性和简单易上手的特性&#xff0c;让它可以在很多不同的工作中发挥重大作用。 正因如此&#xff0c;目前几乎所有大中型互联网企业都在使…

python写完程序怎么运行

python有两种运行方式&#xff0c;一种是在python交互式命令行下运行; 另一种是使用文本编辑器直接在命令行上运行。 注&#xff1a;以上两种运行方式均由CPython解释器编译运行。 当然&#xff0c;也可以将python代码写入eclipse中&#xff0c;用JPython解释器运行&#xff0c…

ACM - 字符串 - 基础(KMP)

字符串 一、KMP1、模板题 HDU1711 Number Sequence2、求最大匹配数 Ⅰ&#xff1a; HDU 2087 剪花布条&#xff08;子串不重叠&#xff09;3、求最大匹配数 Ⅱ&#xff1a;AcWing 831. KMP字符串&#xff08;子串可重叠&#xff09;4、s2 是不是 s1 的翻转&#xff1a;Leetcode…

draw.io二次开发(3)从删删减减开始定制自己的drawio

经过克隆代码、配置IntelliJ/IDEA和Tomcat、以及本地部署&#xff08;详见前几篇&#xff09;之后&#xff0c;终于到了上手改代码的环节了。 首先需要强调的一点是&#xff1a;千万不要去改 *.min.js 文件中的代码&#xff0c;这些文件都是生成的压缩代码&#xff0c;我们一定…

MySQL原理(四):事务

前言 上一篇介绍了 MySQL 的索引&#xff0c;这一篇将介绍事务相关的内容。 在 MySQL 的使用场景中&#xff0c;经常会有一个操作包含多个 SQL 语句&#xff0c;比如转账这个操作&#xff0c;至少包含从甲的账户中扣除金额和给乙的账户中增加金额这两个更新语句。那假如 MySQ…

如何优化VPS服务器性能,提升网站访问速度?

随着互联网的发展&#xff0c;越来越多的企业开始使用VPS服务器来托管其网站。然而&#xff0c;一些企业经常会遇到网站速度慢、响应时间长等问题&#xff0c;这不仅会影响用户的体验&#xff0c;还会导致客户流失。因此&#xff0c;优化VPS服务器的性能&#xff0c;提升网站访…

好程序员:Java培训班包就业靠谱吗?Java培训机构怎么选?

好程序员本身就是培训机构&#xff0c;现在已经10年多了。说句实在话&#xff0c;包就业的机构几乎没有&#xff0c;凡是给你说包就业的机构大多都不靠谱。你还得看机构的培训能力和就业率&#xff0c;其实能否找到工作还得看你自己在培训班学的怎么样了对吧&#xff0c;找工作…

CIAA 网络安全模型 — TLS v1.3 和 HTTPS 协议

目录 文章目录 目录SSL/TLSTLS 1.21. client_hello2. server_hello server_certificate sever_hello_done3. Certificate authentication4. client_key_exchange change_cipher_spec encrypted_handshake_message5. change_cipher_spec encrypted_handshake_message TLS 1…

portraiture3.5.6免费版PS图片磨皮软件

Portraiture是专注于图像后期处理软件研发的 Imagenomic, LLC重头产品之一&#xff0c;在摄影爱好者中极负盛名。Portraiture 可以将繁琐复杂的人像磨皮操作极致简化&#xff0c;不论是普通爱好者或专业后期处理人员&#xff0c;均能一键完成&#xff0c;被称为人像磨皮神器。 …

1. 跨域学习

1. 跨域学习 1.1 什么是跨域 出于浏览器的同源策略限制。同源策略&#xff08;Sameoriginpolicy&#xff09;是一种约定&#xff0c;它是浏览器最核心也最基本的安全功能&#xff0c;如果缺少了同源策略&#xff0c;则浏览器的正常功能可能都会受到影响。可以说Web是构建在同…

最新全网的ChatGPT让AI回答你的任何问题!国内免费用!!

ChatGPT&#xff0c;最近全网最火爆的顶流话题&#xff0c;不管数码爱好者、新闻媒体&#xff0c;还是投资客&#xff0c;无一不在关注着这一牛掰的人工智能项目 它是由OpenAI实验室推出的一款AI工具&#xff0c;拥有极其智能的对话能力&#xff0c;可以回答任何你提出的问题&…

Vuex-状态管理模式

Vuex Vuex 是一个专为 Vue.js 开发的状态管理模式。主要是是做数据交互&#xff0c;父子组件传值可以很容易办到&#xff0c;但是兄弟组件间传值&#xff08;兄弟组件下又有父子组件&#xff09;&#xff0c;页面多并且一层嵌套一层的传值&#xff0c;非常麻烦&#xff0c;这个…

函数数组的运算

函数数组的运算 一&#xff1a;冒泡运算 类似气泡上涌的动作&#xff0c;会将数据在数组中从小到大或者从大到小不断的向前移动。 基本思想&#xff1a; 冒泡排序的基本思想是对比相邻的两个元素值&#xff0c;如果满足条件就交换元素值&#xff0c;把较小的元素移动到数组前…

D31FBE01EC1NF00PARKER比例方向阀

D31FBE01EC1NF00PARKER比例方向阀是宁波秉圣工业&#xff0c;美国派克比例阀主要具有方向功能&#xff0c;流量功能&#xff0c;压力功能&#xff0c;因此主要有三类&#xff1a;方向阀&#xff0c;流量阀&#xff0c;压力阀&#xff0c;其中方向阀和压力阀直接控制和操作你。被…

一起来!白嫖Amazon DynamoDB!!!

Amazon DynamoDB简介 Amazon DynamoDB是由Amazon Web Services&#xff08;AWS&#xff09;提供的一种快速、灵活、全托管的NoSQL数据库服务&#xff0c;支持文档和键/值数据模型。它具有自动扩展、低延迟、高可靠性、高吞吐量等特点&#xff0c;能够处理从几个字节到几TB的数…

tiechui_lesson08_内存的分配和链表

主要是将链表结构的使用&#xff0c;在内核开发中使用起来比较方便的一种数据结构【LIST_ENTRY】。 一、内存的分配 主要是学习一些基本操作。现在推荐使用的动态分配函数【ExAllocatePoolWithTag】 PVOID tempbuffer ExAllocatePoolWithTag(NonPagedPool, 0x1000, xxaa); …

DJ4-6 层次选路

目录 一、层次选路的基本概念 二、域内路由选择 1、RIP* 2、OSPF 三、域间路由选择 BGP 1、AS 互连 2、AS 域间选路任务 3、示例&#xff1a;在 1d 上设置转发表 4、示例&#xff1a;在多个 AS 中做出选择 5、BGP 会话与通告 6、传播可达信息 7、路径属性和 BGP 路…

马蹄集第四期oj

目录 供水管线 黑客小码哥 逆序 来给单词分类 前k小数&#xff08;进阶&#xff09; 前K小数 线段树 队列安排 一元多项式的加法 快排变形 供水管线 难度&#xff1a;钻石 0时间限制&#xff1a;1秒 巴占用内存&#xff1a;128M 在个城市之间原本要规划修建许多条下水管道…