C++11 数据结构3 线性表的循环链式存储,实现,测试

上一节课,我们学了线性表 单向存储结构(也就是单链表),这个是企业常用的技术,且是后面各种的基本,一定要牢牢掌握,如果没有掌握,下面的课程会云里雾里。

一 ,循环链表

1、什么是循环链表

— 概念上:
1、任何数据元素都有一个前驱和一个后继
2、所有的数据元素的关系构成一个逻辑的环

— 实现上:
1、循环链表是一种特殊的单链表
2、尾结点的指针域保存了首结点的地址

2、循环链表的逻辑构成

二 循环链表的插入示意图

头插法第一次插入

头插法非第一次插入

删除非第一个元素

删除第一个元素

三 代码实现

.h实现

#ifndef _003CIRCLELIST_H_
#define _003CIRCLELIST_H_

typedef void CircleList;  //要返回给上层的list 的首地址为 void *,为了阅读起名为CircleList

typedef struct _tag_CircleListNode  //list 中的节点
{
	struct _tag_CircleListNode* next;
}CircleListNode;


//创建循环链表
CircleList* CircleList_Create();


//销毁循环链表
void CircleList_Destroy(CircleList* list);


//清空循环列表
void CircleList_Clear(CircleList* list);


//循环列表中目前存在的元素个数
int CircleList_Length(CircleList* list);


//给循环列表中插入元素,node为要插入的元素的值,pos为位置
int CircleList_Insert(CircleList* list, CircleListNode* node, int pos);


//从循环列表中的pos 位置获得具体的值
CircleListNode* CircleList_Get(CircleList* list, int pos);


//从循环列表中删除pos位置的数据
CircleListNode* CircleList_Delete(CircleList* list, int pos);


//从循环列表中删除 数据 为node 的点
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);



CircleListNode* CircleList_Reset(CircleList* list);



CircleListNode* CircleList_Current(CircleList* list);



CircleListNode* CircleList_Next(CircleList* list);



#endif

底层实现

#include "003CircleList.h"
#include "stdio.h"
#include "stdlib.h"
#include <string.h>


typedef struct _tag_CircleList{
	CircleListNode header;
	CircleListNode* slider; //多了一个游标
	int length;
} TCircleList;

CircleList* CircleList_Create(){
	TCircleList* ret = (TCircleList*)malloc(sizeof(TCircleList));
	if (ret == NULL) {
		printf("CircleList_Create func malloc error\n");
		return ret;
	}
	memset(ret,0,sizeof(TCircleList));
	ret->length = 0;
	ret->header.next = NULL;
	ret->slider = NULL;
	return ret;
}

void CircleList_Destroy(CircleList* list) // O(1)
{
	free(list);
}



void CircleList_Clear(CircleList* list){
	TCircleList* sList = (TCircleList*)list;
	if (sList != NULL)
	{
		sList->length = 0;
		sList->header.next = NULL;
		sList->slider = NULL;
	}
}

int CircleList_Length(CircleList* list){
	TCircleList* sList = (TCircleList*)list;
	int ret = -1;
	if (sList != NULL){
		ret = sList->length;
	}
	return ret;
}

int CircleList_Insert(CircleList* list, CircleListNode* node, int pos) // O(n)
{
	TCircleList* sList = (TCircleList*)list;
	int ret = (sList != NULL) && (pos >= 0) && (node != NULL);
	int i = 0;
	if (ret){
		CircleListNode* current = (CircleListNode*)sList;//current指向头部
		for (i = 0; (i < pos) && (current->next != NULL); i++)
		{
			current = current->next;
		}
		//假设我们要插入的是pos =3,头结点不算,下来从0,1,2,3,4,5,6开始计算
		//循环完成后,current刚好是在 pos=2的位置,
		//要变成的是 2  node   3 ,也就是说node->next要是3
		node->next = current->next;
		//current的->next,现在也是2,指向新的节点node
		current->next = node;

		if (sList->length == 0){
		//如果是第一次插入将slider的指向node
			sList->slider = node;

		}
		sList->length++;

		//如果是头插法,还需要做事情,让最后一个元素链接到这个新节点,
		if (current == (CircleListNode*)sList) {
			CircleListNode * last = CircleList_Get(list,sList->length-1);
			last->next = node;
		}
		//此处要理解,需结合图来看,后续会将 头插法,尾插法,中间插入法的三种图示画一下,方便理解
	}
	return ret;
}



CircleListNode* CircleList_Get(CircleList* list, int pos) // O(n)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;
	int i = 0;
	if ((sList != NULL) && (pos >= 0)){
		CircleListNode* current = (CircleListNode*)sList;
		for (i = 0; i < pos; i++)
		{
			current = current->next;
		}
		ret = current->next;
	}
	return ret;
}



CircleListNode* CircleList_Delete(CircleList* list, int pos) // O(n)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;
	int i = 0;
	if ((sList != NULL) && (pos >= 0)){
		CircleListNode* current = (CircleListNode*)sList;
		CircleListNode* first = sList->header.next;
		CircleListNode* last = (CircleListNode*)CircleList_Get(sList, sList->length - 1);
		for (i = 0; i < pos; i++){
			current = current->next;
		}
		ret = current->next;
		current->next = ret->next;
		sList->length--;
		//如果删除的第一个结点。要额外处理
		if (first == ret){
			//让头结点的next要重新指向,指向的内容是保存在 被删除的节点的next中的。
			sList->header.next = ret->next;
			//让最后一个节点的next也要重新指向,指向的内容是保存在 被删除的节点的next中的。
			last->next = ret->next;
		}

		//如果删除的元素刚好是 游标指向的元素,则将游标往下移动
		if (sList->slider == ret){
			sList->slider = ret->next;
		}

		//如果list只有一个元素,删除后,就没有元素了,那么就需要将
		if (sList->length == 0){
			sList->header.next = NULL;
			sList->slider = NULL;
		}
	}
	return ret;
}

CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node) // O(n)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;
	int i = 0;
	if (sList != NULL){
		CircleListNode* current = (CircleListNode*)sList;
		for (i = 0; i < sList->length; i++){
			if (current->next == node){
				ret = current->next;
				break;
			}
			current = current->next;
		}
		if (ret != NULL){
			CircleList_Delete(sList, i);
		}
	}
	return ret;
}

CircleListNode* CircleList_Reset(CircleList* list) // O(1)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;
	if (sList != NULL){
		sList->slider = sList->header.next;
		ret = sList->slider;
	}
	return ret;
}

CircleListNode* CircleList_Current(CircleList* list) // O(1)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;

	if (sList != NULL){
		ret = sList->slider;
	}
	return ret;
}

CircleListNode* CircleList_Next(CircleList* list) // O(1)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;
	if ((sList != NULL) && (sList->slider != NULL)){
		ret = sList->slider;
		sList->slider = ret->next;
	}
	return ret;
}

测试代码

#include "iostream"
#include <stdio.h>
#include <stdlib.h>

extern "C" {
#include "003CircleList.h"
}

using namespace std;


struct Value
{
	CircleListNode header;
	int v;
};

int main(int argc, char *argv[]){
	int i = 0;
	CircleList* list = CircleList_Create();
	struct Value v1;
	struct Value v2;
	struct Value v3;
	struct Value v4;
	struct Value v5;
	struct Value v6;
	struct Value v7;
	struct Value v8;
	v1.v = 1;
	v2.v = 2;
	v3.v = 3;
	v4.v = 4;
	v5.v = 5;
	v6.v = 6;
	v7.v = 7;
	v8.v = 8;

	CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list));
	for (i = 0; i <  CircleList_Length(list); i++)
	{
		struct Value* pv = (struct Value*)CircleList_Get(list, i);
		printf("%d\n", pv->v);
	}

	//注意这里,这时候list除了头结点外,只有4个元素,1,2,3,4,对应0,1,2,3
	//代码中插入的pos =5,相当于在1和2中间插入一个5.
	CircleList_Insert(list, (CircleListNode*)&v5, 5);

	//因此如下的循环后,打印出来的是 1,5,2,3,4
	for (i = 0; i < CircleList_Length(list); i++)
	{
		struct Value* pv = (struct Value*)CircleList_Get(list, i);
		printf("%d\n", pv->v);
	}

	CircleList_Delete(list, 0);//删除第一个元素,将1删除了

	//再次打印是 5 2 3 4  5 2 3 4
	for (i = 0; i < 2 * CircleList_Length(list); i++)
	{
		struct Value* pv = (struct Value*)CircleList_Get(list, i);
		printf("%d\n", pv->v);
	}

	printf("\n");
	while (CircleList_Length(list) > 0){
		struct Value* pv = (struct Value*)CircleList_Delete(list, 0);
		printf("%d\n", pv->v);
	}

	printf("aaaaaa\n");



	CircleList_Insert(list, (CircleListNode*)&v1, 0);
	CircleList_Insert(list, (CircleListNode*)&v2, 0);
	CircleList_Insert(list, (CircleListNode*)&v3, 0);
	CircleList_Insert(list, (CircleListNode*)&v4, 0);
	CircleList_Insert(list, (CircleListNode*)&v5, 0);
	CircleList_Insert(list, (CircleListNode*)&v6, 0);
	CircleList_Insert(list, (CircleListNode*)&v7, 0);
	CircleList_Insert(list, (CircleListNode*)&v8, 0);

	//注意,这里是用的头插法,因此是8,7,6,5,4,3,2,1,但是第一个插入的是1,因此游标指向1,又因为是循环的,因此下一个是8,结果是1,8,7,6,5,4,3,2
	for (i = 0; i < CircleList_Length(list); i++){
		struct Value* pv = (struct Value*)CircleList_Next(list);
		printf("%d\n", pv->v);
	}

	printf("bbbbbbbbbb\n");

	//游标reset 是指向的第一个元素
	CircleList_Reset(list);


	//1,2,3 将3剔除队列的游戏,游标reset后,指向的是8,因此123,将3剔除队列的有些结果为 6,3,8,4,7,1,5,2
	while (CircleList_Length(list) > 0){
		struct Value* pv = NULL;
		for (i = 1; i < 3; i++){
			CircleList_Next(list);
		}
		printf("ccc\n");//游标reset之后,指向数字8,往后移动了2次,就是指向6
		pv = (struct Value*)CircleList_Current(list);
		printf("%d\n", pv->v);
		CircleList_DeleteNode(list, (CircleListNode*)pv);
	}
	CircleList_Destroy(list);
	return 0;

}

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

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

相关文章

遥测终端赋能水库泄洪监测预警,筑牢度汛安全防线!

4月10日&#xff0c;水利部召开水库安全度汛视频会议。会议要求着力强化水库防洪“四预”措施&#xff0c;加快构建雨水情监测预报“三道防线”&#xff0c;完善预警信息发布机制&#xff0c;推进数字孪生水利工程建设&#xff0c;为科学调度指挥决策提供支持。强调坚决牢牢守住…

基于3D点云的散货库存体积计算

首先&#xff0c;你需要散货库存的点云。 我将使用 IntelRealSense 捕获的散货库存的 .ply文件。 然而&#xff0c;任何其他产生点云的成像技术都同样有效。 点击这里查看本教程的 Github 上的代码。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - …

二叉树的中序遍历 - LeetCode 热题 36

大家好&#xff01;我是曾续缘&#x1f603; 今天是《LeetCode 热题 100》系列 发车第 36 天 二叉树第 1 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输…

爬楼梯(c)

文章目录 描述分析思路关键代码运行结果 描述 给定一个整数数组 cost &#xff0c;其中 cost[i]是从楼梯第i 个台阶向上爬需要支付的费用&#xff0c;下标从0开始。-旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶 要求&#xff1a;请你计算并返回达到楼梯顶部的…

4.17

while(1) { HAL_ADC_Start(&hadc); adcVal HAL_ADC_GetValue(&hadc); TIM3->CCR3 adcVal-2000; } 1.总结串口的发送和接收功能使用到的函数 HAL_UART_Transmit_DMA(&huart1,"hello world",strlen("hello world")); HAL_UART_Tr…

Linux:如何删除指定时间之前修改的文件?

1、与文件有关的时间 在说明如何删除符合这种要求的文件之前&#xff0c;先来看看与文件有关的有哪些时间 简名全名中文名含义atimeaccess time访问时间文件中的数据最后被访问的时间mtimemodify time修改时间文件中的数据最后被修改的时间ctime change time变化时间文件的元…

JavaSE高阶篇-IO流

第一部分 file类 1&#xff09;File类 计算机常识: 1.名字为".jpg"的一定是图片吗? 不一定,有可能是文件夹 2.什么叫做文本文档: 用记事本打开,人能看懂的文件 比如:.txt .html .css等 .doc -> 不是 …

如何安装 IntelliJ IDEA 最新版本——详细教程

IntelliJ IDEA 简称 IDEA&#xff0c;被业界公认为最好的 Java 集成开发工具&#xff0c;尤其在智能代码助手、代码自动提示、代码重构、代码版本管理(Git、SVN、Maven)、单元测试、代码分析等方面有着亮眼的发挥。IDEA 产于捷克&#xff0c;开发人员以严谨著称的东欧程序员为主…

vscode 搭建stm32开发环境记录(eide+cortex-debug+jlink)

前言 clion使用的快过期了&#xff0c;所以就准备使用vscode 来代替clion作为代码开发环境 vscode 插件安装 创建个空白工程 添加项目相关的源文件&#xff0c;和配置宏定义和头文件目录 编译和烧录(ok) 结合cortex-debug 结果(测试ok)

数据可视化-ECharts Html项目实战(13)

在之前的文章中&#xff0c;我们深入学习ECharts动态主题切换和自定义ECharts主题。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 数据可视化-ECharts Html项…

Linux执行命令监控详细实现原理和使用教程,以及相关工具的使用

Linux执行命令监控详细实现原理和使用教程&#xff0c;以及相关工具的使用。 0x00 背景介绍 Linux上的HIDS需要实时对执行的命令进行监控&#xff0c;分析异常或入侵行为&#xff0c;有助于安全事件的发现和预防。为了获取执行命令&#xff0c;大致有如下方法&#xff1a; 遍…

MySQL-笔记-06.数据高级查询

目录 6.1 连接查询 6.1.1 交叉连接&#xff08;cross join&#xff09; 6.1.2 内连接&#xff08;inner join&#xff09; 6.1.3 外连接&#xff08;outer join&#xff09; 6.1.3.1 左外连接&#xff08;left [outer] join&#xff09; 6.1.3.2 右外连接&#xff08;rig…

第2章:车辆纵向控制

2.1 车辆纵向动力学模型 注&#xff1a;车辆的纵向控制是指控制车辆行驶方向上的加减速&#xff0c;使得汽车可以按照期望的速度行驶&#xff0c;并保持安全的前后车距&#xff08;即对汽车油门 / 刹车的控制&#xff09;&#xff1b; 2.1.1 车辆纵向受力模型 &#xff1a;轮胎…

SpringBootSpringCloud升级可能会出现的问题

1.背景 之前负责过我们中台的SpringBoot和Cloud的升级&#xff0c;特次记录分享一下项目中可能出现的问题&#xff0c;方便后续的人快速定位问题。以及下述选择的解决方案都是基于让升级的服务影响和改动最小以及提供通用的解决方案的提前进行选择的。 1.1版本说明 升级前&a…

OpenCV基本图像处理操作(十)——图像特征harris角点

角点 角点是图像中的一个特征点&#xff0c;指的是两条边缘交叉的点&#xff0c;这样的点在图像中通常表示一个显著的几角。在计算机视觉和图像处理中&#xff0c;角点是重要的特征&#xff0c;因为它们通常是图像中信息丰富的区域&#xff0c;可以用于图像分析、对象识别、3D…

JavaSE中的String类

1.定义方式 常见的三种字符串构造 public class Test1 {public static void main(String[] args) {// 使用常量串构造String str1 "abc";System.out.println(str1);// 直接newString对象String str2 new String("ABC");System.out.println(str2);// 使用…

【Linux学习】Linux指令(四)

文章标题 &#x1f680;zip/unzip指令&#xff1a;&#x1f680;tar指令&#xff08;重要&#xff09;&#xff1a;&#x1f680;uname –r指令&#xff1a;&#x1f680;关机指令&#x1f680;几个常用操作 &#x1f680;zip/unzip指令&#xff1a; zip 与 unzip的安装 yum i…

Day20-【Java SE高级】单元测试 反射 注解 动态代理

一、单元测试 就是针对最小的功能单元(方法)&#xff0c;编写测试代码对其进行正确性测试。 1. 咱们之前是如何进行单元测试的?有啥问题? 只能在main方法编写测试代码&#xff0c;去调用其他方法进行测试。无法实现自动化测试&#xff0c;一个方法测试失败&#xff0c;可能…

学习在Debian系统上安装Shadowsocks教程

学习在Debian系统上安装Shadowsocks教程 安装shadowsocks-libev及其所需的依赖启动Shadowsocks服务&#xff1a;如果你想要通过代理本地流量&#xff0c;你可以使用ss-local&#xff1a;启动并设置ss-local&#xff1a;查看状态本地连接 安装shadowsocks-libev及其所需的依赖 …

量化交易为什么独宠Python

“我在学一门叫Python的语言”。“什么是Python&#xff0c;没听说过啊&#xff0c;为什么不学C啊”。这是发生在2014年&#xff0c;上海的一家量化基金&#xff0c;量化研究员和老板之间的对话。 “我想问一下关于Python的课程&#xff0c;什么时候能开班”。“Python啊&#…