【数据结构初阶】--- 顺序表
顺序表,好像学C语言时从来没听过,实际上就是给数组穿了层衣服,本质是一模一样的。
这里的顺序表实际是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增删改查插入,这些基本操作其实平时就会在使用数组的时候用到。
那么,今天就带你深度剖析线性表(数组)的底层原理
这节知识只是个开胃菜,但也属于要必备的知识,所以各位同学不要松懈哦
引入
顺序表有静态的也有动态的
- 静态顺序表,静,无非就是初始化这个静态表后,这个静态表的空间就不能变化了,例如:
struct SeqList
* {
* int arr[20];
* int size;
* };
- 当初始化线性表后,这个数组存的数据就不能更改了
- 那有人会想,我用个宏常量替换数组的大小,到时后想扩大或缩小,更改宏常量就行了
#define N 20 int arr[N]; - 这个想法不错,但也只能在线性表初始化之前更改,如果你初始化后,你正在使用线性表的功能时,内存不够用了怎么办
那么,接下来我所编写的代码就是动态顺序表,当你不够用内存的时候,会自动给你开辟,当看到这里,如果前面【C语言进阶】— 动态内存管理,你看过,那接下来的知识易如反掌,甚至不用看我对代码的注释就可以清楚,没看过的朋友,强烈推荐,很重要!!!数据结构会经常用到这篇的知识
1. 顺序表在内存中的存储方式
是一块连续的内存

2. 顺序表的定义
这里是用结构体定义了一个顺序表类型
typedef int SeqListType;//因为顺序表中存储的数据不见得都是int型,也可能是别的类型数据,所以,想存放别的数据时只需把int换成你想存的数据的类型//动态顺序表
typedef struct SeqList
{SeqListType* data;//指针指向的是SeqListType这个类型的数据int size;//记录当前有效储存数据的个数int capacity;//data开辟数组空间的容量
}SeqList;
初始化、销毁、打印功能
//初始化顺序表(开辟空间,赋初值)
void SLInit(SeqList* ps)
{//开辟一块大小为sizeof(SeqListType) * 4的内存,并把首地址传给指针ps->dataps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);if (ps->data == NULL){perror("malloc");return;}ps->size = 0;ps->capacity = 4;
}
//销毁空间,因为是在堆区开辟的数据,用完要用free函数释放
void SLDestory(SeqList* ps)
{free(ps->data);ps->data = NULL;ps->size = 0;ps->capacity = 0;
}void SLPrint(SeqList* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->data[i]);}printf("\n");
}
3. 顺序表的功能实现
扩容功能,先跳过看头插法
//检查容量,不足顺便扩容
void CheckCapacity(SeqList* ps)
{if (ps->capacity == ps->size){//用realloc给ps->data开辟一个原来2倍的空间SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);if (new_ps != NULL)//判断返是否开辟成功,若为NULL则开辟失败{ps->data = new_ps;new_ps = NULL;ps->capacity *= 2;}else{perror("realloc");return;}}
}
3.1 头插法、尾插法
//头插法
void SLPushFront(SeqList* ps, SeqListType x)
{//检查目前容量,不足就扩容CheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--){ps->data[i + 1] = ps->data[i];}ps->data[0] = x;ps->size++;
}//尾插法
void SLPushBack(SeqList* ps, SeqListType x)
{CheckCapacity(ps);ps->data[ps->size] = x;ps->size++;
}
3.2 头删法、尾删法
//头删法
void SLPopFront(SeqList* ps)
{//这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针assert(ps != NULL);
//*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************assert(ps->size > 0);for (int i = 1; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}
//尾删法
void SLPopBack(SeqList* ps)
{assert(ps != NULL);assert(ps->size > 0);ps->size--;
}
3.3 给指定位置插入数据
void SLInsert(SeqList* ps, int position, SeqListType x)
{assert(ps != NULL);//判断给的位置是否越界,若越界,程序执行完会告诉你这里出的问题assert(position >= 1 && position <= ps->size);CheckCapacity(ps);for (int i = ps->size - 1; i >= position - 1; i--){ps->data[i + 1] = ps->data[i];}ps->data[position - 1] = x;ps->size++;
}
3.4 指定位置删除数据
void SLErase(SeqList* ps, int position)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);assert(ps->size > 0);for (int i = position; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}
3.5 查找指定数据
int SLFind(SeqList* ps, SeqListType x)
{assert(ps != NULL);for (int i = 0; i < ps->size; i++){if (ps->data[i] == x)return i;}return -1;
}
4.顺序表的优缺点
4.1 优点
因为它跟数组一样可以进行下表访问,所以当你要查询数据时时间复杂度为O(1),是常数级,访问速度很快很方便,这与它的内存是一片连续的内存密切相关,因为当你访问数组下标为i的数据时,arr[i]本质是*(arr+i),首元素地址+i解引用
4.3 缺点
也正因它的内存是连续的,所以当你要删除、插入数据时必须要移动后面的数据,时间复杂度是O(N)级别的。
5 完整代码
5.1 头文件 SeqList.h 中的代码
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SeqListType;//动态顺序表
typedef struct SeqList
{SeqListType* data;int size;int capacity;
}SeqList;//初始化顺序表
void SLInit(SeqList* ps);
//销毁顺序表
void SLDestory(SeqList* ps);
//打印顺序表
void SLPrint(SeqList* ps);
//头插法插入数据
void SLPushFront(SeqList* ps, SeqListType x);
//尾插法
void SLPushBack(SeqList* ps, SeqListType x);
//头删法
void SLPopFront(SeqList* ps);
//尾删法
void SLPopBack(SeqList* ps);
//指定位置插入数据
void SLInsert(SeqList* ps, int position, SeqListType x);
//指定位置删除
void SLErase(SeqList* ps, int position);
//查找数据
int SLFind(SeqList* ps, SeqListType x);
5.2 函数实现的文件 SeqList.c
#define _CRT_SECURE_NO_WARNINGS#include"SeqList.h"void SLInit(SeqList* ps)
{ps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);if (ps->data == NULL){perror("malloc");return;}ps->size = 0;ps->capacity = 4;
}void SLDestory(SeqList* ps)
{free(ps->data);ps->data = NULL;ps->size = 0;ps->capacity = 0;
}void SLPrint(SeqList* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->data[i]);}printf("\n");
}//检查容量,不足顺便扩容
void CheckCapacity(SeqList* ps)
{if (ps->capacity == ps->size){//用realloc给ps->data开辟一个原来2倍的空间SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);if (new_ps != NULL){ps->data = new_ps;new_ps = NULL;ps->capacity *= 2;}else{perror("realloc");return;}}
}void SLPushFront(SeqList* ps, SeqListType x)
{//检查目前容量,不足就扩容CheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--){ps->data[i + 1] = ps->data[i];}ps->data[0] = x;ps->size++;}
//尾插法
void SLPushBack(SeqList* ps, SeqListType x)
{CheckCapacity(ps);ps->data[ps->size] = x;ps->size++;
}void SLPopFront(SeqList* ps)
{//这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针assert(ps != NULL);
//*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************assert(ps->size > 0);for (int i = 1; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}
//尾删法
void SLPopBack(SeqList* ps)
{assert(ps != NULL);assert(ps->size > 0);ps->size--;
}void SLInsert(SeqList* ps, int position, SeqListType x)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);CheckCapacity(ps);for (int i = ps->size - 1; i >= position - 1; i--){ps->data[i + 1] = ps->data[i];}ps->data[position - 1] = x;ps->size++;
}void SLErase(SeqList* ps, int position)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);assert(ps->size > 0);for (int i = position; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}int SLFind(SeqList* ps, SeqListType x)
{assert(ps != NULL);for (int i = 0; i < ps->size; i++){if (ps->data[i] == x)return i;}return -1;
}
这两套代码不能直接运行,主函数int main(){}中的内容自己写,就剩调用函数了,让自己动动手敲几行代码吧,好的程序员一定是需要实践的,尽管顺序表这一节相对不难,但里面有些边界值的判断,只有你亲手敲代码的时候才能真正进入思考,加油各位!
相关文章:
【数据结构初阶】--- 顺序表
顺序表,好像学C语言时从来没听过,实际上就是给数组穿了层衣服,本质是一模一样的。 这里的顺序表实际是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增删改查插入,这些基本操作其实平时就会…...
一个完整的java项目通常包含哪些层次(很全面)
1.View层(视图层) 职责:负责数据的展示和用户交互。在Web应用中,View层通常与HTML、CSS和JavaScript等技术相关。 技术实现:在Spring MVC中,View层可以使用JSP、Thymeleaf、FreeMarker等模板引擎来实现。…...
设置电脑定时关机
1.使用快捷键winR 打开运行界面 2.输入cmd ,点击确认,打开命令行窗口,输入 shutdown -s -t 100,回车执行命令,自动关机设置成功 shutdown: 这是主命令,用于执行关闭或重启操作。-s: 这个参数用于指定执行关…...
Java 编译报错:找不到符号? 手把手教你排查解决!
Java 编译报错:找不到符号? 手把手教你排查解决! 在 Java 开发过程中,我们经常会遇到编译器抛出 "找不到符号" 错误。这个错误提示意味着编译器无法在它所理解的范围内找到你所引用的类、变量或方法。这篇文章将带你一步…...
Gitte的使用(Windows/Linux)
Gitte的使用(Windows/Linux) 一、Windows上使用Gitte1.下载程序2.在Gitte上创建远程仓库3.连接远程仓库4.推送文件到远程仓库 二、Linux上使用Gitte1.第一次从仓库上传1.1生成公钥1.2配置SSH公钥1.3新建一个仓库1.4配置用户名和邮箱在Linux中1.5创建仓库…...
c++之旅第十弹——IO流
大家好啊,这里是c之旅第十弹,跟随我的步伐来开始这一篇的学习吧! 如果有知识性错误,欢迎各位指正!!一起加油!! 创作不易,希望大家多多支持哦! 一.流的概念&…...
量化交易:Miniqmt获取可转债数据和交易python代码
哈喽,大家好,我是木头左! 低风险资产除了国债外,还有可转债,兼容有高收益的股性和低风险的债性,号称“下有保底,上不封顶”。 🔍 可转债:金融市场的双面娇娃 可转债&am…...
测试开发之自动化篇 —— 使用Selenium IDE录制脚本!
今天,我们开始介绍基于开源Selenium工具的Web网站自动化测试。 Selenium包含了3大组件,分别为:1. Selenium IDE 基于Chrome和Firefox扩展的集成开发环境,可以录制、回放和导出不同语言的测试脚本。 2. WebDriver 包括一组为不同…...
Django 外键关联数据
在设计数据库的时候,是得需要通过外键的形式将各个表进行连接。 原先的表是这样的 要想更改成这样: 下面是操作步骤: 有两张表是关联的 # 在 models.py 里创建class Department(models.Model):"""部门表""&quo…...
开源与新质生产力
在这个信息技术迅猛发展的时代,全球范围内的产业都在经历着深刻的变革。在这样的背景下,“新质生产力”的概念引起了广泛的讨论。无论是已经成为或正努力转型成为新质生产力的企业,都在寻求新的增长动力和竞争优势。作为一名长期从事开源领域…...
如何将 Windows图片查看器的背景颜色改成浅色(灰白色)?
现在大家基本都在使用Win10系统,我们在双击查看图片时,系统默认使用系统自带的图片(照片)查看器去打开图片。图片查看器的背景色默认是黑色的,如下所示:(因为大家可能会遇到同样的问题ÿ…...
k8s-pod参数详解
目录 概述创建Pod编写一个简单的Pod添加常用参数为Pod的容器分配资源网络相关Pod健康检查启动探针存活探针就绪探针 作用整个Pod参数配置创建docker-registry 卷挂载 结束 概述 k8s中的pod参数详解。官方文档 版本 k8s 1.27.x 、busybox:stable-musl、nginx:stable-alpine3…...
一些计算机网络面试题
TCP建立连接和关闭连接的流程?每个流程的环节? TCP是在传输层的协议,建立的是可靠传输 TCP在传输数据前建立连接是采用三次握手,关闭连接是四次挥手 三次握手:因为目前网络通讯是全双工的,那我假设浏览器…...
transformer - 注意力机制
Transformer 的注意力机制 Transformer 是一种用于自然语言处理任务的模型架构,依赖于注意力机制来实现高效的序列建模。注意力机制允许模型在处理一个位置的表示时,考虑输入序列中所有其他位置的信息,而不仅仅是前面的几个位置。这种机制能…...
三端植物大战僵尸杂交版来了
Hi,好久不见,最近植物大战僵尸杂交版蛮火的 那今天苏音整理给大家三端的植物大战僵尸杂交版包括【苹果端、电脑端、安卓端】 想要下载的直接划到最下方即可下载。 植物大战僵尸,作为一款古老的单机游戏,近期随着B站一位UP主潜艇…...
np.hstack()和np.vstack()函数解释
np.hstack()和np.vstack()函数解释 文章目录 1,np.hstack()1.1,代码1.2,结果 2,np.vstack()2.1,代码2.2,结果 3,np.hstack()和np.vstack()3.1,代码3.2,结果 1,…...
【Linux】进程5——进程优先级
1.进程优先级 1.1.什么是进程优先级 cpu资源分配的先后顺序,就是指进程的优先权(priority)。优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上&#x…...
CNN简介与实现
CNN简介与实现 导语整体结构卷积层卷积填充步幅三维卷积立体化批处理 实现 池化层特点实现 CNN实现可视化总结参考文献 导语 CNN全称卷积神经网络,可谓声名远扬,被用于生活中的各个领域,也是最好理解的神经网络结构之一。 整体结构 相较于…...
【AI大模型】Transformers大模型库(五):AutoModel、Model Head及查看模型结构
目录 一、引言 二、自动模型类(AutoModel) 2.1 概述 2.2 Model Head(模型头) 2.3 代码示例 三、总结 一、引言 这里的Transformers指的是huggingface开发的大模型库,为huggingface上数以万计的预…...
Hadoop yixing(移行),新增表字段,删除表字段,修改存储格式
Hadoop yixing(移行),新增表字段,删除表字段,修改存储格式 一、hadoop中修改存储格式,比如从 textfile 转化为 orc 格式,表中的数据的组织形式要重新改变,就要将重新创建新格式的表将原来的数据按照新的格…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...
