当前位置: 首页 > news >正文

【数据结构与算法】顺序表的定义及初步实现

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:数据结构与算法
🌠 首发时间:2022年9月18日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
🌟 一以贯之的努力 不得懈怠的人生

阅读指南

  • 顺序表的定义
  • 顺序表的实现 —— 静态分配
  • 顺序表的实现 —— 动态分配

顺序表的定义

顺序表 —— 用顺序存储的方式实现线性表顺序存储。把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现

线性表是具有相同数据类型的 n (n≥0) 个数据元素的有限序列,数据类型相同说明每个数据元素所占的空间是一样大的

我们假设线性表第一个元素的存放位置(即首地址)是 LOC(L)LOClocation 的缩写,那么第二个元素的存放位置就是 LOC(L)+数据元素的大小,第三个元素的存放位置就是 LOC(L)+2*数据元素的大小,依此类推…

那么,我们又怎么知道所存放的数据元素的大小呢?

学过 C语言 的小伙伴肯定知道,C语言 中有一个函数 sizeof() 可以帮上我们的忙,我们用 sizeof(ElemType) 这样调用的方式就能得到对应数据元素的大小,其中的 ElemType 就是你的顺序表中存放的数据元素类型,比如 sizeof(int) = 4B,一个整型一般是 4 个字节;此外,我们还可以传自己定义的类型进去,比如,我们定义一个 Customer 类型

typedef struct {
	int num;	//号数
	int people; //人数
} Customer;

相应地,我们使用 sizeof(Customer) 就可以得到这个数据类型的大小为 8B

顺序表的实现 —— 静态分配

#define MaxSize 10				//定义最大长度
typedef struct {
	ElemType data[MaxSize];		//用静态的 “数组” 存放数据元素
	int length;					//顺序表的当前长度
} SqList;						//顺序表的类型定义(静态分配方式)

ElemType data[MaxSize]; 会给各个数据元素分配连续的存储空间,大小为 MaxSize*sizeof(ElemType)

Sq:sequence —— 顺序,序列

#include <stdio.h>
#define MaxSize 10				//定义最大长度
typedef int ElemType;

typedef struct {
	ElemType data[MaxSize];		//用静态的 “数组” 存放数据元素
	int length;					//顺序表的当前长度
} SqList;						//顺序表的类型定义

//基础操作 —— 初始化一个顺序表
void InitList(SqList &L) {
	for(int i = 0; i < MaxSize; i++) {
		L.data[i] = 0;		//将所有数据元素设置为默认初始值
	}
	L.length = 0;
}

//主函数
int main() {
	SqList L;		//声明一个顺序表
	InitList(L);	//初始化顺序表
	//其他操作
	return 0;
}

如果在初始化顺序表中不给 data 数组赋值为 0,会怎么样呢?

#include <stdio.h>
#define MaxSize 10				//定义最大长度
typedef int ElemType;

typedef struct {
	ElemType data[MaxSize];		//用静态的 “数组” 存放数据元素
	int length;					//顺序表的当前长度
} SqList;						//顺序表的类型定义

//基础操作 —— 初始化一个顺序表
void InitList(SqList &L) {
	L.length = 0;
}

//主函数
int main() {
	SqList L;		//声明一个顺序表
	InitList(L);	//初始化顺序表
	//尝试“违规”打印整个数组
	for(int i = 0; i < MaxSize; i++) {
		printf("data[%d]=%d\n", i, L.data[i]);
	}
	return 0;
}

运行这段代码,你会看到很奇怪的数据,或许和我的运行结果不太一样,这是正常的

在这里插入图片描述

为什么会显示这些奇怪的数据呢?

其实啊,程序在给我们这个数组分配内存的时候,我们并不知道这块内存里面之前存储过什么,如果我们不设置数据元素的默认值,就会出现这样的结果

其实,我们这样访问也是违规的,因为初始化顺序表的时候 length 是为 0 的,我们要遍历的条件应该是 i < L.length; 才对,所以数据元素的初始化是可以省略的,不过 length 的初始化这一步骤就不能省略了

如果我们定义的 “数组” 存满了,怎么办呢?

建议直接放弃治疗,顺序表的表长刚开始确定后就无法更改了,因为我们是静态分配空间

有小伙伴就说,那我一开始就声明一个很长的顺序表不就行了,但这样的后果是会浪费我们的内存空间,从这里我们就可以看出来静态分配的局限性了

顺序表的实现 —— 动态分配

#define InitSize 10			//顺序表的初始长度
typedef struct {
	ElemType *data;			//指示动态分配数组的指针
	int MaxSize;			//顺序表的最大容量
	int length;				//顺序表的当前长度
} SqList;					//顺序表的类型定义

C语言 中提供了 mallocfree 这两个函数来让我们动态申请和释放内存空间

malloc 会申请一整片连续的存储空间,然后返回一个指向这一片存储空间开始地址的指针,同时需要我们强制转型为定义的数据元素类型指针

malloc 函数的参数,指明了要分配多大的连续内存空间

L.data = (ElemType *) malloc(sizeof(ElemType) * InitSize);

如果你学习过 C++,那你也可以使用 newdelete 这两个函数来实现同样的功能

#include <stdio.h>
#include <stdlib.h>				//使用 malloc 和 free 函数所需的头文件

#define InitSize 10				//默认最大长度
typedef int ElemType;			//方便我们改变类型

typedef struct {
	ElemType *data;				//指示动态分配数组的指针
	int MaxSize;				//顺序表的最大容量
	int length;					//顺序表的当前长度
} SqList;						//顺序表的类型定义

//初始化顺序表
void InitList(SqList &L);

//增加动态数组的长度
void IncreaseSize(SqList &L, int len);

//主函数
int main() {
	SqList L;			//声明一个顺序表
	InitList(L);		//初始化顺序表
	IncreaseSize(L, 5);

	return 0;
}

//初始化顺序表
void InitList(SqList &L) {
	//用 malloc 函数申请一片连续的存储空间
	L.data = (ElemType *)malloc(sizeof(ElemType)* InitSize);
	L.length = 0;
	L.MaxSize = InitSize;
}

//增加动态数组的长度
void IncreaseSize(SqList &L, int len) {
	ElemType *p = L.data;				//将L数据存储起来
	L.data = (ElemType *)malloc(sizeof(ElemType)* (L.MaxSize + len));

	for (int i = 0; i < L.length; i++) {
		L.data[i] = p[i];				//将数据复制到新区域
	}

	L.MaxSize = L.MaxSize + len;		//顺序表最大长度增加 len
	free(p);							//释放临时的内存空间
}

注意:realloc 函数也可以实现,但建议初学者使用 mallocfree 更能理解背后的过程

顺序表的特点:

  1. 随机访问,既可以在 O(1) 时间内找到第 i 个元素,代码实现为 data[i - 1]
  2. 存储密度高,每个节点只存储数据元素
  3. 拓展容量不方便,即便采用动态分配的方式实现,拓展长度的时间复杂度也比较高
  4. 插入、删除操作不方便,需要移动大量元素

相关文章:

  • 线稿图视频制作--从此短视频平台不缺上传视频了
  • 爬虫工具之Beautiful Soup学习
  • 初识kotlin(初用kotlin一时爽、一直用一直爽)
  • 测试用到的测试工具总结一手
  • 【Vue五分钟】五分钟了解webpack实战配置案例详情
  • 如何让iOS设备上App定时执行后台任务(上)
  • 面试中遇到的错题(持续更新)
  • 还在为sql注入眼花缭乱的过滤而烦恼?一文教您快速找出所有过滤内容
  • vue中的插槽slot
  • 搭建Redis主从复制、哨兵模式
  • 深度解析ArrayList使用
  • 吴恩达深度学习笔记(六)——超参数调试、Batch正则化和程序框架
  • 【甄选靶场】Vulnhub百个项目渗透——项目十六:FristiLeaks_1.3(文件上传,py脚本改写,sudo提权,脏牛提权,源码获取)
  • 苹果iPhone手机iOS16如何取消关闭复制粘贴时不停弹出的剪贴板粘贴提示通知弹窗?
  • Android移动应用开发之ImageView、ProgressBar和Notification的一些简单使用
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 【Linux系统编程】快速查找errno错误码信息
  • 【前端学习】-粗谈选择器
  • Electron入门介绍
  • gf框架之分页模块(五) - 自定义分页
  • HTTP请求重发
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Java读取Properties文件的六种方法
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • node-glob通配符
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • VUE es6技巧写法(持续更新中~~~)
  • Vue组件定义
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 那些年我们用过的显示性能指标
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 一天一个设计模式之JS实现——适配器模式
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • # 达梦数据库知识点
  • #{}和${}的区别?
  • #AngularJS#$sce.trustAsResourceUrl
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (2)STL算法之元素计数
  • (2024)docker-compose实战 (8)部署LAMP项目(最终版)
  • (6)添加vue-cookie
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (ZT)出版业改革:该死的死,该生的生
  • (二)延时任务篇——通过redis的key监听,实现延迟任务实战
  • (附源码)ssm高校实验室 毕业设计 800008
  • (十六)、把镜像推送到私有化 Docker 仓库
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .chm格式文件如何阅读
  • .Net 6.0 Windows平台如何判断当前电脑是否联网
  • .Net 8.0 新的变化
  • .net core 6 redis操作类
  • .Net CoreRabbitMQ消息存储可靠机制