【数据结构】顺序表详解

当我们写完通讯录后,顺序表肯定难不倒你,跟着小张一起来学习顺序表吧!


线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
在这里插入图片描述

在这里插入图片描述

顺序表

概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:

  1. 静态顺序表:使用定长数组存储元素。
    在这里插入图片描述
  2. 动态顺序表:使用动态开辟的数组存储。
    在这里插入图片描述

接口实现

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/68976.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EasyPOI处理excel、CSV导入导出

1 简介 使用POI在导出导出excel、导出csv、word时代码有点过于繁琐&#xff0c;好消息是近两年在开发市场上流行一种简化POI开发的类库&#xff1a;easyPOI。从名称上就能发现就是为了简化开发。 能干什么&#xff1f; Excel的快速导入导出,Excel模板导出,Word模板导出,可以…

k8s环境部署配置

目录 一.虚拟机准备 二.基础环境配置&#xff08;各个节点都做&#xff09; 1.IP和hosts解析 2.防火墙和selinux 3.安装基本软件 4.配置时间同步 5.禁用swap分区 6.修改内核参数并重载 7.配置ipvs 三.docker环境&#xff08;各个节点都做&#xff09; 1.配置软件源并…

【构造】CF Edu 12 D

Problem - D - Codeforces 题意&#xff1a; 思路&#xff1a; 这种题一定要从小数据入手&#xff0c;不然很有可能走歪思路 先考虑n 1的情况&#xff0c;直接输出即可 然后是n 2的情况&#xff0c;如果相加是质数&#xff0c;就输出2个&#xff0c;否则就输出一个 然后…

MATLAB 动态图GIF

MATLAB 动态图GIF 前言一、创建动态图&#xff08;动态曲线、动态曲面&#xff09;1. 创建动画曲线&#xff08;MATLAB animatedline函数&#xff09;2. 创建动画曲面 二. 保存动态图三、完整示例1. 动态曲线&#xff08; y s i n ( x ) ysin(x) ysin(x)&#xff09;2. 动态曲…

【JavaEE进阶】Spring事务和事务传播机制

文章目录 一. 什么是Spring事务二. Spring中事务的实现1. Spring编程式事务2. 声明式事务2.1 trycatch下事务不会自动回滚的解决方案2.2 Transactional 作用范围2.3 Transactional 参数说明2.4 Transactional 工作原理 三. 事务的隔离级别1. 事务的四大特性2. Spring中设置事务…

发生OOM时JVM会退出吗

程序是否退出和发生 OOM 无关 需要明确&#xff0c;程序是否退出和发生 OOM 无关&#xff0c;而和当前是否还有存活的非守护线程有关。 只要还有运行中的子线程&#xff0c;即使 main 线程结束或异常崩溃了&#xff0c;程序也不会停止。 public class TestThreadRun {privat…

sql:SQL优化知识点记录(十)

&#xff08;1&#xff09;慢查询日志 Group by的优化跟Order by趋同&#xff0c;只是多了一个having 开启慢查询日志&#xff1a; 演示一下慢sql&#xff1a;4秒之后才会出结果 查看一下&#xff1a;下方显示慢查询的sql &#xff08;2&#xff09;批量插入数据脚本 函数和存…

质量属性案例-架构真题(二十一)

试题一 某电子商务公司升级会员与促销管理系统&#xff0c;向用户提交个性化服务&#xff0c;提高用户粘性。在项目建立之初&#xff0c;公司领导人一致认为目标是提升会员管理方式的灵活性&#xff0c;由于当前用户规模不大&#xff0c;用户简单&#xff0c;系统方面不需要做…

聊聊如何玩转spring-boot-admin

前言 1、何为spring-boot-admin&#xff1f; Spring Boot Admin 是一个监控工具&#xff0c;旨在以良好且易于访问的方式可视化 Spring Boot Actuators 提供的信息 快速开始 如何搭建spring-boot-admin-server 1、在服务端项目的POM引入相应的GAV <dependency><grou…

图神经网络和分子表征:4. PAINN

如果说 SchNet 带来了【3D】的火种&#xff0c;DimeNet 燃起了【几何】的火苗&#xff0c;那么 PAINN 则以星火燎原之势跨入 【等变】时代。 在 上一节 中&#xff0c;我们提到&#xff0c; PAINN 在看到 DimeNet 取得的成就之后&#xff0c;从另一个角度解决了三体几何问题&a…

无涯教程-Android - 系统架构

Android操作系统是一堆软件组件&#xff0c;大致分为五个部分和四个主要层&#xff0c;如体系结构图中所示。 Linux内核 底层是Linux-Linux 3.6&#xff0c;带有大约115个补丁&#xff0c;这在设备硬件之间提供了一定程度的抽象&#xff0c;并且包含所有必需的硬件驱动程序&am…

RK3568开发笔记(六):开发板烧写ubuntu固件(支持mipi屏镜像+支持hdmi屏镜像)

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/132686096 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

Sentinel 流量控制框架

1. Sentinel 是什么&#xff1f; Sentinel是由阿里中间件团队开源的&#xff0c;面向分布式服务架构的轻量级高可用流量控制组件。 2. 主要优势和特性 轻量级&#xff0c;核心库无多余依赖&#xff0c;性能损耗小。 方便接入&#xff0c;开源生态广泛。 丰富的流量控制场景。 …

AI篇-chatgpt基本用法(文心一言也适用)

目录 &#xff08;1&#xff09;基本规则 &#xff08;2&#xff09;例子1-文章摘要 &#xff08;3&#xff09;例子2-代码生成 &#xff08;4&#xff09;文心一言链接 &#xff08;1&#xff09;基本规则 相比于搜索引擎&#xff0c;ChatGPT的优势在于其高效的想法关联和…

《CTFshow-Web入门》09. Web 81~90

Web 入门 索引web81题解 web82题解原理 web83题解 web84题解 web85题解 web86题解 web87题解原理 web88题解 web89题解 web90题解 ctf - web入门 索引 web81&#xff1a;include() 利用&#xff0c;一句话木马之 Nginx 日志利用。web82~86&#xff1a;include() 利用&#xff…

WebSocket--技术文档--基本概念--《快速了解WebSocket协议》

阿丹&#xff1a; 不断学习新技术&#xff0c;丰富自己了解更多才能扩展更多世界可能。 官网 WebSocket首页、文档和下载 - HTML5开发相关 - OSCHINA - 中文开源技术交流社区 软件简介 WebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。 WebS…

力扣|找出和所对应的两数的下标

从零开始刷力扣&#xff08;bushi 题目放在这&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出和为目标值target的两个整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一…

linux上vscode中.cpp文件中引入头文件.hpp时报错:找不到头文件(启用错误钵形曲线)

当在.cpp文件中引入系统给定的头文件时&#xff1a;#include < iostream > 或者引入自定义的头文件 &#xff1a;#include <success.hpp> 报错&#xff1a;找不到相应的头文件&#xff0c;即在引入头文件的改行底下标出红波浪线 解决方法为&#xff1a; &#…

linux入门---动静态库的加载

目录标题 为什么会有动态库和静态库静态库的实现动态库的实现动静态库的加载 为什么会有动态库和静态库 我们来模拟一个场景&#xff0c;首先创建两个头文件 根据文件名便可以得知add.h头文件中存放的是加法函数的声明&#xff0c;sub.h头文件中存放的是减法函数的声明&#…

文章预览 安防监控/视频存储/视频汇聚平台EasyCVR播放优化小tips

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.265自动转码H.264、平台级联等。为了便于用户二次开发、调用与集成&#xff0c;…