数据结构之顺序表的相关知识点及应用

 个人主页(找往期文章包括但不限于本期文章中不懂的知识点):我要学编程(ಥ_ಥ)-CSDN博客

目录

顺序表的概念及结构

顺序表的分类

顺序表的实现 

在顺序表中增加数据 

在顺序表中删除数据 

在顺序表中查找数据 

顺序表源码


 

顺序表的概念及结构

在了解顺序表之前,得先知道一个东西:线性表。线性表(linear list)是n个具有相同特性的数据元素的有限序列。简单理解就是:线性表指的是具有部分相同特性的一类数据结构的集合。例如:蔬菜分为绿叶类、瓜类、菌菇类。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。 如何理解逻辑结构和物理结构?我们去超市买东西的时候,去付款时要排队,假设有很多人,也就意味着我们需要排成一条对去付款。这是在逻辑上就是一条线性的(我们下意识认为,是理想的),但是实际上在站队时不一定是线性的(现实情况)。但是顺序表在逻辑结构和物理结构上都是线性的。这是因为顺序表的底层实现逻辑是数组。我们知道数组在内存上是连续存放的(实际情况)。

注意:顺序表的底层虽然是数组,但是却是在数组的基础之上对数组进行了封装,实现了增删查改的一些功能。

顺序表的分类

我们知道数组有两种:一种是定长数组,也就是空间大小不可变化,是固定的;还有一种是变长数组,这个变长数组是我们用动态内存开辟函数申请来的(注意区分C99中引入的变长数组)。

根据数组的不同,顺序表也分为两种:静态顺序表(大小不可变),动态顺序表(大小可变)。

顺序表的实现 

这两种顺序表一比较,肯定是第二种的优势明显一些,同样在项目中,动态顺序表的应用远远大于静态顺序表。下面我们就来学习动态顺序表的实现。

首先创建三个文件:SeqList.h  —— 顺序表的头文件  SeqList.c  —— 顺序表的实现  test.c——>测试顺序表

顺序表的创建:

typedef struct SeqList
{SLDataType* arr;//数组指针int size;//记录当前有效的空间大小int capacity;//记录当前总空间大小
}SL;//由于struct SeqList太长,比较麻烦,因此就重新定义

由于数组的类型是暂定为int,后续如果要改动的话,不是很方便,因此也是重定义。

typedef int SLDataType;//数组不一定是int类型

顺序表创建完了之后,就得开始实现它的基本功能:增 删 查 改。

在实现上面那些基本功能之前,我们肯定得把这个顺序表进行初始化。 

//初始化顺序表
void InitSeqList(SL* ps)//由于要改变顺序表,所以传地址
{ps->arr = NULL;//没为数组分配内存空间ps->capacity = 0;ps->size = 0;
}

在顺序表中增加数据 

接下来就开始实现增加数据,这个增加稍微有所不同:是在指定的位置增加数据。

我们先来实现两种特殊的情况:头插和尾插。头插是在顺序表的第一个位置(数组下标为0的位置)插入(增加)数据;尾插是在顺序表的有效数据的末尾插入(增加)数据。

首先,来实现头插:(数据从后往前覆盖)

//在顺序表的头部插入数据
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);//判断是否需要扩容if (ps->size == ps->capacity)//数组满了{int newcapacity = 0;if (ps->capacity == 0){newcapacity = BASE;//如果空间为0,先给BASE(4)个空间}else{newcapacity = 2 * ps->capacity;//扩容后为扩容前的两倍}//上面这个if...else语句,也可以写成下面这样//int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity*sizeof(SLDataType));if (tmp == NULL)//扩容失败{perror("realloc");exit(1);//直接退出程序不在执行(异常退出)//return 1; //这样写也是可以的}//扩容成功ps->arr = tmp;ps->capacity = newcapacity;}//头插数据for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];//size[1] = size[0]; //根据边界来推上面的判断条件}ps->arr[0] = x;ps->size++;
}

注意:在比较大型的项目中,我们写一些功能代码时,一定要去检查功能是否完整。比如:这里我们写这个头插功能的代码时,写完之后一定要去打印结果,看看是否满足我们的要求。 

头插写完就开始写尾插了。

//在顺序表的末尾插入数据
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);//判断是否需要扩容if (ps->capacity == ps->size)//数组满了{int newcapacity = 0;if (ps->capacity == 0)//还没分配空间{newcapacity = BASE;}else{newcapacity = 2 * ps->capacity;}SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity*sizeof(SLDataType));if (tmp == NULL){perror("realloc");exit(1);}ps->arr = tmp;tmp = NULL;ps->capacity = newcapacity;}//尾插数据ps->arr[ps->size++] = x;//上面也可以转为下面的写法//ps->arr[ps->size] = x;//ps->size++;
}

通过观察上面两个代码,我们会发现那个判断是否需要增容的部分是一样的,因此我们就可以把这个判断是否需要增容的部分写成一个函数(可以只判断,也可以把增容的部分也写进去)。

那么上面的代码就可以简化成下面的样子。 

//判断是否需要增容(如果需要,则直接自动增容)
void SLCheckCapacity(SL* ps)
{if (ps->size == ps->capacity)//数组满了{int newcapacity = 0;if (ps->capacity == 0){newcapacity = BASE;//如果空间为0,先给4个空间}else{newcapacity = 2 * ps->capacity;//扩容后为扩容前的两倍}//上面这个if...else语句,也可以写成下面这样//int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity*sizeof(SLDataType));if (tmp == NULL)//扩容失败{perror("realloc");exit(1);//直接退出程序不在执行(异常退出)//return 1; //这样写也是可以的}//扩容成功ps->arr = tmp;ps->capacity = newcapacity;}
}//在顺序表的头部插入数据
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);//判断是否需要扩容SLCheckCapacity(ps);//头插数据for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];//size[1] = size[0]; //根据边界来推上面的判断条件}ps->arr[0] = x;ps->size++;
}//在顺序表的末尾插入数据
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);//尾插数据ps->arr[ps->size++] = x;//上面也可以转为下面的写法//ps->arr[ps->size] = x;//ps->size++;
}

两种特殊的插入写完之后,就得开始写指定插入,就是说在指定的位置插入数据。 (和头插一样,从后往前覆盖)

//在指定位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);SLCheckCapacity(ps);for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];//arr[2] = arr[1]; }ps->arr[pos] = x;ps->size++;
}

在顺序表中删除数据 

接下来就是删除数据。同样删除数据也根据上面的方式来先实现特殊方式:头删和尾删。 

注意删除数据时,一定要判断是否有数据。

先来看头删。(数据从前往后覆盖)

void SLPopFront(SL* ps)
{assert(ps);assert(ps->size);//看看有没有数据for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

尾删就比较简单了。 

void SLPopBack(SL* ps)
{assert(ps);assert(ps->size);//ps->arr[size-1] = 0; //这一步可有可无ps->size--;
}

接下来就是写在指定位置删除数据。 

在顺序表中查找数据 

最后,我们就要实现在顺序表中查找数据。 

找数据的话,就是简单的循环找就行了。

//在顺序表中查找数据
int SLFind(SL* ps, SLDataType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (ps->arr[i] == x){return i;//找到了,返回x在顺序表中的下标}}return -1;//没找到
}

上面就是顺序表的全部逻辑以及实现。

顺序表源码

下面是顺序表的源码:

SeqList.c

#include "SeqList.h"//初始化顺序表
void InitSeqList(SL* ps)
{ps->arr = NULL;//没为数组分配内存空间ps->capacity = 0;ps->size = 0;
}//销毁顺序表
void SLDestroy(SL* ps)
{if (ps->arr)//有可能我们还没有使用(为空){free(ps->arr);}ps->arr = NULL;ps->size = 0;ps->capacity = 0;
}//打印顺序表
void SLPrint(const SL* ps)//只是打印不想被更改数据
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}printf("\n");//可以选择换行,不写也没关系
}//判断是否需要增容(如果需要,则直接自动增容)
void SLCheckCapacity(SL* ps)
{if (ps->size == ps->capacity)//数组满了{int newcapacity = 0;if (ps->capacity == 0){newcapacity = BASE;//如果空间为0,先给4个空间}else{newcapacity = 2 * ps->capacity;//扩容后为扩容前的两倍}//上面这个if...else语句,也可以写成下面这样//int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity*sizeof(SLDataType));if (tmp == NULL)//扩容失败{perror("realloc");exit(1);//直接退出程序不在执行(异常退出)//return 1; //这样写也是可以的}//扩容成功ps->arr = tmp;ps->capacity = newcapacity;}
}//在顺序表的头部插入数据
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);//判断是否需要扩容SLCheckCapacity(ps);//ps是一个指针(没有&,因此也是一个值)//头插数据for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];//size[1] = size[0]; //根据边界来推上面的判断条件}ps->arr[0] = x;ps->size++;
}//在顺序表的末尾插入数据
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);//尾插数据ps->arr[ps->size++] = x;//上面也可以转为下面的写法//ps->arr[ps->size] = x;//ps->size++;
}//在指定位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);SLCheckCapacity(ps);for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];//arr[2] = arr[1]; }ps->arr[pos] = x;ps->size++;
}//删除顺序表头部的数据
void SLPopFront(SL* ps)
{for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}//删除顺序表尾部的数据
void SLPopBack(SL* ps)
{assert(ps);assert(ps->size);//ps->arr[size-1] = 0; //这一步可有可无ps->size--;
}//删除指定位置的数据
void SLErase(SL* ps, int pos)
{assert(ps);assert(ps->size);for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}//在顺序表中查找数据
int SLFind(SL* ps, SLDataType x)
{for (int i = 0; i < ps->size; i++){if (ps->arr[i] == x){return i;//找到了,返回x在顺序表中的下标}}return -1;//没找到
}

SeqList.h 

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>#define BASE 4typedef int SLDataType;//数组不一定是int类型typedef struct SeqList
{SLDataType* arr;//数组指针()int size;//记录当前有效的空间大小int capacity;//记录当前总空间大小
}SL;void InitSeqList(SL* ps);//初始化顺序表void SLDestroy(SL* ps);//销毁顺序表void SLPrint(const SL* ps);//打印顺序表的数据void SLPushFront(SL* ps, SLDataType x);//在顺序表的头部插入数据void SLPushBack(SL* ps, SLDataType x);//在顺序表的末尾插入数据void SLInsert(SL* ps, int pos, SLDataType x);//在指定位置插入数据void SLPopFront(SL* ps);//删除顺序表头部的数据void SLPopBack(SL* ps);//删除顺序表尾部的数据void SLErase(SL* ps, int pos);//删除指定位置的数据int SLFind(SL* ps, SLDataType x);//在顺序表中查找数据

好啦!本期数据结构顺序表的学习就到此为止啦!我们下一期再一起学习吧! 

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

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

相关文章

安全的通信协议HTTPS被攻击改采用什么防护方案

随着互联网的发展&#xff0c;保护用户在网上交换的敏感信息的安全性变得至关重要。HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;作为一种安全的通信协议&#xff0c;通过加密数据传输&#xff0c;保护用户的隐私和数据安全。然而&#xff0c;尽管HTTPS提…

Java配置自定义校验

1、自定义注解State message、groups、payload package com.zhang.anno;import com.zhang.validartion.StateValidation; import jakarta.validation.Constraint; import jakarta.validation.Payload;import java.lang.annotation.*;import static java.lang.annotation.Eleme…

list的使用

前言 我们前面已经对string和vector进行了学习使用&#xff0c;以及对他们的底层进行了模拟实现&#xff01;本期我们继续学习STL的另外一个容器---list。 本期内容介绍 什么是list&#xff1f; list的常用接口 什么是list? 还是来看看官方的文档说明&#xff01; 这里通过…

钉钉事件订阅前缀树算法gin框架解析

当钉钉监测到发生一些事件&#xff0c;如下图 此处举例三个事件user_add_org、user_change_org、user_leave_org&#xff0c;传统的做法是&#xff0c;我们写三个if条件&#xff0c;类似下图 这样字符串匹配效率比较低&#xff0c;于是联想到gin框架中的路由匹配算法&#xff0…

jangow-01-1.0.1 靶机渗透

信息收集部分&#xff1a; 1.nmap存活探测&#xff1a; 2.nmap端口探测&#xff1a; 3.nmap服务探测&#xff1a; 这里应该是21和80端口&#xff0c;&#xff0c;但21需要登陆访问&#xff0c;用不了&#xff0c;问题不大。 web&#xff1a; 页面&#xff1a; 发现RCE: 1.we…

docker容器环境安装记录(MAC M1)(完善中)

0、背景 在MAC M1中搭建商城项目环境时&#xff0c;采用docker统一管理开发工具&#xff0c;期间碰到了许多环境安装问题&#xff0c;做个总结。 1、安装redis 在宿主机新建redis.conf文件运行创建容器命令&#xff0c;进行容器创建、端口映射、文件挂载、以指定配置文件启动…

【SCI绘图】【曲线图系列1 python】绘制扫描点平滑曲线图

SCI&#xff0c;CCF&#xff0c;EI及核心期刊绘图宝典&#xff0c;爆款持续更新&#xff0c;助力科研&#xff01; 本期分享&#xff1a; 【SCI绘图】【曲线图1 python】绘制扫描点平滑曲线图 1.环境准备 python 3 import numpy as np import pandas as pd import proplot …

win10上一个详细的Django开发入门例子

1.Django概述 Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MTV的框架模式&#xff0c;即模型M&#xff0c;视图V和模版T。 Django 框架的核心组件有&#xff1a; 用于创建模型的对象关系映射&#xff1b; 为最终用户设计较好的管理界面&#xff1b…

使用Arcpy进行数据批处理-批量裁剪

时空大数据使我们面临前所未有的机遇和挑战&#xff0c;尤其在地学、遥感或空间技术等专业领域&#xff0c;无疑是一个全新的时代。 伴随着时空大数据的到来&#xff0c;海量数据的处理是一个所有科研工作者都无法忽视的重要问题。传统的数据&#xff08;主要指空间数据&#x…

Docker实战教程 第1章 Linux快速入门

2-1 Linux介绍 为什么要学Linux 三个不得不学习 课程需要&#xff1a;Docker开发最好在Linux环境下。 开发需要&#xff1a;作为一个后端程序员&#xff0c;是必须要掌握Linux的&#xff0c;这是找工作的基础门槛。 运维需要&#xff1a;在服务器端&#xff0c;主流的大型服…

SDWebImage源码解析---疑难问题解答

SDWebImage的简单流程图&#xff1a; 上图大致流程是对的&#xff0c;有几个没写到的地方&#xff1a; 首先判断url的类型是不是URL类型或string类型&#xff0c;判断url是否为nil占位图更早一些&#xff0c;在url判断后&#xff0c;就行显示占位图加载沙盒中对应的图片后&…

Premiere Pro 2024:赋予创意翅膀,让你的视频飞翔 mac/win版

Premiere Pro 2024&#xff0c;作为Adobe旗下的旗舰视频编辑软件&#xff0c;自推出以来&#xff0c;一直在视频制作领域占据着重要的地位。随着技术的不断进步和创新&#xff0c;Premiere Pro 2024为用户带来了前所未有的编辑体验&#xff0c;重新定义了视频制作的标准。 Pre…

数字逻辑分析仪初体验

为啥会用到这玩意儿&#xff0c;要从一个荒诞的需求开始。想在市面上找一款特别低空飞行的监控&#xff0c;而且不想它一直开着监控&#xff0c;最好是我在外面远程指挥它起飞&#xff0c;飞去厨房&#xff0c;飞去洗手间&#xff0c;甚至飞去阳台&#xff0c;查看水龙头情况啊…

Nuxt 项目的创建

中文文档&#xff1a;https://nuxt.com.cn/docs/getting-started/installation#%E6%96%B0%E9%A1%B9%E7%9B%AE Nuxt 项目创建的先决条件&#xff1a; Node.js 版本 18.0.0 及以上文本编辑器&#xff1a;VS Code Volar 插件 或 Webstorm 执行如下命令&#xff0c;创建 Nuxt 项目…

【Canvas技法】用椭圆绘制经纬线

【图示】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>用椭圆绘制经纬线</title><style type"text/css"…

相对论中关于光速不变理解的补充

近几个月在物理直播间聊爱因斯坦相对论&#xff0c;发现好多人在理解爱因斯坦相对论关于基本假设&#xff0c;普遍认为光速是不变的&#xff0c;质能方程 中光速的光速不变的&#xff0c;在这里我对这个假设需要做一个补充&#xff0c;他是基于质能方程将光速C 在真是光速变化曲…

Allavsoft for Mac v3.27.0.8852注册激活版 优秀的视频下载工具

Allavsoft for Mac是一款功能强大的多媒体下载和转换工具&#xff0c;支持从各种在线视频网站和流媒体服务下载视频、音频和图片。它具备批量下载和转换功能&#xff0c;可将文件转换为多种格式&#xff0c;以适应不同设备的播放需求。此外&#xff0c;Allavsoft还提供视频编辑…

最新版两款不同版SEO超级外链工具PHP源码

可根据个人感觉喜好自行任意选择不同版本使用&#xff08;版V1或版V2&#xff09; 请将zip文件全部解压缩即可访问&#xff01; 源码全部开源&#xff0c;支持上传二级目录访问 #已更新增加大量高质量外链&#xff08;若需要增加修改其他外链请打开txt文件&#xff09; #修…

贪心算法|53.最大子序和

力扣题目链接 class Solution { public:int maxSubArray(vector<int>& nums) {int result INT32_MIN;int count 0;for (int i 0; i < nums.size(); i) {count nums[i];if (count > result) {result count;}if (count < 0) count 0;}return result;} …

SpringBoot+ECharts+Html 字符云/词云案例详解

1. 技术点 SpringBoot、MyBatis、thymeleaf、MySQL、ECharts 等 2. 准备条件 在mysql中创建数据库echartsdb&#xff0c;数据库中创建表t_comment表&#xff0c;表中设置两个字段word与count&#xff0c;添加表中的数据。如&#xff1a;附件中的 echartsdb.sql 3. SpringBoot…