【数据结构】带你轻松拿捏顺序表(内附源码)

news2024/11/23 22:03:19

在这里插入图片描述

君兮_的个人主页

勤时当勉励 岁月不待人

C/C++ 游戏开发


Hello,米娜桑们,这里是君兮_,今天正式开始开新坑啦!在接下来的这一个月来我会逐步带大家了解初阶数据结构的知识,如果是你主修的是计算机专业数据结构的重要性不言而喻,哪怕在以后你工作面试时,它也是面试官们最喜欢考察的内容之一,如果你想对这部分内容有一个系统又深刻的认识,那么不要错过这个专栏的内容哦!!我会努力用通俗易懂的话来带大家入门的!
好了,废话不多说,开始今天的学习吧!

顺序表

  • 前言
  • 一.线性表
    • 1.线性表的概念
  • 二.顺序表
    • 2.1 概念及结构
    • 2.2 顺序表的分类
    • 2.3 动态顺序表的实现
      • 顺序表的初始化
      • 顺序表的扩容
      • 顺序表的销毁
      • 顺序表的打印
      • 顺序表的尾插与尾删
      • 顺序表的头插与头删
      • 在顺序表中任意位置的删除与插入
      • 顺序表中数据的查找与修改
    • 2.4 测试一下我们的动态顺序表
    • 2.5顺序表的优缺点
  • 总结

前言

ps:顺序表中难以理解的点应该就是malloc以及realloc的使用了,这是有关C语言的动态内存管理的知识,我们主要目的是介绍顺序表的因此不做缀叙,如果你感兴趣之后我也会更新有关博客并且把链接贴在这里哒!

一.线性表

我们知道,顺序表是最基础的线性表,那么什么是线性表呢?

1.线性表的概念

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

二.顺序表

2.1 概念及结构

  • 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
  • 注意:这里我们要知道的是,顺序表存放的数据是连续的并且在内存中是真正依次存放的,这一点是与链表最大的区别!

2.2 顺序表的分类

  • 顺序表一般有两种形式
  • 1. 静态顺序表:使用定长数组存储元素。
// 静态顺序表
#define N 1000
typedef int SLDataType;

struct SeqList
{
	SLDataType a[N];
	int size;
};
  • 静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用第二种顺序表——动态顺序表,根据需要动态的分配空间大小。
  • 2.动态顺序表
// 动态顺序表
typedef int SLDataType;//重命名数据类型,方便以后直接修改

typedef struct SeqList
{
	SLDataType* a;//指向动态开辟的数组
	int size;        // 存储有效数据个数
	int capacity;    // 容量空间大小
}SL;
  • 顾名思义,动态顺序表使用动态开辟的数组储存,这样更方便我们实时根据数据的多少调整数组的大小。。
  • 以下是上面这段代码要提醒的几处要点:
  • 1.我们在上面这段代码中使用了两个typedef,但是用处却不相同
    • 第一处的typedef实际是为了方便我们的使用,因为我们也不知道我们的顺序表是用来存储什么类型的数据的,因此我们这里就定义一个SLDataType,下面的代码中统一把数据类型用它来代替,这样一来,我们以后想要改变存储的数据类型,只需要改动这里即可,比如我们现在想要存储double类型的数据
typedef double SLDataType;
    • 第二处的typedef是将这个结构体重命名,方便我们接下来的使用,接下来的结构体直接写出‘SL’就行。

接下来我们来具体实现一下动态顺序表的各种功能以及使用方法


2.3 动态顺序表的实现

顺序表的初始化

  • 我们确定了顺序表内的基本内容,我们得把顺序表先初始化一下,不然连空间都还没开辟咋使用呢?
//初始化顺序表
void SLInit(SL* ps)
{
    ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);//申请四个字节的空间,如果不够再进行增容
    if (ps->a == NULL)
    {
        perror("malloc failed");
        exit (-1);
    }
    ps->size = 0;
    ps->capacity = 4;
}
  • 我们通过malloc为我们的顺序表申请了四个字节的空间,同时此时我们还未填入数据,因此size初始化为0,容量空间大小为4.
  • 但是我们只是申请了空间,还不知道是否成功,因此我们又通过分辨动态数组是否为NULL并通过perror报错来判断我们开辟内存的操作是否成功。

顺序表的扩容

  • 我们初始化了一个顺序表,但是我们并不知道我们申请的空间是否够用,如果不够用,我们就得扩容一下。因此我们这里的逻辑应该是先判断容量是否够用,够用就不用做多余的操作,如果不够用,就申请扩容
//检查空间,如果顺序表满了,就进行增容操作
void SLCheckCapacity(SL* ps)
{
    assert(ps);//断言防止传入ps为空
    //判断一下是否空间满了,如果满了就把容量增加到原来的2倍
    if (ps->size == ps->capacity)//有效数据是否占满所有空间
    {
        SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * 2 * (sizeof(SLDataType)));//利用realloc扩容
        if (tmp == NULL)
        {
            perror("realloc failed");
            exit(-1);
        }
        ps->a = tmp;
        ps->capacity *= 2;
    }
}
  • 我们这里通过realloc将空间扩容到原来的2倍,当我们通过perror判断扩容成功后将扩容赋给我们的动态数组,由于扩容到原来的两倍,这里的容量空间大小也要变为原来的2倍。

顺序表的销毁

  • 当我们使用完顺序表后,由于我们是用malloc开辟的内存,因此必须把它给销毁并且释放掉
void SLDestroy(SL* ps)
{
    free(ps->a);//释放malloc申请的空间并且将a指向NULL
    ps->a = NULL;
    ps->capacity = ps->size = 0;

}
  • 在销毁并释放内存的同时,我们也需要把size和capacity初始化一下。

顺序表的打印

  • 当我们把数据插入或者删除后,我们就需要打印一下顺序表来看看我们的操作是否执行成功
void SLPrint(SL* ps)
{
    int i =0;
    for (i = 0; i < ps->size; i++)
        printf("%d ", ps->a[i]);//把有效数据通过循环都打印一下
    printf("\n");
}
  • 如果说之前的都是前菜,我们接下来进入今天的正餐,也是顺序表中最难理解的一部分,就是咱们所谓的增删查改

顺序表的尾插与尾删

  • 什么叫尾插和尾删呢?
  • **所谓尾插就是从顺序表的末尾插入数据,而尾删就是把最后一个数据给删除**
//顺序表尾插
void SLPushBack(SL* ps, SLDataType x)
{
    assert(ps);
    SLCheckCapacity(ps);//先判断以下顺序表是否已满,未满才能插入数据
    ps->a[ps->size] = x;//把数组a中size位置的数据赋值为x,size位置即最后一位
    ps->size++;//插入数据成功,有效数据+1
}
  • 其中,尾删时,我们需要检查是否越界,比如你就俩数据,你尾删了10次,那么此时你的有效数据就成负的了,这时你是不是就越界了?
  • 检查是否越界有两种方法我愿称之为温柔检查法和暴力检查法,这又是怎么个回事呢?
//顺序表尾删
void SLPopBack(SL* ps)
{

    assert(ps);
    温柔的检查
    //if (ps->size == 0)
    //    return;
    //暴力检查
    assert(ps->size > 0);
    ps->a[ps->size - 1] = 0;//把最后一个数据抹掉
    ps->size--;//有效数据-1

}
  • 啥叫温柔检查呢?
  • 当我们size已经删没的时候(size == 0),说明有效数据都已经删完了,这时我们就直接return即可。此时程序既不会往下走导致越界,也不会报错。
  • 而暴力检查法就如你的严父一样
  • 我们直接通过assert断言,,如果它发现你越界了,就会直接掏出七匹狼(报错),并且告诫你错在哪里,如下所示
    在这里插入图片描述
    。。。放错了,如下所示:
    在这里插入图片描述

顺序表的头插与头删

  • 与尾插和尾删类似,顺序表的头插与头删就是在开头进行插入与删除数据操作
/顺序表的头插
void SLPushFront(SL* ps, SLDataType x)
{
    assert(ps);
    SLCheckCapacity(ps);//判断容量是否已满
    //挪动数据
    int End = ps->size - 1;
    while (End >= 0)
    {
        ps->a[End + 1] = ps->a[End];
        End--;
    }
    ps->a[0] = x;
    ps->size++;

}
  • 在头插的过程中,我们需要注意的是,在头部插入一个数据,我们就需要把顺序表中的数据都朝后挪动一位给头部插入让位。
//顺序表的头删
void SLPopFront(SL* ps)
{
    assert(ps);
    int begin = 0;
    while (begin < ps->size-1)
    {
        ps->a[begin] = ps->a[begin + 1];
        begin++;
    }
    ps->size--;
}
  • 在头删时,与头插类似,我们需要把所有数据都往前挪动一位来弥补头删的空缺,最后再把有效数据减少一位即可。

在顺序表中任意位置的删除与插入

//在顺序表中pos位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
    assert(ps);
    SLCheckCapacity(ps);//检查容量空间大小
    assert(pos >= 0 && pos <= ps->size);//判断pos的合法化,避免插入到非法坐标中
    int end = ps->size - 1;
    while (end >= pos)//把pos位置后的数据都朝后挪动一位,给插入的数据腾位置
    {
        ps->a[end + 1] = ps->a[end];
        end--;
    }
    ps->a[pos] = x;//插入数据
    ps->size++;//有效数字+1

}
  • 这里与头插类似,只不过我们需要挪动的数据变成了pos位置后的数据,它们都需要朝后挪动一位
//在顺序表中删除pos位置的值
void SLErase(SL* ps, int pos)
{
    assert(ps);
    assert(pos >= 0 && pos <= ps->size);
    int begin = pos;
    while (begin < ps->size-1)
    {
        ps->a[begin] = ps->a[begin + 1];//pos后的数据都朝前挪动一位
        begin++;
    }
    ps->size--;//有效数据-1
}
  • 与头删类似,pos后面的数据都要朝前挪动一位来填补删除pos位置上数据的空缺

顺序表中数据的查找与修改

  • 顺序表中数据的查找
//查找顺序表中是否有某个数,如果有就返回该数的下标

int SLFind(SL* ps, SLDataType x)
{
    assert(ps);

    for (int i = 0; i < ps->size; i++)
    {
        if (ps->a[i] == x)
        {
            return i;
        }
    }

    return -1;
}
  • 通过遍历数组的形式来查找是否存在某个数据,找到就返回该数据所在的位置,没找到就返回-1。
  • 顺序表中的修改
  • 要想修改,我们需要知道要修改哪个位置的数据以及我们要把该位置上的数据修改成多少
void SLModify(SL* ps, int pos, SLDataType x)
{
    assert(ps);

    assert(pos >= 0 && pos < ps->size);//判断要修改数据的坐标是否合法

    ps->a[pos] = x;//修改数据
}


2.4 测试一下我们的动态顺序表

  • 好了,我们顺序表的基本功能都已经通过代码实现了,我们现在来挑选几个重要的功能测试一下
int main()
{
    SL s1;
    SLInit(&s1);
    SLPushBack(&s1, 1);
    SLInsert(&s1, 1, 50);
    SLPushFront(&s1, 20);
    SLPrint(&s1);
  
    int ret=SLFind(&s1, 50);
    if(ret!=-1)
    printf("找到了,下标是%d\n", ret);
    SLErase(&s1, 0);
    SLPopBack(&s1);
   
    SLPrint(&s1);
    SLModify(&s1, 0, 666);
    SLPrint(&s1);

    
    return 0;
}

在这里插入图片描述

  • 仔细对比一下我们要实现的功能,发现雀氏没有问题

2.5顺序表的优缺点

  • 存在的问题:
  • 1. 中间/头部的插入删除,时间复杂度为O(N)
  • 2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。(尤其是异地扩容)
  • 3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。
    例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间
  • 优点
  • 1.尾删和尾插足够快
  • 2.能通过下标实现随机访问和修改(这点是非常重要的)

总结

  • 今天的内容到这里就结束了,我们带大家具体的实现了顺序表的编写。由于篇幅原因,在顺序表编写中存在的易错点以及各种细节的知识我们放到下一篇再讲,同时也会讲几个相关的面试题目加深大家对这些易错点的理解。
  • ps:其实是熬夜写博客有点太晚了,博主有点顶不住了,看在博主这么努力的份上真的不留下你的三连吗?!!
  • 好了,如果你有任何疑问欢迎在评论区或者私信我提出,大家下次再见啦!

新人博主创作不易,如果感觉文章内容对你有所帮助的话不妨三连一下这个新人博主再走呗。你们的支持就是我更新的动力!!!

**(可莉请求你们三连支持一下博主!!!点击下方评论点赞收藏帮帮可莉吧)**

在这里插入图片描述

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

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

相关文章

深度学习论文: Q-YOLO: Efficient Inference for Real-time Object Detection及其PyTorch实现

深度学习论文: Q-YOLO: Efficient Inference for Real-time Object Detection及其PyTorch实现 Q-YOLO: Efficient Inference for Real-time Object Detection PDF: https://arxiv.org/pdf/2307.04816.pdf PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTorch代…

LeetCode 918. Maximum Sum Circular Subarray【数组,动态规划】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

215.数组中的第K个最大元素-C++

题目来源&#xff1a;力扣 题目描述&#xff1a; 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法…

Java书签 #解锁MyBatis的4种批量插入方式及ID返回姿势

1. 今日书签 项目开发中&#xff0c;我们经常会用到单条插入和批量插入。但是实际情况可能是&#xff0c;项目初期由于种种原因&#xff0c;在业务各处直接使用单条插入SQL进行开发&#xff08;未开启批处理&#xff09;&#xff0c;在后面的迭代中&#xff0c;系统性能问题渐…

Spring Boot 集成 Redis 三种模式实践汇总

背景 项目的某个模块集成了 SpringBoot Redis 包&#xff0c;客户端使用 Lettuce&#xff0c;Redis 测试环境单机模式。但是现场反馈的 Redis 环境是集群&#xff0c;如果简单的修改 spring.redis 配置为集群的配置信息&#xff0c;程序能否能无缝衔接呢&#xff1f; 本文记录…

Android Studio 的版本控制Git

Android Studio 的版本控制Git。 Git 是最流行的版本控制工具&#xff0c;本文介绍其在安卓开发环境Android Studio下的使用。 本文参考链接是&#xff1a;https://learntodroid.com/how-to-use-git-and-github-in-android-studio/ 一&#xff1a;Android Studio 中设置Git …

智安网络|实现数据安全:探索数据动态脱敏的落地策略

在当今数字化时代&#xff0c;数据安全成为企业和组织管理中的头等大事。然而&#xff0c;数据共享和数据大规模处理的需求也日益增长&#xff0c;这就需要在数据传输和存储过程中采取措施来保护用户的隐私。数据动态脱敏技术应运而生&#xff0c;为解决数据隐私和保护的问题提…

【雕爷学编程】Arduino动手做(93)--- 0.96寸OLED液晶屏模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

Doris(二) -通过外部表同步数据

前言 参考网址 1.官网 2.ODBC External Table Of Doris 3.Apache doris ODBC外表使用方式 第一步 创建 RESOURCE DROP RESOURCE IF EXISTS mysql_test_odbc; CREATE EXTERNAL RESOURCE mysql_test_odbc PROPERTIES ( "type" "odbc_catalog", "…

《JeecgBoot系列》 表单实现指定列导出Excel“合并单元格“

表单"合计"列导出Excel合并单元格 注解&#xff1a; Excel 参数&#xff1a;mergeVertical&#xff0c;参数设置为Boolean类型&#xff0c;默认为false。当设置为true时&#xff0c;可以纵向合并内容相同的单元格 1.需求 介绍&#xff1a;每个类别下有多个型号&…

linux免交互

免交互 指不需要人为控制就可以完成的自动化操作。 shell脚本和免交互是一个概念&#xff0c;但是有两种写法。shell脚本基于bash。 here Document 免交互 它是一种标准输入&#xff0c;只能接收正确的命令&#xff0c;它主要是使用i/o重定向的方式将命令的列表提供给交互式…

BFS()

目录 多源BFS 矩阵距离 最小步数模型 魔板 八数码 双端队列广搜 电路维修 双向广搜 字串变换 A* 第K短路 多源BFS 单源BFS是求一个点到起点的最短距离 多源BFS是求有很多个起点&#xff0c;某一点到离它最近一个起点的距离 矩阵距离 给定一个 N 行M 列的 01矩阵…

【每日一题】—— C. Tiles Comeback (Codeforces Round 888 (Div. 3))

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

FTP客户端登录报错:Login failed

FTP客户端登录报错&#xff1a;Login failed 是selinux的问题&#xff0c;一般用户无法登录用户主目录 [rootchenshuyi ~]# setsebool -P tftp_home_dir 1

Filebeat学习笔记

Filebeat基本概念 简介 Filebeat是一种轻量级日志采集器&#xff0c;内置有多种模块&#xff08;auditd、Apache、Nginx、System、MySQL等&#xff09;&#xff0c;针对常见格式的日志大大简化收集、解析和可视化过程&#xff0c;只需一条命令即可。之所以能实现这一点&#…

【Go语言】Golang保姆级入门教程 Go初学者介绍chapter1

Golang 开山篇 Golang的学习方向 区块链研发工程师&#xff1a; 去中心化 虚拟货币 金融 Go服务器端、游戏软件工程师 &#xff1a; C C 处理日志 数据打包 文件系统 数据处理 很厉害 处理大并发 Golang分布式、云计算软件工程师&#xff1a;盛大云 cdn 京东 消息推送 分布式文…

集睿致远推出CS5466多功能拓展坞方案:支持DP1.4、HDMI2.1视频8K输出

ASL新推出的 CS5466是一款Type-C/DP1.4转HDMI2.1的显示协议转换芯片,&#xff0c;它通过类型C/显示端口链路接收视频和音 频流&#xff0c;并转换为支持TMDS或FRL输出信令。DP接收器支持81.Gbp s链路速率。HDMI输出端口可以作为TMDS或FRL发射机工作。FRL发射机符合HDMI 2.1规范…

08综合评价作业

某核心企业需要在6个待选的零部件供应商中选择一个合作伙伴&#xff0c;各待选供应商有关数据如表1所列&#xff0c;试从中选择一个最优供应商(理想解法) 评价指标产品质量产品价格/元地理位置/km售后服务/h技术水平经济效益供应能力/件市场影响度交货情况10.83326213.20.20.1…

Emacs之实现鼠标/键盘选中即拷贝外界内容(一百二十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

linux操作历史history定制

history记录 Linux中历史操作记录history是一个很有用的功能&#xff0c;有时忘记了&#xff0c;翻翻以前的命令&#xff0c;十分方便。 # 展示所有历史记录 history # 筛选历史记录 history | grep nginx # 清除全部记录 -c history -c # 指定删除某一行,15是行号 history -…