C语言实现顺序表

顺序表

1.线性表

线性表是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。

在这里插入图片描述

2.顺序表

顺序表的概念

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

1. 静态顺序表:使用定长数组存储元素。

在这里插入图片描述

2.动态顺序表:使用动态开辟的数组存储。

在这里插入图片描述

顺序表的接口实现

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间 大小,所以下面我们实现动态顺序表。

接口如下:

// 对数据的管理:增删查改
void SeqListInit(SeqList* ps);                    // 顺序表的初始化
void SeqListDestroy(SeqList* ps);                 // 顺序表的销毁
void SeqListPrint(SeqList* ps);                   // 顺序表的打印
void Check_Capacity(SeqList* ps);                 // 检查是否需要扩容
void SeqListPushBack(SeqList* ps, SLDataType x);  // 顺序表的尾插
void SeqListPushFront(SeqList* ps, SLDataType x); // 顺序表的头插
void SeqListPopFront(SeqList* ps);                // 顺序表的头删
void SeqListPopBack(SeqList* ps);                 // 顺序表的尾删// 顺序表查找
int SeqListFind(SeqList* ps, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);
//修改指定下标位置元素
void SeqListModify(SeqList *ps, int pos, SLDataType x);

初始化顺序表

首先,我们要创建一个顺序表类型,该顺序表类型包括了顺序表的起始位置、记录顺序表内已有元素个数的计数器(size),以及记录当前顺序表的容量的变量(capacity)。

typedef int SLDataType;   //顺序表类型
typedef struct SeqList   // 顺序表的结构(动态)
{SLDataType* a;		//声明了一个指向顺序表的指针,姑且称它为“顺序表指针”int size; 			//记录当前顺序表内元素个数int capacity;		//记录当前顺序表的最大容量
} SeqList;

然后,我们需要一个初始化函数,对顺序表进行初始化。

// 顺序表的初始化
void SeqListInit(SeqList *ps) {assert(ps);		//断言,ps不能为NULLps->a = NULL;   //刚开始时顺序表为空,顺序表指针为NULLps->size = 0;	//起始时元素个数为0ps->capacity = 0;	//容量为0
}

销毁顺序表

因为顺序表所用的内存空间是动态开辟在堆区的,所以我们在使用完后需要及时对其进行释放,避免造成内存泄漏

// 顺序表的销毁
void SeqListDestroy(SeqList *ps) {assert(ps);free(ps->a);//释放顺序表指针指向的空间ps->a = NULL;ps->size = 0;    //元素个数置0ps->capacity = 0;//容量置0
}

顺序表打印

// 顺序表的打印
void SeqListPrint(SeqList *ps) {assert(ps);//循环遍历顺序表for (int i = 0; i < ps->size; i++) {printf("%d ", ps->a[i]);}printf("\n");
}

插入数据

仔细想想,我们每次需要增加数据的时候,首先都应该先检查顺序表内元素个数是否已达顺序表容量上限。若已达上限,那么我们就需要先对顺序表进行扩容,然后才能增加数据。

检查容量

// 扩容
void Check_Capacity(SeqList *ps) {if (ps->capacity == ps->size) {// 如果容量等于大小说明满了需要扩容   如果size和capacity为0,说明是初始化状态,默认给4个字节,否则就扩容两倍int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;// 将扩容的指针放到临时变量中SLDataType *tmp = (SLDataType *) realloc(ps->a, sizeof(SLDataType) * newcapacity);//	若传入realloc的指针为空指针(NULL),则realloc函数的作用相当于malloc函数。if (tmp == NULL) {perror("realloc fail");exit(-1);} else {ps->a = tmp;ps->capacity = newcapacity;}}
}

头插

要想在顺序表的表头插入数据,那么就需要先将顺序表原有的数据从后往前依次向后挪动一位,最后再将数据插入表头。

// 顺序表的头插
void SeqListPushFront(SeqList *ps, SLDataType x) {assert(ps);Check_Capacity(ps);   //检查容量// 头插-插入到顺序表的最开始的位置,下标是0,但是需要挪动整个顺序表for (int i = ps->size; i >= 0; i--) // 将所有数据挪动到下一个位置{// size的下标是最后位置的下一个ps->a[i] = ps->a[i - 1];}ps->a[0] = x;ps->size++;
}

尾插

尾插相对于头插就比较简单了,直接在表尾插入数据即可。

// 顺序表的尾插
void SeqListPushBack(SeqList *ps, SLDataType x) {assert(ps);Check_Capacity(ps);// 尾插-插入到顺序表中的下一个位置,下标也就是ps->sizeps->a[ps->size] = x;ps->size++;
}

指定位置插入

要做到在指定下标位置插入数据,首先我们需要得到一个下标位置,然后从该下标位置开始(包括该位置),其后的数据从后往前依次向后挪动一位,最后将数据插入到该下标位置。pos默认从1开始。

// 顺序表在pos位置插入x,
void SeqListInsert(SeqList *ps, int pos, SLDataType x) {pos--;//pos对应数组下标,pos默认从1开始。assert(ps);assert(pos >= 0 && pos <= ps->size);Check_Capacity(ps);//将pos后面的数据向后挪动一个位置for (int i = ps->size; i > pos; i--) {ps->a[i] = ps->a[i - 1];}ps->a[pos] = x;ps->size++;
}

我们可以发现,头插和尾插实际上就是在下标为0的位置和下标为ps->size的位置插入数据,也就意味着我们可以统一使用该函数来实现头插和尾插。

// 顺序表的尾插
void SeqListPushBack(SeqList *ps, SLDataType x) {SeqListInsert(ps, ps->size + 1, x);//+1 是对应逻辑下标,pos位置默认从1开始
}// 顺序表的头插
void SeqListPushFront(SeqList *ps, SLDataType x) {SeqListInsert(ps, 1, x);			//1是逻辑下标,真实下标是0
}

删除数据

头删

要删除表头的数据,我们可以从下标为1的位置开始,依次将数据向前覆盖即可。

// 顺序表的头删
void SeqListPopFront(SeqList *ps) {assert(ps);assert(ps->size);// 将顺序中除了第一个位置的所有数据向前挪动一个下标for (int i = 0; i < ps->size - 1; i++){ps->a[i] = ps->a[i + 1];}ps->size--;
}

尾删

尾删就更简单了,直接将顺序表的元素个数减一即可。

// 顺序表的尾删
void SeqListPopBack(SeqList *ps) {assert(ps);assert(ps->size);   //保证顺序表不为空ps->size--;
}

指定下标位置删除

要删除指定下标位置的数据,我们只需要从下标位置开始,其后的数据从前向后依次覆盖即可。

//顺序表删除pos位置的值
void SeqListErase(SeqList *ps, int pos) {pos--;//对其数组下标assert(ps);assert(pos >= 0 && pos < ps->size);//从pos位置开始,后面数据都向前挪动一个位置for (int i = pos; i < ps->size - 1; i++) {ps->a[i] = ps->a[i + 1];}ps->size--;
}

同样的道理,头删和尾删实际上也就是删除下标为0的位置和下标为ps->size - 1的位置的数据,也就意味着我们可以统一使用该函数来实现头删和尾删。

// 顺序表的头删
void SeqListPopFront(SeqList *ps) {SeqListErase(ps, 1);
}// 顺序表的尾删
void SeqListPopBack(SeqList *ps) {SeqListErase(ps, ps->size);
}

查找数据

查找数据也相对简单,直接遍历一次顺序表即可,若找到了目标数据,则停止遍历,并返回该数据的下标,否则返回-1。

// 顺序表查找
int SeqListFind(SeqList *ps, SLDataType x) {int i = 0;for (i = 0; i < ps->size; i++) {if (ps->a[i] == x) {return i + 1;//i+1对应逻辑下标}}return -1;//找不到返回-1
}

测试代码:

#include "SeqList.h"
void Test1() {struct SeqList s1;SeqListInit(&s1);SeqListPushBack(&s1, 1);SeqListPushBack(&s1, 2);SeqListPushBack(&s1, 3);SeqListPushBack(&s1, 4);SeqListPrint(&s1);//1 2 3 4SeqListPopFront(&s1);SeqListPopFront(&s1);SeqListPrint(&s1);//3 4SeqListPopBack(&s1);SeqListPopBack(&s1);SeqListPrint(&s1);//SeqListPushFront(&s1, 0);SeqListPushFront(&s1, -1);SeqListPrint(&s1);        //-1 0SeqListInsert(&s1, 1, 61);//下标为1插入一个61SeqListInsert(&s1, 2, 62);//下标为1插入一个62SeqListInsert(&s1, 3, 63);//下标为1插入一个63SeqListInsert(&s1, 4, 64);//下标为1插入一个64SeqListPrint(&s1);        //61 62 63 64 -1 0SeqListErase(&s1, 1);SeqListErase(&s1, 1);SeqListErase(&s1, 1);SeqListErase(&s1, 1);SeqListPrint(&s1);//-1 0SeqListDestroy(&s1);
}int main() {Test1();return 0;
}

修改数据

修改数据,就直接对该位置的数据进行再次赋值即可。

//修改指定下标位置元素
void SeqListModify(SeqList *ps, int pos, SLDataType x) {pos--;//对应数组下标assert(ps);assert(pos >= 0 && pos < ps->size);//检查输入下标的合法性ps->a[pos] = x;                //修改数据
}

3.顺序表的问题及思考

问题:

  1. 中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到 200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

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

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

相关文章

OpenAI 发布企业版ChatGPT-4

OpenAI 发布企业版ChatGPT-4 ChatGPT Enterprise 版本功能ChatGPT Enterprise 对比ChatGPT Enterprise 不同点未来发布计划OpenAI 发布企业版ChatGPT-4 OpenAI 宣布,鉴于ChatGPT的爆炸性成果,推出了针对企业的 ChatGPT Enterprise 版 ChatGPT Enterprise 版本功能 包含所有…

软件工程(十二) 设计模式之创建型模式

我们传统的23种设置模式如下 创建型模式:用于创建对象 工厂方法(Factory Method) 模式抽象工厂(Abstract Factory) 模式原型(Protptype) 模式单例(Singleton) 模式构建器模式结构型模式:建立更大的结构 适配器(Adapter)模式桥接(Bridge)模式组合(Composite)模式装饰(D…

WPF怎么实现文件拖放功能winform怎么实现拖拽功能

WPF怎么实现文件拖放功能winform怎么实现文件拖拽功能&#xff0c;在管理员模式下wpf winform怎么实现文件的拖拽功能 WPF实现文件拖放功能&#xff0c;正常情况并没有什么问题&#xff0c;但是如果你的程序使用管理员身份启动&#xff0c;你就会发现文件拖放功能就会失效。同…

JAVA-x和y的最大值

输入两个整数 x 和 y&#xff0c;请你编写一个函数&#xff0c;int max(int x, int y)&#xff0c;计算并输出 x 和 y 的最大值。 输入格式 共一行&#xff0c;包含两个整数 x 和 y 。 输出格式 共一行&#xff0c;包含一个整数&#xff0c;表示两个数中较大的那个数。 数据范围…

【MySQL】MySQL权限管理

权限范围管理权限动态权限 授权显示 GRANT 权限 撤销权限激活角色在服务器级别激活角色在用户级别激活角色在会话级别激活角色 感谢 &#x1f496; 在上一篇文章【MySQL】MySQL里的用户账户和角色是什么&#xff1f;如何管理&#xff1f;中&#xff0c;我们了解了MySQL中用户与…

【二维差分】

二维差分 #include<iostream> using namespace std; const int N 100011;int n,m,q;int a[N][N],b[N][N];int main(){cin>>n>>m>>q;for(int i1;i<n;i)for(int j1;j<m;j)cin>>a[i][j];for(int i1;i<n;i)for(int j1;j<m;j)b[i][j] …

jmeter+nmon+crontab简单的执行接口定时压测

一、概述 临时接到任务要对系统的接口进行压测&#xff0c;上面的要求就是&#xff1a;压测&#xff0c;并发2000 在不熟悉系统的情况下&#xff0c;按目前的需求&#xff0c;需要做的步骤&#xff1a; 需要有接口脚本需要能监控系统性能需要能定时执行脚本 二、观察 >针…

后端开发基础概念

后端开发基础概念 目前处于项目上手阶段&#xff0c;在学习项目过程中&#xff0c;有一些一知半解或者不明白含义的专业名词或者缩写&#xff0c;在此汇总。里面的内容很多都是基于个人理解&#xff0c;水平有限如果有出错的地方还请各位大佬批评指正。 2023年8月31日00:34:22…

恒运资本:市盈率怎么算?

市盈率&#xff08;P/E ratio&#xff09;是判别一家公司股票价格合理性的一个重要目标&#xff0c;也是投资者评估公司股票投资价值的重要参阅目标。市盈率越高&#xff0c;表明相对于公司的收益来说&#xff0c;该公司的股票定价越高。市盈率越低&#xff0c;则表明该股票被低…

App与小程序工具总结

文章目录 前言Burpsuite抓包问题LPosedJustTrustMe 绕过 SSL Pining小程序的反编译APP脱壳&#xff0c;反射大师、frida反射大师Frida 总结 前言 在进行渗透工作的时候&#xff0c;遇到过的App、小程序也不少了&#xff0c;有简单的&#xff0c;也有加固的比较不错的&#xff…

聊聊mybatis-plus的SafetyEncryptProcessor

序 本文主要研究一下mybatis-plus的SafetyEncryptProcessor SafetyEncryptProcessor mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/SafetyEncryptProcessor.java public class SafetyEncryptProcessor implements EnvironmentPostProc…

技术深入解析与教程:网络安全技术探秘

第一章&#xff1a;引言 在当今数字化时代&#xff0c;网络安全已经成为了重要议题。随着各种信息和业务在网络上的传输与存储&#xff0c;安全问题也日益突出。本文将带您深入探讨网络安全领域中的关键技术&#xff0c;涵盖渗透测试、漏洞挖掘以及恶意软件分析等方面&#xf…

PCD点云文件外部框框坐标计算

PCD点云文件直接提取的是点云的坐标&#xff0c;不是最外面的box的坐标&#xff0c;因此可以通过&#xff1a; max_b octree.get_max_bound() min_b octree.get_min_bound()分别得到最大最小的xyz坐标&#xff0c;之后进行计算 点的序号和位置对应如下&#xff1a; 所有的…

【数据结构与算法 模版】高频题刷题模版

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【】&#xff0c;使用【】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为&#xff1a;目标公…

vue 在IOS移动端中 windon.open 等跳转外部链接后,返回不触发vue生命周期、mounted等相关事件-解决方法

做了一个列表的h5页面&#xff0c;通过点击列表跳转到外部链接&#xff0c;然后返回是回到原来页面状态&#xff0c;类似缓存。发现在ios端返回后&#xff0c;vue 的mounted() 、create()、路由监听等方法都不会执行。在安卓和pc 端都能正常调用。 解决方案&#xff1a;监听pa…

一篇文章学会C#的正则表达式

https://blog.csdn.net/qq_38507850/article/details/79179128 正则表达式 一句话概括就是用来对字符串根据自己的规则进行匹配的&#xff0c;可以匹配(返回)出符合自己要求的匹配结果&#xff0c;有人说字符串类的函数也可以&#xff0c;确实是这样&#xff0c;但是字符串的函…

软考A计划-网络工程师-复习背熟-路由器与交换配置和网络安全

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

ARM DIY(五)摄像头调试

前言 今天&#xff0c;就着摄像头的调试&#xff0c;从嵌入式工程师的角度&#xff0c;介绍如何从无到有&#xff0c;一步一步地调出一款设备。 摄像头型号&#xff1a;OV2640 开发步骤 分为 2 个阶段 5 个步骤 阶段一&#xff1a; 设备树、驱动、硬件 阶段二&#xff1a; 应…

【SpringBoot】详细介绍Spring Boot中@Component

在Spring Boot中&#xff0c;Component是一个通用的注解&#xff0c;用于标识一个类是Spring框架中的组件。Component注解是Spring的核心注解之一&#xff0c;它提供了自动扫描和实例化bean的功能。 具体来说&#xff0c;Component注解的作用是将一个普通的Java类转化为Spring…

《C和指针》笔记18:前缀++ 和后缀++

C 语言里有前缀 和后缀&#xff0c;使用还是有点不同的。对应的还有--操作符&#xff0c;但它们的工作原理与此相同&#xff0c;只是它所执行的是减值操作而不是增值操作。我们只要掌握的原理&#xff0c;--的原理也就知道了。 在这里我们把符号叫做操作符&#xff0c;把它操作…