【数据结构】---顺序表的实现

news2024/9/21 10:56:17

最近学校开始学习数据结构了,没事就手搓一个顺序表。

🌈线性表

线性表是n个具有相同特性的数据元素的有限序列,是一种实际中广泛使用的数据结构,常见的线性表有 顺序表、链表、栈、队列、字符串。
线性表在逻辑上是线性结构,即连续的一条直线,但在 物理上不一定连续,线性表在物理上存储时,通常以数组和链式结构的形式存储。

1.1啥是顺序表?

顺序表是在计算机内存中 数组 的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。

1.2顺序表和数组的区别

两者的区别主要有:
1.顺序表可以实现动态增长,而普通数组的长度是固定的
2顺序表要求插入的数据在内存中是连续的,普通数组的数据存放可以不连续。

🌈顺序表功能实现:

2.1初始化顺序表SLInit()

在实现初始化之前,我们先来创建一个顺序表类型,其中包括顺序表起始位置的指针容量数据个数

静态顺序表类型:

#define N 100
typedef int SLDateType;
//类型命名

//静态顺序表--N太小可能不够用
struct Seqlist
{
    //int a[N];
    //以后我不想用int类型的a[]
    SLDateType  a[N];
    int sz;
};

这个的致命缺点就是如果说存储的数据多,而N小的话就不行了,一般写线性表都是用动态版的。

动态顺序表类型:

#define N 100
typedef int SLDateType;

typedef struct SeqList
{
    SLDateType* a;   //记录顺序表的起始位置
    int capacity;   //顺序表的容量
    int size;       //记录顺序表中的数据个数
}SL;

typedef为C语言的关键字,作用是为一种数据类型定义一个新名字

初始化函数SLInit():

void SLInit(SL* ps)
{
    assert(ps);    //判断是否为空,如果是空就不再进行了
    ps->a = NULL;   //指针赋值为空
    ps->capacity = ps->size = 0;  //整形赋值为0
}
上面的SL* ps之所以用到指针,就是因为在进行初始化的时候需要将模板也一并改掉。所以用指针找到原来的地址来改变模板。

2.2销毁顺序表SLDestory()

因为顺序表所用的内存空间是动态开辟在堆区的,所以我们在使用完后需要及时对其进行释放,避免造成内存泄漏

void SLDestory(SL* ps)
{
    assert(ps);   //断言,如果顺序表是空,就不在销毁了
    free(ps->a);      //这一步最为关键 ,把指针释放掉
    ps->a = NULL;   //指针赋值为空
    ps->capacity = ps->size = 0;  //整形赋值为0
}

其实销毁和初始化基本大差不差,最关键的一点就是销毁时有一个free来释放指针。

assert的好处

我们在其中运用到了assert函数,它包含在assert.h头文件中。
assert的好处就是判空,如果一个顺序表是空的,那就不需要进行初始化,和销毁,如果调用这两个函数就直接报错。可以让我们更快的找到漏洞。

2.3打印顺序表SLPrint()

需要打印的个数就是size的大小,然后一个for循环就可以了。

void SLPrint(SL* ps)
{
    assert(ps != NULL);
    for (int i = 0; i < ps->size; i++)
    {
        printf("%d-", ps->a[i]);
    }
    printf("\n");
}

2.4顺序表的容量检查SLCheckCapacity()

检查顺序表的容量是否够用是顺序表的一个很关键的问题。其实检查容量的大小很好检查的,只要判断size和capacity的大小就可以了。

  • 如果capacity>size,就说明顺序表还没有存满。

  • 如果capacity=size,就说明需要扩容了。

void SLCheckCapacity(SL* ps)
{
    if (ps->capacity == ps->size)  //需要扩容了
    {    //先扩容
        int newCapacity = ps->capacity == 0 ? 4 : 2 *ps->capacity;    //1
        //再让指针指向这块空间
        SLDateType* tmp = (SLDateType*)realloc(ps->a, newCapacity * sizeof(SLDateType));
        if (tmp == NULL)   
        {
            //如果扩容后的容量还是空,直接报错
            printf("realloc fail\n");
            exit(-1);
        } 
        ps->a = tmp;     //3
        ps->capacity = newCapacity;
    }
}

这里面1中的代码可能有些不好理解,它的意思是如果说capacity是0的话,就给他4个空间,如果不是0,就把它的空间扩大2倍,然后复制给newCapacity。

2中的代码是新建一个指针,这个指针的目的是让原来的指针指向新开辟的空间。

接下来就该实现重点功能了。

🌈顺序表的增删实现

3.尾插功能SLPushBack()

尾插就是在顺序表的表尾插入数据,所以第一步是先判断顺序表的容量大小。容量不够就扩容。

//尾插
void SLPushBack(SL* ps, SLDateType x)
{
    //检查容量空间
    SLCheckCapacity(ps);
    
    ps->a[ps->size] = x;
    ps->size++;
}

之所以有两个参数,这两个参数中,参数1代表的是顺序表,参数2代表的是要插入的数。

4.头插功能SLPushFront()

要想在顺序表的表头插入数据,那么就需要先将顺序表原有的数据从后往前依次向后挪动一位,最后再将数据插入表头。

头插成功的前提是顺序表还有空间可供插入。所以第一步就是检查容量。容量够就进行插入操作。

//头插
void SLPushFront(SL* ps, SLDateType x)
{
    //检查容量
    SLCheckCapacity(ps);

    int end = ps->size - 1;  //size-1代表的就是顺序表存储的最后一个数
    while (end >= 0)
    {
        ps->a[end + 1] = ps->a[end];
        --end;
    }
    ps->a[0] = x;
    ps->size++;
}

就是把顺序表往后挪一位,然后把第一位插入x就行了。

5.尾删功能SLPopBack()

尾删就是把最末尾数据给干掉,但是其实仔细想一下,直接把顺序表元素个数-1就行了。

//尾删
void SLPopBack(SL* ps)
{
    assert(ps->size > 0);
    //顺序表不为空才可以进行尾删,否则直接报错
    ps->size--;
}

6.头删功能SLPopFront()

头删功能和头插正好相反,头删是往前覆盖。

//头删
void SLPopFront(SL* ps)
{
    assert(ps->size > 0);
    int begin = 1;  //数据最起码是一个,不是空顺序表
    while (begin < ps->size)
    {
        ps->a[begin - 1] = ps->a[begin];
        begin++;
    }
    ps->size--;  //数据减少一个
}

出现多删情况:

如果说把顺序表删成空后还继续删除,那会出现啥情况?

这个就会报错,之所以能检查出来错误就是上面咱们写的断言判断出来的。

其实这几种情况用到的不很多,最多的就是任意位置的插入删除操作。

🌈任意位置的插入删除

7.任意位置(pos)插入

这个pos是位置英文的缩写,在任意位置,我们首先就要指定位置pos.所以又加入一个形参。然后后移pos后面的数据。

对于这个插入来说,我们要进行两次断言:

首先·顺序表不为空,其次:指定的位置在顺序表中,不能小于0,不能大于size。

接下来就是往后移动。

//某位置插入
void SLInsert(SL * ps, int pos, SLDateType x)
{
    //先防止越界
    SLCheckCapacity(ps);
    assert(ps);
    assert(pos >= 0 && pos <= ps->size);//加上=,可实现尾插
    int end = ps->size - 1;//从后往前
    while (end >= pos)
    {
        ps->a[end + 1] = ps->a[end];
        --end;
    }
    ps->a[pos] = x;
    ps->size++;
}

8.任意位置(pos)删除

这个跟前面的没事两样,就是加一个形参pos,然后把pos以后的数往前移动。

//某位置删除
void SLErase(SL* ps, int pos)
{
    assert(ps);
    assert(pos >= 0 && pos < ps->size);
    //不能等于
    int begin = pos;
    for (begin = pos; begin < ps->size - 1; begin++)
    {
        ps->a[begin] = ps->a[begin + 1];
    }
    ps->size--;
}

🌈查改功能的实现

写到这一步,顺序表基本就写完了,只剩下几个功能了。

9.查找功能实现SLfind()

要想实现查找功能,还需要两个形参,但是第二个形参是操作者查找的对象,比如我要去顺序表中查找2,那第二个形参就是2。

如果找到就返回该数字,没找到返回-1.

//查找
int SLFind(SL* ps, SLDateType x)
{
    assert(ps);
    for (int i = 0; i < ps->size; i++)
    {
        if (ps->a[i] == x)
        return i;
    }
    return -1;//没找到
}

10.修改功能SLModify()

//修改
int SLModify(SL* ps, int pos, SLDateType x)
{
    assert(ps);
    assert(pos >= 0 && pos < ps->size);

    ps->a[pos] = x;
}

这里直接在pos位置上修改就行了。

🌈总结:

到这顺序表的大致功能函数就解决完了,但是还是要说一个问题--》

为啥第一个参数要用指针?

形参是实参的一份临时拷贝,修改功能函数里的形参并不会改变实参的任何数据。

就好比你去复印户口簿,复印完发现复印件的地址错了,你如果说你只改复印件上的,那下一次复印地址还是错的。所以必须要通过复印件连着户口本上的一起改成正确地址才行。

这个用指针就是上面的意思,要想改变顺序表中的数据,就要通过指针把顺序表上的内容一起改变。

上面的打印函数就可以不要指针,但是为了更准确和一致,把它也带上把。

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

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

相关文章

【C语言学习笔记】:静态库

一、什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个人的代码都从零开始&#xff0c;因此库的存在意义非同寻常。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作…

基于”PLUS模型+“生态系统服务多情景模拟预测实践技术应用

生态系统服务是人类直接或间接从生态系统中获得的惠益&#xff0c;在应对城市挑战和实施可持续发展方面发挥着至关重要的作用。随着全球城市化的快速发展, 频繁的人类活动导致了土地利用的快速变化&#xff0c;导致生态系统结构和功能的变化&#xff0c;影响生态系统服务的供应…

【Nginx】Docker配置ngnix,实现同服务器ip多站点多域名

Docker配置ngnix&#xff0c;实现同服务器ip&#xff0c;多域名映射多站点 本文首发于 慕雪的寒舍 1.说明 一般情况下&#xff0c;我们的域名映射到ip后&#xff0c;默认访问的是80端口。如果你的服务器只部署了一个服务&#xff0c;这样也是够用的。 但是很多项目对性能的占…

CAN总线详细介绍

1.1 CAN是什么&#xff1f; CAN 最终成为国际标准 &#xff08; ISO11898(高速应用)和 ISO11519&#xff08;低速应用&#xff09;&#xff09;&#xff0c;是国际上应用最广泛的现场总线之一。 1.2 CAN总线特点 多主方式: 可以多主方式工作&#xff0c;网络上任意一个节点…

前端学习第一阶段——第五章(上)

5-1 CSS基本选择器 01-CSS层叠样式表导读 02-CSS简介 03-体验CSS语法规范 04-CSS代码风格 05-CSS选择器的作用 06-标签选择器 07-类选择器 08-使用类选择器画盒子 09-类选择器特殊使用-多类名 10-id选择器 11-通配符选择器 5-2 CSS样式 12-font-family设置字体系列 13-font-s…

商场技术点-3

1.后端服务校验 1.1JSR-303介绍 JSR是Java Specification Requests的缩写&#xff0c;意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR&#xff0c;以向Java平台增添新的API和服务。JSR已成为Java界的一个…

springboot项目配置文件加密

1背景&#xff1a; springboot项目中要求不能采用明文密码&#xff0c;故采用配置文件加密. 目前采用有密码的有redis nacos rabbitmq mysql 这些配置文件 2技术 2.1 redis nacos rabbitmq 配置文件加密 采用加密方式是jasypt 加密 2.1.1 加密步骤 2.1.2 引入maven依赖 …

Android进阶之路 - StringUtils、NumberUtils 场景源码

忘记是在去年还是前年的时候遇到一个需要检测所传字符串是否为数字的场景&#xff0c;开始使用 NumberUtils.isNumber() 提示错误 &#xff0c;没有解决问题&#xff08;可能是因为依赖版本导致&#xff09;&#xff0c;最后使用的是StringUtils.isNumeric()&#xff0c;当时关…

剑指 Offer 43. 1~n 整数中 1 出现的次数

题目 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进制表示中1出现的次数。 例如&#xff0c;输入12&#xff0c;1&#xff5e;12这些整数中包含1 的数字有1、10、11和12&#xff0c;1一共出现了5次。 思路 要求出小于等于 n 的非负整数中数字 1 出现的个数…

Prometheus系列(五)grafana web 配置邮件告警

目录 1. contact points&#xff08;创建告警渠道&#xff09; 2. Notification policies&#xff08;创建告警通道匹配规则&#xff09; 3. Alert rules&#xff08;配置告警策略&#xff09; 告警配置 告警页面名词解释&#xff1a; 1. contact points&#xff08;创建告…

玩转数据结构之Java实现线段树

前言 线段树是一种二叉搜索树&#xff0c;线段树的每个结点都存储了一个区间&#xff0c;也可以理解成一个线段&#xff0c;在这些线段上进行搜索操作得到你想要的答案。 线段树的适用范围很广&#xff0c;可以在线维护修改以及查询区间上的最值&#xff0c;求和。更可以扩充到…

一文浅谈sql中的 in与not in,exists与not exists的区别以及性能分析

文章目录1. 文章引言2. 查询对比2.1 in和exists2.2 not in 和not exists2.3 in 与 的区别3. 性能分析3.1 in和exists3.2 NOT IN 与NOT EXISTS4. 重要总结1. 文章引言 我们在工作的过程中&#xff0c;经常使用in&#xff0c;not in&#xff0c;exists&#xff0c;not exists来…

Unity2018.4.x~2021.3.x版 Android资源处理

注意&#xff1a;本文都是针对使用Gradle编译从Unity2018.4.x到Unity2020.3.x都是可以直接将Android的适配资源直接放到${PROJECT_PATH}/Assets/Plugins/Android/对应的目录下的&#xff0c;如&#xff1a;在此目录下可以方安卓平台对应的assets、res目录及子目录资源&#xff…

前端的CSS样式表知识提要

文章目录前言基本概念屏幕尺寸屏幕分辨率屏幕像素密度/像素密度/屏幕密度视口和浏览器窗口长度单位&#xff1a;px、em/rem和vhCSS属性的继承与覆盖CSS选择器CSS 布局基础盒子模型绝对定位和相对定位display属性浮动正常布局流&#xff08;normal flow&#xff09;Flexbox 布局…

【Python表白代码】 2.14“Valentine‘s Day”“没别的意思 就是借着特殊日子说声喜欢你”你在哪儿?我去见你~(各种玫瑰源码合集)

导语 Valentines Day Every man is a poet when he is in love 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 哈喽&#xff01;我是你们的木木子吖~ 情人节又到了&#xff0c;礼物备好了没&am…

k8s部署Prometheus+Grafana

1.prometheus简介 Prometheus是一个开源的系统监控和警报工具包&#xff0c;最初由SoundCloud开发的&#xff0c;社区活跃&#xff0c;2016年加入了云原生计算基金会成为继Kubernetes之后的第二个托管项目&#xff1b;普罗米修斯以时间序列数据的形式收集并存储度量值&#xff…

python爬虫--xpath模块简介

一、前言 前两篇博客讲解了爬虫解析网页数据的两种常用方法&#xff0c;re正则表达解析和beautifulsoup标签解析&#xff0c;所以今天的博客将围绕另外一种数据解析方法&#xff0c;它就是xpath模块解析&#xff0c;话不多说&#xff0c;进入内容&#xff1a; 一、简介 XPat…

2023年谷歌蜘蛛池最全指南

本文主要是2023年关于谷歌蜘蛛池的一系列疑问&#xff0c;我们逐一提供解答。 本文由光算创作&#xff0c;有可能会被修改和剽窃&#xff0c;我们佛系对待这种行为吧。 首先最常见的新手问题是“什么叫谷歌蜘蛛池&#xff1f;” 答案是&#xff1a;谷歌蜘蛛池是一个深度研究谷…

微服务网关(九)负载均衡底层详细

微服务网关&#xff08;九&#xff09;负载均衡 四大负载均衡策略 随机负载 随机挑选目标服务器IP 轮询负载 ABC三台服务器&#xff0c;以ABCABC的顺序依次轮询 加权轮询 给目标服务器设置访问权重值&#xff0c;按照权重轮询负载 一致性哈希轮询 固定的一个客户端IP请求访…

图文解答之最短路径||

最短路径|| 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为“Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为“Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上角到…