C语言数据结构专题--顺序表(1基础)

前言

我们在对C语言有一定的了解之后,我们就可以开始数据结构的学习了,数据结构多用指针、结构体、动态内存开辟等知识,若对这些知识还不太了解的朋友,就需要加深其理解了,那么废话不多说,我们正式开始本节的学习

什么是数据结构

数据结构是由"数据" 和 "结构" 两个词相组合得到的 

那么什么是数据呢?

例如:常见的数字1、2、3、4、5等,手机通讯录里面保存的联系人信息(号码、名字等)、浏览器网页里面的信息,这些都统称为数据

什么是结构呢?

当我们需要使用大量同一类型的数据时,我们手动的定义大量独立的变量对程序来说可读性很差,我们往往可以借助数组等这些数据结构将大量的数据组织在一起,所以结构也可以理解为组织数据的方式

举个通俗易懂的例子:我们如果想要在一大片草场上寻找一名叫 "多莉" 的羊难度会很大,但是我们要在羊圈中找到 "多莉" 就会很简单,羊圈就好像一个结构,而羊就好似数据

概念:数据结构是计算机存储、组织数据的⽅式。数据结构是指相互之间存在⼀种或多种特定关系 的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么⽅式构成,以及数据元素之间呈现的结构

其中:在数据结构中最基础的数据结构就是数组

顺序表

顺序表是线性表的一种,其中:线性表是具有相同特征的数据结构的集合

线性表的特征:

1.物理结构不一定连续(数据间的地址不一定是连续的)

2.逻辑结构是连续的

顺序表在物理结构和逻辑结构层面上都是连续的

顺序表的底层就是数组,顺序表是在数组的基础上进行维护、封装

可能有人就会产生疑问:既然都有数组了,数组也可以实现数据的增加、删除、查询、修改功能,那么顺序表的存在又有什么意义呢?

当我们想修改、插入或者删除数组中的某个数据时,我们首先需要通过循环来查找数组之中已有元素的个数,再进行数据的修改、插入或者删除,我们每次进行操作的时候都需要进行循环的处理,这样就会很浪费时间和空间,此时顺序表的存在就有意义了

顺序表的底层虽然是数组,但是数组提供了很多现成的办法来处理数据,相比直接使用数组会更加的简洁高效

顺序表分类

1.静态顺序表

struct Seqlist
{int arr[100];int size;//记录顺序表当前有效数据的个数};

静态顺序表的底层是一个定长的数组,在代码的编译阶段,我们就确认了数组的大小

2.动态顺序表

//动态顺序表
struct Seqlist
{int* arr;int size;//有效的数据个数int capacity;//记录空间大小
};

那么动态顺序表和静态顺序表二者相比较谁更好呢?

对于静态顺序表而言。若是数组大小给小了,则会导致空间不够用的问题,可能导致数据的丢失;若是数组大小给大了,则会导致空间的浪费

所以动态顺序表更好,因为它更加灵活

我们在实现顺序表时,我们通常把它分成两个文件

头文件:顺序表的结构,声明实现顺序表的方法(头文件实现的是类似于书的目录的功能)

源文件:实现顺序表的方法

同时我们在实现顺序表的时候还需要多创建一个文件,用于检测其功能是否正常

​
//动态顺序表
struct Seqlist
{int* arr;int size;//有效数据的个数int capacity;//空间大小
};​

我们先在头文件中定义顺序表结构体,考虑到我们存入顺序表中的数据类型不一定为 int 类型,还有可能是 char 类型的数据,所以我们可以定义一下,用定义表示当前顺序表中存储数据的类型,具体操作如下:

typedef int SLDataType;//动态顺序表
struct Seqlist
{SLDataType* arr;int size;//有效数据的个数int capacity;//空间大小
};

我们可以重命名一下结构体类型来简化代码:

//动态顺序表
typedef struct Seqlist
{SLDataType* arr;int size;//有效数据的个数int capacity;//空间大小
}SL;

下面我们就来实现一下顺序表的各种功能:

顺序表的初始化

在源文件中初始化前,我们需要在头文件中声明一下

void SLInit(SL ps);

此时我们就可以在源文件中初始化

#include"Seqlist.h"
void SLInit(SL s)
{s.arr = NULL;s.size = s.capacity = 0;}

由于我们不知道初始化是否成功,我们就可以在测试文件中通过打印各个成员的值,通过在屏幕上面打印的值在观察出初始化是否成功

我们按照我们的常规理解来写出如下代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"void SLTest01()
{SL s1;SLInit(s1);}int main()
{SLTest01();return 0;
}

当我们在运行程序的时候,我们发现它报错了

这个报错非常的奇怪,编译器说我们使用了未初始化的局部变量 s1,而我们现在所做的操作不正是要初始化吗?这样不是自相矛盾吗?

我们仔细分析一下就可以发现问题:

我们传入的 sl 是形式参数,我们采取的是传值操作,由于 sl 本来就是没有初始化的变量,无法进行传值操作,要想解决这个问题,我们需要使用传址操作,现在我们就来改一下代码:

#include"Seqlist.h"
void SLInit(SL* ps)
{ps->arr = NULL;ps->size = ps->capacity = 0;}
void SLInit(SL* ps);

此时我们的初始化代码就成功完成了

顺序表的销毁

因为数组的大小是动态开辟的,需要使用到动态内存函数,我们需要释放开辟的空间,那么我们就需要采取如下的操作:

顺序表的头部 插入 和 删除 + 顺序表的 尾部 插入 和 删除
顺序表的尾插

举个例子:若数组里面只有四个数据

0123

                              0                     1                  2                    3             4               5

此时 size = 4  capacity = 6,我们想要在它的尾部插入 x = 5

我们可以发现我们想要插入数据的地方的下标就是 size 的大小,同时再插入数据之前,我们需要先保证有空间允许去插入,如果空间不够,我们还需要去申请空间,那么我们该怎么申请空间呢?

首先,空间的申请需要使用到 malloc calloc realloc 函数,当申请的空间被使用完了我们还需要去增容,那么我们要申请多大的空间呢?一次增容又该增多大呢?

申请空间的规则:增容通常来说是成倍数的增加,一般是两倍增容,若空间是一个一个地去增加,那么插入数据也得一个一个的去插入,这样会非常的麻烦,若要频繁的增容,程序运行的效率就会大大降低;若一次进行大量的增容,就会造成空间的浪费。所以采取两倍两倍的增容是最合理的

4 --------> 8 --------> 16 --------> 32 --------> 64

通过以上几个注意事项,我们可以写出代码:

​
//尾插
void SLPushBack(SL* ps, SLDataType x)
{if (ps == NULL){return;}//插入数据前看空间够不够if (ps->capacity == ps->size){//申请空间//判断capacity是否为0int newCapaciy = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, ps->newCapacity * sizeof(SLDataType));if (tmp == NULL){perror("realloc fail!");exit(1);//直接退出程序,不再继续执行}//空间申请成功ps->arr = tmp;ps->capacity = newCapaciy;}ps->arr[ps->size] = x;++ps->size;//或者写作 ps->arr[ps->size++] = x;ps->size++;}​

注意:我们在增容的时候需要临时创建一个变量用于存储增容后的指针,若是直接将 其赋给 arr,倘若空间申请失败,原数据内容也会丢失,导致不可逆的错误

我们在 test.c 中测试一下这个代码

#include"Seqlist.h"void SLTest01()
{SL s1;SLInit(&s1);//测试尾插代码SLPushBack(&s1, 1);SLPushBack(&s1, 2);SLPushBack(&s1, 3);SLPushBack(&s1, 4);SLPushBack(&s1, 5);//.....SLDestory(&s1);}int main()
{SLTest01();return 0;
}

这样我们的尾插操作就完成了

顺序表的头插

举个例子:

0123

                               0                 1                    2                     3              4(size)

在开始进行头插之前。我们同样的需要判断插入数据的空间够不够,不够的话我们需要增容。

头插需要在下标为0的地方插入数据,但是原数组下标为0的地方已经有了数据,这时我们应该怎么插入数据呢?

我们此时需要把原顺序表中的数据统一向后挪动一位

0123

若我们此时需要插入一个数据 x = 6

由于我们在进行头插和尾插之前都需要判断空间是否足够,此时我们就可以把判断空间的代码单独封装成一个函数:

void SLCheckCapacity(SL* ps)
{//插入数据前看空间够不够if (ps->capacity == ps->size){//申请空间//判断capacity是否为0int newCapaciy = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapaciy * sizeof(SLDataType));if (tmp == NULL){perror("realloc fail!");exit(1);//直接退出程序,不再继续执行}//空间申请成功ps->arr = tmp;ps->capacity = newCapaciy;}
}

在数据挪动的时候,我们可以发现:数组中的最后一个元素,经过挪动后,需要到达下标为 size 处

所以由此我们可以写出头插的代码:

//头插
void SLPushFront(SL* ps, SLDataType x)
{if (ps == NULL){return;}SLCheckCapacity(ps);//先让顺序表中的数据向后挪动一位for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];//arr[1] = arr[0]}ps->arr[0] = x;ps->size++;}

我们再在 test.c 文件里面测试一下:

#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"void SLTest01()
{SL s1;SLInit(&s1);//测试尾插代码SLPushFront(&s1, 1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLPushFront(&s1, 5);//.....SLDestory(&s1);}int main()
{SLTest01();return 0;
}

我们运行调试,可以发现头插功能成功实现

由于每次都运行调试比较麻烦,我们来定义一个函数用于打印顺序表中的元素:

//打印
void SLPrint(SL s)
{for (int i = 0; i < s.size; i++){printf("%d ", s.arr[i]);}printf("\n");
}
顺序表的尾部删除
0123

我们首先需要判断顺序表是否为空,为空的话就不能执行删除操作

我们可以知道,每次删除完数据以后,数组里面的数据就会减少一个,所以 size--

此时我们就能够很轻松地写出尾部删除的代码:

//尾部删除
void SLPopBack(SL* ps)
{if (ps == NULL){return;}if (ps->size == 0){return;}--ps->size;}
我们来测试一下
void SLTest01()
{SL s1;SLInit(&s1);测试尾插代码SLPushBack(&s1, 1);SLPushBack(&s1, 2);SLPushBack(&s1, 3);SLPushBack(&s1, 4);SLPrint(s1);SLPopBack(&s1);SLPrint(s1);//.....SLDestory(&s1);}int main()
{SLTest01();return 0;
}
顺序表的头部删除

我们在删除完数据以后,需要把顺序表里原有的顺序整体向前挪动一位

根据这个,我们可以写出代码如下:

//头部删除
void SLPopFront(SL* ps)
{if (ps == NULL){return;}if (ps->size == 0){return;}//数据整体前挪for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

我们测试一下:

#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"void SLTest01()
{SL s1;SLInit(&s1);测试尾插代码SLPushBack(&s1, 1);SLPushBack(&s1, 2);SLPushBack(&s1, 3);SLPushBack(&s1, 4);SLPrint(s1);SLPopFront(&s1);SLPrint(s1);//.....SLDestory(&s1);}int main()
{SLTest01();return 0;
}

代码运行成功

顺序表代码合并

头文件

#pragma once
#include <stdio.h>
#include <stdlib.h>typedef int SLDataType;//动态顺序表
typedef struct Seqlist
{SLDataType* arr;int size;//有效数据的个数int capacity;//空间大小
}SL;//顺序表的初始化
void SLInit(SL* ps);
//顺序表的销毁
void SLDestory(SL* ps);
//顺序表的头部 插入 和 删除 + 顺序表的 尾部 插入 和 删除
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);void SLPopBack(SL* ps);
void SLPopFront(SL* ps);//顺序表的打印
void SLPrint(SL s);

源文件

#define _CRT_SECURE_NO_WARNINGS 1
//静态顺序表
//struct Seqlist
//{
//	int arr[100];
//	int size;//记录顺序表当前有效数据的个数
//
//
//};//动态顺序表
//struct Seqlist
//{
//	int* arr;
//	int size;//有效的数据个数
//	int capacity;//记录空间大小
//};#include"Seqlist.h"
void SLInit(SL* ps)
{ps->arr = NULL;ps->size = ps->capacity = 0;}//顺序表的销毁
void SLDestory(SL* ps)
{if (ps->arr){free(ps->arr);}ps->arr = NULL;ps->size = ps->capacity = 0;
}void SLCheckCapacity(SL* ps)
{//插入数据前看空间够不够if (ps->capacity == ps->size){//申请空间//判断capacity是否为0int newCapaciy = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapaciy * sizeof(SLDataType));if (tmp == NULL){perror("realloc fail!");exit(1);//直接退出程序,不再继续执行}//空间申请成功ps->arr = tmp;ps->capacity = newCapaciy;}
}//尾插
void SLPushBack(SL* ps, SLDataType x)
{if (ps == NULL){return;}SLCheckCapacity(ps);ps->arr[ps->size] = x;++ps->size;//或者写作 ps->arr[ps->size++] = x;}//头插
void SLPushFront(SL* ps, SLDataType x)
{if (ps == NULL){return;}SLCheckCapacity(ps);//先让顺序表中的数据向后挪动一位for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];//arr[1] = arr[0]}ps->arr[0] = x;ps->size++;}//打印
void SLPrint(SL s)
{for (int i = 0; i < s.size; i++){printf("%d ", s.arr[i]);}printf("\n");
}//尾部删除
void SLPopBack(SL* ps)
{if (ps == NULL){return;}if (ps->size == 0){return;}--ps->size;}
//头部删除
void SLPopFront(SL* ps)
{if (ps == NULL){return;}if (ps->size == 0){return;}//数据整体前挪for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

测试文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"void SLTest01()
{SL s1;SLInit(&s1);测试尾插代码SLPushBack(&s1, 1);SLPushBack(&s1, 2);SLPushBack(&s1, 3);SLPushBack(&s1, 4);SLPrint(s1);SLPopBack(&s1);SLPrint(s1);//.....SLDestory(&s1);}int main()
{SLTest01();return 0;
}

结尾

顺序表代码的实现整体难度不是很大,但是要求我们在编写代码的时候要考虑仔细,若是有疏忽就很容易出现错误。因为顺序表的内容很多,我就将顺序表封装成了两节,本节我们讲解的是顺序表的基本内容,下一节为大家带来顺序表更高级的内容实现:顺序表删除、插入指定位置的数据。谢谢您的浏览

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

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

相关文章

Web攻击越发复杂,企业如何保护云上业务

如今&#xff0c;电子政务、电子商务、网上银行、网上营业厅等依托Web应用&#xff0c;为广大用户提供灵活多样的服务。在这之中&#xff0c;流量攻击堪称是Web应用的最大敌人&#xff0c;黑客通过流量攻击获取利益、竞争对手雇佣黑客发起恶意攻击、不法分子通过流量攻击瘫痪目…

约数与倍数-第12届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第45讲。 约数与倍数&#…

用html写一个爱心

<!DOCTYPE html> <html lang"zh-CN"><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8" /><title>爱您</title><style>* {padding: 0;margin: 0;}body {background-color: pin…

【软件测试】测试常见知识点汇总

测试常见知识点汇总 一、什么是测试1.1 测试和调试的区别1.2 什么是需求1.2.1 用户需求1.2.2 软件需求 1.3 测试用例要素1.4 软件的生命周期及各阶段概述1.5 开发模型和测试模型&#xff08;记住特点和适用场景&#xff09;1.5.1 开发模型1.5.1.1 瀑布模型&#xff08;自上而下…

armlinux-外部中断

s3c2440的中断框图 如果我们单纯配置一个按键的外部中断&#xff0c;就不存在子中断与优先级的问题。 由于是按键的外部中断&#xff0c;通过引脚的高低电平来触发。所以我们要先配置引脚的功能。 我们使用按键1&#xff0c;终端源为EINT8&#xff0c;对应引脚GPG0 通过用户手…

java中大型医院HIS系统源码 Angular+Nginx+SpringBoot云HIS运维平台源码

java中大型医院HIS系统源码 AngularNginxSpringBoot云HIS运维平台源码 云HIS系统是一款满足基层医院各类业务需要的健康云产品。该产品能帮助基层医院完成日常各类业务&#xff0c;提供病患预约挂号支持、病患问诊、电子病历、开药发药、会员管理、统计查询、医生工作站和护士工…

响应跨域的两种方式

第一种&#xff1a; Configuration public class CorsConfication {Beanpublic CorsWebFilter corsWebFilter() {UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource();CorsConfiguration corsConfiguration new CorsConfiguration();//1、配置跨…

(源码+部署+讲解)基于Spring Boot和Vue的大学生快递代取服务平台的设计与实现

一、引言 本报告旨在详细阐述基于Spring Boot后端框架和Vue前端框架的大学生快递代取服务平台的设计与实现过程。该平台旨在为大学生提供便捷的快递代取服务&#xff0c;解决因时间冲突或距离过远而无法及时取件的问题。通过该平台&#xff0c;用户可以发布代取需求&#xff0c…

4核8G服务器配置性能怎么样?4核8G12M配置服务器能干啥?

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…

【SQL Server】1. 认识+使用

1. 创建数据库的默认存储路径 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft SQL Server 2008 R2 当我们选择删除数据库时&#xff0c;对应路径下的文件也就删除了 2. 导入导出数据工具的路径 3. 注册数据库遇到的问题 ??? 目前的问题就是服务器新建…

(源码+部署+讲解)基于Spring Boot和Vue的宠物领养系统的设计与实现

一、引言 本报告旨在详细描述基于Spring Boot后端框架和Vue前端框架的宠物领养系统的设计与实现过程。宠物领养系统旨在为宠物主人和领养者提供一个便捷的平台&#xff0c;实现宠物的信息发布、领养申请、信息管理等功能。通过该系统&#xff0c;宠物主人可以快速找到适合的领养…

c语言数据结构(10)——冒泡排序、快速排序

欢迎来到博主的专栏——C语言数据结构 博主ID&#xff1a;代码小豪 文章目录 冒泡排序冒泡排序的代码及原理快速排序快速排序的代码和原理快速排序的其他排序方法非递归的快速排序 冒泡排序 相信冒泡排序是绝大多数计科学子接触的第一个排序算法。作为最简单、最容易理解的排序…

【保姆级讲解如何安装与配置Node.js】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【JAVA】postman import certificates in project 导入证书pfx

1. 打开这个按钮 2. File ->Settings 3. 打开“certificates”, Add certificates 添加证书 4. 输入证书地址&#xff0c;然后选择证书文件pfx , 输入证书密码。点击添加就可以了。 特别提醒&#xff1a; 推荐本地自己证书验证软件&#xff0c;“KeyStore” 这个软件可以…

接口调用成功后端却一直返回404

vuespringboot 我在vue.config.js中配置了向后端的反向代理 然后使用了axios向后端发送post请求 可以看到可以接收到前端传来的值 但是前端控制台却报了 “xhr.js:245POST http://localhost:7777/api/login 404 (Not Found)” 最后询问我那智慧的堂哥... ... 解决办法是把C…

深入了解 Python 中标准排序算法 Timsort

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ Timsort&#xff1a;一个非常快速的、时间复杂度为 O ( n l o g n ) O (n \ log\ n) O(n log n)、稳健&#xff08;即不改变等值元素间的相对顺序&#xff09;的排序算法&#xff0c;在处理真实世界数…

RDD算子(四)、血缘关系、持久化

1. foreach 分布式遍历每一个元素&#xff0c;调用指定函数 val rdd sc.makeRDD(List(1, 2, 3, 4)) rdd.foreach(println) 结果是随机的&#xff0c;因为foreach是在每一个Executor端并发执行&#xff0c;所以顺序是不确定的。如果采集collect之后再调用foreach打印&#xf…

SpringMVC --- 老杜

1、什么是SpringMVC&#xff1f; SpringMVC是一个基于Java实现了MVC设计模式的请求驱动类型的轻量级Web框架&#xff0c;通过把Model&#xff0c;View&#xff0c;Controller分离&#xff0c;将web层进行职责解耦&#xff0c;把复杂的web应用分成逻辑清晰的及部分&#xff0c;…

Adobe Bridge 2024:连接创意,探索无限可能 mac/win版

Adobe Bridge 2024&#xff0c;作为Adobe家族中的一款强大的创意管理工具&#xff0c;再次革新了数字资产管理和工作流程优化的标准。这款软件不仅继承了Adobe Bridge一贯的直观界面和强大功能&#xff0c;更在多个方面进行了突破性的改进。 Bridge 2024软件获取 全面的资源管…

内网穿透的应用-如何在Android Termux上部署MySQL数据库并实现无公网IP远程访问

文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 前言 Android作为移动设备&#xff0c;尽管最初并非设计为服务器&#xff0c;但是随着技术的进步我们可以将Android配置为生产力工具&#xff0c;变成一个随身…