【双向链表】带头双向循环(1)

news2024/11/27 20:36:02

目录

链表的分类

Test.c主函数

test1头插

test2头删

test3尾插

test4尾删

test5查询

test6插入pos位置后一个

test7删除pos

Test.c总代码 

DList.h头文件&函数声明

头文件

函数声明

DList.h总代码

DList.c函数实现

Createnode创建节点

LTInit初始化

LTPrint打印

LTPushFront头插

LTPopFron头删

LTPushBack尾插

LTPopBack尾删

LTFind查询

LTInsertAfter在pos后面插入

LTErase删除pos数据

LTDestroy销毁释放

DList.c总代码


今天实现一个带头双向循环链表。

  • 🙂判断为NULL的情况
  • 🙂判断二级指针&一级指针(想要改变实参不仅可以用指针,而且可以用return
  • 🙂判断先实现哪一步
  • ❌野指针的出现
  • ❌修改指针用二级指针
  • ❌修改结构体用一级指针
  • ❌内存泄露的问题,需要释放
  • ❌释放完空间,指针需要置NULL
  • 🆗无头单项不循环链表适用OJ链表比较多
  • 🆗带头双向不循环适用生活工作场景比较多
  • 🆗C++是兼容C
  • ❌【单链表】一般都不带头//需要二级指针处理//除了!链表分割
  • ❌【双向链表】需要带头
  • 🙂【单链表】Singly linked list(SLT)
  • 🙂【双链表】Double linked list(LT)
  • 🙂【顺序表】Sequention table list(SL)

链表的分类

前面我们学习了单链表。但是链表并不仅限只有【单链表】。【链表】是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

 单向或者双向

带头或者不带头

 

循环或者非循环

 

 这些特征组合起来可以构成:8种形态的链表。

 

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

【无头单项非循环链表】&【带头双向循环链表】

  • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多
  • 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。实际生活中使用很多。

在前面我们已经学过 无头单项不循环链表,这里我们来实现 带头双向循环链表 

Test.c主函数

int main()
{
	LTNode* phead = LTInit();//初始化
	test1(phead);//头插
	test2(phead);//头删
	test3(phead);//尾插
	test4(phead);//尾删
    test5(phead);//查询某个数字

	test6(phead);//插入pos位置的前一个/后一个元素
	test7(phead);//删除pos位置的元素
	LTDestroy(phead);//空间释放/防止内存泄露
	return 0;
}

test1头插

#include"DList.h"
void test1(LTNode* phead)//头插
{
	LTPushFront(phead, 7);
	LTPushFront(phead, 3);
	LTPushFront(phead, 4);
	LTPushFront(phead, 7);
	LTPushFront(phead, 8);
	LTPushFront(phead, 9);
	LTPrint(phead);
}

test2头删

void test2(LTNode* phead)//头删
{
	LTPopFront(phead);
	LTPopFront(phead);
	LTPopFront(phead);
	LTPrint(phead);
}

test3尾插

void test3(LTNode* phead)//尾插
{
	LTPushBack(phead, 77);
	LTPushBack(phead, 99);
	LTPrint(phead);
}

test4尾删

void test4(LTNode* phead)//尾删
{
	LTPopBack(phead);
	LTPopBack(phead);
	LTPopBack(phead);
	LTPopBack(phead);
	LTPrint(phead);
}

test5查询

void test5(LTNode* phead)//查询
{
	LTNode* node = LTFind(phead, 4);
	if (node == NULL)
	{
		printf("没找到");
	}
	else
	{
		printf("%d", node->val);
	}
}

test6插入pos位置后一个

void test6(LTNode* phead)//插入pos位置的前一个/后一个元素
{
	LTNode* pos = LTFind(phead, 4);
	LTInsertAfter(phead, pos, 77);
	LTInsertAfter(phead, pos, 66);
	LTInsertAfter(phead, pos, 99);
	LTPrint(phead);
}

test7删除pos

//删除pos位置的元素
void test7(LTNode* phead)
{
	LTNode* pos = LTFind(phead, 4);
    if(pos)//查询成功才进入
    {
	  LTErase(phead, pos);
      pos=NULL;
    }
	LTPrint(phead);
}

Test.c总代码 

#include"DList.h"
void test1(LTNode* phead)//头插
{
	LTPushFront(phead, 7);
	LTPushFront(phead, 3);
	LTPushFront(phead, 4);
	LTPushFront(phead, 7);
	LTPushFront(phead, 8);
	LTPushFront(phead, 9);
	LTPrint(phead);
}

void test2(LTNode* phead)//头删
{
	LTPopFront(phead);
	LTPopFront(phead);
	LTPopFront(phead);
	LTPrint(phead);
}

void test3(LTNode* phead)//尾插
{
	LTPushBack(phead, 77);
	LTPushBack(phead, 99);
	LTPrint(phead);
}

void test4(LTNode* phead)//尾删
{
	LTPopBack(phead);
	LTPopBack(phead);
	LTPopBack(phead);
	LTPopBack(phead);
	LTPrint(phead);
}

void test5(LTNode* phead)//查询
{
	LTNode* node = LTFind(phead, 4);
	if (node == NULL)
	{
		printf("没找到");
	}
	else
	{
		printf("%d", node->val);
	}
}

void test6(LTNode* phead)//插入pos位置的前一个/后一个元素
{
	LTNode* pos = LTFind(phead, 4);
	LTInsertAfter(phead, pos, 77);
	LTInsertAfter(phead, pos, 66);
	LTInsertAfter(phead, pos, 99);
	LTPrint(phead);
}

//删除pos位置的元素
void test7(LTNode* phead)
{
	LTNode* pos = LTFind(phead, 4);
    if(pos)//查询成功才进入
    {
	  LTErase(phead, pos);
      pos=NULL;
    }
	LTPrint(phead);
}


int main()
{
	LTNode* phead = LTInit();//已经被初始化了
	test1(phead);//头插
	test2(phead);//头删
	test3(phead);//尾插
	test4(phead);//尾删
    test5(phead);//查询某个数字

	test6(phead);//插入pos位置的前一个/后一个元素
	test7(phead);//删除pos位置的元素
	LTDestroy(phead);//空间释放/防止内存泄露
    phead=NULL;
	return 0;
}

DList.h头文件&函数声明

头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
assert(//)//括号里是可以出现的情况为真 若括号里的表达式为假那就❌

函数声明

  • 声明节点 
typedef int LTDateType;
//声明节点
typedef struct DListNode
{
	LTDateType val;
	struct DListNode* prev;
	struct DListNode* next;
}LTNode;
  •  初始化(可以用指针修改实参/也可以用返回值改变实参)
LTNode* LTInit();
  • 头插
void LTPushFront(LTNode*phead, LTDateType x);
  • 头删
void LTPopFront(LTNode* phead);
  • 尾插
void LTPushBack(LTNode* phead,LTDateType x);
  • 尾删
void LTPopBack(LTNode* phead);
  • 打印
void LTPrint(LTNode* phead);
  • 查询
LTNode* LTFind(LTNode* phead);
  • 在pos的后面插入一个元素
void LTInsertAfter(LTNode* phead, LTNode* pos, LTDateType x);
  • 删除pos位置的元素
void LTErase(LTNode* phead, LTNode* pos);
  • 销毁释放 
void LTDestroy(LTNode* phead);

DList.h总代码

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

typedef int LTDateType;

//定义节点
typedef struct DListNode
{
	LTDateType val;
	struct DListNode* prev;
	struct DListNode* next;
}LTNode;

//初始化
LTNode* LTInit();//不用二级指针/用返回值改变实参
void LTPushFront(LTNode*phead, LTDateType x);//头插
void LTPopFront(LTNode* phead);//头删
void LTPushBack(LTNode* phead,LTDateType x);//尾插
void LTPopBack(LTNode* phead);//尾删
void LTPrint(LTNode* phead);//打印
LTNode* LTFind(LTNode* phead);//查询
void LTInsertAfter(LTNode* phead, LTNode* pos, LTDateType x);//在pos的后面插入一个元素
void LTErase(LTNode* phead, LTNode* pos);//删除pos位置的元素
void LTDestroy(LTNode* phead);//销毁释放

DList.c函数实现

//初始化
LTNode* LTInit()
{
	LTNode* phead = Createnode(-1);
	phead->prev = phead;
	phead->next = phead;
	return phead;
}

Createnode创建节点

#include"DList.h"
//创造节点
LTNode* Createnode(LTDateType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);//程序停止
	}
	newnode->val = x;
	newnode->prev = NULL;
	newnode->next = NULL;
	return newnode;
}

LTInit初始化

  • 通过形参改变实参可以使用 【指正】
  • 通过形参改变实参可以使用 【return】
//初始化
LTNode* LTInit()
{
	LTNode* phead = Createnode(-1);
	phead->prev = phead;
	phead->next = phead;
	return phead;
}

 

LTPrint打印

//打印
void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead;
	printf("<=>");
    printf("%d<=>", cur->val);
	cur = cur->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->val);
		cur = cur->next;
	}
	printf("\n");
}

LTPushFront头插

//头插
void LTPushFront(LTNode* phead,LTDateType x)
{
	assert(phead);
	LTNode* newnode = Createnode(x);
	newnode->next = phead->next;
	phead->next->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}

LTPopFron头删

  • 一定不能把头节点哨兵位给删除了会出现野指针的问题❌ 
//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	LTNode* next = cur->next;
	assert(cur != phead);	//考虑如果链表为NULL
	phead->next = next;
	next->prev = phead;
	free(cur);
	cur = NULL;
}

 

LTPushBack尾插

//尾插
void LTPushBack(LTNode* phead, LTDateType x)
{
	assert(phead);
	LTNode* newnode = Createnode(x);
	newnode->prev = phead->prev;
	phead->prev->next = newnode;
	newnode->next = phead;
	phead->prev = newnode;
}

 

LTPopBack尾删

  • 一定不能把头节点哨兵位给删除了会出现野指针的问题❌  
//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->prev;
	LTNode* next = cur->prev;
	assert(cur != phead);
	phead->prev = next;
	next->next = phead;
	free(cur);
	cur = NULL;
}

 

LTFind查询

  • 查询到某个数据的位置意味着可以修改这个位置前后的数据 
//查询
//查询某个数,存在返回节点地址,不存在返回NULL
LTNode* LTFind(LTNode* phead, LTDateType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

LTInsertAfter在pos后面插入

//在pos的后面插入一个元素
void LTInsertAfter(LTNode* phead, LTNode*pos, LTDateType x)
{
	assert(phead);
	LTNode* newnode = Createnode(x);
	newnode->next = pos->next;
	pos->next->prev = newnode;
	pos->next = newnode;
	newno

 

LTErase删除pos数据

//删除pos位置的元素//
void LTErase(LTNode* phead, LTNode* pos)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* cur = pos->prev;
	LTNode* next = pos->next;
	cur->next = next;
	next->prev = cur;
	free(pos);
	pos = NULL;
}

 

LTDestroy销毁释放

//销毁
void LTDestroy(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
}

DList.c总代码

#include"DList.h"
//创造节点
LTNode* Createnode(LTDateType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);//程序停止
	}
	newnode->val = x;
	newnode->prev = NULL;
	newnode->next = NULL;
	return newnode;
}
//初始化
LTNode* LTInit()
{
	LTNode* phead = Createnode(-1);
	phead->prev = phead;
	phead->next = phead;
	return phead;
}
//打印
void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead;
	printf("<=>");
    printf("%d<=>", cur->val);
	cur = cur->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->val);
		cur = cur->next;
	}
	printf("\n");
}
//头插
void LTPushFront(LTNode* phead,LTDateType x)
{
	assert(phead);
	LTNode* newnode = Createnode(x);
	newnode->next = phead->next;
	phead->next->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}
//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	LTNode* next = cur->next;
	assert(cur != phead);	//考虑如果链表为NULL
	phead->next = next;
	next->prev = phead;
	free(cur);
	cur = NULL;
}
//尾插
void LTPushBack(LTNode* phead, LTDateType x)
{
	assert(phead);
	LTNode* newnode = Createnode(x);
	newnode->prev = phead->prev;
	phead->prev->next = newnode;
	newnode->next = phead;
	phead->prev = newnode;
}
//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->prev;
	LTNode* next = cur->prev;
	assert(cur != phead);
	phead->prev = next;
	next->next = phead;
	free(cur);
	cur = NULL;
}
//查询
//查询某个数,存在返回节点地址,不存在返回NULL
LTNode* LTFind(LTNode* phead, LTDateType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
//在pos的后面插入一个元素
void LTInsertAfter(LTNode* phead, LTNode*pos, LTDateType x)
{
	assert(phead);
	LTNode* newnode = Createnode(x);
	newnode->next = pos->next;
	pos->next->prev = newnode;
	pos->next = newnode;
	newnode->prev = pos;
}
//删除pos位置的元素//
void LTErase(LTNode* phead, LTNode* pos)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* cur = pos->prev;
	LTNode* next = pos->next;
	cur->next = next;
	next->prev = cur;
	free(pos);
	pos = NULL;
}
//销毁
void LTDestroy(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
    cur=NULL;
}
//形式参数这里就可以置NULL 
//实际参数到主函数里面置NULL


 

✔✔✔✔✔最后感谢大家的阅读,若有错误和不足,欢迎指正!乖乖敲代码哦! 

代码---------→【唐棣棣 (TSQXG) - Gitee.com】

联系---------→【邮箱:2784139418@qq.com】

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

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

相关文章

1~2亿条数据需要缓存之安装redis集群(哈希取余分区、一致性哈希算法分区、哈希槽分区)

安装redis集群 面试题 1~2亿条数据需要缓存&#xff0c;请问如何设计这个存储案例??? 回答: 单机单台100%不可能&#xff0c;肯定是分布式存储&#xff0c;用redis如何落地&#xff1f; 上述问题阿里P6~P7工程案例和场景设计类必考题目&#xff0c; 一般业界有3种解决方案 …

STM32H743 RTC精密数字校准 深度剖析

一、问题 项目中数据报文收到的RTC时间总是会慢一些,经过实际几天的测试得出结论:24小时要慢5S左右。根据手册我了解到可以有误差但不会差这么多,所以进行了如下分析并解决问题。 二、分析 1.影响RTC准确性的因素罗列 硬件基础误差(也就是待校准部分) …

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

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

【用unity实现100个游戏之15】开发一个类保卫萝卜的Unity2D塔防游戏3(附项目源码)

文章目录 先看本次实现的最终效果前言绘制炮塔UI炮塔转向敌人生成炮弹旋转我们的子弹对敌人造成伤害&#xff0c;回收子弹自动发射子弹添加攻击间隔显示伤害字体设计通用泛型单例创建更多炮塔升级增加伤害升级缩短攻击间隔添加货币杀死敌人获取金币源码完结 先看本次实现的最终…

有趣的按钮分享

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 广告打完&#xff0c;我们进入正题&#xff0c;先看效果&#xff1a; 废话不多&#xff0c;上源码&#xff1a; <button class&quo…

牛只识别 牛脸识别 个体识别 身份识别

融合YOLOv5s与通道剪枝算法的奶牛轻量化个体识别方法 Light-weight recognition network for dairy cows based on the fusion of YOLOv5s and channel pruning algorithm 论文链接 知网链接 点击进入正文 该文章讨论了奶牛花斑、光照条件、不同剪枝方法、不同剪枝率对准确率的…

OPPO发布AndesGPT大模型;Emu Video和Emu Edit的新突破

&#x1f989; AI新闻 &#x1f680; OPPO发布全新ColorOS 14及自主训练的AndesGPT大模型 摘要&#xff1a;OPPO在2023 OPPO开发者大会上发布了全新的ColorOS 14&#xff0c;并正式推出了自主训练的安第斯大模型&#xff08;AndesGPT&#xff09;。AndesGPT拥有对话增强、个人…

Linux学习教程(第三章 Linux文件和目录管理)2

第三章 Linux文件和目录管理(初识Linux命令) 十一、Linux 删除空目录(rmdir命令) Linux rmdir命令:删除空目录 和 mkdir 命令(创建空目录)恰好相反,rmdir(remove empty directories 的缩写)命令用于删除空目录,此命令的基本格式为: [root@localhost ~]# rmdir […

应用在城市井盖积水检测中的深水液位传感芯片

城市井盖积水检测系统以实现城市下水道水雨情信息“全要素、全量程、全覆盖”自动测报为目标&#xff0c;具备下水道水位、雨量、流速、流量、雨量、气象参数、现场图像、视频等水文信息采集、传输、处理及预警等功能&#xff0c;有效提升了雨水情信息的时效性和准确度&#xf…

【Java开发的主要应用领域】

【点我-这里送书】 本人详解 作者&#xff1a;王文峰&#xff0c;参加过 CSDN 2020年度博客之星&#xff0c;《Java王大师王天师》 公众号&#xff1a;JAVA开发王大师&#xff0c;专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生&#xff0c;期待你的…

类BERT模型蒸馏实战

机器学习模型已经变得越来越大&#xff0c;以至于训练模型可能会给那些没有空闲集群的人带来痛苦。 此外&#xff0c;即使使用训练好的模型&#xff0c;当你的硬件与模型对其运行的期望不符时&#xff0c;推理的时间和内存成本也会飙升。 因此&#xff0c;为了缓解这个问题&…

idea 环境搭建及运行java后端源码

1、 idea 历史版本下载及安装 建议下载和我一样的版本&#xff0c;2020.3 https://www.jetbrains.com/idea/download/other.html&#xff0c;idea分为专业版本&#xff08;Ultimate&#xff09;和社区版本&#xff08;Community&#xff09;&#xff0c;前期可以下载专业版本…

新品|CASAIM-IS(2ND)自动化智能检测系统正式上市,打造更高效、更智能、更安全新体验!

全新第二代中科广电CASAIM-IS自动化智能检测系统正式上市&#xff0c;集合CASAIM最新的“智能控制、智能成像、智能检测”三智技术&#xff0c;为中小型精密复杂工件测量及检测提供一站式高效全自动化智能检测解决方案

设计模式(5)-使用设计模式实现简易版springIoc

自定义简易版springIoc 1 spring使用回顾 自定义spring框架前&#xff0c;先回顾一下spring框架的使用&#xff0c;从而分析spring的核心&#xff0c;并对核心功能进行模拟。 数据访问层。定义UserDao接口及其子实现类 public interface UserDao {public void add(); }public…

使用VC++设计程序,进行全局固定阈值分割、自适应阈值分割

图像分割 文章目录 图像分割实验内容一、全局固定阈值分割全局固定阈值分割的原理全局固定阈值分割的实验代码全局固定阈值分割的实验现象 二、自适应阈值分割自适应阈值分割的实验原理自适应阈值分割的实验代码自适应阈值分割的实验现象 实验内容 实验目的&#xff1a; &…

移交计划书、移交确认单

项目移交过程文件&#xff1a; 1、移交计划书 2、移交确认单 1、移交计划 2、移交确认单

Day48 力扣动态规划 : 647. 回文子串 |516.最长回文子序列 |动态规划总结篇

Day48 力扣动态规划 : 647. 回文子串 &#xff5c;516.最长回文子序列 &#xff5c;动态规划总结篇 647. 回文子串第一印象看完题解的思路dp递推公式初始化递归顺序 实现中的困难感悟代码 516.最长回文子序列第一印象我的尝试遇到的问题 看完题解的思路dp递推公式初始化 实现中…

基于springboot实现大学生体质测试管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现大学生体质测试管理系统演示 摘要 大学生体质测试管理系统提供给用户一个简单方便体质测试管理信息&#xff0c;通过留言区互动更方便。本系统采用了B/S体系的结构&#xff0c;使用了java技术以及MYSQL作为后台数据库进行开发。系统主要分为系统管理员、教师…

C/C++高频面经-秋招篇

自己在秋招找工作过程中遇到的一些C/C面试题&#xff0c;大中小厂都有&#xff0c;分享出来&#xff0c;希望能帮到有缘人。 C语言 snprintf()的使用 函数原型为int snprintf(char *str, size_t size, const char *format, …) 两点注意&#xff1a; (1) 如果格式化后的字符…