【数据结构】顺序表的深度刨剖析

前言:

在上一篇文章中,我们已经对数据结构有了一定了解,我们可以通过优化空间复杂度或者时间复杂度从而提高我们程序运行或存储速率。至此我们就知道了数据结构的重要性,所以今天我们将要了解和学习一种实用的数据结构——线性表

一、线性表概述:

线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列,是最基本、最简单也最常用的一种数据结构。

线性表中的数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其他数据元素都是首尾相连的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是线性表,但存储层次上属于链式存储,是把最后一个数据元素的尾指针指向了首位节点)

线性表在逻辑上是线性结构,但是在物理结构并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。 常见的线性表:顺序表、链表、栈、队列、字符串等等

二、顺序表:

了解完线性表后,就要进入我们今天的主题,我们今天最主要学的的是线性表中的顺序表。

  1. 概念和结构:

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,并在数组上完成数据的增删查改。其本质就是数组,但与数组不同的是:顺序表在其数组本质的基础上,还要求数据连续存储,不能跳跃或间隔存储。

顺序表一般可分为两类:

静态顺序表:使用一定长度的数组存储元素。但是静态顺序表存在很明显的缺陷,即大小固定,数据少了就会造成空间浪费,数据多了空间不够导致不能存储。

#define N 10
typedef int SLDataType;
typedef struct SeqList
{
    SLDataType arr[N];
    int size;
}SeqList;

动态顺序表:使用动态开辟的数组存储元素。动态顺序表很好弥补了静态顺序表的弊端,使空间申请变得灵活,我们可根据情况进行扩容操作从而申请到合适大小的空间。

typedef int SLDataType;
typedef struct SeqList
{
    SLDataType* a;
    int size;
    inte capacity;
}SeqList;

2、顺序表的实现:

现在开始,我们开始实现顺序:

①初始化顺序表:

void SeqListInit(SeqList* ps)
{
    assert(ps);//防止传入空指针;

    ps->a = malloc(sizeof(SLDateType) * INIT_CAPACITY);

    if (ps->a == NULL)
    {
        perror("malloc fail");
    }

    ps->size = 0;
    ps->capacity = INIT_CAPACITY;

}

②销毁顺序表:

void SeqListDestroy(SeqList* ps)
{
    assert(ps);//防止传入空指针;
    free(ps->a);
    ps->a = NULL;
    ps->capacity = ps->size=0;
}

③顺序表尾插:

尾插即在最后一个元素下一个位置上插入元素,也就是size的位置上,插入之后,总数据+1也就是size++。

void SeqListPushBack(SeqList* ps, SLDateType x)
{
    assert(ps);
    //检查是否需要扩容
    if (ps->size == ps->capacity)
    {
        SLDateType* tmp = (SLDateType)realloc(ps->a, sizeof(SLDateType) * ps->capacity + 5);
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        ps->a = tmp;
        ps->capacity += 5;
        
    }
    
    ps->a[ps->size++] = x;
}

④顺序表尾删:

尾删就是把最后一个元素删除,所以size不能为0,删除之后总数据减一,size--。

void SeqListPopBack(SeqList* ps)
{
    assert(ps);
    assert(ps->size > 0);
    ps->size--;
    
}

⑤顺序表头插:

头插也就是在第一个元素前面插入第一个元素,因为我们使用数组实现的顺序表所以头插需要挪动元素。

插入之后,总数据+1也就是size++。

void SeqListPushFront(SeqList* ps, SLDateType x)
{
    assert(ps);
    if (ps->size == ps->capacity)
    {
        SLDateType* tmp = (SLDateType)realloc(ps->a, sizeof(SLDateType) * ps->capacity + 5);
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        ps->a = tmp;
        ps->capacity += 5;

    }

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

}

⑥顺序表的头删除:

与头插类似,同样需要挪动元素,只不过挪动方向变了,头插是往后挪,头删是往前挪,删除之后总数据减一,size--。

void SeqListPopFront(SeqList* ps)
{
    assert(ps);
    int count = 1;
    for (count = 1; count <= ps->size; count++)
    {
        ps->a[count - 1] = ps->a[count];
    }
    ps->size--;
}

⑦打印顺序表:

void SeqListPrint(SeqList* ps)
{
    if (ps->size == 0)
    {
        printf("顺序表为空");
        return;
    }
    for (int i = 0; i < ps->size; i++)
    {
        printf("%d ", ps->a[i]);
    }
    printf("\n");
}

⑧顺序表中查找:

int SeqListFind(SeqList* ps, SLDateType x)
{
    assert(ps);
    for (int i = 0; i < ps->size; i++)
    {
        if (ps->a[i] == x)
        {
            return i;
        }
        return -1;
    }
}

⑨在指定下标插入:

插入之后,总数据+1也就是size++。

void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
    assert(ps);
    assert(pos >= 0 && pos <= ps->size);

    if (ps->size == ps->capacity)
    {
        SLDateType* tmp = (SLDateType)realloc(ps->a, sizeof(SLDateType) * ps->capacity + 5);
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        ps->a = tmp;
        ps->capacity += 5;

    }

    for (int i = ps->size; i > pos;i--)
    {
        ps->a[i] = ps->a[i - 1];
    }
    ps->a[pos] = x;
    ps->size++;
}

⑩删除指定下标位置元素:

删除之后总数据减一,size--。

void SeqListErase(SeqList* ps, int pos)
{
    assert(ps);
    assert(pos >= 0 && pos <= ps->size);
    
    
    for (int i = pos; i < ps->size-1; i++)
    {
        ps->a[i] = ps->a[i + 1];
    }
    ps->size--;

}

总结:

顺序表相对来说比较简单,很重要一点就是在执行操作时,要思考是否要进行断言操作,避免使用空指针。文章到此就结束啦。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

批量保存网页为单个网页文件

有时候&#xff0c;总有会遇到一些奇怪的需求&#xff0c;各种搜索都找不到答案&#xff0c;本次记录批量保存网页到单个网页文件。使用背景&#xff1a;只想简单的解决问题&#xff0c;不涉及编程网页带格式,将网页存为PDF格式会变量太大&#xff0c;一个个的处理太累涉及技术…

【差分数组】

差分数组一维差分差分数组的作用差分矩阵结语一维差分 输入一个长度为 n 的整数序列。接下来输入 m个操作&#xff0c;每个操作包含三个整数 l,r,c&#xff0c;表示将序列中 [l,r] 之间的每个数加上 c &#xff0c;请你输出进行完所有操作后的序列。 输入格式 第一行包含两个…

Spring容器实现原理-Spring的结构组成与核心类

Spring容器基本用法 bean是Spring中最核心的东西&#xff0c;因为Spring就像是个大水桶&#xff0c;而bean就像是容器中的水&#xff0c;水桶脱离了水便没有什么用处了&#xff0c;让我们先看看bean的定义&#xff1a; /*** ClassName MyTestBean* Author jiaxinxiao* Date 2…

基于51单片机的室内湿度加湿温度声光报警智能自动控制装置设计

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;单片机湿度 获取完整无水印论文报告&#xff08;内含电路原理图和源程序代码&#xff09; 在日常生活中加湿器得到了广泛的应用&#xff0c;但是现有的加湿器都需要手工控制开启和关闭并且不具备对室内空气温湿度的监测&am…

【微信小程序】-- 页面导航 -- 编程式导航(二十三)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

chartgpt 告诉我的,loss 函数的各种知识

一、libtorch中常见的损失函数及其使用场景的总结1. CrossEntropyLoss:CrossEntropyLoss&#xff08;交叉熵损失&#xff09;主要用于分类任务。它适用于多分类问题&#xff0c;其中每个样本只属于一个类别&#xff08;互斥&#xff09;。该损失函数将预测概率与真实标签的one-…

Android DataBinding 自定义View实现数据双向绑定

看不懂的可以先看看单向数据绑定&#xff1a;Android DataBinding数据变化时自动更新界面_皮皮高的博客-CSDN博客 然后再确定已经启动了dataBinding的情况下&#xff0c;按下面的顺序来&#xff1a; 首先创建一个自定义View&#xff1a; import android.content.Context imp…

中国蚁剑AntSword实战

中国蚁剑AntSword实战1.基本使用方法2.绕过安全狗连接3.请求包修改UA特征伪造RSA流量加密4.插件使用1.基本使用方法 打开蚂蚁宝剑&#xff0c;右键添加数据&#xff1a; 输入已经上传马的路径和连接密码&#xff1a; 测试连接&#xff0c;连接成功&#xff01; GetShell了&…

【Linux】Linux下权限的理解

前言&#xff1a;在之前我们已经对基本的指令进行了深入的学习&#xff0c;接下来我将带领大家学习的是关于权限的相关问题。在之前&#xff0c;我们一直是使用的【root】用户&#xff0c;即为“超级用户”&#xff0c;通过对权限的学习之后&#xff0c;我们就会慢慢的切换到普…

大公司为什么禁止SpringBoot项目用Tomcat?

前言 在SpringBoot框架中&#xff0c;我们使用最多的是Tomcat&#xff0c;这是SpringBoot默认的容器技术&#xff0c;而且是内嵌式的Tomcat。同时&#xff0c;SpringBoot也支持Undertow容器&#xff0c;我们可以很方便的用Undertow替换Tomcat&#xff0c;而Undertow的性能和内…

动态规划---线性dp和区间dp

动态规划(三) 目录动态规划(三)一&#xff1a;线性DP1.数字三角形1.1数字三角形题目1.2代码思路1.3代码实现(正序and倒序)2.最长上升子序列2.1最长上升子序列题目2.2代码思路2.3代码实现3.最长公共子序列3.1最长公共子序列题目3.2代码思路3.3代码实现4.石子合并4.1题目如下4.2代…

STM32外设-定时器详解

0. 概述 本文针对STM32F1系列&#xff0c;主要讲解了其中的8个定时器的原理和功能 1. 定时器分类 STM32F1 系列中&#xff0c;除了互联型的产品&#xff0c;共有 8 个定时器&#xff0c;分为基本定时器&#xff0c;通用定时器和高级定时器基本定时器 TIM6 和 TIM7 是一个 16 位…

【springcloud 微服务】Spring Cloud Alibaba Nacos使用详解

目录 一、前言 二、nacos介绍 2.1 什么是 Nacos 2.2 nacos 核心能力 2.2.1 服务发现和服务健康监测 2.2.2 动态配置服务 2.2.3 动态 DNS 服务 2.2.4 服务及其元数据管理 2.2.5 nacos生态地图 2.3 与其他配置中心对比 三、nacos快速部署 3.1 获取安装包 3.2 修改脚…

如何成为优秀的程序员

崔宝秋&#xff0c;现任小米首席架构师、小米云平台负责人。1995年赴美留学&#xff0c;纽约州立大学石溪分校计算机科学系博士毕业&#xff0c;曾任IBM高级工程师和高级研发经理、雅虎搜索技术核心团队主任工程师、LinkedIn主任工程师&#xff0c;2012年回国加入小米科技。 20…

【深度强化学习】(3) Policy Gradients 模型解析,附Pytorch完整代码

大家好&#xff0c;今天和各位分享一下基于策略的深度强化学习方法&#xff0c;策略梯度法是对策略进行建模&#xff0c;然后通过梯度上升更新策略网络的参数。我们使用了 OpenAI 的 gym 库&#xff0c;基于策略梯度法完成了一个小游戏。完整代码可以从我的 GitHub 中获得&…

51单片机8*8 LED点阵实现原理讲解

文章目录前言一、LED8*8点阵的原理二、LED8*8点阵原理图三、74HC595模块讲解四、74HC595模块写一个字节数据代码讲解总结前言 本篇文章将为大家讲解LED8*8点阵的使用方法。 一、LED8*8点阵的原理 LED 88点阵是由64个LED灯珠组成的&#xff0c;它们排列在一个88的矩阵中。每个…

手机验证发送及其验证(基于springboot+redis)保姆级

在Java开发中&#xff0c;发送手机验证码时需要考虑以下几个问题&#xff1a; 验证码的有效期&#xff1a;验证码应该有一定的有效期&#xff0c;一般设置为几分钟或者十几分钟。过期的验证码应该被认为是无效的&#xff0c;不能用于验证用户身份。手机号码格式的校验&#xf…

Docker【基本使用】

1&#xff1a;启动Docker1.1&#xff1a;操作systemctl start docker.service1.2&#xff1a;常见问题【第一步】启动docker&#xff0c;提示启动失败&#xff0c;查询运行状态systemctl start docker.service【第二步】查询docker运行状态&#xff0c;提示不支持SELinux【第三…

你还不会递归?告别困惑,我来教你

文章目录如何理解“递归”&#xff1f;递归需要满足的三个条件如何编写递归代码&#xff1f;递归代码要警惕堆栈溢出递归代码要警惕重复计算最后说一句&#x1f431;‍&#x1f409;作者简介&#xff1a;大家好&#xff0c;我是黑洞晓威&#xff0c;一名大二学生&#xff0c;希…

多线程(三):Thread 类的基本属性

上一个篇章浅浅了解了一下 线程的概念&#xff0c;进程与线程的区别&#xff0c;如何实现多线程编程。 而且上一章提到一个重要的面试点&#xff1a; start 方法和 run 方法的区别。 start 方法是从系统那里创建一个新的线程&#xff0c;这个线程会自动调用内部的run 方法&…
最新文章