【数据结构】顺序表详解
当我们写完通讯录后,顺序表肯定难不倒你,跟着小张一起来学习顺序表吧!
线性表
线性表(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中的插件管理来实现逆向工程。希望对需要的小伙伴有帮助哈哈…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...