顺序表:数据结构的建筑积木

朋友们大家好啊,本节内容我们进入数据结构的第二节,顺序表有关内容,同步我们会学习计组原理与cpp相关知识,求三连啊!

本节我们重点探讨动态顺序表关于插入数据和删除数据的多种情况的分析

顺序表

  • 线性表
  • 顺序表
    • 静态顺序表
  • 动态顺序表
    • 顺序表的初始化与销毁
    • 顺序表头部尾部的插入与删除
    • 指定位置插入和删除

线性表

线性表是数据结构的一种,它是一组具有相同数据类型的数据元素的有限序列。在线性表中,除了第一个和最后一个数据元素之外,每个数据元素均只有一个直接前驱和一个直接后继。线性表的元素个数n(n≥0)定义为线性表的长度,当n=0时,称为空表。

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储

线性表的物理存储结构影响其操作的效率,主要分为两种:

  1. 顺序存储结构:
  2. 链式存储结构:

我们接下来介绍顺序表有关内容

顺序表

介绍顺序表之前,我们谈论一下数组

数组是程序设计中的一种基本数据结构,它是同一数据类型元素的集合,这些元素在内存中按照顺序排列,占据连续的内存空间。数组是静态的数据结构,它的大小在定义时就已确定,并且在整个生命周期中保持不变。数组可以是一维的,也可以是多维的(如二维数组、三维数组等)。

特点:

  • 静态结构:一旦定义,大小不可变。
  • 连续的内存空间。
  • 支持随机访问,即可以通过索引访问任意元素。
  • 大小固定,一旦数组被声明,它的大小就被确定下来,不能动态地增加或减少元素。

那么对于顺序表,通常使用数组作为其底层的物理结构,但它是一个更高级别的抽象。与“裸”数组不同的是,顺序表通常提供了一组用于操作和访问其元素的API接口,如插入、删除、搜索等操作,并且它们的实现细节对使用者是隐藏的。在一些实现中,顺序表还可以动态地调整其大小以适应元素数量的变化,这是通过在后台自动重新分配内存和复制现有元素到一个更大(或更小)的数组来实现的。

数组有给定长度的,也有动态的,顺序表也分为静态和动态

静态顺序表

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

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

在这里,我们设置了一个定长数组arr,size为它的有效数据的个数,这里有效数据的个数是指已经初始化或赋值的部分,同时我们用新的类型别名 SLDataType 来代表 int 类型,这种操作再数据结构中非常常见,主要目的:

类型抽象:通过使用类型别名,可以将数据类型抽象化。这意味着如果将来需要改变数据类型(比如从 int 改为 float 或者某个结构体类型),只需修改 typedef 行的定义,而不用修改整个代码中的多个地方。这提高了代码的可维护性。我们展开讨论:

假设您在一个较大的项目中定义了一个数据类型别名 SLDataType 来代表 int,并在多个函数和数据结构中广泛使用了这个别名。现在,我们来看看如果需要更改这个数据类型,类型别名如何简化这个过程。

typedef int SLDataType; // 初始类型别名定义// 使用SLDataType的函数
void processElement(SLDataType element) {// ... 处理逻辑 ...
}// 使用SLDataType的数据结构
typedef struct {SLDataType array[10];int size;
} DataArray;

在这个初始代码中,SLDataType 被用于函数 processElement 和结构体 DataArray。

更改数据类型
现在,假设您决定将 SLDataType 从 int 更改为 float。这种情况下,您只需修改 typedef 行:

typedef float SLDataType; // 修改类型别名

由于 SLDataType 被用于整个项目中,这一改变会自动应用于所有使用了 SLDataType 的地方。这意味着您不需要逐个查找和替换每个 int 类型的实例。processElement 函数和 DataArray 结构体现在都会使用 float 而不是 int,而且不需要对它们的代码进行任何修改

动态顺序表

动态顺序表是线性表顺序存储方式的一种动态实现,它能够根据需要动态调整内存空间的大小,从而适应元素数量的变化

typedef struct SeqList
{SLDataType *array;size_t size;size_t capacity;
}SL;

这里size为有效数据的大小,capacity为空间容量,后续的增容与此变量息息相关

这里我们构建一个头文件(seq.h),包含所要调用所有顺序表函数的信息:

typedef struct SeqList
{SLDataType* a;int size; // 有效数据个数int capacity; // 空间容量
}SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插⼊删除 / 尾部插⼊删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//指定位置之前插⼊/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);

接下来我们逐个讲解

顺序表的初始化与销毁

思考下面这种方式能不能进行初始化?

void SLInit(SL ps)

在这里插入图片描述
这串代码并不能改变原来所创建的ps,这里是传值调用,为了使这种修改有效,需要通过指针传递SL结构。这样,SLInit将获取一个指向SL结构实例的指针,使其能够修改原始实例的内容。

修改如下:

void SLInit(SL*ps)
{ps->array = NULL;ps->size = 0;ps->capacity = 0;
}

在这里插入图片描述
这里若ps为空指针,则可能发生未定义行为,我们在开始进行判断

void SLInit(SL* ps)
{if (ps == NULL) { // 检查 ps 是否为 NULL// 可以打印错误信息,直接返回或者采取其他措施fprintf(stderr, "Error: NULL pointer passed to SLInit.\n");return;}ps->array = NULL;ps->size = 0;ps->capacity = 0;
}

在头文件中我们进行声明,在SeqList.c中完成函数的功能,在test.c文件中进行测试代码。
在这里插入图片描述
销毁

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

在这里插入图片描述
free(ps->array);这行代码的作用是释放顺序表(SeqList)中动态分配的数组内存。当ps->array不为NULL时,表示array指向了一块之前分配的内存,使用free来释放这块内存

顺序表头部尾部的插入与删除

这里我们定义四组函数,分别表示顺序表尾部的插入与删除,头部的插入与删除

void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPopBack(SL* ps);//尾删
void SLPushFront(SL* ps, SLDataType x);//头插
void SLPopFront(SL* ps);//头删

首先来讨论尾部插入,这里有几种情况,即尾部有没有空间插入
在这里插入图片描述
如果空间足够,直接在尾部放入数据即可。
比如插入数据“6”

ps->arr[ps->size++]=6;

如果size和capacity相等了,说明满了,如果再存储则会越界;这里就需要扩容

if (ps->size == ps->capacity)
{ int newcapacity = ps->capacity == 0 ? 4 : ps->capacity;SLDataType* tmp = realloc(ps->array, sizeof(SLDataType) * newcapacity);
}

由于初始化的时候我们将capacity的值赋值为0,这里进行新的capacity扩容的时候就用三目运算符如果为0,赋予值为4,如果不为零,给其2倍扩容。

realloc进行扩容,判断是否开辟成功

 if (tmp==NULL){perror("realloc fail");return;}

如果开辟成功,将新开辟的空间的地址给arr数组

ps->array = tmp;

完整的扩容代码如下:

 if (ps->size == ps->capacity){ int newcapacity = ps->capacity == 0 ? 4 : ps->capacity;SLDataType* tmp = (SLDataType*) realloc(ps->array, sizeof(SLDataType) * newcapacity);if (tmp==NULL){perror("realloc fail");return;}ps->array = tmp;ps->capacity = newcapacity;}

我们将这串代码放在SLCheckcapacity中
在这里插入图片描述

我们在测试文件中进行测试
在这里插入图片描述
调试如下
在这里插入图片描述
结果打印
在这里插入图片描述
接下来看头部插入
直接调用SLCheckcapacity检查空间是否足够

void SLPushFront(SL* ps, SLDataType x)
{SLCheckcapacity(ps);
}

头部插入,我们需要将数据往后挪动

void SLPushFront(SL* ps, SLDataType x)
{SLCheckcapacity(ps);int end =ps-> size;while (end >= 0){ps->array[end + 1] = ps->array[end];end--;}ps->array[0] = x;ps->size++;
}

最后让size指向下一个位置
示例如下:
在这里插入图片描述

接下来我们讨论尾部删除

尾删如何删除呢?

这里我们需要讨论是否有数据去删除

代码如下:

void SLPopBack(SL* ps)
{if (ps->size == 0){printf("The SeqList is empty, no elements to remove.\n");}else{ps->size--;}
}

顺序表的size属性标志着其中有效元素的数量。当我们进行size- -操作时,我们实际上是在逻辑上减少了顺序表中的元素数量,而不是在物理上从内存中移除该元素。被"删除"的元素在内存中依然存在,只是我们不再将其视为顺序表的一部分。

在多数情况下,顺序表的实现不会立即释放每次删除操作后的内存空间,因为频繁的内存分配和释放操作会影响性能。相反,它保留这些空间以支持未来的添加操作,从而提高了整体的内存使用和管理效率。

在这里插入图片描述

我们这里使用if语句可以避免size减为负值,避免后续插入时候缺少数据!!!

头部删除
头部删除,我们可以用数据往前挪动进行数据覆盖

void SLPopFront(SL* ps)
{if (ps->size == 0){printf("The SeqList is empty, no elements to remove.\n");return;}for (size_t i = 1; i < ps->size; i++){ps->array[i - 1] = ps->array[i];}ps->size--;
}

指定位置插入和删除

这里定义两个函数,删除或插入指定位置pos的数据

void SLinsert(SL* ps,int pos, SLDataType x);//顺序表在pos位置插入x
void SLErase(SL* ps, int pos);//顺序表删除pos位置的值

关于数据插入,首先判断三件事:

  1. ps是否为空指针
  2. pos是否在规定位置
  3. 空间是否足够
void SLinsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos < ps->size);SLCheckcapacity(ps);
}

接下来进行数据挪动

void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps != NULL);assert(pos >= 0 && pos <= ps->size); SLCheckCapacity(ps);for (int i = ps->size; i > pos; --i){ps->array[i] = ps->array[i - 1];}// 在pos位置插入新元素ps->array[pos] = x;ps->size++;
}

进行检查
在这里插入图片描述

这里进行思考,如果pos等于size是什么结果?

如果pos等于size,size是有效位置长度,在pos位置插入数据,则相当于尾插

指定位置删除
首先进行判断是否为空指针和指定位置,再进行删除,代码如下

void SLErase(SL* ps, int pos)
{assert(ps != NULL);assert(pos >= 0 && pos < ps->size); // 确保pos在有效范围内// 从pos位置开始,将后面的元素前移for (size_t i = pos; i < ps->size - 1; i++){ps->array[i] = ps->array[i + 1];}// 更新顺序表的大小ps->size--;
}

这里pos就不能等于size!

本节内容到此结束!感谢观看!

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

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

相关文章

[职场] 研发工程师的简历该怎么写 #学习方法#媒体#学习方法

研发工程师的简历该怎么写 研发工程师的工作内容有&#xff1a; 根据开发项目的要求&#xff0c;制订机械结构、外形的设计方案。 按时完成上级分配的机械、外形设计任务。 负责自己设计的结构及外形安装&#xff0c;并解决安装过程中出现的问题。 协助设计产品的材料选购…

前端JavaScript篇之JavaScript有哪些内置对象

目录 JavaScript有哪些内置对象常见的内置对象的简单描述和参考代码总结 JavaScript有哪些内置对象 JavaScript中常见的内置对象包括&#xff1a; Object&#xff1a;用于创建对象的构造函数&#xff0c;对象是键值对的集合&#xff0c;用于存储和传递数据。 Array&#xff1…

python计算两个DataFrame的指定两列中,相同的数据有多少

目的&#xff1a;查询数据1和数据2中&#xff0c;red与red列相同 并且blue与blue列相同的&#xff0c;情况有多少。 &#xff08;备注&#xff1a;两个数据中格式不一致&#xff0c;需要经过json提取等处理步骤&#xff09; 思路步骤&#xff1a; 1、读取数据1&#xff0c;筛选…

【MongoDB】跨库跨表查询(python版)

MongoDB跨表跨库查询 1.数据准备&#xff1a;2.跨集合查询3.跨库查询应该怎么做&#xff1f; 讲一个简单的例子&#xff0c;python连接mongodb做跨表跨库查询的正确姿势 1.数据准备&#xff1a; use order_db; db.createCollection("orders"); db.orders.insertMan…

2021-10-12 51蛋骗鸡数码管前7位显示1-7第8位显示0-9

缘由 51单片机数码管问题-编程语言-CSDN问答 #include "REG52.h" sbit K1 P3^0; sbit K2 P3^1; sbit K3 P3^2; sbit K4 P3^3; bit k1,wk0; unsigned char code SmZiFu[]{63,6,91,79,102,109,125,7,127,111,128,255,64};//0-9.消隐- unsigned char Js0,miao0,fen…

【MIT 6.S081】2020, 实验记录(5),Lab: lazy allocation

目录 Task 1: Eliminate allocation from sbrk()Task 2: Lazy allocationTask 3: Lazytests and Usertests 在学习了 page fault 这一节课后&#xff0c;了解了操作系统是如何结合 page table 和 trap 利用 page fault 来实现一系列的神奇的功能。这个 lab 就是在 XV6 中实现 l…

Codeforces Round 651 (Div. 2)C 博弈 奇偶数 偶数的表示

Submission #244500083 - Codeforces 题目&#xff1a; 思路&#xff1a; 此题要从奇偶性上入手。&#xff08;注意除的是奇因数&#xff0c;即一个奇数。我想成质数了&#xff09; 1.当A选手开局是1时&#xff0c;A败。 2.当A选手开局是2和奇数时&#xff0c;A必胜。&…

【Qt加密播放器】登录窗口功能补充

输入框小设计 目的&#xff1a;实现鼠标点击输入框时的聚焦效果。 首先在LoginForm构造函数中为账号和密码输入框添加事件过滤器。关于事件过滤器的具体介绍可以参考这篇博文&#xff1a;Qt消息机制和事件 ui->nameEdit->installEventFilter(this); ui->pwdEdit->…

常用换源总结

1.Ubuntu16.04更换国内源 在Ubuntu系统上使用apt-get install进行软件安装或更新的时候&#xff0c;由于使用的是国外源&#xff0c;导致下载速度很慢或者连接超时&#xff0c;需要更换下载源。 1.将系统原始的源文件进行备份 sudo cp /etc/apt/sources.list /etc/apt/source…

docker踩坑记录

踩坑记录 1.1 后台启动容器&#xff0c;实际没有启动 现象&#xff1a; 后台启动centos&#xff0c;结果执行docker ps命令&#xff0c;容器没启动。 原因&#xff1a; docker是以容器启动的&#xff0c;必须要有个前台进程&#xff0c;若是全部都是后台deamon守护进程&…

ChatGPT实战100例 - (12) 结构化提示词 LangGPT 实战

文章目录 ChatGPT实战100例 - (12) 结构化提示词 LangGPT 实战一、LangGPT是什么?二、远古诗人 vs 现代诗人三、LangGPT Role模板实战 - 甩锅王Role模板特征提取四、 用AI实现提示词结构化ChatGPT实战100例 - (12) 结构化提示词 LangGPT 实战 一、LangGPT是什么? 随着大模型…

代码随想录算法训练营第42天 | 01背包问题,你该了解这些! 01背包问题,你该了解这些! 滚动数组 416. 分割等和子集

目录 01背包问题&#xff0c;你该了解这些&#xff01; 01 背包 二维dp数组01背包 &#x1f4bb;实现代码 01背包问题&#xff0c;你该了解这些&#xff01; 滚动数组 一维dp数组&#xff08;滚动数组&#xff09; &#x1f4bb;实现代码 416. 分割等和子集 &#x1f…

前后端数据校验

前端校验内容 前端开发中的必要校验&#xff0c;可以保证用户输入的数据的准确性、合法性和安全性。同时&#xff0c;这些校验也有助于提供良好的用户体验和防止不必要的错误提交到后端。 1、必填字段校验&#xff1a; 对于必填的字段&#xff0c;需确保用户输入了有效的数据…

二叉树可视化

二叉树可视化 运行演示代码和程序已上传二叉树知识平衡二叉树红黑树最优二叉搜索树哈夫曼树KD树B树和B树 参考 运行演示 学习二叉树总是脑补图像&#xff0c;实在是恶心&#xff0c;就想写一个能可视化的二叉树&#xff0c;结果没控制好&#xff0c;功能越想越多&#xff0c;先…

红日三打靶!!!

红日三&#xff0c;黑盒测试 环境搭建一.外网打点1.网段探测2.端口服务扫描3.目录扫描4.网站漏洞扫描5.汇总&#xff0c;找破绽6.登陆MySQL改密码 7.进入后台&#xff0c;找能写马的地方8.蚁剑连接9.disable_functions绕过1.蚁剑插件绕过2.bypass_disablefunc_via_LD_PRELOAD绕…

Django模型(四)

一、数据操作初始化 from django.db import models# Create your models here. class Place(models.Model):"""位置信息"""name = models.CharField(max_length=32,verbose_name=地名)address = models.CharField(max_length=64,null=True,verbo…

利用OpenCV实现物流与生产线自动化的革命性突破

背景介绍&#xff1a; 在当今高度自动化的时代&#xff0c;物流和生产线上的每一个环节都关乎企业的核心竞争力。传统的生产方式往往依赖于人工检测和操作&#xff0c;这不仅效率低下&#xff0c;而且容易出错。为了解决这一问题&#xff0c;越来越多的企业开始寻求利用计算机视…

【高阶数据结构】红黑树

文章目录 前言什么是红黑树红黑树的性质红黑树结点的定义红黑树的插入情况一情况二情况三插入代码总结 验证是否为红黑树红黑树的删除 前言 前面我们学习了 AVL 树——高度平衡的二叉搜索树&#xff0c;AVL 树保证了结点的左右子树的高度差的绝对值不超过 1&#xff0c;也就是…

【正点原子STM32】STM32时钟系统(时钟树、时钟源、分频器和倍频系数、锁相环、STM32CubeMX时钟树、系统时钟配置步骤)

一、认识时钟树 1.1、什么是时钟&#xff1f;1.2、认识时钟树&#xff08;F1&#xff09;1.3、认识时钟树&#xff08;F4&#xff09;1.4、认识时钟树&#xff08;F7&#xff09;1.5、认识时钟树&#xff08;H7&#xff09; 二、配置系统时钟 2.1、系统时钟配置步骤2.2、外设…

绝世唐门:霍挂六个十万年魂环,一穿七灭团再现,淘汰赛顺利晋级

Hello,小伙伴们&#xff0c;我是拾荒君。 国漫《斗罗大陆2绝世唐门》第32期超前爆料&#xff0c;霍雨浩开局便释放六个十万年魂环&#xff0c;以绝对的气场碾压天灵学院代表队。首次参与高级魂师大赛&#xff0c;霍雨浩便大放异彩秀出超级霍挂&#xff0c;此等操作就连当初的唐…