【数据结构初阶】--- 顺序表
顺序表,好像学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 格式,表中的数据的组织形式要重新改变,就要将重新创建新格式的表将原来的数据按照新的格…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...

【Java多线程从青铜到王者】单例设计模式(八)
wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本,sleep也是可以指定时间的,也就是说时间一到就会解除阻塞,继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒),wait能被notify提前唤醒…...