【数据结构】 顺序表
文章目录
- 1 线性表
- 2 顺序表
- 2.1 概念及结构
- 2.2 接口实现
- 2.3 数组相关面试题
- 2.4 顺序表的问题与思考
1 线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就是说是连续的一条直线。但是在物理结构上不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。


2 顺序表
那么今天我们首先来学习顺序表,然后再学习链表,有一个循序渐进的效果
2.1 概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
- 静态顺序表:使用定长数组存储元素。
这种顺序表空间固定,那么存储到一定数量时就不能存储,不太方便
- 动态顺序表:使用动态开辟的数组存储
动态开辟就很好解决了空间不够的问题。
2.2 接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。
静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。
所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
//那么我们先来实现接口函数#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
typedef int SLDataType;
#define INIT_CAPACITY 4typedef struct SeqList//顺序表的创建
{SLDataType* s;//作为动态数组来初始化int size;//元素个数应该为正数int capacity;//初始容量大小
}SL;
//写接口函数时,要想我们对顺序表有什么操作?void SLInit(SL* ps);//顺序表的初始化
void SLDestroy(SL* ps);//顺序表的销毁
void SLPrint(SL* ps);//打印顺序表
void SLCheckCapacity(SL* ps);//检查容量是否充足
void SLPushBack(SL* ps, SLDataType x);//在尾部插入数据
void SLPopBack(SL* ps);//在尾部删除数据
void SLPushFront(SL* ps, SLDataType x);//在首部插入数据
void SLPopFront(SL* ps);//在首部删除数据
void SLInsert(SL* ps, int pos, SLDataType x);//在中间插入某个数据
void SLErase(SL* ps, int pos);//删除中间某个数据
int SLSearch(SL* ps, SLDataType x);//寻找某个元素并且返回下标
写完接口函数时,就要实现函数了
注意:在写代码时,最好写完一个接口函数就要进行调试,这样找错误就很方便。
//首先是对顺序表的初始化
void SLInit(SL* ps)
{assert(ps);//进行断言,ps指向的对象不为空ps->s = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);//malloc开辟空间时,后面要考虑开辟失败的情况,要进行检查if (ps->s == NULL){perror("malloc fail");return;}ps->size = 0;//初始化没有元素ps->capacity = INIT_CAPACITY;//进行初始化
}
//打印顺序表
void SLPrint(SL* ps)
{assert(ps);//如果是空的顺序表就不能打印int i = 0;for (i = 0; i < ps->size; i++){printf("%d ", ps->s[i]);}printf("\n");//打印完换行
}
//进行容量检查
void SLCheckCapacity(SL* ps)
{assert(ps);//判断为不为空顺序表if (ps->size == ps->capacity)//在进行尾部插入数据时要先判断空间够不够,不够要进行扩容{SLDataType* ptr =(SLDataType*)realloc(ps->s, sizeof(SLDataType) * ps->capacity * 2);//这里使用新的指针是因为如果开辟失败返回空指针,就不会影响到原来开辟的内存空间if (ptr == NULL){perror("realloc fail");return;}ps->s = ptr;//开辟成功将新开辟空间的指针赋给原来的指针ps->capacity *= 2;//因为空间扩大了两倍,那么容量也要扩大两倍}
}
//进行尾部插入数据
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);//断言可以找到问题SLCheckCapacity(ps);//尾部插入要判断最后一个元素有没有空间,没有则开辟ps->s[ps->size] = x;//将数据赋给最后一个元素ps->size++;//同时将元素个数加一//SLInsert(ps, ps->size, x);//这个函数先不用管,后面会讲
}
//进行头部插入数据
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);//判断为不为空SLCheckCapacity(ps);//判断容量够不够for (int i = ps->size - 1; i >= 0; i--){ps->s[i + 1] = ps->s[i];//将所有的数往后移一位}ps->size++;//元素要加一ps->s[0] = x;//进行赋值//SLInsert(ps, 0, x);//后面会讲
}
//进行尾部删除
void SLPopBack(SL* ps)
{assert(ps);//判断不为空assert(ps->size > 0);//判断顺序表中有没有元素ps->size--;//直接删除一个元素
}
//进行头部删除
void SLPopFront(SL* ps)
{assert(ps);//判读为不为空assert(ps->size > 0);//当这个数组是空的,size就为负数了//那么头部删除就可以进行覆盖,可以用memmove函数;也可以进行一个一个覆盖int begin = 0;for (begin = 1; begin <= ps->size - 1; begin++){ps->s[begin - 1] = ps->s[begin];//后一个将前一个进行覆盖}ps->size--;//让元素减一
}
//进行插入数据
void SLInsert(SL* ps, int pos, SLDataType x)//这个插入包括了头插和尾插,所以可以进行复用
{assert(ps);//判断为不为空assert(pos >= 0 && pos <= ps->size);//判断指定下标要合法,等于0就是要头插,等于ps->size就是要尾插SLCheckCapacity(ps);//检查容量是否充足int end = ps->size - 1;while (end >= pos){ps->s[end + 1] = ps->s[end];end--;//往后一个一个赋值}ps->s[pos] = x;//最后在pos位置赋值为xps->size++;//元素个数加一
}
//进行删除元素
void SLErase(SL* ps, int pos)
{assert(ps);//判断为不为空assert(pos >= 0 && pos < ps->size);//判断位置要合法int begin = pos + 1;while (begin < ps->size){ps->s[begin - 1] = ps->s[begin];++begin;//一个一个往前覆盖}ps->size--;//元素要减一
}
//进行元素的查找,找到了则返回下标,没找到则返回-1
int SLSearch(SL* ps, SLDataType x)//返回元素下标,要有返回值
{assert(ps);//判断为不为空for (int i = 0; i < ps->size; i++){if (ps->s[i] == x)return i;//进行循环查找,找到了就返回下标}return -1;//循环结束代表没找到,返回-1表示没找到
}
//最后进行顺序的销毁
void SLDestroy(SL* ps)//销毁顺序表时,要释放内存,并且将指针设为空
{assert(ps);//判断为不为空free(ps->s);//释放指针所指向的内存空间ps->s = NULL;//同时将指针置为空ps->size = ps->capacity = 0;//元素和容量置为0
}
注意:在进行传参数时,如果要改变所指向的内容,就要传地址过去,不要传值过去,切记!!!
2.3 数组相关面试题
顺序表的内容完成后,那么接下来就要写写题目来巩固能力了
- 原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度O(1)。
https://leetcode.cn/problems/remove-element/
//首先我们可以将元素覆盖的方式去写。
int removeElement(int* nums, int numsSize, int val) {int src = 0;//作为遍历数组的元素int dst = 0;//作为返回数组的新长度while (src < numsSize){if (nums[src] != val){nums[dst++] = nums[src];//如果不等于元素不相等,那么将dst加一,src也加一}src++;//如果相等,只有src加一,dst不加一,等下个元素进行覆盖}return dst;//最后返回数组的新长度
}//也可以这样写
int removeElement(int* nums, int numsSize, int val) {int src = 0;int dst = 0;while (src < numsSize){if (nums[src] != val){nums[dst++] = nums[src++];//上面的方式更加简洁,因为src哪种情况都要加一}else{src++;}}return dst;
}
//
- 删除排序数组中的重复项。
https://leetcode.cn/problems/remove-duplicates-from-sorted-array/
//这道题目和上一个思路差不多,每个数字之只能出现一次,返回数组的新长度
//我们使用快慢指针
int removeDuplicates(int* nums, int numsSize){int src = 1;//让src为1int dst = 0;while (src < numsSize){if (nums[dst] == nums[src]){src++;//两个数相等就src加1}else{nums[++dst] = nums[src++];//不相等就进行赋值}}return dst + 1;//最后返回dst+1
}//结合图片进行理解
//
- 合并两个有序数组
https://leetcode.cn/problems/merge-sorted-array/
//这里的非递减是可能递增也可能相等
//这里我们可以从头一个一个比较,小的放前面,大的放后面,但这样比较实在麻烦。换个角度,如果从后面开始放就会方便很多。
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){int end1 = m - 1;//作为最后一个元素比较int end2 = n - 1;int dst = m + n - 1;while (end1 >= 0 && end2 >= 0){if (nums2[end2] > nums1[end1]){nums1[dst--] = nums2[end2--];//如果大,那么num2放元素}else{nums1[dst--] = nums1[end1--];//如果小,那么num1放元素}}while (end2 >= 0)//这里判断end2,如果还有元素,依次放上去//不判断end1的原因是num1的元素就在num1中,不用进行移植{nums1[dst--] = nums2[end2--];}
}
//结合动态理解!(https://img-blog.csdnimg.cn/7a9667b48f7d40e48155cfb77ec0dd45.gif#pic_center)
以上题目希望可以加深和理解顺序表
2.4 顺序表的问题与思考
问题:
- 中间/头部的插入删除,时间复杂度为O(n)
- 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
- 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们在再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
如何解决以上问题呢?那就要使用链表了!!!
这次我们学习了顺序表,下次学习链表,希望各位学习永无止境!!!o_O
晚风庭院落梅初。淡云来往越疏疏。–李清照
相关文章:
【数据结构】 顺序表
文章目录1 线性表2 顺序表2.1 概念及结构2.2 接口实现2.3 数组相关面试题2.4 顺序表的问题与思考1 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序…...
Elasticsearch 集群规划- 单台机器核心数计算公式
在做集群规划的时候,到底需要给集群的每个节点多少个核心数?这个问题一直困扰了我很久。最近一段时间做千亿数据,PB存储量集群规划的时候,突然想明白了这件事,大致可以用一个公式来计算!我觉得这是一个非常…...
Tesla都使用什么编程语言?
作者 | 初光 出品 | 车端 备注 | 转载请阅读文中版权声明 知圈 | 进“汽车电子与AutoSAR开发”群,请加微“cloud2sunshine” 总目录链接>> AutoSAR入门和实战系列总目录 带着对更美好未来的愿景,特斯拉不仅成为有史以来最有价值的汽车公司&…...
1143. 最长公共子序列——【Leetcode每日刷题】
1143. 最长公共子序列 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些…...
【并发基础】线程的通知与等待:obj.wait()、obj.notify()、obj.notifyAll()详解
目录 〇、先总结一下这三个方法带来的Java线程状态变化 一、obj.wait() 1.1 作用 1.2 使用前需要持有线程共享对象的锁 1.3 使用技巧 二、obj.notify(All)() 1.1 notify() 方法 1.1.1 调用notify()或notifyAll()不会释放线程的锁 1.2 notifyAll() 方法 1.3 使用技巧 三、使用实…...
css黏性定位-实现商城的分类滚动的标题吸附
传统的黏性定位是使用js通过计算高度来实现的,当元素滚动到一定位置时吸附在当前位置。下面我们通过css来实现黏性定位功能。 黏性定位 黏性定位目前主流的浏览器已经全部支持,顾名思义,黏性定位具有吸附的效果,其实它是positio…...
@Component和@bean注解在容器中创建实例区别
Component和Bean的区别 在Spring Boot中,Component注解和Bean注解都可以用于创建bean。它们的主要区别在于它们的作用范围和创建方式。 Component注解是一种通用的注解,可以用于标注任何类。被标注的类将被Spring容器自动扫描并创建为一个bean。这个bea…...
不写注释就是垃圾
最近Linux6.2出来了增加了很多新的东西,有看点的是,Linux确实要可以在Apple M1上面运行了,这应该是一个很大的新闻,如果有这么稳定的硬件支持,那对于Linux来说相当于又打下了一大片的江山。其中关于Linux6.2的特性罗列…...
深信服一面
1.C变量存储在哪里,生命周期是怎样的 2.静态成员变量和成员函数的特性,在哪里用过吗 3.new和delete是什么,和malloc和free对比有啥优势 4.new能不能重载,重载new有什么用 5.多态是怎么实现的,有什么优势和目的 6.…...
【C语言】深度理解指针(中)
前言✈上回说到,我们学习了一些与指针相关的数据类型,如指针数组,数组指针,函数指针等等,我们还学习了转移表的基本概念,学会了如何利用转移表来实现一个简易计算器。详情请点击传送门:【C语言】…...
步进电机运动八大算法
引导一种模块化(Module)设计思想,将传统步进电机的控制器(controller)、驱动器(Driver)、运动算法(Arithmetic)三合一。 对比国内外步进电机驱动原理和已有工作,结合各种硬件特性,改进或实现了可实际移植并用于步进电机控制八大算法。本产品…...
如果你持续大量的教坏ChatGPT,它确实会变坏
你输出的很多数据是经过人工标注吗,以确保可以正常对外展示出来,而不是有性别歧视、种族歧视或者其它意识形态为多数人所不认同的内容产生? 作为AI语言模型,我并不直接处理或输出任何数据,我的任务是通过对输入的自然语…...
opencv学习(二)图像阈值和平滑处理
图像阈值ret, dst cv2.threshold(src, thresh, maxval, type)src: 输入图,只能输入单通道图像,通常来说为灰度图dst: 输出图thresh: 阈值maxval: 当像素值超过了阈值(或者小于阈值,…...
【含源码】用python做游戏有多简单好玩
有很多同学问我还有其他什么小游戏吗,游戏是怎么做的,难不难。我就用两篇文章来介绍一下,如何使用Python做游戏。 兔子与灌 俄罗斯方块 休闲五子棋 走迷宫 推箱子 消消乐 超多小游戏玩转不停↓ 更多小游戏可以评论区讨论哦,喜欢…...
C++常用函数
std::sort std::sort 函数用于对数组或容器进行排序,可以按照默认的升序排序或指定比较函数进行排序。 语法如下: template <class RandomAccessIterator> void sort(RandomAccessIterator first, RandomAccessIterator last);template <clas…...
Android Framework基础到深入篇
Android Framework基础到深入篇 KernelSU Android上基于内核的Root方案 Android系统源码下载/编译篇...
【Go进阶训练营】聊一下go的gc原理
背景 正好周末时间,就打算梳理以下自己对go gc的理解。跳出语言层面来说,gc分为两种,一种是手动创建,手动销毁。另一种就是由自动分配自动销毁,前者就是c,c的代表,后者就是java,go。 而整个流程…...
英飞凌Tricore原理及应用介绍05_中断处理之中断路由(IR)模块详解
目录 1.概述1.1相关缩写2 TC3xx中IR特性介绍3.SRN(中断服务请求优先级)3.1 寄存器中的各Bit位讲解3.2 如何改变SRN配置4. 实际应用介绍4.1 如何利用SRC寄存器检查OS中断配置是否正确?1.概述 在Tricore架构中允许有多个中断源包括片上外设及外部中断世间产生的中断请求,以打…...
微搭问答002-移动端上传的文件如何在PC端下载
遇到一个问题,就是上传的图片,在手机上可以下载了,但在电脑上怎么下载到电脑 里,包括上传的文件 点击查看页面就可以吧,在企业工作台里 我做了查看页面,小程序可以,但H5和电脑页面不行 你创建一…...
初识JVM
目录 引言 JVM是什么? JVM和java有什么联系? JDK、JRE、JVM有什么区别 为什么学习JVM? JVM——从内存管理开始 运行时数据区域 分区讲解 堆 方法区 程序计数器 本地技术栈 虚拟机栈 对象的创建 指针碰撞: 空闲列表…...
企业网络改造不求人:手把手教你深信服防火墙旁挂部署(含NQA配置避坑指南)
企业级防火墙旁挂部署实战:深信服设备零基础配置指南 当企业网络规模逐步扩大,业务系统日益复杂,网络安全防护往往成为IT运维团队最头疼的问题之一。传统防火墙部署通常需要对现有网络架构进行大规模调整,不仅实施周期长ÿ…...
嵌入式系统程序运行机制与存储器优化
嵌入式系统程序运行机制深度解析1. 程序运行基础架构1.1 冯诺依曼体系结构现代计算机系统(包括嵌入式设备)都基于冯诺依曼模型构建,该模型包含五个核心组件:运算器(ALU):执行算术和逻辑运算控制器(CU):协调…...
Android定位模拟技术全解析:基于系统级Hook的位置伪造实现方案
Android定位模拟技术全解析:基于系统级Hook的位置伪造实现方案 【免费下载链接】FakeLocation Xposed module to mock locations per app. 项目地址: https://gitcode.com/gh_mirrors/fak/FakeLocation 在移动应用开发与测试过程中,精准控制定位信…...
开源项目依赖管理:从冲突解决到高效协作的实践指南
开源项目依赖管理:从冲突解决到高效协作的实践指南 【免费下载链接】IPED IPED Digital Forensic Tool. It is an open source software that can be used to process and analyze digital evidence, often seized at crime scenes by law enforcement or in a corp…...
保姆级教程:用Qt搞定蓝牙串口通信,从连接云台到指令队列完整流程
保姆级教程:用Qt实现蓝牙串口通信全流程实战 在智能硬件开发领域,蓝牙串口通信就像一座连接数字世界与物理世界的桥梁。想象一下,你手中的Qt程序能够通过简单的指令让云台精准转动,或者让智能小车按照预定路线行驶——这种"软…...
毕设程序java基于的动漫分析与交流平台 基于Spring Boot的二次元文化社区与作品分享系统 Java驱动的ACG内容聚合与互动服务平台
毕设程序java基于的动漫分析与交流平台31sl5luf(配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。随着互联网技术的飞速发展和Z世代文化消费的崛起,动漫产业已从边缘亚文…...
别让AI被‘带坏’:手把手教你用开源工具复现大模型越狱攻击(附防御实战)
大模型安全攻防实战:从开源工具复现到防御策略部署 当ChatGPT在2022年底掀起AI浪潮时,很少有人预料到三年后的大模型会面临如此复杂的对抗攻击。作为一名长期从事AI安全测试的工程师,我亲眼见证了攻击手段从最初的简单提示注入发展到如今的神…...
京东AI优势持续升级,京东的AI大棋局怎么看?
日前,京东媒体沟通会召开,会上,京东展示了其在大模型、数字人、AI硬件及企业级解决方案上的最新布局。这次畅谈让我们看到了更多的京东大棋局,京东的AI战略并非单纯的技术军备竞赛,而是一场围绕“降本增效”与“生态重…...
惊艳效果展示:实时手机检测-通用镜像识别复杂场景手机案例
惊艳效果展示:实时手机检测-通用镜像识别复杂场景手机案例 1. 开箱即用的手机检测神器 想象一下这样的场景:你需要快速检测一张照片中有多少部手机,可能是为了分析会议记录、监控考场纪律,或者统计零售店铺的顾客行为。传统方法…...
R语言实战:单因素方差分析从数据导入到结果解读(附完整代码)
R语言实战:单因素方差分析从数据导入到结果解读(附完整代码) 当你第一次面对一组实验数据,试图比较不同处理组间的差异时,单因素方差分析(One-way ANOVA)往往是首选方法。作为R语言数据分析的基…...

