当前位置: 首页 > news >正文

数据结构学习之顺序表

        在C语言学习到一定阶段之后,接下来我们就进入到了数据结构的部分内容。

目录

数据结构与线性表

顺序表

        顺序表分类:

        接下来我们要写一段代码实现动态顺序表。

首先我们需要准备三个文件:

1.接下来我们要定义一个数据表

2.当创建号我们的顺序表之后,我们要对他进行初始化

3.而动态内存创建后就必须有销毁

4.接下来我们要对顺序表进行各种操作:增删插改

尾部插入数据

 头部插入数据

尾部删除数据

头部删除数据:

指定位置之前插入数据

指定位置查找数据

完整的代码:


数据结构与线性表

        数据就像草原的一群羊一样,如果你要在草原上找到一个叫做“咩咩”的羊很难,但是如果你要找一头“3号”羊是比较简单的。数据结构就是像羊圈管理羊群一样管理数据。

        因此可以说,数据结构就是计算机存储和组织数据的方式。

        数组就是一种很基础的数据结构,用来存储一群同类型的数据。但是随着我们要对数据进行的操作愈加复杂(访问、修改、插入数据等等等等),频繁的访问数组已经严重影响了计算机运行的效率,因此简单的数组已经无法满足我们的需要了,因此我们需要进入第一个数据结构类型——线性表。

        线性表是n个具有相同特性的数据元素的有限序列,是一种广泛应用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串等等。

        但是注意线性表在逻辑上是连续的线性结构,但是物理上不一定是连续的。线性表在物理上储存的时候通常以数组和链式结构的形式储存。

顺序表

        顺序表在底层上是数组。

        那么既然已经有数组了,我们为什么需要顺序表呢?举例说明:

int a[100]={1,2,3,4,...X...};

        对于如上所示的100个元素的数组a。如果我们要修改其中一个数字就找先遍历数组找到对应元素修改,如果要插入和删除数组的话,都需要遍历这个100个元素的数组。这个效率显然是很低的。因此我们需要一个更好用、效率更高的工具:顺序表。

        顺序表是在数组的基础上加上了增删查改等方法的一种储存形式。顺序表的特性是在物理结构上连续,在逻辑结构上也连续。

        这是一个固定长度的数组

int a[10]={0};

        这是动态内存开辟出来的数组,确定大小后再去申请。

int *arr

        顺序表分类:

        静态顺序表:

​
​
Stack SeqList
{int arr[100];//定长数组int size;//顺序表当前有效的数据个数}​​

        动态顺序表:

​
Stack Seqlist
{int *arr;//int size//有效数字个数int capacity;//空间大小
};​

相较于静态顺序表,动态顺序表可以动态增容。

        接下来我们要写一段代码实现动态顺序表。

首先我们需要准备三个文件:

Seqlist.c——实现顺序表的方法
Seqlist.h——顺序表结构,顺序表声明,方法

test.c——测试代码

1.接下来我们要定义一个数据表

2.当创建号我们的顺序表之后,我们要对他进行初始化

这里我们用传值返回就会报错,原因:传值返回是值拷贝,但是这里s1没有值。
所以这里要用传址返回。

3.而动态内存创建后就必须有销毁

4.接下来我们要对顺序表进行各种操作:增删插改

声明:

尾部插入数据

原理展示:

要申请多大的空间?

 增容一般是2到3倍(2倍更常见)。因为一次性给太多空间就会像静态顺序表那样浪费大量空间而得不偿失,而过少就会导致需要频繁地扩容而导致程序运行效率过低

 尾部插入数据函数一:

尾部插入数据函数二:

因此这部分的代码为:

但是这个结构是比较脆弱的,如果用户输入了这么一个东西,它就会报错:读取访问权限冲突。

	SLPushback(NULL, 5);

 所以我们要对这个函数进行改造。

温柔的方式

//温柔的解决方式
if (ps->arr == NULL)
{return;
}
if (ps->size == ps->capacity)//空间不够了
{
//code
}

暴力的方式

//暴力判断
assert(ps->arr!= NULL);
//等价于assert(ps)

尾部插入的代码:

//尾部插入数据
void SLPushback(SL* ps, STDatatype x)
{//暴力判断assert(ps->arr!= NULL);//等价于assert(ps)if (ps->size == ps->capacity)//空间不够了{//申请空间//要增容只能用realloc函数,不能用malloc和callocint newcapacity = ps->capacity == 0 ? 4:2*ps->capacity;STDatatype*tmp = realloc(ps->arr, newcapacity * 2 * sizeof(STDatatype));//tmp指的是结构体当中的数组arrif (tmp==NULL )//realloc可能申请失败所以要判断{perror("realloc error");return 1;//程序推出}//申请成功ps->arr = tmp;ps->capacity = newcapacity;}ps->arr[ps->size++] = x;
}
 头部插入数据

 原理图:

如图所示,如果要申请空间挪动数据的话从最后一位开始

 代码为:

尾部删除数据

原理图:

        

代码: 

  

头部删除数据:

原理图:

代码:  

指定位置之前插入数据

原理:

代码; 指定位置删除数据

原理图:

        代码:

指定位置查找数据

查找数据相对来说比较简单,这里受限于篇幅,只讲解暴力查找的算法,即循环遍历数组

完整的代码:

Seqlist.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include<assert.h>
//定义顺序表的结构//#define N 100
//静态顺序表
//struct SeqList
//{
//	int arr[N];
//	int size;//有效数据个数
//};
typedef int STDatatype;
//这里是为了将来方便替换数组存储数据的类型
// 如果将来需要替换数据类型为char,只需要将int改为char即可//动态顺序表
typedef struct SeqList
{STDatatype* arr; int size;//有效数据个数int capacity;//顺序表空间大小
}SL;//这是为了将来为了方便调用结构体//顺序表初始化
void SLInit(SL* ps);//顺序表的销毁
void SLDestory(SL* ps);
//校验顺序表空间够不够
void SLCheckCapacity(SL* ps);
//顺序表的打印
void SLPrint(SL s);
//顺序表头部/尾部插入数据
void SLPushback(SL*ps, STDatatype x);//尾部插入数据
void SLPushfront(SL*ps, STDatatype x);//头部插入数据void SLPopback(SL*ps);	//尾部删除数据
void SLPopfront(SL*ps);//头部删除数据
//指定位置之前插入数据/删除数据/查找数据
void SLInsert(SL* ps, int pos,STDatatype x);//插入数据
//ps表示在ps所在的数组里插入数据
//pos在指定顺序表里下标的位置
//x为插入的数据
void SLDelete(SL* ps,int pos);//删除数据
int SLFind(SL* ps, STDatatype x);//查找数据

Seqlist.c

#define _CRT_SECURE_NO_WARNINGS
#include"Seqlist.h"
//Seqlist.c——实现顺序表的方法
//Seqlist.h——顺序表结构,顺序表声明,方法
//顺序表的初始化
void SLInit(SL *ps)
{ps->arr = NULL;//需要包含头文件<stdlib.h>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->size == ps->capacity)//空间不够了{//申请空间//要增容只能用realloc函数,不能用malloc和callocint newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDatatype* tmp = realloc(ps->arr, newcapacity * 2 * sizeof(STDatatype));//tmp指的是结构体当中的数组arrif (tmp == NULL)//realloc可能申请失败所以要判断{perror("realloc error");return 1;//程序推出}ps->arr = tmp;ps->capacity = newcapacity;}
}
//顺序表的打印
void SLPrint(SL s)
{for (int i = 0;i < s.size;i++){printf("%d ", s.arr[i]);}printf("\n");
}
//尾部插入数据
void SLPushback(SL* ps, STDatatype x)
{//温柔的方式// if(ps == NULL)// {//	return;// }//暴力判断assert(ps);//等价于assert(ps->arr!= NULL);//校验空间够不够SLCheckCapacity(ps);ps->arr[ps->size++] = x;
}
//头部插入数据
void SLPushfront(SL* ps, STDatatype x)
{assert(ps);SLCheckCapacity(ps);//先让顺序表中整体的数据向后移动一位。for (int i = ps->size - 1;i >=0;i--){ps->arr[i+1]=ps->arr[i];//最后为arr[1]=arr[0]}ps->arr[0] = x;ps->size++;
}//尾部删除数据
void SLPopback(SL* ps, STDatatype x)
{assert(ps);assert(ps->size != 0);//顺序表不为空//这段代码是否存在无所谓:ps->arr[ps->size - 1] = -1;--ps->size;
}
//头部删除数据
void SLPopfront(SL* ps)
{assert(ps);assert(ps->size != 0);//数据表整体向前移动一位for (int i = 0;i <= ps->size - 1;i++){ps->arr[i] = ps->arr[i+1];}ps->size--;
}
//指定位置之前插入数据
void SLInsert(SL* ps, int pos, STDatatype x)
{assert(ps);//pos必须>=0且<=sizeassert(pos>=0&&pos<=ps->size);SLCheckCapacity(ps);//校验空间够不够//让pos位置及之后的数据集体向后一位for (int i = ps->size-1;i >= pos;i--){ps->arr[i+1] = ps->arr[i];//最后为arr[pos+1]=arr[pos]}//pos位置空出来了ps->arr[pos] = x;ps->size++;
}
//指定位置删除数据
void SLDelete(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos <ps->size);for (int i=pos;i<ps->size-1;i++){ps->arr[i] = ps->arr[i+1];//最后为arr[size-2]=arr[size-1]}ps->size--;}
//指定位置查找数据
int SLFind(SL* ps, STDatatype x)
{assert(ps);//遍历数组查找for (int i = 0;i <= ps->size - 1;i++){if (x == ps->arr[i]){//找到了return i;}else {continue;}}return -1;//无效的下标表示没找到
}

test.c

 

#define _CRT_SECURE_NO_WARNINGS
#include"Seqlist.h"
void SLtest1()
{SL s1;SLInit(&s1);//这里我们不能用传值返回//传值返回是值拷贝,但是这里s1没有值//所以这里要用传址返回//增删查改操作。//顺序表的插入//测试尾插SLPushback(&s1,1);SLPushback(&s1,2);SLPushback(&s1,3);SLPushback(&s1,4);//测试头插SLPushfront(&s1, 5);SLPushfront(&s1, 6);SLPrint(s1);// 测试头删SLPopfront(&s1);SLPopfront(&s1);SLPrint(s1);// 测试尾删SLPopback(&s1);SLPopback(&s1);SLPrint(s1);//顺序表的销毁SLDestory(&s1);
}
void SLtest2()
{SL s1;//初始化SLInit(&s1);//尾部插入数据SLPushback(&s1, 1);SLPushback(&s1, 2);SLPushback(&s1, 3);SLPushback(&s1, 4);//在指定位置之前插入数据SLInsert(&s1, 0, 99);SLInsert(&s1, s1.size, 88);SLPrint(s1);//99 1 2 3 4 88//删除指定位置的数据SLDelete(&s1, 2);SLPrint(s1);//查找指定数据int F=SLFind(&s1, 3);if (F >= 0){printf("找到了,位置是%d\n",F);}else{printf("没找到\n");}销毁SLDestory(&s1);
}
int main()
{SLtest1();SLtest2();return 0;
}

        顺序表的内容并没有结束,但是受限于篇幅原因我只能先到这里了,感谢各位读者朋友的阅读,求一个赞,谢谢。

http://www.xdnf.cn/news/244927.html

相关文章:

  • 基于开源链动2+1模式AI智能名片S2B2C商城小程序的个性化与小众化消费社群构建研究
  • KDD 2025 | (8月轮)时空数据(Spatial-temporal)论文总结
  • 如何用AI生成生成个人简历
  • 浅析 MegEngine 对 DTR 的实现与改进
  • 【docker学习笔记】如何删除镜像启动默认命令
  • Docker启动nacos
  • zephyr架构下扫描和解析Beacon数据
  • Learning vtkjs之TriangleFilter
  • 开发板型号 ESP32-DevKitC-32模块型号 ESP32-WROOM-32 和主控芯片 ESP32-D0WDQ6-V3
  • 电子秤检测管理系统开发实战:从数据采集到可视化大屏
  • Python Cookbook-6.14 实现状态设计模式
  • Windows下Python3脚本传到Linux下./example.py执行失败
  • 3D版同步帧游戏
  • 案例:自动化获取Web页面小说(没钱修什么仙)——selenium
  • mem0 安装与测试:一个强大的对话记忆管理工具
  • 机器人手臂控制器:EMC电磁兼容解决(一)
  • 分寝室(C++完成)
  • 阿里云自动备份网站,阿里云自动备份网站的方法
  • kotlin中 热流 vs 冷流 的本质区别
  • 编程语言全景解析与编程技巧深度探索
  • 基于MyBatis的银行转账系统开发实战:从环境搭建到动态代理实现
  • 人工智能——DBSCAN 聚类算法
  • Webug4.0靶场通关笔记07- 第9关反射XSS和第10关存储XSS
  • 【Quest开发】极简版!透视环境下抠出身体并能遮挡身体上的服装
  • 免费实用的图像处理工具箱​
  • Java 泛型参数问题:‘ResponseData.this‘ cannot be referenced from a static contex
  • 原型模式(Prototype Pattern)详解
  • K8S - ReplicaSet 与 Deployment 深度解析与实战
  • Curl 全面使用指南
  • 【含文档+PPT+源码】基于大数据的交通流量预测系统