数据结构-循环链表和双向链表

目录

  • 前言
  • 一、循环链表
    • 1.1 循环链表的介绍
    • 1.2 循环链表的实现
  • 二、双向链表
  • 总结

前言

本篇文章介绍数据结构中的循环链表和双向链表

一、循环链表

1.1 循环链表的介绍

将单链表的形式稍作改变,单链表的最后一个结点指向第一个结点
对第一个结点概念的说明:
对于不带头结点的链表,第一个结点指首元结点(如图1.1所示)
在这里插入图片描述

图1.1 不带头结点

对于带头结点的链表,第一个结点指头结点(如图1.2所示)
在这里插入图片描述

图1.2 带头结点

本篇文章以带头结点的循环链表为研究对象
与单链表相比,循环链表没有增加新的存储空间,但是,从循环链表中任一结点出发,都能访问到所有结点。
带头结点的循环链表又有两种表示方式,分别是
头指针表示循环链表:头指针指向头结点(如图1.3所示)
在这里插入图片描述

图1.3 头指针表示

头指针表示循环单链表 { 找 a 1 的时间复杂度 O ( 1 ) 找 a n 的时间复杂度 O ( n ) 头指针表示循环单链表 \begin{cases} 找a_1的时间复杂度O(1) \\ 找a_n的时间复杂度O(n) \end{cases} 头指针表示循环单链表{a1的时间复杂度O(1)an的时间复杂度O(n)

尾指针表示循环链表:尾指针指向最后一个结点(如图1.4所示)
在这里插入图片描述

图1.4 尾指针表示

头指针表示循环单链表 { a 1 的存储位置: R → n e x t → n e x t a n 的存储位置: R 找 a 1 和 a n 的时间复杂度都为 O ( 1 ) 头指针表示循环单链表 \begin{cases} a_1的存储位置:R \rightarrow next \rightarrow next \\ a_n的存储位置:R \\ 找a_1和a_n的时间复杂度都为O(1) \end{cases} 头指针表示循环单链表 a1的存储位置:Rnextnextan的存储位置:Ra1an的时间复杂度都为O(1)

1.2 循环链表的实现

这里使用带头结点的循环链表,并且使用尾指针表示循环链表。
循环链表的定义和表示

//定义返回值常量
#define SUCCESS 1
#define ERROR 0


//假设数据元素类型为char
typedef char ElemType;

//定义结点类型
struct Node;				  	
typedef struct Node* PNode;   //假设作为结点指针类型
struct Node {
	ElemType data;   //数据域
	PNode next;		//指针域
};

typedef struct Node* LinkList;  //假设作为单链表类型

  1. 创建空表
    step1 使用malloc()函数创建一个sizeof(struct Node)大小的空间作为头结点
    step2 将头结点的指针域指向自身,表示一个空表

    //4.1 创建一个空表,返回头指针
    LinkList createNullList_loopLink(void)
    {
    	LinkList linkList = (LinkList)malloc(sizeof(struct Node));
    	if (NULL == linkList)
    	{
    		printf("malloc fail!\n");
    		return NULL;
    	}
    	linkList->next = linkList;  //头结点的指针域指向自身表示空表
    	return linkList;
    }
    
  2. 销毁循环链表
    step1 首先从头结点开始销毁
    step2 最后销毁尾指针指向的结点

    //4.2 销毁一个循环单链表
    void destroyList_loopLink(LinkList* R)
    {
    	assert(R && *R);
    	PNode phead = (*R)->next;
    	while (phead != *R)        //销毁除了尾结点的其余结点
    	{
    		PNode q = phead->next;
    		free(phead);
    		phead = q;
    	}
    	//销毁尾结点
    	free(*R);
    	*R = NULL;
    }
    
  3. 循环链表的插入
    step1 查找 a i − 1 a_{i-1} ai1的存储位置p
    step2 使用flag标记 a i − 1 a_{i-1} ai1的存储位置是否恰好为尾指针*R指向的位置
    step3 生成一个数据域为e的结点newNode
    step4 插入新结点,newNode指针域指向 a i a_i ai a i − 1 a_{i-1} ai1指针域指向newNode
    step5 根据flag的值改变R的指向,flag=1,*R = newNode

    //4.5 在第i个元素之前插入数据元素e
    int insertElem_loopLink(LinkList* R, int i, ElemType e)
    {
    	assert(R && *R);
    	PNode p = (*R)->next;          //头指针
    	int j = 0;
    	int flag = 0;				//用于标记位置i-1是否恰好等于尾指针指向的结点
    	while (p != *R && j < i - 1) //寻找第i-1位置的结点
    	{
    		p = p->next;
    		j++;
    	}
    	if (j > i - 1)           //判断i是否小于1
    		return ERROR;
    	if (p == *R)			//p==R ,条件成立有两种情况,第一,i-1是否恰好等于尾指针指向;第二,i-1大于表长
    	{ 
    		if (j != i - 1)    //如果i-1大于表长,返回ERROR
    			return ERROR;
    		else               //i-1位置恰好在尾指针指向的结点
    			flag = 1;
    	}
    	//新建结点
    	PNode newNode = (PNode)malloc(sizeof(struct Node));
    	if (NULL == newNode)
    	{
    		printf("malloc fail!\n");
    		return ERROR;
    	}
    	newNode->data = e;
    	newNode->next = p->next;
    	p->next = newNode;
    	if (flag)
    		*R = newNode;
    	return SUCCESS;
    }
    
  4. 循环链表的删除
    step1 查找 a i − 1 a_{i-1} ai1的存储位置 p p p
    step2 保存要删除的结点存储位置 q q q,即 q = p → n e x t q = p \rightarrow next q=pnext
    step3 使结点 a i − 1 a_{i-1} ai1的指针域指向 a i + 1 a_{i+1} ai+1,即 p → n e x t = q → n e x t p \rightarrow next = q \rightarrow next pnext=qnext
    step4 判断 q q q的指向是否与 ∗ R *R R的指向相同,若相同,则 ∗ R = p *R = p R=p
    step5 free(q),返回SUCCESS

    //4.6 删除第i个元素
    int deleteElem_loopLink(LinkList* R, int i)
    {
    	assert(R && *R);
    	PNode p = (*R)->next;
    	int j = 0;
    	while (p != *R && j < i - 1)
    	{
    		p = p->next;
    		j++;
    	}
    	if (p == *R || j > i - 1)
    		return ERROR;
    	PNode q = p->next;
    	p->next = q->next;
    	if (q == *R)  //判断删除结点是否为尾指针指向的结点
    		*R = p;
    	free(q);
    	return SUCCESS;
    }
    
  5. 循环链表的建立(头插法)
    step1 新建头结点phead并将phead的指针域指向自身
    step2 定义尾指针R,并指向phead
    step3 循环新建结点,插入头结点之后
    step4 判断插入的结点是否为第一个,若是,则R = newNode
    step5 最后返回R

    //4.7 循环单链表的建立(头插法)
    //带头结点
    //返回尾指针
    LinkList createLoopLinkList_head(ElemType* element, int length)
    {
    	assert(element);
    	//创建头结点
    	LinkList phead = (LinkList)malloc(sizeof(struct Node));
    	if (NULL == phead)
    	{
    		printf("malloc fail!\n");
    		return NULL;
    	}
    	phead->next = phead;
    	PNode R = phead;  //声明尾指针
    	int i = 0;
    	for (; i < length; i++)
    	{
    		//新建结点
    		PNode newNode = (PNode)malloc(sizeof(struct Node));
    		if (NULL == newNode)
    		{
    			printf("malloc fail!\n");
    			free(phead);
    			return NULL;
    		}
    		newNode->data = element[i];
    		newNode->next = phead->next;
    		phead->next = newNode;
    		if (newNode->next == phead)              //判断是否为第一个插入结点
    		{
    			R = newNode;
    		}
    	}
    	return R;
    }
    
  6. 合并两个循环链表
    step1 保存Ta的头指针
    step2 Ta的尾指针指向Tb的首元结点
    step3 释放Tb的头结点
    step4 Tb的尾指针指向Ta的头结点

    //4.8 合并两个循环单链表(Tb合并在Ta之后)
    //返回合并后的尾指针
    LinkList mergeLoopLinkList(LinkList Ta, LinkList Tb)
    {
    	if (NULL == Ta)
    		return Tb;
    	if (NULL == Tb)
    		return Ta;
    	PNode head_Ta = Ta->next;		//保存Ta的头指针
    	Ta->next = Tb->next->next;		//Ta尾指针指向Tb的首元结点
    	free(Tb->next);					//释放Ta的头结点
    	Tb->next = head_Ta;				//Tb的指针域指向Ta的头结点
    	return Tb;
    }
    

二、双向链表

…待续

总结

…待续

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

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

相关文章

Echarts地图实现:山东省报考人数

Echarts地图实现&#xff1a;山东省报考人数 效果预览 设计思路 数据可视化&#xff1a;选择地图作为数据展示的方式&#xff0c;可以直观地展示山东省不同城市的报考人数分布。交互性&#xff1a;通过ECharts的交互功能&#xff0c;如提示框&#xff08;tooltip&#xff09;…

致远互联FE协作办公平台 codeMoreWidget SQL注入致RCE漏洞复现

0x01 产品简介 致远互联FE协作办公平台是一款为企业提供全方位协同办公解决方案的产品。它集成了多个功能模块&#xff0c;旨在帮助企业实现高效的团队协作、信息共享和文档管理。 0x02 漏洞概述 致远互联FE协作办公平台 codeMoreWidget.jsp接口处存在SQL注入漏洞,未经授权攻…

有哪些防爬虫的方法

防爬虫的方法有robots.txt文、user-agent过滤、ip限制、验证码、动态页面生成、频率限制、动态url参数和反爬虫技术等。详细介绍&#xff1a;1、robots.txt文件&#xff0c;用于告诉搜索引擎爬虫哪些页面可以访问&#xff0c;哪些页面禁止访问&#xff1b;2、ip限制&#xff0c…

机器学习入门指南:理解基本概念与常见算法

目录 什么是机器学习&#xff1f; 机器学习的基本概念 1. 训练数据 2. 特征工程 3. 模型评估 监督学习与非监督学习的区别 监督学习 非监督学习 常见的机器学习算法 1. 线性回归与逻辑回归 2. 决策树与随机森林 3. 支持向量机&#xff08;SVM&#xff09; 4. K近邻…

2小时动手学习扩散模型(pytorch版)【入门版】【代码讲解】

2小时动手学习扩散模型&#xff08;pytorch版&#xff09; 课程地址 2小时动手学习扩散模型&#xff08;pytorch版&#xff09; 课程目标 给零基础同学快速了解扩散模型的核心模块&#xff0c;有个整体框架的理解。知道扩散模型的改进和设计的核心模块。 课程特色&#xf…

学生宿舍管理系统

摘 要 随着高校规模的不断扩大和学生人数的增加&#xff0c;学生宿舍管理成为高校日常管理工作中的重要组成部分。传统的学生宿舍管理方式往往依赖于纸质记录和人工管理&#xff0c;这种方式不仅效率低下&#xff0c;而且容易出错&#xff0c;无法满足现代高校管理的需求。因此…

不同node版本的切换及其指定版本vue-cli脚手架下载

目录 一.清空本地已安装node.js版本 二.装nvm管理工具 三.安装指定node版本 四.使用nvm命令切换或删除指定node版本 五.在指定node版本下下载指定vue-cli脚手架 一.清空本地已安装node.js版本 1.按健winR弹出窗口&#xff0c;键盘输入cmd&#xff0c;然后敲回车。 2.输入…

这是我见过的大模型 RAG 优化方案与实践最全总结了

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。提前准备才是完全之策。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c…

QT基本对话框(基本对话框、工具盒类、进度条、调色板与电子钟、可扩展对话框、程序启动画面)

此篇文章通过实例介绍基本对话框的用法。首先介绍标准文件对话框&#xff08;QFileDialog&#xff09;、标准颜色对话框&#xff08;QColorDialog&#xff09;、标准字体对话框&#xff08;QFontDialog&#xff09;、标准输入对话框&#xff08;QInputDialog&#xff09;以及标…

VMware ESXi 技术

目录 一、VMware ESXi安装 1. 在VMware WorkStation中创建一台虚拟机 2. 进入VMware ESXi控制台 3. 配置VMware ESXi网络 二、使用Web网页端登录管理ESXi 1. 分配许可证密钥&#xff08;选做&#xff09; 2. 管理ESXi 三、VMware ESXi控制台 1. 创建虚拟机 2. 定制虚拟…

laravel Dcat Admin 入门应用(七)列copyable和自定义copy

laravel Dcat Admin 入门应用&#xff08;七&#xff09;列copyable和自定义copy Dcat Admin 是一个基于 Laravel-admin 二次开发而成的后台构建工具&#xff0c;只需很少的代码即可构建出一个功能完善的高颜值后台系统。支持页面一键生成 CURD 代码&#xff0c;内置丰富的后台…

深入了解Qt 控件:Display Widgets部件(1) 以及 QT自定义控件(电池)

QT自定义控件(电池&#xff09; Chapter1 QT自定义控件(电池&#xff09;Chapter2 Qt教程 — 3.5 深入了解Qt 控件&#xff1a;Display Widgets部件(1)1 Display Widgets简介2 如何使用Display Widgets部件 Chapter3 Qt自定义控件电池组件使用前言一、最基本的使用方法二、Batt…

Navicat安装与连接教程

navicat 的安装 官网&#xff1a;https://www.navicat.com.cn/ 进入官网之后点击左上角的产品&#xff0c;然后往下滑动就可以看见许多类型&#xff0c;我们使用的是MongoDB数据库&#xff0c;所以就下载Navicat 17 for MongoDB 进入到这里之后&#xff0c;选择自己的系统版本…

基于Vue.js的电商前端模板:Vue-Dashboard-Template的设计与实现

摘要 随着电子商务的飞速发展&#xff0c;前端页面的设计和实现变得愈发重要。本文介绍了一个基于Vue.js的电商前端模板——Vue-Dashboard-Template&#xff0c;旨在提供一个高性能、易扩展的电商平台前端解决方案。该模板遵循响应式设计、模块化、组件化开发等设计原则&#…

使用python创建虚拟环境及exe打包

使用python创建虚拟环境及exe打包 使用python创建虚拟环境在虚拟环境使用PyQt进行exe封装 使用python创建虚拟环境 优点&#xff1a; &#xff08;1&#xff09;可以实现环境的即插即用&#xff08;2&#xff09;可以在使用pyqt打包时实现最小体积使用库——venv 进入你要创…

业务代码插件式开发实践

在学习编程初期&#xff0c;会接触到设计模式的概念&#xff1a;23种设计模式&#xff0c;单例模式&#xff0c;策略模式&#xff0c;… 。接触业务研发后&#xff0c;对设计模式的使用和实践有了更深的见解。 使用设计模式是目的为了更高效的支撑业务诉求&#xff0c;如何在保…

selenium4如何指定chrome和firefox的驱动(driver)路径

pythonpytestselenium框架的自动化测试脚本。 原本用的chrome&#xff0c;很久没用了&#xff0c;今天执行&#xff0c;发现chrome偷偷升级&#xff0c;我的chromedriver版本不对了。。。鉴于访问chrome相关网站太艰难&#xff0c;决定弃用chrome&#xff0c;改用firefox。因为…

基于SSM+Jsp的疫情居家办公OA系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

vue使用glide.js实现轮播图(可直接复制使用)

效果图 可以实现自动轮播&#xff0c;3种切换方式&#xff1a;直接滑动图片、点击两侧按钮、点击底部按钮 体验链接:http://website.livequeen.top 实现 一、引入依赖 1、控制台引入依赖 npm install glidejs/glide 2、在css中引用 <style scoped> import glidejs/g…

华为达芬奇与英伟达CUDA架构对比分析

华为达芬奇与英伟达CUDA&#xff0c;必有一战&#xff01; 大数据产业创新服务媒体 ——聚焦数据 改变商业 当初英特尔和微软&#xff0c;搞出来个Wintel&#xff0c;制霸电脑时代很多年。从某种意义上&#xff0c;英伟达的CUDA&#xff0c;就相当于CPU时代的windows&#x…