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,一经查实,立即删除!

相关文章

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

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

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

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

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

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

App与小程序工具总结

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

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

第一章&#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;目标公…

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

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

ARM DIY(五)摄像头调试

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

【跟小嘉学 Rust 编程】二十、进阶扩展

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…

拿来即用修改密码功能

<template><div><!-- 重置密码 --><el-dialogtitle"修改密码"v-model"state.resetPwdDialogVisible":showClose"state.firstLogin ! 1"width"550px"close"onCancel":close-on-click-modal"false&…

uniapp结合Canvas+renderjs根据经纬度绘制轨迹(二)

uniapp结合Canvasrenderjs根据经纬度绘制轨迹 文章目录 uniapp结合Canvasrenderjs根据经纬度绘制轨迹效果图templaterenderjsjs数据结构 ​ 根据官方建议要想在 app-vue 流畅使用 Canvas 动画&#xff0c;需要使用 renderjs 技术&#xff0c;把操作canvas的js逻辑放到视图层运…

Python爬虫框架之Selenium库入门:用Python实现网页自动化测试详解

概要 是否还在为网页测试而烦恼&#xff1f;是否还在为重复的点击、等待而劳累&#xff1f;试试强大的Selenium&#xff01;让你的网页自动化测试变得轻松有趣&#xff01; 一、Selenium库到底是什么&#xff1f; Selenium 是一个强大的自动化测试工具&#xff0c;它可以让你直…

华为 连接OSPF和RIP网络---OSPF和RIP网络相互引入

路由引入简介 不同路由协议之间不能直接共享各自的路由信息&#xff0c;需要依靠配置路由的引入来实现。 获得路由信息一般有3种途径&#xff1a;直连网段、静态配置和路由协议。可以将通过这3种途径获得的路由信息引入到路由协议中&#xff0c;例如&#xff0c;把直连网段引入…

Matlab图像处理-图像缩放

基本概念 图像缩放是指将给定的图像在x轴方向按比例缩放a倍&#xff0c;在y轴方向按比例缩放b倍&#xff0c;从而获得一幅新的图像。 如果ab&#xff0c;即在x轴方向和y轴方向缩放的比率相同&#xff0c;则称这样的比例缩放为图像的全比例缩放。 如果a≠b&#xff0c;图像比…

Nacos安装

一、下载Nacos1.4.1二、单机版本安装 2.1 将下载的nacos安装包传输到服务器2.2 解压文件2.3 进入bin目录下 单机版本启动2.4 关闭nacos2.5 访问Nacos地址 IP&#xff1a;8848/nacos三、集群版本的安装 3.1 复制nacos安装包&#xff0c;修改为nacos8849&#xff0c;nacos8850&am…

C# | DBSCAN聚类算法实现 —— 对直角坐标系中临近点的点进行聚类

C# | DBSCAN聚类算法实现 聚类算法是一种常见的数据分析技术&#xff0c;用于将相似的数据对象归类到同一组或簇中。其中&#xff0c;DBSCAN&#xff08;Density-Based Spatial Clustering of Applications with Noise&#xff09;是一种基于密度的聚类算法&#xff0c;能够有效…

Spark有两种常见的提交方式:client 模式和 cluster 模式对机器 CPU 的影响

Spark有两种常见的提交方式&#xff1a;client 模式和 cluster 模式。这两种方式对机器 CPU 的影响略有不同 &#xff0c;请参考以下说明 Client 模式&#xff1a; 在 Client 模式下&#xff0c;Spark Driver 运行在提交任务的客户端节点上&#xff08;即运行 spark-submit 命…

云服务器利用Docker搭建sqli-labs靶场环境

一、安装宝塔面板 使用xshell、electerm、SecureCRT等远程终端连接登陆上云服务器&#xff0c;在Linux宝塔面板使用脚本安装 安装后&#xff0c;如下图&#xff1a;按照提示&#xff0c;在云服务器防火墙/安全组放行Linux宝塔面板的端口 在浏览器打开上述网址&#xff0c;登…

【SpringSecurity】七、SpringSecurity集成thymeleaf

文章目录 1、thymeleaf2、依赖部分3、定义Controller4、创建静态页面5、WebSecurityConfigurerAdapter6、权限相关7、当用户没有某权限时&#xff0c;页面不展示该按钮 1、thymeleaf 查了下读音&#xff0c;leaf/li:f/&#xff0c;叶子&#xff0c;前面的单词发音和时间time一…