数据结构与算法(七)静态链表

目录

前言

一、静态链表的引入

二、线性表的静态链表存储结构

三、静态链表的插入操作

四、静态链表的删除操作

五、静态链表的优缺点总结

1、优点

2、缺点

3、小结

六、单链表小结——Tecent面试题

1、普通解法:

2、高级解法:


前言

        静态链表是一种比较古老的用法了,一般用在没有指针操作的传统语言上,对于我们日常工作的使用,是大概率用不上这个操作方式的。考虑到考试的需要以及对静态链表思想的把握,笔者还是将这一部分的内容纳入数据结构与算法专栏的学习内容当中,如果没有考试需求的读者,可以根据自身情况忽略该部分介绍。另外后面加入了腾讯的一道面试题,供大家一起学习!

        专栏地址:数据结构与算法(c语言实例)_Felix Du的博客-CSDN博客

        欢迎各位批判指正!


一、静态链表的引入

        我们都知道,C语言是一个伟大的语言,他的魅力在于指针的灵活性,使得它可以非常容易地操作内存中的地址和数据,这比其他高级语言更加灵活方便。(面向对象使用对象引用机制间接地实现了指针的某些功能),但是早期的编程语言并没有C语言、JAVA,只有原始的Basic,Fortran等早期的编程语言,这些语言没有类似于C的指针功能,但是他们又想描述单链表,这要怎么来实现呢?

        我们就不卖关子了,有人想出了用数组代替指针来描述单链表。这种用数组描述的链表我们叫做静态链表,这种描述方法叫做游标实现法。为什么叫它静态数组呢?原因很简单,因为我们声明的时候需要确定他的空间有多大(和数组一样),所以就说这是一个静态链表。

静态链表实例

        我们需要注意两个很特殊的元素,第一个是下标为0的数据,一个是maxsize-1的数据,他们两个都是不存放东西的。

二、线性表的静态链表存储结构

        上文我们了解到了静态链表的一些基本情况,那么下面我们将来研究线性表的链式存储结构。

#define MAXSIZE 1000
typedef struct
{ElemType data;//数据int cur;//游标(Cursor)
}
Component,StaticLinkList[MAXSIZE];

        除了第一个和最后一个之外,每一个元素的游标都是指向下一个数据。

        对于静态链表的初始化相当于初始化数组,代码如下:

Status InitList(StaticLinkList space)
{int i;for(i=0;i<MAXSIZE-1;i++)space[i].cur = i + 1;space[MAXSIZE-1].cur = 0;return 1;
}

        几个注意的点:

        1)我们对数组的第一个和最后一个元素做特殊处理,他们的data不存放数据

        2)我们通常把未使用的数组元素称为备用链表

        3)数组的第一个元素,即下标为0的哪个元素的cur就存放备用链表的第一个结点的下标。

        4)数组的最后一个元素,即下标为MAXSIZE-1的cur则存放在第一个有数值的元素下标,相当于单链表中的头结点作用。

        那么这个时候可能大家会有疑问:这样子不还是挂羊头卖狗肉吗?这不还是数组,似乎没看出太多单链表的端倪。那么下面我们就从操作的角度来剖析一下,静态链表究竟是如何模拟单链表进行插入和删除的操作呢?

三、静态链表的插入操作

        我们先来看一看静态链表是如何实现元素的插入的。静态链表中要解决的是:如何用静态模拟动态链表结构的存储空间分配,也就是需要的时候申请,不需要的时候释放。我们在前面的文章中提到过,在动态链表中,结点的申请和释放分别借用C语言中的malloc()和free()两个函数来实现。而在静态链表当中,我们操作的是数组,不存在像动态链表一样的结点申请和释放问题,所以我们需要自己实现这两个函数,才可以做到插入和删除操作。

        而为了辨明数组中投哪些分量未被使用,解决的方法就是将所有未被使用过的及已被删除的分量用游标链成一个备用的链表。每次当我们进行插入时,便可以从备用链表上取得第一个结点作为待插入的新结点。我们可以结合下面的图片来进行理解,这里我们假设要在A的后面插入B这个元素:

       这里是一个静态链表,第一个元素的游标存放的是备用链表的下标,,最后一个元素的游标存放的是第一个元素的下标。

插入操作的图解

        我们要把B插入在C的前面,那么A就把下一个元素指向了B,将B的下标5作为了他的游标,然后B把自己的游标改成了他下一个元素C原本的下标2。那么当我们读取A之后要读取A的下一个元素,我们就会去访问到B了,那么B就成功地插入到A的后面了。

        我们下面来看一下代码,代码由两部分组成:

        首先是获得空间分量的下标:

int Malloc_SLL(StaticLinkList space)
{int i = space[0].cur;if(sapce[0].cur)space[0].cur = space[i].cur;//把它的下一个分量用来作为备用return i;
}

        第一件事:把数组的第一个元素游标指向的下标给获取出来,将第一个元素指向它的下一个元素,插入之后就不是变成了空闲分量。代码如下:

//在静态链表L中第i个元素之前插入新的数据元素
Status ListInsert(StaticLinkList L, int i,ElemType e)
{int j,k,l;k = MAXSIZE-1;//数组的最后一个元素if(i<1 || i>ListLength(L)+1){return ERROR;}j = Malloc_SLL(L);if(j){L[j].data = e;for(l=1 ; l <=i-1 ;i++){k = L[k].cur;}L[j].cur = L[k].cur;L[k].cur = j;return 1;}return ERROR;
}

四、静态链表的删除操作

        那么我们都知道,有插入就会有删除,我们在B插入进来后,想要把C给删除,那么我们该怎么来做呢?

删除之前的静态链表

             我们来分析一下:C离开了队伍,那么原本B的游标2指向的位置就没有元素了,那么这个时候我们需要顺位下来,将原本指向C的游标指向下一位D,那么B的游标就更改成3了,但是这个时候我们发现,B和C原本的游标都是3,这就产生了矛盾,所以我们除了处理B的游标,我们还要处理C原来的游标。我们将C归到备用链表中,那么C的游标就要指向备用链表6,然后原来6指向2,这样就把备用链表也给串起来了

删除之后的静态链表

        代码操作如下:

//删除在L中第i个数据元素
Status ListDelete(StaticLinkList L, int i)
{int j,k;if(i<1 || i>ListLength(L)){return ERROR;}k = MAXSIZE-1;for(j = 1; j<=i-1;j++){k = L[k].cur;//k1 = 1,k2 = 5}j = L[k].cur; //j = 2L[k].cur = L(L,j);return 1;
}//将下标为k的空闲结点回收到备用链表
void Free_SLL(StaticLinkList space,int k)
{space[k].cur = space[0].cur;space[0].cur = k;
}//返回L中数据元素个数
int ListLength(StaticLinkList L)
{int j = 0;int i = L[MAXSIZE-1].cur;while(i){i = L[i].cur;j++;}return j;
}

五、静态链表的优缺点总结

1、优点

        在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中插入和移动操作需要移动大量元素的缺点。

2、缺点

        没有解决连续存储分配(数组)带来的表长难以确定的问题。失去了顺序存储结构随机存储的特性。

3、小结

        总的来说,静态链表其实是为了给没有指针的编程语言设计的一种实现单链表功能的方法。尽管我们可以用单链表就不用静态链表了,但这样的思考方式是非常巧妙地,应该理解其思想,以备不时之需。

六、单链表小结——Tecent面试题

【题目】快速找到未知长度和单链表的中间结点。

1、普通解法:

        很简单,我们只需要遍历一遍单链表用来确定单链表的长度L。然后再次从头结点出发循环L/2次找到单链表的中间结点。这个算法的时间复杂度为O(L+\frac{L}{2})=O(\frac{3L}{2})

2、高级解法:

        有一个很巧妙的方法:利用快慢指针来解决!原理操作如下,设置两个指针*search 和*mid都是指向单链表的头结点。其中*search指针的移动速度是*mid的两倍。当*search指向末尾结点的时候,mid就正好在中间了。这也是标尺的思想。具体的代码演示如下:

Status GetMidNode(LinkList L,ElemType *e)
{LinkList search,mid;mid = search = L;while(search->next != NULL){//search移动的速度是mid的两倍if(search -> next ->next != NULL){serach = search -> next -> next;mid = mid ->next;}else{search = search -> next;}}*e = mid ->data;return 1;
}

(本节完)


参考资料:

1、线性表9_哔哩哔哩_bilibili 鱼C小甲鱼

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web安全 - 重放攻击(Replay Attack)

文章目录 OWASP 2023 TOP 10导图1. 概述2. 重放攻击的原理攻击步骤 3. 常见的重放攻击场景4. 防御重放攻击的技术措施4.1 使用时效性验证&#xff08;Time-Based Tokens&#xff09;4.2 单次令牌机制&#xff08;Nonce&#xff09;4.3 TLS/SSL 协议4.4 HMAC&#xff08;哈希消息…

C#基于SkiaSharp实现印章管理(10)

向PDF文件插入印章图片比之前实现的向图片文件插入印章麻烦得多。   最初的想法是使用PDF浏览控件在线打开PDF文件&#xff0c;然后在控件中实现鼠标移动时动态显示印章&#xff0c;点击鼠标时向当前PDF页面的鼠标点击位置插入图片。由于是.net 8的Winform项目&#xff0c;选…

MySQL联合索引、索引下推Demo

1.联合索引 测试SQL语句如下&#xff1a;表test中共有4个字段(id, a, b, c)&#xff0c;id为主键 drop table test;#建表 create table test(id bigint primary key auto_increment,a int,b int,c int )#表中插入数据 insert into test(a, b, c) values(1,2,3),(2,3,4),(4,5,…

初试React前端框架

文章目录 一、React概述二、React核心特性1、组件化设计2、虚拟DOM3、生态系统 三、实例操作1、准备工作2、创建项目结构3、启动项目4、编写React组件5、添加React样式6、运行项目&#xff0c;查看效果 四、实战小结 一、React概述 大家好&#xff0c;今天我们将一起探索React…

基于Zynq SDIO WiFi移植一(支持2.4/5G)

基于SDIO接口的WIFI&#xff0c;在应用上&#xff0c;功耗低于USB接口&#xff0c;且无须USB Device支持&#xff0c;满足某些应用场景 1 硬件连接 2 Vivado工程配置 3 驱动编译 3.1 KERNRL CONFIG (build ENV) 修改 export KERNELPATH<path of kernel header>export T…

JavaSE——面向对象8:Object类详解(==与equals的区别、hashCode、toString方法)

目录 一、与equals()的区别 (一)是一个比较运算符 (二)equals是Object类中的方法&#xff0c;只能判断引用类型 (三)equals方法重写练习 1.练习1 2.练习2 3.练习3 二、hashCode方法 三、toString方法 1.默认返回&#xff1a;全类名(包名类名)哈希值的十六进制 (1)不…

初识Django

前言: 各位观众老爷们好&#xff0c;最近几个月都没怎么更新&#xff0c;主要是最近的事情太多了&#xff0c;我也在继续学习Django框架&#xff0c;之前还参加了一些比赛&#xff0c;现在我会开始持续更新Django的学习&#xff0c;这个过程会比较久&#xff0c;我会把我学习的…

微积分-反函数6.5(指数增长和衰减)

在许多自然现象中&#xff0c;数量的增长或衰减与其大小成正比。例如&#xff0c;如果 y f ( t ) y f(t) yf(t) 表示在时间 t t t 时某种动物或细菌种群的个体数量&#xff0c;那么似乎可以合理地假设增长速率 f ’ ( t ) f’(t) f’(t) 与种群 f ( t ) f(t) f(t) 成正比…

Redis的基本使用

简介 传统的数据库是 关系数据库&#xff0c;但是Redis是键值对数据库传统的数据库是基于 磁盘存储的&#xff0c;但是Redis是基于 内存存储的 基于内存&#xff0c;读写性能更高内存是不大的&#xff0c;只能存储热点信息 安装 绿色软件&#xff0c;安装即可使用 安装服务 手…

【MySQL】子查询、合并查询、表的连接

目录 一、子查询 1、单行子查询 显示SMITH同一部门的员工信息 2、多行子查询 in关键字 查询和10号部门的工作岗位相同的雇员的名字、岗位、工资、部门号&#xff0c;但是筛选出的雇员的部门不能有10号部门 all关键字 查询工资比30号部门中所有雇员工资高的雇员的姓名、…

LLM端侧部署系列 | PowerInfer-2助力AI手机端侧部署47B大模型 (论文解读)

引言 简介 PowerInfer-2 概述 神经元感知的运行时推理 多态神经元引擎 内存中的神经元缓存 灵活的神经元加载 Neuron-Cluster-Level Pipeline 生成执行计划 执行 总结 0. 引言 一雨池塘水面平&#xff0c;淡磨明镜照檐楹。东风忽起垂杨舞&#xff0c;更作荷心万点声…

十、敌人锁定

方法&#xff1a;通过寻找最近的敌人&#xff0c;使玩家的面朝向始终朝向敌人&#xff0c;进行攻击 1、代码 在这个方法中使用的是局部变量&#xff0c;作为临时声明和引用 public void SetActorAttackRotation() {Enemys GameObject.FindGameObjectsWithTag("Enemy&qu…

工程机械车辆挖掘机自卸卡车轮式装载机检测数据集VOC+YOLO格式2644张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2644 标注数量(xml文件个数)&#xff1a;2644 标注数量(txt文件个数)&#xff1a;2644 标注…

Vue+NestJS项目实操(图书管理后台)

一、项目搭建 前端基于vben进行二次开发 在Github下载vben框架&#xff0c;搜索vben即可 下载地址&#xff1a;https://github.com/vbenjs/vue-vben-admin 下载完成后&#xff0c;进行安装依赖&#xff0c;使用命令&#xff1a; // 下载依赖 pnpm install// 运行项目 pnpm …

每日一练:地下城游戏

174. 地下城游戏 - 力扣&#xff08;LeetCode&#xff09; 题目要求&#xff1a; 恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里&#xff0c;他必须穿过地下城并通过对抗恶魔…

基于facefusion的换脸

FaceFusion是一个引人注目的开源项目&#xff0c;它专注于利用深度学习技术实现视频或图片中的面部替换。作为下一代换脸器和增强器&#xff0c;FaceFusion在人脸识别和合成技术方面取得了革命性的突破&#xff0c;为用户提供了前所未有的视觉体验。 安装 安装基础软件 安装…

数据链路层(以太网简介)

一.以太网数据帧结构&#xff1a; 目的地址&#xff0c;源地址&#xff0c;类型这三个被称为帧头&#xff0c;数据则被称为载荷&#xff0c;CRC则被称为帧尾&#xff08;校验和&#xff09; 二.数据帧结构分析 1.目的地址和源地址 i.地址解释 这两个地址指的是mac地址&#x…

国庆游玩计划安排

地点&#xff1a;上海前滩四方城 地点&#xff1a;船长酒吧 地点&#xff1a;上海&#x1f3db;️外滩华尔道夫

httpsok-v1.17.0-SSL通配符证书自动续签

&#x1f525;httpsok-v1.17.0-SSL通配符证书自动续签 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具&#xff0c;基于全新的设计理念&#xff0c;专为 Nginx 、OpenResty 服务器设计。已服务众多中小企业&#xff0c;稳定、安全、可靠。 一行命令&#xff0c;一分钟轻…

HTML基础用法介绍一

VS code 如何快速生成HTML骨架注释是什么&#xff1f;为什么要写注释&#xff1f;注释的标签是什么&#xff1f;标题标签段落标签换行标签与水平线标签 (都是单标签&#xff09;文本格式化标签图片标签超链接标签音频标签视频标签 &#x1f698;正片开始 VS code 如何快速生成…