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

news2024/11/27 16:41:10

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

一 ,循环链表

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;

}

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

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

相关文章

腾讯AI Lab:“自我对抗”提升大模型的推理能力

本文介绍了一种名为“对抗性禁忌”&#xff08;Adversarial Taboo&#xff09;的双人对抗语言游戏&#xff0c;用于通过自我对弈提升大型语言模型的推理能力。 &#x1f449; 具体的流程 1️⃣ 游戏设计&#xff1a;在这个游戏中&#xff0c;有两个角色&#xff1a;攻击者和防守…

【七 (2)FineBI FCP模拟试卷-平台新增用户留存分析】

目录 文章导航一、字段解释1、用户平台登录信息表格2、用户平台激活信息表格 二、需求三、操作步骤1、建立用户平台登录信息表格和用户平台激活信息表格的关联关系2、将用户平台激活信息表格的激活日期添加到用户平台登录信息表格3、新增公式列&#xff0c;计算激活时间和登录时…

SpringCloud(一)

微服务框架 一、分布式架构 分布式架构︰根据业务功能对系统进行拆分&#xff0c;每个业务模块作为独立项目开发&#xff0c;称为一个服务。 优点: 降低服务耦合有利于服务升级拓展 微服务是一种经过良好架构设计的分布式架构方案&#xff0c;微服务架构特征: 单一职责:微…

源码解读——SplitFed: When Federated Learning Meets Split Learning

源码地址 1. 源码概述 源码里一共包含了5个py文件 单机模型&#xff08;Normal_ResNet_HAM10000.py&#xff09;联邦模型&#xff08;FL_ResNet_HAM10000.py&#xff09;本地模拟的SFLV1&#xff08;SFLV1_ResNet_HAM10000.py&#xff09;网络socket下的SFLV2&#xff08;SF…

MySQL的内外连接

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;MySQL &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容主要介绍了MySQL中的内外连接 文章目录 MySQL的内外连接…

如何用ChatGPT进行论文撰写?

原文链接&#xff1a;如何用ChatGPT进行论文撰写&#xff1f;https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601619&idx1&snb686fbe87dedfac2df3a6afe780b2ffe&chksmfa820c34cdf5852251dca64597024ea62ddbde280086535ec251f4b62b848d9f9234688384e6…

C语言——字符函数和字符串函数

1.assert断言&#xff08;判断程序运行时是否符合条件&#xff09; <assert.h>头文件定义了宏assert&#xff08;&#xff09;&#xff0c;⽤于在运⾏时确保程序符合指定条件&#xff0c;如果不符合&#xff0c;就报 错终⽌运⾏。这个宏常常被称为“断⾔”。 assert() …

带缓存的输入输出流(I/O)

文章目录 前言一、带缓冲的输入输出流是什么&#xff1f;二、使用方法 1.BufferedInputStream与BufferedOutputStream类2.BufferedReader与BufferedWriter类总结 前言 输入输出流可以视为&#xff0c;从A点把货物搬运至B点。那么带缓冲的意思可以视为用货车把A点的货物搬运至B点…

Chatgpt掘金之旅—有爱AI商业实战篇|品牌故事业务|(十六)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、AI技术创业在品牌故事业务有哪些机会&#xff1f; 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。随…

象棋教学辅助软件介绍

背景 各大象棋软件厂商都有丰富的题目提供训练&#xff0c;但是其AI辅助要么太弱&#xff0c;要么要付费解锁&#xff0c;非常不适合我们这些没有赞助的业余棋手自行训练&#xff0c;于是我需要对其进行视觉识别&#xff0c;和AI训练&#xff0c;通过开启这个辅助软件&#xf…

学习了解大模型的四大缺陷

由中国人工智能学会主办的第十三届吴文俊人工智能科学技术奖颁奖典礼暨2023中国人工智能产业年会于2024年4月14日闭幕。 会上&#xff0c;中国工程院院士、同济大学校长郑庆华认为&#xff0c;大模型已经成为当前人工智能的巅峰&#xff0c;大模型之所以强&#xff0c;是依托了…

【iOS开发】(二)react Native基础语法+样式+布局20240417

【IOS开发】 前言&#xff1a;&#xff08;一&#xff09;我们已经搭建好了基础环境&#xff0c;和iOS环境&#xff0c;并创建和在模拟器上成功运行了一个app&#xff0c;mywdm。 目录标题 一&#xff0c; 如何进行模拟器调试二&#xff0c;基础语法&#xff1a;1 掌握reactjs…

spring boot: 使用MyBatis从hive中读取数据

一、hive表&#xff1a; 启动hiveserver2 二、添加mybatis starter和hive依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instan…

微信小程序展示倒计时

html <view class"countdown"> <text>倒计时&#xff1a;</text> <text wx:for"{{countdown}}" wx:key"index">{{item}}</text> </view> ts data: {countdown: [], // 存放倒计时数组 targetTime:…

Table表格(关于个人介绍与图片)

展开行&#xff1a; <el-table :data"gainData" :border"gainParentBorder" style"width: 100%"><el-table-column type"expand"><template #default"props"><div m"4"><h3>工作经…

NVM下载、NVM配置、NVM常用命令

NVM(nodejs版本管理切换工具)下载、配置、常用命令 0、NVM常用命令 nvm off // 禁用node.js版本管理(不卸载任何东西) nvm on // 启用node.js版本管理 nvm install <version> // 安装node.js的命名 version是版本号 例…

谈谈我的实习生活

距离实习已经过去快一年了&#xff0c;说真的&#xff0c;很多关于实习的事情我都已经忘记了。今天正好我有空&#xff0c;就想着写一些东西&#xff0c;思来想去&#xff0c;就想着要不把实习的生活给记录下来&#xff0c;就当给自己留一个回忆&#xff0c;毕竟这也是我人生中…

春秋云境:CVE-2022-32991[漏洞复现]

从CVE官网查询该漏洞相关信息 该漏洞是由于welcome.php中的eid参数包含了SQL注入漏洞 则我们的目标就在于寻找welcome.php地址以及相关的可注入eid参数 开启靶机 先在页面正常注册、登录一个账号。密码随便填 进入了home目录&#xff0c;这里有三个话题可以选择开启 随便选…

AI大模型探索之路-应用篇15:GLM大模型-ChatGLM3-6B私有化本地部署

目录 前言 一、ChatGLM3-6B 简介说明 二、ChatGLM3-6B 资源评估 三、购买云服务器 四、git拉取GLM 五、pip安装依赖 六、运行测试 七、本地部署安装 总结 前言 ChatGLM3-6B 是 OpenAI 推出的一款强大的自然语言处理模型&#xff0c;它在前两代模型的基础上进行了优化和…

【数据工具】ArcGIS批量出图工具箱

工具下载链接&#xff1a;数据下载链接 我们在使用Arcgis制图的过程中&#xff0c;经常会遇到需要大量出图的情况&#xff0c;如何将做好的图批量导出jpg是一件令人头疼的问题。 今天小编就给大家分享俩个ArcGIS批量出图的工具箱&#xff0c;一个可以批量导出图层为jpg&#…