【数据结构】顺序表详解
当我们写完通讯录后,顺序表肯定难不倒你,跟着小张一起来学习顺序表吧!
线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表
概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
- 静态顺序表:使用定长数组存储元素。
- 动态顺序表:使用动态开辟的数组存储。
接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
typedef struct pro
{int* p;// 指向动态开辟的数组int size;// 有效数据个数int capcity;// 容量空间的大小}pro;
void SeqListInit(pro* ps );//初始化顺序表
void CheckCapacity(pro* ps);//判断是否空间不够,进行扩容
void SeqListPushBack(pro* ps,int x);//尾插
void SeqListPushFront(pro* ps, int x);//头插
void SeqListPopBack(pro* ps);//尾删
void SeqListPopFront(pro* ps);//头删
void SeqListPrint(pro* ps);//打印顺序表
void SeqListInsert(pro* ps, int pos, int x);//随意插
void SeqListErase(pro* ps, int pos);//随意删
void SeqListFind(pro* ps, int pos);//查找
void SeqListmodify(pro* ps, int x,int y);//修改
void SeqListDestory(pro* ps);//销毁
结构体定义和创建一个结构体变量
typedef struct pro
{int* p;// 指向动态开辟的数组int size;// 有效数据个数int capcity;// 容量空间的大小}pro;
int main()
{pro info;//定义一个结构体变量
}
初始化顺序表
void SeqListInit(pro* ps )//初始化顺序表
{ps->p = NULL;ps->size = 0;ps->capcity = 0;}
初始化顺序表,有效数据个数为0,容量空间大小为0,还未给数据动态开辟空间,指向动态开辟空间的指针指向空地址
扩容顺序表
void CheckCapacity(pro* ps)//判断是否空间不够,进行扩容
{if (ps->size == ps->capcity){int newcapcity = ps->capcity == 0 ? 4 : (ps->capcity) * 2;int* tmp = (int*)realloc(ps->p, sizeof(int) * newcapcity);if (tmp == NULL){perror("realloc fail!!");}ps->p = tmp;ps->capcity = newcapcity;}}
什么时候需要扩容顺序表呢??当顺序表刚被初始化,你要进行插入数据时,发现容量空间已经满了,此时必须要扩容空间,当你要插入第一个数据时,在此之前,顺序表没有任何数据,容量空间为0,然后要插入数据的话,也必须扩容。
条件判断如果有效数据个数等于容量大小时,分两种情况,第一种,刚开始的时候,有效数据个数和容量大小都为0的时候,第二种,当要插入数据时,发现此时的有效数据个数和容量大小相等时,且不等于0.对空间进行扩容, newcapcity变量是新的容量大小,当需要扩容的时候,直接新容量为原来的2倍,刚开始,他的容量是0,采用三目运算符,如果容量是0的话,就给四个空间大小,如果不是就开原来容量的2倍。将realloc来的空间的地址存放在tmp指针里面,如果realloc失败就返回空指针,打印错误信息,realloc成功的话就将tmp中存放扩容的地址交给指针p,然后容量大小更新为newcapcity。
尾插
void SeqListPushBack(pro* ps,int x)//尾插
{CheckCapacity(ps);ps->p[ps->size] = x;ps->size++;}
每次插入都要判断是否需要扩容
然后有效数据+1.
头插
void SeqListPushFront(pro* ps, int x)//头插
{CheckCapacity(ps);int end = ps->size - 1;while (end>=0){ps->p[end + 1] = ps->p[end];end--;}ps->p[0] = x;ps->size++;}
头插一个数据,必须将后面的数据向后面移动,移动的过程中可能超过容量大小,所以在插入时都需要进行扩容判断
如果按这个顺序移动数据当1挪到2的位置的时候,2这个数据就会被覆盖,所以我们必须从后往前面挪
当数据挪到后面之后,然后在第一个位置填入x,第一个位置也就是下标为0的位置。
在下标为0的地方填入 插入的数据x,然后ps->size+1;
尾删
void SeqListPopBack(pro* ps)//尾删
{assert(ps);assert(ps->size > 0);ps->size--;}
尾巴要删除一个数据的话,我们需要将删除的数据改为0吗?如果要删除的数据本来就是0呢?所以我们只需要将ps->size–;因为打印的时候只打印到下标为ps->size-1的位置,打印出来看起来就像 我们删除了这个数据,注意这里用断言是因为在删除的时候ps->size–,当ps->size<0的时候,在添加数据时
ps->p[-1]=x;这个是不合理的,在ps->size<0时,直接报错,第一个断言是为了防止空指针。
头删
void SeqListPopFront(pro* ps)//头删
{assert(ps->size > 0);int begin = 1;while (begin<ps->size){ps->p[begin - 1] = ps->p[begin];begin++;}ps->size--;}
这里的断言和上面是一个道理,然后相比尾删向后挪动数据,头删是往前挪数据,吸取尾删的教训,我们可以直到移动的顺序是
定义一个变量begin=1,首先是要将数据2移动到数据1的位置,对应的操作是
ps->p[begin - 1] = ps->p[begin];然后begin++,依次将数据3挪到数据2的位置,数据4挪到数据3的位置。循环最后一次是将数据5挪到数据4的位置,也就是begin=4,ps->size=5.则循环判断条件为beginsize,循环结束后将
ps->size–;
顺序表的打印
void SeqListPrint(pro* ps)//打印顺序表
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->p[i]);}}
循环遍历顺序表,将每个数据打印出来
随意插
void SeqListInsert(pro* ps, int pos, int x)//随意插
{CheckCapacity(ps);assert(pos>=0&&pos<=ps->size);int end = ps->size - 1;while (end>=pos){ps->p[end + 1] = ps->p[end];end--;}ps->p[pos] = x;ps->size++;
}
断言是为了检查插入的位置是否合理
当有5个数据时,pos的可能取值如图所示,推广到一般
就是pos>=0&&pos<=ps->size,如果我们想在pos=2的位置插入一个x,我们应该怎么做呢?
1.插入一个x,我们需要将3,4,5向后移动,必须先移动5,定义一个变量end,
end变量的初值给ps->size-1,也就是4,要想将数据5向后挪动,对应的操作是ps->p[end + 1] = ps->p[end];然后end–;循环分别将4,3向后挪动,循环结束后,将数据x插入到pos=2的位置,对应操作为ps->p[pos] = x;然后有效数据大小ps->size++;
随意删
void SeqListErase(pro* ps, int pos)//随意删
{int begin = pos;assert(pos >= 0 && pos <= ps->size);while (begin<ps->size-1){ps->p[begin] = ps->p[begin+1];begin++;}ps->size--;
}
断言判断删除的数据的位置是否合理,和随意插的那里一样
如果我们要删除数3,然后数据3后面的数据向前挪动,第一步就是将数据4移动到数据3的位置,定义一个变量begin=pos=2;对应的操作为
ps->p[begin] = ps->p[begin+1];,然后begin++;将数据5移动到最开始数据4的地方。最后一次循环是将数据5移动到数据4的地方,也就是begin最后等于3,ps->size=5,则循环判断条件是begin< ps->size-1,循环结束将ps->size–;
顺序表的查找
void SeqListFind(pro* ps, int pos)//查找
{assert(pos >= 0 && pos < ps->size);printf("你查找的下标是%d,对应的数据是%d", pos, ps->p[pos]);
}
断言保证查找位置的合理性,因为函数传参pos 刚好是要查找数据的下标,直接打印出来
顺序表的修改
void SeqListmodify(pro* ps, int x,int y)//修改
{for (int i = 0; i < ps->size; i++){if (x == ps->p[i]){ps->p[i] = y;}}}
x为修改前的值,y是修改之后的值,循环遍历顺序表,将顺序表中所有的x都修改成y
顺序表的销毁
void SeqListDestory(pro* ps)//销毁
{ps->capcity = 0;ps->size = 0;free(ps->p);ps->p = NULL;}
销毁一个顺序表,将顺序表的容量置为0,顺序表的有效数据个数置为0,将p指针所指向的动态开辟的内存空间释放了,由于释放了动态开辟的内存空间,所有p指向的空间未初始化,p成为野指针,为了防止野指针,将p置为空指针。
整体代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct pro
{int* p;// 指向动态开辟的数组int size;// 有效数据个数int capcity;// 容量空间的大小}pro;
void SeqListInit(pro* ps )//初始化顺序表
{ps->p = NULL;ps->size = 0;ps->capcity = 0;}
void CheckCapacity(pro* ps)//判断是否空间不够,进行扩容
{if (ps->size == ps->capcity){int newcapcity = ps->capcity == 0 ? 4 : (ps->capcity) * 2;int* tmp = (int*)realloc(ps->p, sizeof(int) * newcapcity);if (tmp == NULL){perror("realloc fail!!");}ps->p = tmp;ps->capcity = newcapcity;}}
void SeqListPushBack(pro* ps,int x)//尾插
{CheckCapacity(ps);ps->p[ps->size] = x;ps->size++;}
void SeqListPushFront(pro* ps, int x)//头插
{CheckCapacity(ps);int end = ps->size - 1;while (end>=0){ps->p[end + 1] = ps->p[end];end--;}ps->p[0] = x;ps->size++;}
void SeqListPopBack(pro* ps)//尾删
{assert(ps);assert(ps->size > 0);ps->size--;}
void SeqListPopFront(pro* ps)//头删
{assert(ps->size > 0);int begin = 1;while (begin<ps->size){ps->p[begin - 1] = ps->p[begin];begin++;}ps->size--;}
void SeqListPrint(pro* ps)//打印顺序表
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->p[i]);}}
void SeqListInsert(pro* ps, int pos, int x)//随意插
{CheckCapacity(ps);assert(pos>=0&&pos<=ps->size);int end = ps->size - 1;while (end>=pos){ps->p[end + 1] = ps->p[end];end--;}ps->p[pos] = x;ps->size++;
}
void SeqListErase(pro* ps, int pos)//随意删
{int begin = pos;assert(pos >= 0 && pos <= ps->size);while (begin<ps->size-1){ps->p[begin] = ps->p[begin+1];begin++;}ps->size--;}
void SeqListFind(pro* ps, int pos)//查找
{assert(pos >= 0 && pos < ps->size);printf("你查找的下标是%d,对应的数据是%d", pos, ps->p[pos]);
}
void SeqListmodify(pro* ps, int x,int y)//修改
{for (int i = 0; i < ps->size; i++){if (x == ps->p[i]){ps->p[i] = y;}}}
void SeqListDestory(pro* ps)//销毁
{ps->capcity = 0;ps->size = 0;free(ps->p);ps->p = NULL;}
int main()
{pro info;SeqListInit(&info);printf("尾插:");SeqListPushBack(&info, 1);SeqListPushBack(&info, 2);SeqListPushBack(&info, 3);SeqListPushBack(&info, 4);SeqListPrint(&info);printf("\n");printf("头插:");SeqListPushFront(&info, 7);SeqListPushFront(&info, 6);SeqListPushFront(&info, 5);SeqListPushFront(&info, 5);SeqListPrint(&info);printf("\n");printf("尾删:");SeqListPopBack(&info);SeqListPrint(&info);printf("\n");printf("头删:");SeqListPopFront(&info);SeqListPrint(&info);printf("\n");printf("随意插:");SeqListInsert(&info, 1, 1);SeqListPrint(&info);printf("\n");printf("随意删:");SeqListErase(&info,1,1);SeqListPrint(&info);printf("\n");printf("查找:");SeqListFind(&info, 3);printf("\n");printf("修改:");SeqListmodify(&info, 1, 2);SeqListPrint(&info);printf("\n");printf("销毁:");SeqListDestory(&info);SeqListPrint(&info);
}
相关文章:

【数据结构】顺序表详解
当我们写完通讯录后,顺序表肯定难不倒你,跟着小张一起来学习顺序表吧! 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表&#x…...

HTML 播放器效果
效果图 实现代码 <!DOCTYPE HTML> <html><head><title>爱看动漫社区 | 首页 </title><link href"css/bootstrap.css" relstylesheet typetext/css /><!-- jQuery --><script src"js/jquery-1.11.0.min.js"…...
C++常用23种设计模式总结(三)------装饰模式
往期回顾 C常用23种设计模式总结(一)------单例模式 C常用23种设计模式总结(二)------观察者模式 什么是装饰模式 装饰模式是一种结构型设计模式,它允许你在运行时为对象动态添加新的行为。该模式通过将对象放入包装器中来实现这一点,这个包装器会实现与…...

选择O型圈时要考虑哪些因素?
为您的应用选择正确的O型圈对于确保适当的密封和较佳性能至关重要。O型圈可用的材料和尺寸多种多样,做出正确的选择可能需要知道一些重要的知识点。在本文中,我们将讨论选择O型圈时需要考虑的一些关键因素。 1、材料兼容性:先要考虑的因素是…...
安全管理中心技术测评要求项
1.系统管理-通过系统管理员进行系统管理操作 1-0/2-2/3-2/4-2 a)对系统管理员进行身份鉴别,只允许其通过特定的命令或操作界面进行系统管理操作,并对这些操作进行审计 b)通过系统管理员对系统的资源和运行进行配置、控制和管理&am…...

Hibernate(Spring Data)抓取策略
文章目录 示例代码放到最后,使用的是Springboot 项目1. 简介2. Hibernate抓取策略分类2.1 即时加载(Eager Loading)2.2 延迟加载(Lazy Loading)2.3 子查询加载(Subselect Loading)2.4 基于批处理…...

【高阶数据结构】map和set的介绍和使用 {关联式容器;键值对;map和set;multimap和multiset;OJ练习}
map和set的介绍和使用 一、关联式容器 关联式容器和序列式容器是C STL中的两种不同类型的容器。 关联式容器是基于键值对的容器,其中每个元素都有一个唯一的键值,可以通过键值来访问元素。关联式容器包括set、multiset、map和multimap。 序列式容器是…...

系统架构技能之设计模式-单件模式
一、开篇 其实我本来不是打算把系统架构中的一些设计模式单独抽出来讲解的,因为很多的好朋友也比较关注这方面的内容,所以我想通过我理解及平时项目中应用到的一 些常见的设计模式,拿出来给大家做个简单讲解,我这里只是抛砖引玉,…...

Redis进阶 - JVM进程缓存
原文首更地址,阅读效果更佳! Redis进阶 - JVM进程缓存 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-advance-jvm-process-cache.html 传统缓存的问题 传统的缓存策略一般是请求到达 Tomcat 后,先查询 Redis &…...
SD-WAN带您告别高成本、单一功能和安全性差
现今,随着企业规模不断扩大和分散办公越来越普遍,企业对于网络的需求也变得越来越高。然而,传统的组网方式面临着很多的问题,比如:成本高、功能单一、安全性差等问题。 传统组网方式有哪些? 传统的组网方式…...

面试必备:揭秘ArrayList和LinkedList,区别、优缺点与使用场景
大家好,我是你们的小米!今天我要跟大家聊一个在面试中经常被问到的热门话题——ArrayList和LinkedList的区别、优缺点以及它们的使用场景。作为程序员,掌握这些知识点不仅可以在面试中脱颖而出,还能帮助我们更好地在项目中选择合适…...

【局部活动轮廓】使用水平集方法实现局部活动轮廓方法研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Git 同步远程新的同名分支
背景 因为远程分支的提交记录过多,导致本地的commit内容过大,会产生一些问题: 第一次拉取时间较长占用本地和远程的存储 原因 因为项目已有一些年头,若是每次文件提交比较大,那么占用空间就更大 解决方案 该方案…...
PingCode DevOps 团队:企业CICD流水线可能会遇到的问题及解法
CICD 流水线是指一系列自动化的构建、测试和部署步骤,用于将应用程序从开发到生产环境的过程。在 CICD 流水线中,每个步骤都是自动化的,并且在完成后会触发下一个步骤的执行。 CICD 的价值 CICD 流水线可以帮助团队更快地交付产品ÿ…...

【LeetCode题目详解】第九章 动态规划part01 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯 (day38补)
本文章代码以c为例! 一、力扣第509题:斐波那契数 题目: 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:…...

图像处理 信号处理板 设计原理图:367-基于zynq XC7Z100 FMC接口通用计算平台
基于zynq XC7Z100 FMC接口通用计算平台 一、板卡概述 板卡由SoC XC7Z100-2FFG900I芯片来完成卡主控及数字信号处理,XC7Z100内部集成了两个ARM Cortex-A9核和一个kintex 7的FPGA,通过PL端FPGA扩展FMC、光纤、IO等接口,PS端ARM扩展网络、USB、R…...
PHP中header()的七种用法
我们在实际开发中经常使用header()实现一些功能,这篇文章介绍关于header()的7中用法,需要的伙伴的开参考一下。 PHP header()的7中用法: 1、跳转页面 可以使用header()实现跳转页面功能。 header(Location:.$url); // $url 跳转页面的地址…...

臻图信息以数字孪生技术推动智慧小区数字化建设
伴随着智慧城市建设进程的加速发展,加速传统小区的管理与服务向智能化升级转型。运用智慧化的管理和服务,利用信息技术和物联网等技术手段,将传统的居住区域与智能设备相结合,实现楼宇、社区设施、服务管理的数字化、网络化、智能…...

15.CSS发光按钮的悬停特效
效果 源码 <!DOCTYPE html> <html> <head><title>CSS Modern Button</title><link rel="stylesheet" type="text/css" href="style.css"> </head> <body><a href="#" style=&quo…...
MyBatis —— 动态SQL和缓存
前言 在上一篇文章中荔枝梳理了一些特殊的SQL查询和一对多、多对一的映射关系,而在这篇文章中荔枝将会梳理有关MyBatis动态SQL和MyBatis缓存的相关知识,同时也稍微了解了有关MyBatis中借助MAVEN中的插件管理来实现逆向工程。希望对需要的小伙伴有帮助哈哈…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...

Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...

认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...