[数据结构]顺序表

1、顺序表的概念及结构

1.1 线性表

线性表( linear list )是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。
案例:蔬菜分为绿叶类、⽠类、菌菇类。线性表指的是具有部分相同特性的⼀类数据结构的集合

2、顺序表分类

2.1顺序表和数组的区别

顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接⼝

2.2顺序表分类

1.静态顺序表:

概念:使用定长数组存储元素

代码示例:

typrdef int SLDataType;
#define N 8
typedef struct SeqList
{SLDataType a[N];//定长数组int size;       //有效数据个数
}SL;

这就是一个静态顺序表,它又一定的缺陷。

容易出现:空间给少了不够用,给多了造成空间浪费

2.动态顺序表

它的特点是:按需申请

3.动态顺序表的实现

我们首先创建相应的头文件和程序文件

我们现在头文件中,引用头文件,定义所需要的结构体和函数

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLDataType;typedef struct SeqList
{SLDataType* arr; //存储数据的底层结构int capacity;    //记录顺序表的空间大小int size;        //记录顺序表当前有效的数据个数
}SL;
//初始化:
void SLInit(SL* ps);
//销毁
void SLDestroy(SL* ps);
//顺序表的头部 / 尾部插入
void SLPushFront(SL* ps, SLDataType x);
void SLPushBack(SL* ps, SLDataType x);
//顺序表的头部 / 尾部删除 
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);//打印
void SLPrint(SL* ps);
//删除指定位置的值
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);

我们先定义一个动态顺序表

注意:这行代码是为了设定我们的数据类型

1.初始化

接下来我们要初始化我们的顺序表。所以我们定义了这个函数

接着我们去源文件写完这个函数,让指针指向NULL大小设定为0完成初始化

void SLInit(SL* ps) 
{ps->arr = NULL;ps->size = ps->capacity = 0;
}

写完了初始化我们可以开始写功能

首先就是头插和尾插

我们接着完善

2.头插

我们先来写头插函数

void SLPushFront(SL* ps, SLDataType x)

首先我们来思考以下问题,一个数组如何头插,以及目前的内存大小能否插入新的数据

假设足够:

数组头插,我们一般将数组的各个元素后移一位然后将数组arr[0]赋值成我们要插入的数据

void SLPushFront(SL* ps, SLDataType x)
{assert(ps);
for (int i = ps->size; i > 0; i--) //i = 1{ps->arr[i] = ps->arr[i - 1]; //ps->arr[1] = ps->arr[0]}ps->arr[0] = x;ps->size++;
}

我们不难写出这个函数但是它对吗?

显然存在一定的问题,我们前面的条件是设置在空间充足的情况下,如果空间不足的话,我们该怎么办呢?

当然是扩容啦!!!

所以我们再写一个检查空间是否充足的函数,如果不足顺便扩容。

那么,既然说到扩容,我们应该怎样扩容呢?

我这里有三种扩容方式:

1.一次扩容一个空间

2.一次扩容多个大小的空间

3.成倍的增加空间(1.5倍,2倍)

这里我推荐第三种方法。

理由:

        第一种一次扩容一个空间,好处是不会造成空间的浪费,缺点是如果我们输入大量数据时,它需要多次开辟,导致程序效率低下。

        第二种一次开辟多个空间,有效解决了第一种导致程序小路低下的问题,但是,它也有相应的问题,我们不能确定一次开辟多大的空间合适,如果开辟小了,一样也会和第一种一样多次扩容,影响程序效率,但如果一下周四开辟空间过大,也会导致空间被浪费。

我们先定义函数:

void SLCheckCapacity(SL* ps)

接着判断是否需要扩容,然后扩容空间,但是由于我们初始化直接是NULL所以这里我再加上一个三目操作符,总体代码如下:

void SLCheckCapacity(SL* ps)
{if (ps->size == ps->capacity){int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));if (tmp == NULL) {perror("realloc fail!");exit(1);}//扩容成功ps->arr = tmp;ps->capacity = newCapacity;}
}

这里我设置了tmp变量是为了防止扩容失败。这里我选择的就是扩容原来的两倍。

接下来我们按照上面的思路把头插完善

void SLPushFront(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);for (int i = ps->size; i > 0; i--) //i = 1{ps->arr[i] = ps->arr[i - 1]; //ps->arr[1] = ps->arr[0]}ps->arr[0] = x;ps->size++;
}

3.尾插

做完了头插,我们可以来试试尾插,数组中尾插是神简单的,如图:

如果空间充足,我们可以直接再尾部插入我们的数据然后吧size++,不够的话先扩容然后再执行

void SLPushBack(SL * ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);ps->arr[ps->size++] = x;
}

这样头插和尾插就完成了

4.头删

完成了插入那么我们还需要完成删除,删除相比较插入它有什么不同?删除不需要太在意空间。

现在我们先来完成头删。

在数组中我们怎么完成头删的呢?如图

我们一般是把每个数向前移动一位,数组有效长度-1,及size--;

代码示例:

void SLPopFront(SL* ps)
{assert(ps);assert(ps->size);for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

注意:我们要确保ps和ps->size不为NULL

5.尾删

这个操作实现起来其实非常简单,我们可以直接size--;

代码示例:

void SLPopBack(SL* ps)
{assert(ps);assert(ps->size);   ps->size--;}

完成这些,那么我要上难度了,删除指定位置的值/插入指定位置的值

6.删除指定位置的值

具体思路,就是遍历去寻找所需数值,然后并将该数值之后的数据的下标前移,siza--如图:

代码示例:

void SLErase(SL* ps, int pos) {assert(ps);assert(pos >= 0 && pos < ps->size);//pos以后的数据往前挪动一位for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];//ps->arr[i-2] = ps->arr[i-1];}ps->size--;
}

7.在指定位置插入值

思路,找到数值将该数值及其后的向后移动一位。size++

如图:

代码示例:

void SLInsert(SL* ps, int pos, SLDataType x) {assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);//pos及之后的数据往后挪动一位,pos空出来for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1]; //ps->arr[pos+1] = ps->arr[pos]}ps->arr[pos] = x;ps->size++;
}

注:这是插入,要检查空间是否足够

8.打印

完成这些我们可以来尝试打印我们的顺序表,类似打印数组。

代码示例:

void SLPrint(SL* ps) 
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}printf("\n");
} 

9.销毁顺序表

在我们之前讲过动态内存开辟,最后要再释放。

代码示例:

void SLDestroy(SL* ps) 
{assert(ps);if (ps->arr) {free(ps->arr);}ps->arr = NULL;ps->size = ps->capacity = 0;
}

最后来展示程序代码:

#include"SeqList.h"
void SLInit(SL* ps) 
{ps->arr = NULL;ps->size = ps->capacity = 0;
}
void SLDestroy(SL* ps)
{assert(ps);if (ps->arr) {free(ps->arr);}ps->arr = NULL;ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{if (ps->size == ps->capacity){int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));if (tmp == NULL) {perror("realloc fail!");exit(1);}//扩容成功ps->arr = tmp;ps->capacity = newCapacity;}
}
void SLPushBack(SL * ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);ps->arr[ps->size++] = x;
}
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);for (int i = ps->size; i > 0; i--) //i = 1{ps->arr[i] = ps->arr[i - 1]; //ps->arr[1] = ps->arr[0]}ps->arr[0] = x;ps->size++;
}
void SLPopBack(SL* ps)
{assert(ps);assert(ps->size);   ps->size--;}
void SLPopFront(SL* ps)
{assert(ps);assert(ps->size);for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x) {assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1]; //ps->arr[pos+1] = ps->arr[pos]}ps->arr[pos] = x;ps->size++;
}
void SLErase(SL* ps, int pos) 
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
void SLPrint(SL* ps) 
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}printf("\n");
} 
void SLDestroy(SL* ps) 
{assert(ps);if (ps->arr) {free(ps->arr);}ps->arr = NULL;ps->size = ps->capacity = 0;
}
//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x) {assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);//pos及之后的数据往后挪动一位,pos空出来for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1]; //ps->arr[pos+1] = ps->arr[pos]}ps->arr[pos] = x;ps->size++;
}
//删除指定位置数据
void SLErase(SL* ps, int pos) {assert(ps);assert(pos >= 0 && pos < ps->size);//pos以后的数据往前挪动一位for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];//ps->arr[i-2] = ps->arr[i-1];}ps->size--;
}

这样一个循序表完成了,你可以用设计的函数来进行操作。

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

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

相关文章

调用一个第三方api,但是它返回是异步回调的,如何把这个异步回调做成同步

如果你需要将一个异步回调的API调用转换为同步调用&#xff0c;有几种常见的方法可以实现&#xff1a; 1. 使用Future和Promise&#xff1a;在调用异步API时&#xff0c;可以创建一个Future对象并返回给调用方。在异步回调执行完成后&#xff0c;使用Promise对象来设置Future的…

从0开始学C++ 第二十七课 数据结构入门 - 数组与链表

第二十七课&#xff1a;数据结构入门 - 数组与链表 学习目标&#xff1a; 理解数组的基本概念和操作。掌握链表的基本结构与特点。学会在C中定义和操作数组和链表。了解数组和链表的基本使用场景。 学习内容&#xff1a; 数组&#xff08;Array&#xff09; 概念&#xff1a…

js vue调用activex ocx

js vue调用activex ocx 与IE调用方式不同处 CLSID和TYPE <OBJECT id"MultiplyDemo" refocx1 CLSID"{8EEF7302-1FC8-4BA0-8EA5-EC29FDBCA45B}" TYPE"application/x-itst-activex" width15% height15%></OBJECT>//调用方式1 //或是 …

品牌价值的累积与倍增:指数函数的含义及其在企业运营中的应用

品牌的价值日益凸显。品牌价值的累积与倍增不仅是企业追求的目标&#xff0c;也是市场竞争的重要标志。指数函数作为一种数学模型&#xff0c;对于描述品牌价值的增长具有重要意义。本文将深入探讨指数函数的含义及其在企业运营中的应用&#xff0c;并分析如何通过持续创新、品…

【DevOps】Jenkins Extended E-mail 邮件模板添加自定义变量

文章目录 1、配置Jenkins邮箱2、配置告警模板1、配置Jenkins邮箱 略 2、配置告警模板 自定义变量:DYSK_PYTEST_STATUS // Uses Declarative syntax to run commands inside a container. pipeline {agent {kubernetes {cloud "kubernetes" //选择名字是kuberne…

绘制PCB封装库的神器

我们平时绘制PCB封装时&#xff0c;一般都是按照元器件数据表上的封装图来绘制&#xff0c;有一些ECAD软件提供了向导&#xff0c;但是这个过程还是比较繁琐。现在有很多大厂比如TI都会提供芯片封装库&#xff0c;一般需要Ultra Librarian软件才能打开&#xff0c;然后再转换成…

【ZYNQ入门】第十篇、基于FPGA的图像白平衡算法实现

目录 第一部分、关于白平衡的知识 1、MATLAB 自动白平衡算法的实现 1.1、matlab代码 1.2、测试效果 1.3 测试源图 2、为什么摄像头采集的图像要做白平衡 3、自动白平衡算法总结 4、FPGA设计思路 4.1、实时白平衡的实现 4.2、计算流程优化思路 第二部分、硬件实…

如何查看Linux CPU占有率

目录 1、top 2、htop 3、vmstat 4、mpstat 5、iostat 查看嵌入式设备CPU占有率是评估系统资源使用情况的重要方式。 在Linux系统中&#xff0c;有多种方法可以查看CPU占有率&#xff0c;这里介绍几种常用的命令行工具。 1、top 这是最常用的命令之一&#xff0c;它提供了…

LeetCode-题目整理【3】:买卖股票的最佳时机

买卖股票的最佳时机 都是求最大利润&#xff0c;但是在没有限制&#xff0c;如121和122&#xff0c;动态规划稍微复杂一些&#xff0c;建议不用&#xff0c;到最后两道难题&#xff0c;题目有限制&#xff0c;使用动态规划通过求解子问题的最优解来逐步求解原问题的最优解。 买…

C语言之反汇编查看函数栈帧的创建与销毁

文章目录 一、 什么是函数栈帧&#xff1f;二、 理解函数栈帧能解决什么问题呢&#xff1f;三、 函数栈帧的创建和销毁解析3.1、什么是栈&#xff1f;3.2、认识相关寄存器和汇编指令3.2.1 相关寄存器3.2.2 相关汇编命令 3.3、 解析函数栈帧的创建和销毁3.3.1 预备知识3.3.2 代码…

AI对比:ChatGPT和文心一言的区别和差异

目录 一、ChatGPT和文心一言大模型的对比分析 1.1 二者训练的数据情况分析 1.2 训练大模型数据规模和参数对比 1.3 二者3.5版本大模型对比总结 二、ChatGPT和文心一言功能对比分析 2.1 二者产品提供的功能情况分析 2.2 测试一下各种功能的特性 2.2.1 文本创作能力 2.2…

婴儿洗衣机怎么选?热门品牌希亦、觉飞、由利详细测评

宝宝的衣物是不是要和大人的衣服分开洗呢&#xff1f;这是很多新手爸妈都会遇到的一个问题。有的人认为&#xff0c;宝宝的衣服要单独洗&#xff0c;以免被大人的衣服上的细菌污染&#xff1b;有的人认为&#xff0c;宝宝的衣服可以和大人的衣服一起洗&#xff0c;这样可以节省…

优先级队列(堆)

目录 1 概念 2 堆的概念 2.1小根堆 2.2大根堆 3堆的存储方式​​​​​​​ 4、堆的创建 4.1堆向下调整 5、时间复杂度 6、堆的插入&#xff08;向上调整&#xff09; 7、堆的删除 8、PriorityQueue的特性 9、堆排序 1 概念 我们知道的队列&#xff0c;队列是一…

leetcode---Z字形变换

题目&#xff1a; 将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 "PAYPALISHIRING" 行数为 3 时&#xff0c;排列如下&#xff1a;之后&#xff0c;你的输出需要从左往右逐行读取&#xff0c;产生…

Nginx入门教程+案例

摘要&#xff1a;Nginx 是一款轻量级的 Web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。本文将介绍 Nginx 的基本概念、安装方法、配置文件以及一些常见应用场景&#xff0c;并通过一个简单的案例展示 Nginx 的实际应用。 一、Nginx 基本概…

redis高可用之主从部署

文章目录 前言1. 同步以及命令传播1.1 同步1.2 命令传播 2. 解决从服务器断线重连2.1 解决方案 3. PSYNC命令4. 复制步骤1:设置主服务器的地址和端口步骤2:建立套接字连接 ——其实就是建立TCP连接步骤3:发送PING命令步骤4:身份验证步骤5:发送端口信息步骤6:同步步骤7:命令传播…

鸿蒙5.0发布时间已定!何处寻得移动开发加速器?

直接在百度上搜索「鸿蒙5.0发布时间」&#xff0c;出来的结果&#xff0c;那一个比一个焦虑~~ 百度的AI基于综合内容判断得出&#xff0c;鸿蒙5.0的发布时间在2023-04-17 百度知道推的答案是202年年4月中 但不管几月&#xff0c;“鸿蒙元年”似乎都是确定的&#xff0c;就是…

Linux切换jdk版本

参考文献&#xff1a;Linux 多个JDK的版本 脚本切换 - C小海 - 博客园 (cnblogs.com)

树莓派ubuntu:CSI接口摄像头安装驱动程序及测试

树莓派中使用OV系列摄像头&#xff0c;网上能搜到的文章资源太老了&#xff0c;文章中提到的摄像头配置选项在raspi-config中并不存在。本文重新测试整理树莓派摄像头的驱动安装、配置、测试流程说明。 libcamera 新版本中使用libcamera作为摄像头驱动程序。 libcamera是一个…

【计算机系统组成原理】操作系统处理器深入介绍

博主介绍&#xff1a;✌全网粉丝喜爱、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦&#xff01; &#x1f345;附上相关C语言版源码讲解&#x1f345; &#x1f44…