【数据结构】顺序表的基本操作实现

文章目录

  • 前言
  • 一、顺序表的概念及结构
  • 二、顺序表的基本操作实现
    • 1.顺序表的创建
    • 2.顺序表初始化
    • 3.顺序表扩容
    • 4.头插和尾插
    • 5.头删和尾删
    • 6.查找
    • 7.任意位置插入
    • 8.任意位置删除
    • 9.顺序表的销毁
  • 三、总结

前言

从本篇我们开始学习数据结构初阶的内容。

首先我们了解一下什么是线性表:

线性表是n个具有相同特性的数据元素的有限序列。

常见的线性表有:顺序表(Sequence List)、链表(Linked List)、栈(Stack)、队列(Queue)、字符串(String)等
堆(Heap)、二叉树(Binary Tree)、图(Graph) 这些是非线性结构。

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表的存储结构主要分为两种:顺序存储结构和链式存储结构。

而今天我们主要讲解的是线性表中的顺序表

一、顺序表的概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,这种存储结构称为顺序存储结构。顺序表一般情况下采用数组存储,在数组上完成数据的增删查改等基本操作。

二、顺序表的基本操作实现

1.顺序表的创建

顺序表是用数组来进行存储的,因此我们可以在一个结构体中使用一个数组来存储数据,一个变量来记录数据个数。

静态顺序表:使用定长数组存储元素

typedef int SLDataType;//方便后续随时改数据类型
#define N 100 //数组最大长度typedef struct SeqList
{SLDataType a[N];int size;	//有效数据个数
}SL;//命名为SL

但静态顺序表只适用于确定知道需要存多少数据的场景。
因为静态顺序表的数组长度N如果定大了,空间开多了浪费;N太小了则不够用。

所以我们一般使用动态顺序表,避免空间的浪费的同时也能随时扩容。

动态顺序表:根据需要来动态地分配空间大小。

#define INIT_SZ 10 //初始空间大小
#define INC_SZ 4   //每次扩容的数量
typedef int SLDataType;//方便改存储的数据类型typedef struct SeqList
{SLDataType* a;int size;	//有效数据个数int capacity;//空间容量
}SL;//命名为SL

2.顺序表初始化

void SLInit(SL* ps)
{assert(ps);SLDataType* ptr = (SLDataType*)calloc(INIT_SZ, sizeof(SLDataType));if (NULL == ptr){perror("calloc");//打印错误信息return;}ps->a = ptr;ps->size = 0; //初始存储的数据个数为0ps->capacity = INIT_SZ; //初始容量
}

assert()函数如果传入参数是空(NULL),vs会报错并显示具体行数。头文件#include<assert.h>。这样写可以提高代码的健壮性。

我们可以用malloc或者calloc来动态开辟数组,calloc能直接将开辟的空间初始化为0。
忘了怎么使用的小伙伴,可以复习一下另一篇动态内存管理的详解。

3.顺序表扩容

因为我们初始化时只开辟了一定的空间大小,后续在插入数据的时候,要检查可用空间是否足够,不够的话我们需要对原有空间进行扩容。

//检查容量是否已满
void CheckCapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity)//数据个数与容量相等说明可用空间已满,需要扩容{SLDataType* tmp = (SLDataType*)realloc(ps->a, (ps->capacity + INC_SZ) * sizeof(SLDataType));if (NULL == tmp){perror("realloc");return;}ps->a = tmp;ps->capacity += INC_SZ;}
}

用realloc进行扩容,每次扩容的大小可以根据需要来改变,每次扩容一定数量,或者扩为原有空间的2倍。

4.头插和尾插

顺序表是用一维数组来存储的,顺序表的基本操作都是在数组的基础上进行的。

我们要对一个一维数组进行头插,也即在第一个元素前面插入一个数据,需要将数组整体往后挪一位。
具体方法为:数组从后往前,依次往后移动一位。只能从后往前,如果从前往后会覆盖掉原有数据。

void SLPushFront(SL* ps, SLDataType x)
{assert(ps);CheckCapacity(ps);//检查容量,如果已满则扩容for (int i = ps->size; i > 0; i--){ps->a[i] = ps->a[i - 1];}ps->a[0] = x;//插入数据ps->size++;	//有效数据个数+1
}

而顺序表尾插就较为简单,直接在末尾插入。

void SLPushBack(SL* ps, SLDataType x)
{assert(ps);CheckCapacity(ps);ps->a[ps->size++] = x;
}

注意: 后面我们介绍完 在任意位置插入和删除操作后,头插尾插以及头删尾删都可以用这两个操作完成。
之所以详细写出头插尾插以及头删尾删的具体实现是为了方便大家理解。

5.头删和尾删

头删则需要将数组中每一个元素向前挪动一位。从前往后,将后一个位置的元素赋值给当前位置

void SLPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);for (int i = 0; i < ps->size - 1; i++){ps->a[i] = ps->a[i + 1];}ps->size--;//有效个数-1
}

尾删只需要将顺序表长度-1即可。

但是注意,删除的空间不能free掉,因为动态开辟的空间不能局部释放。

void SLPopBack(SL* ps)
{assert(ps);assert(ps->size > 0);//free(ps->a[ps->size-1]);//不能这样写!!!ps->size--;
}

6.查找

查找函数可以根据数值找到对应的下标并返回,不存在则返回-1

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;
}

当然也可以根据下标返回对应的数值

int SLFind(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);//查找的下标要在有效范围内return ps->a[pos];
}

7.任意位置插入

插入函数可以在下标为pos的位置插入数据,需要将pos位置及后面的数据的往后移一位,然后将插入数据存放在pos位置处。

void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);//插入的位置要符合逻辑CheckCapacity(ps);for (int i = ps->size; i > pos; i--){ps->a[i] = ps->a[i - 1];}ps->a[pos] = x;ps->size++;
}

实现SLInsert函数后,我们的头插和尾插函数可以用一行代码替代。

SLInsert(ps, 0, x);	//头插
SLInsert(ps, ps->size, x);//尾插

8.任意位置删除

删除pos位置的数据,需要将pos后面的数据整体往前移一位。
往前移需要从前往后遍历顺序表,往后移需要从后往前遍历顺序表。

void SLErase(SL* 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--;
}

至此,我们的头删和尾删函数也可以用一行代码替代。

SLErase(ps, 0);//头删
SLErase(ps, ps->size - 1);//尾删

在顺序表的具体实现时,我们就不用再写头插、尾插、头删、尾删了,一个插入函数和一个删除函数足矣。

9.顺序表的销毁

别忘了,我们的顺序表是动态开辟的, 动态开辟空间使用完之后一定要及时free释放,否则会导致内存泄漏。

void SLDestroy(SL* ps)
{free(ps->a);ps->a = NULL;ps->size = ps->capacity = 0;ps = NULL;
}

三、总结

顺序表属于顺序存储结构,物理空间连续,一般是以数组的形式存储的。

顺序表的优点

可以随机访问任意位置的元素,效率为O(1),尾插尾删效率很高。

顺序表的缺点

1.中间或者头部的插入删除效率很低,时间复杂度为O(N)。
2.扩容有一定的消耗,可能会有一定空间的浪费。

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

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

相关文章

【doghead】mac: clion2024.1启动崩溃

doghead 是在mac 下跑各种数据的因此&#xff0c;在配置了环境后, 进行mac 构建 【doghead】mac构建&#xff0c;首先对mac的clion进行安装 mac 下安装clion2024.1 之前可能装过crack的clion教育优惠的clion跟之前的应该不同clion2024.1 在mac的新系统下崩溃google下竟然没有…

腾讯云CentOS7使用Docker安装ElasticSearch与Kibana详细教程

文章目录 一、安装ElasticSearch二、安装Kibana 一、安装ElasticSearch 使用Docker拉取ElasticSearch镜像 这里版本选择的是7.15.2 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.15.22. 查看ElasticSearch的镜像id docker images3. 创建ElasticSearch容器 …

鸿蒙应用开发系列 篇一:鸿蒙系统概述

文章目录 系列文章鸿蒙系统的历史HarmonyOS 与 OpenHarmony鸿蒙系统的技术架构与核心特性内核层:安全与效率的双轮驱动系统服务层:分布式服务,重构连接的维度框架层:开发者的效率与创意舞台应用层:全场景应用生态的繁荣鸿蒙系统与其他操作系统与Android、iOS的比较:与AOS…

鸿蒙开发仿咸鱼TabBar

鸿蒙开发自定义TabBar&#xff0c;实现tabBar 上中间按钮凸起效果 第一步、定义数据模型 export default class TabItemData{defaultIcon: ResourceselectedIcon: Resourcetitle: stringisMiddle: booleanconstructor(defaultIcon:Resource, selectedIcon:Resource, title:st…

Java——数组的定义和使用

目录 1.什么是数组 2.创建数组 3.数组的使用 3.1获取数组长度 3.2遍历数组 3.3数组作为方法的参数 3.4数组作为参数的返回值 4.数组转字符串 5.数组拷贝 5.1Arrays.copyOf(arr,arr.length) 5.2Arrays.copyOfRange(arr,2,6) 1.什么是数组 数组就是具有相同属性的集合&am…

php中常见的运算符和使用方法

PHP中常见的运算符包括算术运算符、赋值运算符、比较运算符、逻辑运算符、位运算符、字符串运算符、三元条件运算符&#xff08;也称为三目运算符&#xff09;、递增/递减运算符等。以下是这些运算符的简要说明和使用方法&#xff1a; 算术运算符&#xff1a; &#xff1a;加法…

【设计模式】之装饰器模式

系列文章目录 【设计模式】之模板方法模式 【设计模式】之责任链模式 【设计模式】之策略模式 【设计模式】之工厂模式&#xff08;三种&#xff09; 前言 今天给大家介绍23种设计模式中的装饰器模式。&#x1f308; 一、什么是装饰器模式 装饰器模式&#xff08;Decora…

【C++】文件

目录 文件文件分类文本文件的读写(ASCII文件)的读写打开文件打开文件的方式关闭文件将数据写入ASCII文件从ASCII文件读入数据 二进制存储对比ASCII和二进制存储用成员函数read和write读写二进制文件打开方式文件的读入与读出 文件 所谓文件&#xff0c;一般指存储在外部介质上…

c#学习基础1

一、复杂数据类型 1&#xff09;概述 2&#xff09;枚举 1.基本概念 枚举是一个比较特别的存在&#xff0c;它是一个被命名的整形常量的集合&#xff0c;一般用它来表示状态&#xff0c;类型等 1.1申明枚举和申明枚举变量 1.2申明枚举语法 2.在哪里申明枚举 3.枚举的使用 4…

Java 获取 Outlook 邮箱的日历事件

Java 获取 Outlook 邮箱的日历事件 1.需求描述2.实现方案3.运行结果 IDE&#xff1a;IntelliJ IDEA 2022.3.3 JDK&#xff1a;1.8.0_351 Outlook&#xff1a;Microsoft Office 2016 1.需求描述 比如现在需要获取 Outlook 邮箱中四月的全部的会议安排&#xff0c;如下图所示 …

双非二本找工作前的准备day20(算法-二叉树系列)

学习目标&#xff1a; 每天复习代码随想录上的题目1-2道算法&#xff08;时间充足可以继续&#xff09; 今日碎碎念&#xff1a; 1&#xff09;今天开始是二叉树系列 2&#xff09;出租屋里不知道干啥&#xff0c;看看书啊刷刷算法&#xff0c;打打游戏&#xff0c;学学技术…

anaconda、cuda、tensorflow、pycharm环境安装

anaconda、cuda、tensorflow、pycharm环境安装 anaconda安装 anaconda官方下载地址 本文使用的是基于python3.9的anaconda 接下来跟着步骤安装&#xff1a; 检验conda是否成功安装 安装CUDA和cuDNN 提醒&#xff0c;CUDA和cuDNN两者必须版本对应&#xff0c;否者将会出错…

three.js 中ShaderChunk的, common.glsl 介绍

1. three.js 中ShaderChunk的, common.glsl 介绍 在three.js中&#xff0c;我们可以编写底层的shader代码&#xff0c;是通过 THREE.ShaderMaterial 来进行的。 但是three.js他也通过了一些 shader 代码片段&#xff0c;哎我们可以通过引入这些 shader代码片段&#xff0c;在自…

Educational Codeforces Round 165 (Rated for Div. 2) (C、D)

1969C - Minimizing the Sum 题意&#xff1a; 思路&#xff1a;观察到操作数很小&#xff0c;最值问题操作数很容易想到dp&#xff0c;用表示第个元素&#xff0c;操作了次的最小值总和&#xff0c;转移的时候枚举连续操作了几次即可&#xff0c;而连续操作了几次即将全部变成…

陈随易:论技术思维和产品思维

大家好&#xff0c;我是不被定义的前端之虎陈随易。 我的个人网站是&#xff1a;https://chensuiyi.me&#xff0c;欢迎大家眼熟我。 写这篇文章呢&#xff0c;源于一次群聊。 群友有一个产品&#xff0c;其中涉及到免费用户和付费用户对 pdf 的查看权限问题&#xff0c;使用…

图像处理ASIC设计方法 笔记21 标记ASIC的顶层状态机

目录 (一)标记ASIC的工作流程1 ASIC首先从控制寄存器内读出待标记图像的基本参数2若写入了有效的启动命令,则进入下面一帧图像的标记过程。3 ASIC通过接口模块从FIFO1中读取待标记的图像4一帧图像初步标记完成后进行等价表的整理压缩5从临时标记存储器中读取临时标记送入标记…

gitee本地项目上传

1.先生成SSH密钥 ssh-keygen -t rsa -C "trueelegance163.com" 2.gitee配置公钥&#xff0c;设置对应的公钥名称比如:localtest; Git 全局设置:若刚安装完Git&#xff0c;需要进行Git的配置【若已配置完成&#xff0c;此步骤可以跳过】 git config --global user…

SQL注入基础-4

GetSHELL 一、文件读写注入 1、原理&#xff1a;利用文件读写权限进行注入&#xff0c;可以写入一句话木马&#xff0c;也可以读取系统文件的敏感信息。 2、要求和条件&#xff1a;用户最高权限(可以尝试更改secure_file_priv)和网站绝对路径 3、网站的绝对路径 (1)常见路…

大语言模型从Scaling Laws到MoE

1、摩尔定律和伸缩法则 摩尔定律&#xff08;Moores law&#xff09;是由英特尔&#xff08;Intel&#xff09;创始人之一戈登摩尔提出的。其内容为&#xff1a;集成电路上可容纳的晶体管数目&#xff0c;约每隔两年便会增加一倍&#xff1b;而经常被引用的“18个月”&#xf…

CSS精灵图、字体图标、HTML5新增属性、界面样式和网站 favicon 图标

精灵图 为什么要使用精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度,因此&#xff0c;为了有效地减少服务…