【数据结构】 顺序表
文章目录
- 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——从内存管理开始 运行时数据区域 分区讲解 堆 方法区 程序计数器 本地技术栈 虚拟机栈 对象的创建 指针碰撞: 空闲列表…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)
注:文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件:STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...
深度解析云存储:概念、架构与应用实践
在数据爆炸式增长的时代,传统本地存储因容量限制、管理复杂等问题,已难以满足企业和个人的需求。云存储凭借灵活扩展、便捷访问等特性,成为数据存储领域的主流解决方案。从个人照片备份到企业核心数据管理,云存储正重塑数据存储与…...
[C++错误经验]case语句跳过变量初始化
标题:[C错误经验]case语句跳过变量初始化 水墨不写bug 文章目录 一、错误信息复现二、错误分析三、解决方法 一、错误信息复现 write.cc:80:14: error: jump to case label80 | case 2:| ^ write.cc:76:20: note: crosses initialization…...

