【数据结构】实现顺序表

大家好,我是苏貝,本篇博客带大家了解顺序表,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
在这里插入图片描述


目录

  • 一.概念及结构
  • 二.接口实现
    • 2.1 创建顺序表结构体
    • 2.2 初始化顺序表
    • 2.3 销毁顺序表
    • 2.4 打印顺序表
    • 2.5 尾插
    • 2.6 头插
    • 2.7 尾删
    • 2.8 头删
    • 2.9 任意位置插入
    • 2.10 任意位置删除
    • 2.11 查找并返回下标
  • 三.模块化代码实现
    • 3.1 SeqList.h
    • 3.2 SeqList.c
    • 3.3 test.c
    • 3.4 结果演示

一.概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1.静态顺序表:使用定长数组存储元素。
在这里插入图片描述

2.动态顺序表:使用动态开辟的数组存储。

在这里插入图片描述


二.接口实现

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

首先在编译器中建立3个文件:SeqList.h文件,即一个头文件,用来声明;SeqList.c文件,用来实现顺序表的增删查改基本功能;test.c文件,用来测试代码


2.1 创建顺序表结构体

因为顺序表信息包括一个动态数组、数据个数和数组容量,后两者都是int型,数组的类型不确定,而且它们所占空间大小不同,所以我们想到创建一个顺序表结构体,为了书写方便,我们将struct SeqList重命名为SL

因为我们不知道多态数组的类型,所以将它的类型定为SLDataType,如果我们后来知道了动态数组的类型,比如是int型,只要将int重命名为SLDataType即可

typedef int SLDataType;typedef struct SeqList
{SLDataType* a;//指向动态开辟的数组int size;//有效数据个数int capacity;//数组的容量
}SL;

2.2 初始化顺序表

因为初始化顺序表会改变顺序表,所以不能采用传值调用,只能传址,即将顺序表的地址传过来。因为顺序表结构体不能为NULL,所以对它断言,接口都需要使用顺序表结构体,所以每个接口都需要对它断言。
注意:size是最后一个有效数据的下一个索引,因为初始化的时候还没有有效数据,所以size置为0

void SLInit(SL* s)
{assert(s);s->a = NULL;s->size = 0;s->capacity = 0;
}

2.3 销毁顺序表

因为我们开辟的是动态数组,所以需要在程序结束前将数组所占空间释放,再让指向该数组的指针a置为NULL

void SLDestroy(SL* s)
{assert(s);if (s->a != NULL){free(s->a);s->size = 0;s->capacity = 0;s->a = NULL;}
}

2.4 打印顺序表

打印顺序表不需要改变顺序表,为什么我们还是采用传址调用呢?因为相较于传值调用,两者都可以实现该接口,但是因为函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降 。简单来讲就是因为形参是实参的一份临时拷贝,若实参(即结构体)过大,那么形参也会较大,这样就会导致效率降低。但传结构体变量的地址的话,形参的大小只是4个或8个字节,效率较传结构体更高。所以我们还是采用传址调用

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

2.5 尾插

尾插即在动态数组的最后一个有效数据后面插入一个数据。在插入数据之前,我们要判断是否需要增容,增容的条件是有效数据个数size>=数组容量capacity。增容时我们一般将新的容量置为原来容量的2倍,因为我们初始化的时候将初始容量置为0,所以先要对容量判断,如果=0就将新的容量置为4,如果不为0则将新的容量置为原来容量的2倍。因为要增加容量,所以数组的大小会改变,所以用realloc来动态开辟数组,返回值是调整后起始位置的地址。但realloc也可能开辟空间失败,此时它的返回值是NULL,所以我们先要对它的返回值判断,如果为NULL,返回错误信息,不为NULL,将返回值赋值给指向数组的指针a

因为我们准备写的插入接口包含尾插、头插和在任意位置插入,插入时都要判断是否需要增容,所以不妨将增容的代码封装为一个函数

void SLCheckCapacity(SL* s)
{if (s->size >= s->capacity){int newcapacity = (s->capacity == 0 ? 4 : s->capacity * 2);SLDataType* tmp = (SLDataType*)realloc(s->a, sizeof(SLDataType) * newcapacity);if (tmp == NULL){perror("realloc fail");return;}s->a = tmp;s->capacity = newcapacity;}
}

插入成功后,将有效数据个数size++

void SLPushBack(SL* s, SLDataType x)
{assert(s);//判断是否需要增容SLCheckCapacity(s);s->a[s->size] = x;s->size++;
}

2.6 头插

头插时我们要将所有数据都向后挪一位,再将要插入的数据放在数组索引为0的位置上,不要忘记将有效数据个数size++

void SLPushFront(SL* s, SLDataType x)
{assert(s);//判断是否需要增容SLCheckCapacity(s);for (int i = s->size-1; i >=0; i--){s->a[i + 1] = s->a[i];}s->a[0] = x;s->size++;
}

2.7 尾删

尾删即将最后一个有效数据删除,在删除前要保证有效数据的个数>=1,所以最后一个有效数据的索引>=0,所以要size>0,对它断言。尾删只需要将size- -

void SLPopBack(SL* s)
{assert(s);assert(s->size > 0);s->size--;
}

2.8 头删

头删即将第一个有效数据删除,然后将所有的有效数据都向前挪一位。头删也需要对size断言,删除成功后记得将size- -

void SLPopFront(SL* s)
{assert(s);assert(s->size > 0);for (int i = 1; i < s->size; i++){s->a[i - 1] = s->a[i];}s->size--;
}

2.9 任意位置插入

这个接口需要的形参有指向顺序表结构体的指针,插入位置的下标以及插入的元素。pos要>=0并且要<=size,pos=0时即头插,pos=size时即尾插,对pos断言。最后size++

void SLInsert(SL* s, int pos, SLDataType x)
{assert(s);assert(pos >= 0 && pos <= s->size);SLCheckCapacity(s);for (int i = s->size-1; i >=pos; i--){s->a[i + 1] = s->a[i];}s->a[pos] = x;s->size++;
}

2.10 任意位置删除

要使用3个断言,将要删除位置下标之后的元素都向前挪一位,最后size- -

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

2.11 查找并返回下标

对动态数组遍历,如果有要查找的元素,返回其下标,否则返回-1

int SLFind(SL* s, SLDataType x)
{assert(s);for (int i = 0; i < s->size; i++){if (s->a[i] == x)return i;}return -1;
}

三.模块化代码实现

3.1 SeqList.h

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLDataType;typedef struct SeqList
{SLDataType* a;int size;int capacity;
}SL;//初始化顺序表
void SLInit(SL* s);
//打印
void SLPrint(SL* s);
//销毁
void SLDestroy(SL* s);
//头插
void SLPushFront(SL* s, SLDataType x);
//尾插
void SLPushBack(SL* s, SLDataType x);
//头删
void SLPopFront(SL* s);
//尾删
void SLPopBack(SL* s);
//任意位置插入
void SLInsert(SL* s, int pos, SLDataType x);
//任意位置删除
void SLErase(SL* s, int pos);
//查找并返回下标
int SLFind(SL* s, SLDataType x);

3.2 SeqList.c

#include"SeqList.h"//初始化顺序表
void SLInit(SL* s)
{assert(s);s->a = NULL;s->size = 0;s->capacity = 0;
}//打印
void SLPrint(SL* s)
{assert(s);for (int i = 0; i < s->size; i++){printf("%d ", s->a[i]);}printf("\n");
}//销毁
void SLDestroy(SL* s)
{assert(s);if (s->a != NULL){free(s->a);s->size = 0;s->capacity = 0;s->a = NULL;}
}//增容
void SLCheckCapacity(SL* s)
{if (s->size >= s->capacity){int newcapacity = (s->capacity == 0 ? 4 : s->capacity * 2);SLDataType* tmp = (SLDataType*)realloc(s->a, sizeof(SLDataType) * newcapacity);if (tmp == NULL){perror("realloc fail");return;}s->a = tmp;s->capacity = newcapacity;}
}//头插
void SLPushFront(SL* s, SLDataType x)
{assert(s);//判断是否需要增容SLCheckCapacity(s);for (int i = s->size-1; i >=0; i--){s->a[i + 1] = s->a[i];}s->a[0] = x;s->size++;
}//尾插
void SLPushBack(SL* s, SLDataType x)
{assert(s);SLCheckCapacity(s);s->a[s->size] = x;s->size++;
}//头删
void SLPopFront(SL* s)
{assert(s);assert(s->size > 0);for (int i = 1; i < s->size; i++){s->a[i - 1] = s->a[i];}s->size--;
}//尾删
void SLPopBack(SL* s)
{assert(s);assert(s->size > 0);s->size--;
}//任意位置插入
void SLInsert(SL* s, int pos, SLDataType x)
{assert(s);assert(pos >= 0 && pos <= s->size);SLCheckCapacity(s);for (int i = s->size-1; i >=pos; i--){s->a[i + 1] = s->a[i];}s->a[pos] = x;s->size++;
}//任意位置删除
void SLErase(SL* s,int pos)
{assert(s);assert(s->size > 0);assert(pos >= 0 && pos <= s->size);for (int i = pos; i < s->size; i++){s->a[i] = s->a[i + 1];}s->size--;
}//查找并返回下标
int SLFind(SL* s, SLDataType x)
{assert(s);for (int i = 0; i < s->size; i++){if (s->a[i] == x)return i;}return -1;
}

3.3 test.c

#include"SeqList.h"void test1()
{SL s1;SLInit(&s1);SLPushFront(&s1, 1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLPrint(&s1);SLPushBack(&s1, 2);SLPushBack(&s1, 3);SLPushBack(&s1, 4);SLPrint(&s1);SLPopFront(&s1);SLPopFront(&s1);	SLPrint(&s1);SLPopBack(&s1);SLPopBack(&s1);SLPrint(&s1);SLDestroy(&s1);
}void test2()
{SL s1;SLInit(&s1);SLPushFront(&s1, 1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLPrint(&s1);SLInsert(&s1, 1, 10);SLInsert(&s1, 0, 10);SLPrint(&s1);SLErase(&s1, 3);SLErase(&s1, 2);SLPrint(&s1);SLDestroy(&s1);
}void test3()
{SL s1;SLInit(&s1);SLPushFront(&s1, 1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLPrint(&s1);int ret=SLFind(&s1, 3);printf("%d\n", ret);SLDestroy(&s1);
}int main()
{test3();return 0;
}

3.4 结果演示

1.test1
在这里插入图片描述

2.test2
在这里插入图片描述

3.test3
在这里插入图片描述


好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️

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

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

相关文章

Unity 设置鼠标

目录 前言 图标样式的设置 代码控制 编辑器直接修改 图标的显隐 CursorLockMode Cursor.visible 前言 本章主要对鼠标图标样式还有鼠标显隐进行设置 图标样式的设置 代码控制 有时候需要有改变鼠标样式的需求可以使用如下代码 Cursor.SetCursor(this.mouseTexture, Vec…

[word] 怎么删除文字底纹 #职场发展#其他

怎么删除文字底纹 怎么删除文字底纹?我们在录入文字到文档的时候&#xff0c;或者是复制网上内容时&#xff0c;都会带有格式&#xff0c;有时候还会遇到删除不掉的问题。今天给大家分享小技巧&#xff0c;解决你的问题。 1、删除文字底纹 文档自带的底纹&#xff0c;删除技…

C++实现智能指针(涉及知识点:重载运算符,内存泄露的风险)

案例 有时候代码很长&#xff0c;很容易就忘了释放P。导致内存泄露&#xff0c;在程序结束后才会释放。内存泄露的风险 如果代码需要的内存很大&#xff0c;前面的代码用完了new申请的内容不去释放&#xff0c;就会被一直占用着&#xff0c;后面可能不够用了造成程序崩溃。解决…

linux文件权限备份、恢复-linux文件权限如何备份、恢复-getfacl/setfacl备份恢复文件权限

0、序 在运维这条路上走久了&#xff0c;你能听到或者遇到这样的事情就越多&#xff0c;甚至是你自己干过的&#xff1a; 一个信心满满的运维人员一个不小心&#xff0c;输入 "chmod -R 777 /" 导致一个巨大的悲剧&#xff0c;然后整个部门从上到下被撸一顿。虽然…

牛客周赛 Round 31

D. 思路&#xff1a;使用map构造两个链表。 #include <bits/stdc.h> using namespace std;map<int,int> l,r; int main() {int q;cin>>q;int op-1e9-1;int ed1e91;r[op]ed;l[ed]op;while(q--){int a;cin>>a;if(a1){int x,y;cin>>x>>y;int…

MIT6.5830 实验3

前置回顾 在实验2中&#xff0c;完成了增删查改、排序、分组、聚合、连接等基本操作&#xff0c;在已提供 sql 解析器的基础上&#xff0c;能够运行进本的 sql 语句。都是逻辑层的实现&#xff0c;没有涉及物理存储方面的内容。 实验目标 实现最简单的基于锁的transaction&am…

MicroPython ESP32开发:通过寄存器直接访问外围设备

可以通过直接读写寄存器来控制 ESP32 的外设。这就需要阅读数据手册&#xff0c;了解要使用哪些寄存器以及要写入哪些值。下面的示例展示了如何打开和更改 MCPWM0 外设的预分频器。 from micropython import const from machine import mem32# Define the register addresses …

【STM32+HAL库+CubeMX】UART轮询收发、中断收发、DMA收发方法及空闲中断详解

&#xff08;转载&#xff09;原文链接&#xff1a;https://blog.csdn.net/qq_39344192/article/details/131470735 1. 什么是UART&#xff1f; UART是一种异步串行通信接口&#xff0c;常用于通过串口与外部设备进行通信。它通过发送和接收数据帧来实现数据传输&#xff0c;使…

postman 文档、导出json脚本 导出响应数据 response ,showdoc导入postman json脚本 导出为文档word或markdown

保存、补全尽可能多的数据、描述 保存响应数据 Response&#xff1a;&#xff08;如果导出接口数据&#xff0c;会同步导出响应数据&#xff09; 请求接口后&#xff0c;点击下方 Save as Example 可以保存响应数据到本地&#xff08;会在左侧接口下新增一个e.g. 文件用来保…

使用_NT_SYMBOL_PATH在启动VS前设置PDB路径

一、背景 由于公司相关项目的开发管理方式&#xff0c;导致公司会存在多个分支的版本正在开发/测试中。 在这样的背景下&#xff0c;我的日常工作中有时会出现存在某个分支的项目软件的某个功能出现了问题需要我去排查解决&#xff0c;而我当前并不在该分支上开发。于是只能安装…

C++泛编程(3)

类模板基础 1.类模板的基本概念2.类模板的分文件编写3.类模板的嵌套 &#xff08;未完待续...&#xff09; 在往节内容中&#xff0c;我们详细介绍了函数模板&#xff0c;这节开始我们就来聊一聊类模板。C中&#xff0c;类的细节远比函数多&#xff0c;所以这个专题也会更复杂。…

Llama2大模型开源,大模型的Android时代来了?

就昨天凌晨,微软和Meta宣布Llama2大模型开源且进一步放开商用,一下朋友圈刷屏。要知道,开源界最强大的模型就是过去Meta开源的Llama,而现在Llama2更强大,又开放商用,更有微软大模型霸主企业撑腰(微软既投资大模型界的IOS——ChatGPT,又联合发布大模型的Android——Llam…

【2月比赛合集】28场可报名的数据挖掘大奖赛,任君挑选!

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 Kaggle&#xff08;2场比赛&#xff09;阿里天池&#xff08;…

数据库学习笔记2024/2/4

随笔 1. 为什么学? 认识数据,熟悉数据,掌握数据。 进企业必备技能。 2. 怎么学? 1、MySQL数据库就是存储和管理数据的一个大型软件,这个软件有一个专门的语言叫SQL,主要学的是SQL语言,但想要达到企业用人标准,就还得学会熟练使用MySQL这个软件。 2、学习分三阶段: 一. …

微信小程序课设(基于云开发)

微信小程序通过Laf云平台接入ChatGPT实现聊天&#xff0c;回答方式采用流式回答。 以下是图片展示其页面 回答次数通过卡密兑换 以下是对话页面的代码 <!--pages/content/content.wxml--><view class"container"><view class"div" hidde…

软件测试学习笔记-使用jmeter进行接口测试

使用jmeter之前首先需要安装jdk&#xff0c;配置其环境变量&#xff0c;然后再安装jmeter 软件测试学习笔记-使用jmeter进行接口测试 1. 断言1. 响应断言2. JSON断言 2. 参数化1. 用户定义的变量2. CSV参数化 3. 接口关联4. 连接数据库 1. 断言 对某个接口其中一些信息进行判断…

DiskGenius v4.30专业版下载

DiskGenius是一款专业级的数据恢复软件&#xff0c;算法精湛、功能强大&#xff0c;用户群体广泛&#xff1b;支持各种情况下的文件恢复和分区恢复&#xff0c;恢复效果好&#xff1b;文件预览、扇区编辑、加密分区恢复、Ext4分区恢复、RAID恢复等高级功能应有尽有&#xff0c;…

Vue3+TS+Vite+Pinia最全学习总结

VUE3介绍 vue2和vue3之间的区别 因为需要遍历data对象上所有属性&#xff0c;所以如果data对象属性结构嵌套很深&#xff0c;就会存在性能问题。因为需要遍历属性&#xff0c;所有需要提前知道对象上有哪些属性&#xff0c;才能将其转化为getter和setter,所以vue2中无法将data新…

一起畅玩!幻兽帕鲁服务器1分钟开服,不服来战!

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…

Facebook的社交影响力:用户行为解析与趋势

在当今数字时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分&#xff0c;而Facebook作为全球最大的社交平台之一&#xff0c;其社交影响力愈发显著。本文将深入分析Facebook的社交影响力&#xff0c;解析用户行为&#xff0c;同时探讨当前和未来的社交趋势。 社…