动态顺序表的增删改查(数据结构)

news2024/9/29 1:38:49

目录

一、顺序表

二、静态顺序表

三、动态顺序表

3.1、动态顺序表的实现

3.2、动态顺序表的实现

3.3.1、结构体创建

3.3.2、初始化

3.3.3、销毁数据

3.3.4、增容空间

3.3.5、尾插数据

3.3.6、头插数据

3.3.7、删除尾数据

3.3.8、打印数据

3.3.9、删除头数据

3.3.10、在指定位置之前插入数据

3.3.11、删除指定位置的数据

3.3.12、查找数据是否存在

四、全部代码


顺序表是数据结构的基础知识,通过学习次内容,可以让我们了解顺序表中数据中是如何进行存储,删除,插入数据的。

一、顺序表

顺序表在逻辑上与物理上是连续存放的,比如我们之前学习的数组,数组中包含许多数,每个数据在内存中存储都是连续的,在我们逻辑思维上存储也是连续的,当然,据我自己所了解,所以数据都需要是具有逻辑存储的,如果不是逻辑存储,那么根本就无法找到此数据。

以下是数组存储的数据:

顺序表与数组的区别:

顺序表的底层结构是数组,对于数组进行封装,实现了增删查改的接口。

使用数组很单一,无法进行修改,而使用顺序表进行存储数据,我们可以任意进行数据的修改。

二、静态顺序表

使用静态顺序表存储数据:

静态顺序表的大小被限制死了,无法进行扩容。

空间给少了不够用,空间给多了浪费。

三、动态顺序表

SLDataType* a;

为什么在这动态顺序表中,数据需要加上“*”,这是因为创建的是一个动态的顺序表,我们可以使用malloc,realloc进行开辟新空间来存储a,如果使用静态数据“a”的话,数据的存储位置就无法变动,当我们在增加数据时候,数据不够了,我们使用realloc开辟新空间,并且空间的其实位置进行改变,那么a就地址就会更改。

3.1、动态顺序表的实现

我们需要创建三个文件,node.h , node.c , test.c

node.h存储头文件与函数的参数

node.c实现函数

test.c为我们的主函数

代码小提示:

在编写代码的时候,我们需要勤测试,在完成一个函数体时,就需要测试,避免写完一大堆函数以后再测试,这样会无从下手。

3.2、动态顺序表的实现

在这部分,我只进行函数的讲解,想要查看全部源码的同学,可以拉到博客的最后进行查看

3.3.1、结构体创建

typedef int SLDatatype;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
	SLDatatype* arr;
	int size;     // 有效数据个数
	int capacity; // 空间容量
}SL;

我们把int类型的名字改变成了SLDatatype,这样可以方便进行数据类型大小的修改。

3.3.2、初始化

test.c

SL s;  //新建结构体
SLInit(&s);  //取地址传给函数进行初始化
//初始化
void SLInit(SL* ps) //进行传址调用,否则堆栈在运行完如何销毁的时候,内容也会消失
{
	ps->arr = NULL;                 //把数据给置空
	ps->size = ps->capacity = 0;   //把数据大小与空间也置零
}

因为是顺序表,无需修改首元素的地址,所以我们不需要进行SL* lisk=NULL;把新创建的内容给置为空。我们只要在一个空间进行增删改查。

初始化时我们需要把结构体中的arr置为空,数据大小与空间大小置为0

3.3.3、销毁数据

//销毁
void SLDestroy(SL* ps)
{
	if (ps->arr)    //相当于ps->arr != NULL
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

当需要销毁数据时,需要把有数据的空间给释放掉,把释放掉空间的地址给置为NULL,避免这个地址变为野指针,影响其他软件的运行,然后我们把size数据大小与capacity 空间大小都置为0。

3.3.4、增容空间

//增容空间
void SLCheckCapacity(SL* ps)
{
	//判断空间是否充足
	if (ps->size == ps->capacity)
	{
		//增容//0*2 = 0
		//若capacity为0,给个默认值,否则×2倍
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDatatype* tmp = (SLDatatype*)realloc(ps->arr, newCapacity * sizeof(SLDatatype));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}

对于动态顺序表来说,每次增容的大小我们统一规定为:新空间大小 = 2 * 原空间大小。

当原空间大小为0时,我们可以增加一行进行判断,如果原空间等于0那么我们就随机给个默认值,我们这里默认值给的是4个整型。

在扩容完以后,我们将原空间的数据与空间大小都指向为新的地址。

3.3.5、尾插数据

//插入数据
void SLPushBack(SL* ps, SLDatatype x)
{
	//粗暴的解决方法---断言
	assert(ps);//等价于assert(ps != NULL)

	温柔的解决方式
	//if (ps == NULL)
	//{
	//	return;
	//}
	SLCheckCapacity(ps);
	ps->arr[ps->size++] = x;  //每插入一个数据,有效数据大小size就会+1
}

assert(ps)断言是为了判断ps是一个空指针。

当ps->arr[0]=1;进行完这个操作,我们如果想继续插入2,那么我们就要让arr的大小增加1位,此时就变成了ps->arr[1]=2。因此得出代码:ps->arr[ps->size++] = x;

3.3.6、头插数据

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];
	}
	//下标为0的位置空出来
	ps->arr[0] = x;

	ps->size++;
}

这段代码里的注释应该是很清楚的表示了

我们需要把表中数据集体往后移动一位,让新数据放到首元素。

3.3.7、删除尾数据

//删除
void SLPopBack(SL* ps)
{
	assert(ps);       //判断ps不为空,如果为空,代码没必要运行
	assert(ps->size); //判断有效数据大小不为空,如果为空,代码没必要运行

	//ps->arr[ps->size - 1] = -1;  //多余了
	ps->size--;    //直接将有效数据个数-1就达到我们尾删的效果
}

我们可以直接将有效数据个数-1就达到我们尾删的效果

3.3.8、打印数据

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

这个很好理解,不过多阐述了。

3.3.9、删除头数据

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];   //i = size-2
	}
	ps->size--;
}

将数据集体往前移动一位。

移动前:

移动后:

3.3.10、在指定位置之前插入数据

//在指定位置之前插入数据(空间足够才能直接插入数据)
void SLInsert(SL* ps, SLDatatype x, int pos)
{
	assert(ps);   //判断ps是否为空
	assert(pos >= 0 && pos <= ps->size);    //判断指定位置的数据是否在顺序表中有效

	SLCheckCapacity(ps);

	//pos及之后的数据整体向后移动一位
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1]; //pos+1   ->   pos
	}

	ps->arr[pos] = x;
	ps->size++;
}

插入前:

插入后:

3.3.11、删除指定位置的数据

//删除指定位置的数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	//还有更多的限制:如顺序表不能为空....

	//pos之后的数据整体向前挪动一位
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];// size-2 <- size-1
	}
	ps->size--;
}

删除前:

删除后:

3.3.12、查找数据是否存在

//查找数据
int SLFind(SL* ps, SLDatatype x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)  //如果arr指向的数据等于x那么就return i
		{
			return i;
		}
	}
	//没有找到:返回一个无效的下标
	return -1;
}
test.c

int find = SLFind(&s, 222);
if (find < 0)
{
	printf("没找到\n");
}
else
{
	printf("找到了\n");
}

如果return返回的数字小于零,打印没找到,否则就打印找到了

四、全部代码

node.h

typedef int SLDatatype;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
	SLDatatype* arr;
	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 SLPushFront(SL* ps, SLDatatype x);
//尾删
void SLPopBack(SL* ps);
//头删
void SLPopFront(SL* ps);
//查找指定数据
int SLFind(SL* ps, SLDatatype x);
//指定位置之前插⼊
void SLInsert(SL* ps, int pos, SLDatatype x);
//指定位置之前删除数据
void SLErase(SL* ps, int pos);
node.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "node.h"

//初始化
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
//销毁
void SLDestroy(SL* ps)
{
	if (ps->arr)//相当于ps->arr != NULL
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
	//判断空间是否充足
	if (ps->size == ps->capacity)
	{
		//增容//0*2 = 0
		//若capacity为0,给个默认值,否则×2倍
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDatatype* tmp = (SLDatatype*)realloc(ps->arr, newCapacity * sizeof(SLDatatype));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}
//插入数据
void SLPushBack(SL* ps, SLDatatype x)
{
	//粗暴的解决方法---断言
	assert(ps);//等价于assert(ps != NULL)

	温柔的解决方式
	//if (ps == NULL)
	//{
	//	return;
	//}
	SLCheckCapacity(ps);
	ps->arr[ps->size++] = x;
}
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];
	}
	//下标为0的位置空出来
	ps->arr[0] = x;

	ps->size++;
}


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

//删除
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size);

	//ps->arr[ps->size - 1] = -1;//多余了
	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];//i = size-2
	}
	ps->size--;
}
//在指定位置之前插入数据(空间足够才能直接插入数据)
void SLInsert(SL* ps, SLDatatype x, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);

	SLCheckCapacity(ps);

	//pos及之后的数据整体向后移动一位
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1]; //pos+1   ->   pos
	}

	ps->arr[pos] = x;
	ps->size++;
}
//删除指定位置的数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	//还有更多的限制:如顺序表不能为空....

	//pos之后的数据整体向前挪动一位
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];// size-2 <- size-1
	}
	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;
		}
	}
	//没有找到:返回一个无效的下标
	return -1;
}
test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "node.h"

void SLtest01()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	//SLPushBack(&s, 5);
	//SLPushBack(&s, 6);
	//SLPushBack(NULL , 6);
	//
	//SLPushFront(&s, 1);
	//SLPushFront(&s, 2);
	//SLPushFront(&s, 3);
	//SLPushFront(&s, 4);
	SLPrint(&s); //1 2 3 4

	//SLPopBack(&s);
	//SLPrint(&s);
	//SLPopBack(&s);
	//SLPrint(&s);
	//SLPopBack(&s);
	//SLPrint(&s);
	//SLPopBack(&s);
	//SLPrint(&s);
	//SLPopBack(&s);
	//SLPrint(&s);
	//
	//SLPopFront(&s);
	//SLPrint(&s);
	//SLPopFront(&s);
	//SLPrint(&s);
	//SLPopFront(&s);
	//SLPrint(&s);
	//SLPopFront(&s);
	//SLPrint(&s);
	//SLPopFront(&s);
	//SLPrint(&s);

	//SLInsert(&s, 11, 0);
	//SLPrint(&s);
	//SLInsert(&s, 22, s.size);
	//SLPrint(&s);
	//SLInsert(&s, 33, 1);
	//SLPrint(&s);

	//SLErase(&s, 1);
	//SLPrint(&s); //1 3 4
	//SLErase(&s,s.size-1);
	//SLPrint(&s);//2 3

	int find = SLFind(&s, 222);
	if (find < 0)
	{
		printf("没找到\n");
	}
	else
	{
		printf("找到了\n");
	}

	SLDestroy(&s);
	//SLDestroy(&s);//ctrl+d
}

int main()
{
	SLtest01();
	return 0;
}

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

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

相关文章

设备管理系统-TPM(PC+APP/PDA全流程)高保真Axure原型 源文件分享

随着科技的不断发展&#xff0c;企业对于设备管理的需求也日益增强。为了满足企业在设备管理方面的各种需求&#xff0c;站长为大家整理了一套设备管理系统TPM&#xff08;PCAPP/PDA全流程&#xff09;高保真Axure原型&#xff0c;通过这套原型&#xff0c;企业能够实现对设备的…

TDSQL-C电商小助手,AI驱动的智能化数据洞察新纪元

前言&#xff1a; 在数字经济蓬勃发展的今天&#xff0c;电商行业作为数字化转型的先锋&#xff0c;正以前所未有的速度积累着海量数据。这些数据不仅是交易记录的简单堆砌&#xff0c;更是企业洞察市场趋势、优化运营策略、提升用户体验的宝贵财富。然而&#xff0c;如何从这…

【2024.9.28练习】青蛙的约会

题目描述 题目分析 由于两只青蛙都在跳跃导致变量多&#xff0c;不妨采用物理题中的相对运动思想&#xff0c;设青蛙A不动&#xff0c;青蛙B每次跳米&#xff0c;两只青蛙的距离为米。正常来说&#xff0c;只要模拟青蛙B与青蛙A的相对运动过程&#xff0c;最终当青蛙B与青蛙A距…

Goweb---Gorm操作Mysql数据库(一)

本文重点&#xff1a; db.AutoMigrate()这个函数的理解&#xff1a; AutoMigrate是GORM提供的一个方法&#xff0c;用于自动迁移你的模型&#xff08;即数据库表结构&#xff09;到数据库中&#xff0c;确保数据库表与你的Go结构体&#xff08;模型&#xff09;保持一致。 首先…

第十四届蓝桥杯真题Python c组D.平均(持续更新)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;蓝桥杯关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 【问题描述】 有一个长度为n的数组(n是10的倍数)&#xff0c;每个数 …

vscode配置Eslint后保存出现大量波浪线

解决问题&#xff1a;配置代码格式化 快捷键打开设置&#xff1a;ctrlshiftP 输入&#xff1a; format code 选择&#xff1a;

MySQL-数据库约束

1.约束类型 类型说明NOT NULL非空约束 指定非空约束的列不能存储NULL值 DEFAULT默认约束当没有给列赋值时使用的默认值UNIQUE唯一约束指定唯一约束的列每行数据必须有唯一的值PRIMARY KEY主键约束NOT NULL和UNIQUE的结合&#xff0c;可以指定一个列霍多个列&#xff0c;有助于…

qt6 使用QPSQL

qt6 自带pg数据库驱动&#xff1a; pro文件加个说明&#xff1a; 引用位置添加&#xff08;按需添加&#xff0c;这里我就大致加一下&#xff09;&#xff1a; test code: 理想情况当然是要用pool,这里只是演示调用而已 QSqlError DbTool::testConnection(const QString &…

LSTM预测未来30天销售额

加入深度实战社区:www.zzgcz.com&#xff0c;免费学习所有深度学习实战项目。 1. 项目简介 本项目旨在利用深度学习中的长短期记忆网络&#xff08;LSTM&#xff09;来预测未来30天的销售额。LSTM模型能够处理时序数据中的长期依赖问题&#xff0c;因此在销售额预测这类涉及时…

19款奔驰E300升级新款触摸屏人机交互系统

《19 款奔驰 E300 的科技焕新之旅》 在汽车科技日新月异的时代&#xff0c;19 款奔驰 E300 的车主们为了追求更卓越的驾驶体验&#xff0c;纷纷选择对爱车进行升级改装&#xff0c;其中新款触摸屏人机交互系统的改装成为了热门之选。 19 款奔驰 E300 作为一款经典车型&#x…

Vue和axios零基础学习

Vue的配置与项目创建 在这之前要先安装nodejs 安装脚手架 官网 Home | Vue CLI (vuejs.org) 先运行&#xff0c;切换成淘宝镜像源&#xff0c;安装速度更快 npm config set registry http://registry.npm.taobao.org 创建项目 用编译器打开一个空文件&#xff0c;在终端输入…

DMA的原理

一、介绍 DMA&#xff08;Direct Memory Access&#xff09;是一种允许设备直接与内存进行数据交换的技术&#xff0c;无需‌CPU干预。DMA的主要功能是提供在‌外设和存储器之间或者存储器和存储器之间的高速数据传输。比如使用ADC进行数据采集&#xff0c;可以直接将数据存入…

【STM32】江科大STM32笔记汇总(已完结)

STM32江科大笔记汇总 STM32学习笔记课程简介(01)STM32简介(02)软件安装(03)新建工程(04)GPIO输出(05)LED闪烁& LED流水灯& 蜂鸣器(06)GPIO输入(07)按键控制LED 光敏传感器控制蜂鸣器(08)OLED调试工具(09)OLED显示屏(10)EXTI外部中断(11)对射式红外传感器计次 旋转编码器…

GAMES101(21~22节,动画和仿真)

Animation 关键帧 动画和几何&#xff08;曲线&#xff09;相关 物理模拟/仿真 牛顿第二定律&#xff1a;F ma 需要清楚网格间相互作用力&#xff0c;也需要把物理仿真和渲染分为两部来看&#xff0c;例如布料模拟&#xff0c;流体模拟 mass spring system质点弹簧系统 …

Nest.js实现一个简单的聊天室

本文将介绍如何使用 Nest.js 和 Uni-app 实现一个简单的实时聊天应用。后端使用 nestjs/websockets 和 socket.io&#xff0c;前端使用 uni-app 并集成 socket.io-client。这个项目允许多个用户同时加入聊天并实时交换消息。 效果图&#xff1a; 一、准备工作 安装 Node.js 和…

数据结构与算法——Java实现 24.中缀表达式转后缀

目录 中缀表达式转后缀表达式 引言 思路 代码 正因为我有能力跨越&#xff0c;考验才会降临 —— 24.9.28 中缀表达式转后缀表达式 引言 Java中的编译器会将我们编写代码中的中缀表达式转化为后缀表达式&#xff0c;然后编译好输出程序 思路 遍历中缀表达式&#xff0c;如果遇…

电脑学习通看不到课程解决办法

电脑学习通看不到课程解决办法 查看学习通时发现没有课程 解决方法1: 更改单位 具体见:超星学习通关于PC版无法查看课程问题解决 解决方法二:添加应用 添加应用 点击账号管理 点击应用管理 添加应用、添加首页这个应用 添加完成后查看首页就能看到课程了 然后就OK啦、就可…

[JavaEE] HTTP/HTTPS

目录 一、HTTP 1.1 HTTP是什么 1.2 HTTP发展史 1.3 HTTP工作过程 1.3.1 抓包工具的原理 1.4 HTTP请求格式 1.4.1认识URL 1.5 HTTP响应格式 1.6 认识HTTP"方法"(method) 1.6.1 GET方法 1.6.2 POST方法 1.6.3 其他方法 1.7 GET 与 POST 的区别 1.8 认识…

Centos安装docker(linux安装docker)——超详细小白可操作手把手教程,包好用!!!

&#x1f9f8;本篇博客重在讲解Centos安装docker&#xff0c;经博主多次在不同服务器上测试&#xff0c;极其的稳定&#xff0c;尤其是阿里的服务器&#xff0c;一路复制命令畅通无阻 &#x1f4dc;后续会退出ububtu安装docker教程&#xff0c;敬请期待 &#x1f4dc;作者首页&…

某客户Oracle RAC无法启动故障快速解决

某日&#xff0c;9:50左右接到好友协助需求&#xff0c;某个客户Oracle RAC无法启动&#xff0c;并发过来一个报错截图&#xff0c;如下&#xff1a; 和客户维护人员对接后&#xff0c;远程登录服务端进行故障分析。 查看hosts信息&#xff0c;首先进行心跳测试&#xff0c;测…