数据结构:顺序表详解

news2024/11/13 20:41:25

数据结构:顺序表详解

  • 一、 线性表
  • 二、 顺序表概念及结构
  • 1. 静态顺序表:使用定长数组存储元素。
  • 2. 动态顺序表:使用动态开辟的数组存储。
  • 三、接口实现
    • 1. 创建
    • 2. 初始化
    • 3. 扩容
    • 4. 打印
    • 5. 销毁
    • 6. 尾插
    • 7. 尾删
    • 8. 头插
    • 9. 头删
    • 10. 插入任意位置数据
    • 11. 删除任意位置数据
    • 12. 查找
    • 13. 修改
  • 四:所有代码


在这里插入图片描述


一、 线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的
线性表在物理上存储时,通常以数组和链式结构的形式.

在这里插入图片描述


二、 顺序表概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

1. 静态顺序表:使用定长数组存储元素。

在这里插入图片描述

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

在这里插入图片描述


三、接口实现

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

基本增删查改接口

//对数据管理 --- 增删查改
void SLInit(SL* ps);			//初始化
void SLDestory(SL* ps);			//释放
void SLPrint(SL* ps);        	//打印
void SLCheakCapacity(SL* ps);	//检查容量 -- 扩容

//头插头删 尾插尾删
void SLPushBack(SL* ps, SLDateType x); //尾插
void SLPopBack(SL* ps);				   //尾删
void SLPushFront(SL* ps, SLDateType x);//头插
void SLPopFront(SL* ps);			   //头删

//返回下标,没找到返回-1
int SLFind(SL* ps, SLDateType);		   //查找元素,返回下标

//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDateType x);	//任意位置插入
//在pos位置删除x
void SLErase(SL* ps, int pos);					//任意位置删除

void SLModify(SL* ps, int pos, SLDateType x);//修改


1. 创建

由于在实际工程中,项目的实现都是采用模块化进行实现的。所以在此处博主也采用了模块化的方式进行实现。
在这里插入图片描述

#pragma once

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{
	SLDateType* a;//指向动态开辟的数组
	int size;	//有效数据的个数
	int capacity;//容量空间的大小
}SL;

为了后续好修改类型数据,在此采用typedef将结构体类型struct SeqList 重新命名为SL
在实际开发过程中,为了开发人员更好的输入数据,一般我们会将输入数据的数据类型重命名为SLDateType。(在本篇博客中,采用typedef将其数据类型int重命名为SLDateType


2. 初始化

初始化时,理论上我们只需要开辟一个空间并置为空指针,并将结构体中的数据全部初始化为0即可。
但在实际开发过程中,我们一般会开辟一定大小的空间(本篇博客开4个空间,但具体开多少,各位可自行选择)。

代码实现:

void SLInit(SL* ps)
{
	assert(ps);
	ps->a = (SLDateType*)malloc(4 * sizeof(SLDateType));//开辟4个空间
	if (ps->a == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	//开辟成功
	ps->capacity = 4;//开辟多少空间,容量变为多少
	ps->size = 0;
}

3. 扩容

在后续我们插入数据时,已开辟容量可能已经无法满足需求了。这是就需要扩容。
那一次扩到多少呢?
在实际开发过程中我们一般是扩到原有空间的两倍。(当然你也可以开1000倍,只要后台空间足够大)

代码实现:

void SLCheakCapacity(SL* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		//开辟空间X2
		SLDateType* tmp = (SLDateType*)realloc(ps->a, ps->capacity * sizeof(SLDateType) * 2);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		//开辟成功
		ps->a = tmp;
		ps->capacity *= 2;
	}
}

4. 打印

上述函数定义完成后,我们通常需要测试打印以下相关数据,来判断相关函数定义是否成功.

代码实现:

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

5. 销毁

由于上述空间是动态开辟的。所以当我们使用完时,要及时销毁,释放空间。

代码实现:

void SLDestory(SL* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}

6. 尾插

尾插:在尾部插入一个数据。
在这里插入图片描述

但是在数据的尾部插入一个数据时,我们需要考虑一个问题:原有空间是否可以容纳新的数据,是否需要扩容。
所以我们在插入数据时,要先调用 SLCheakCapacity函数来检查是否需要扩容。

代码实现:

void SLPushBack(SL* ps, SLDateType x)
{
	assert(ps);
	SLCheakCapacity(ps);//检查是否需要扩容
	ps->a[ps->size] = x;
	ps->size++;
}

7. 尾删

尾删:删除尾部最后的一个元素。
在这里插入图片描述

但尾删同样也要考虑一个问题,空间中是否还有数据给我们删除。
所以在进行尾删时,我们可以采用assert函数断言空间中还有数据。

代码实现:

void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size >= 0);//断言空间中还有元素
	ps->size--;//下标减1
}

在删除数据时,我们不用将原有数据删除。只需要下标减1即可。
原因在于我们时根据下标来使用数据的,当下标减1后,尾部最后一个数据便无法进行访问。

Tips:

  • 越界是不一定报错的,系统对越界的检查是一种设岗抽查。
  • 以VS2022为例,微软公司在数据的开始前和结尾后的一小段空间设有一些特殊值。当程序结束或内存空间释放时,编译器就会检查这些值是否发生改变, 从而触发程序的保护机制。但如果这些值没有发生改变,即使发生越界访问,程序也不会报错。就像如果你酒驾,交警只在二环设关卡,但只要你不去二环,你就没事不会被发现。(每个编译器略有差异)

8. 头插

头插:在数据最开始地方插入数据。
在这里插入图片描述

同样,头插也要调用 SLCheakCapacity函数来检查空间是否足够,是否需要扩容。

代码实现:

void SLPushFront(SL* ps, SLDateType x)
{
	assert(ps);
	SLCheakCapacity(ps);//检查是否需要扩容

	//移动数据
	int i = ps->size-1;
	while (i >= 0)
	{
		ps->a[i + 1] = ps->a[i];
		i--;
	}
	//移动数据完成,插入元素。同时有效个数加1
	ps->a[0] = x;
	ps->size++;
}


9. 头删

头删:删除数据最开始的元素。
在这里插入图片描述

思路和头插类似,只要下标从1开始,所有数据依次向前移动1位,再把有限个数减1即可。
同时头删也需要使用assert函数断言原有空间中还有数据可以删除。

代码实现:

void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size >= 0);//空间中还有数据可以删除
	//移动数据
	for (int begin = 1; begin < ps->size; begin++)
	{
		ps->a[begin - 1] = ps->a[begin];
	}
	ps->size--;//有效个数减1
}

10. 插入任意位置数据

由于顺序表要求数据是连续存放的,所以我们只需要找到输入位置的下标pos即可。

【代码思路】:首先我们要检查输入下标是否合法,是有效下标;并检查是否有足够空间来容纳新数据,是否需要扩容。之后从输入的数据下标开始,所有元素向后移动一位,并把新数据插入到下标为pos处即可。

代码实现:

void SLInsert(SL* ps, int pos, SLDateType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);//检查下标是否合法
	SLCheakCapacity(ps);  //检查空间是否足够
	//移动数据
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

11. 删除任意位置数据

【代码思路】:和插入任何位置数据思想类似。首先我们要检查输入下标pos是否合法。之后从输入下标开始,后一个元素拷贝到前一个元素空间。

代码实现:

void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);//下标是否合法
	//移动数据
	int begin = pos+1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}


12. 查找

【代码思路】:要查找某个元素。由于这里只是最简单的查找,我们直接暴力查找,遍历整个数组返回下标即可。更为复杂的数据查找,会有更高阶的数据结构来实现。

代码实现:

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

13. 修改

【代码思路】: 要实现修改数据,我们只需要先判断输入下标是否合法。在将对应下标数据进行修改即可。

代码实现:

void SLModify(SL* ps, int pos, SLDateType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	ps->a[pos] = x;
}

四:所有代码

SeqList.h源文件

#include <assert.h>
#include <stdlib.h>


//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{
	SLDateType* a;
	int size;
	int capacity;
}SL;


//对数据管理 --- 增删查改
void SLInit(SL* ps);
void SLDestory(SL* ps);
void SLPrint(SL* ps);
void SLCheakCapacity(SL* ps);

//头插头删 尾插尾删
void SLPushBack(SL* ps, SLDateType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDateType x);
void SLPopFront(SL* ps);


//返回下标,没找到返回-1
int SLFind(SL* ps, SLDateType);

//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDateType x);
//在pos位置删除x
void SLErase(SL* ps, int pos);


void SLModify(SL* ps, int pos, SLDateType x);

SeqList.c头文件

#define _CRT_SECURE_NO_WARNINGS

#include "SeqList.h"


void SLInit(SL* ps)
{
	assert(ps);
	ps->a = (SLDateType*)malloc(4 * sizeof(SLDateType));
	if (ps->a == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	//开辟成功
	ps->capacity = 4;
	ps->size = 0;
}


void SLDestory(SL* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}

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

void SLCheakCapacity(SL* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		SLDateType* tmp = (SLDateType*)realloc(ps->a, ps->capacity * sizeof(SLDateType) * 2);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
}


void SLPushBack(SL* ps, SLDateType x)
{
	assert(ps);
	/*SLCheakCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;*/

	SLInsert(ps, ps->size, x);
}


void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size >= 0);
	/*ps->size--;*/
	SLErase(ps, 0);
}


void SLPushFront(SL* ps, SLDateType x)
{
	assert(ps);
	SLCheakCapacity(ps);

	//移动数据
	/*int i = ps->size-1;
	while (i >= 0)
	{
		ps->a[i + 1] = ps->a[i];
		i--;
	}
	ps->a[0] = x;
	ps->size++;*/
	SLInsert(ps, 0, x);
}


void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size >= 0);
	/*for (int begin = 1; begin < ps->size; begin++)
	{
		ps->a[begin - 1] = ps->a[begin];
	}
	ps->size--;*/
	SLErase(ps, 0);
}


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


void SLInsert(SL* ps, int pos, SLDateType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	SLCheakCapacity(ps);

	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;

}



void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	int begin = pos+1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}



void SLModify(SL* ps, int pos, SLDateType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	ps->a[pos] = x;
}


在这里插入图片描述
在这里插入图片描述

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

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

相关文章

pytorch 中 view 和reshape的区别

在 PyTorch&#xff08;一个流行的深度学习框架&#xff09;中&#xff0c; reshape 和 view 都是用于改变张量&#xff08;tensor&#xff09;形状的方法&#xff0c;但它们在实现方式和使用上有一些区别。下面是它们之间的主要区别&#xff1a; 实现方式&#xff1a; reshap…

13年测试经验,性能测试-压力测试指标分析总结,看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 一般推荐&#xf…

Jmeter环境变量配置及测试

上图是Windows版本的测试结果。 Windows系统&#xff1a; win11&#xff1a;“此电脑”——鼠标右键“属性”——“高级系统设置”——“环境变量” 1.1 新建“系统变量”&#xff1a;JMETER_HOME JMETER_HOME变量值为解压后的jmeter路径&#xff0c;如&#xff1a; D:\apach…

AD21原理图的高级应用(三)原理图多通道的应用

&#xff08;三&#xff09;原理图多通道的应用 在很多大型的设计过程中&#xff0c;我们可能会遇到需要重复使用某个图纸&#xff0c;如果使用常规的复制粘贴&#xff0c;虽然可以达到设计要求,但原理图的数量将会变得庞大而烦琐。Altium Designer 支持多通道设计。 多通道设…

数字图像处理(番外)图像增强

图像增强 图像增强的方法是通过一定手段对原图像附加一些信息或变换数据&#xff0c;有选择地突出图像中感兴趣的特征或者抑制(掩盖)图像中某些不需要的特征&#xff0c;使图像与视觉响应特性相匹配。 图像对比度 图像对比度计算方式如下&#xff1a; C ∑ δ δ ( i , j …

数学学习——最优化问题引入、凸集、凸函数、凸优化、梯度、Jacobi矩阵、Hessian矩阵

文章目录 最优化问题引入凸集凸函数凸优化梯度Jacobi矩阵Hessian矩阵 最优化问题引入 例如&#xff1a;有一根绳子&#xff0c;长度一定的情况下&#xff0c;需要如何围成一个面积最大的图像&#xff1f;这就是一个最优化的问题。就是我们高中数学中最常见的最值问题。 最优化…

【C++进阶:哈希--unordered系列的容器及封装】

本课涉及到的所有代码都见以下链接&#xff0c;欢迎参考指正&#xff01; practice: 课程代码练习 - Gitee.comhttps://gitee.com/ace-zhe/practice/tree/master/Hash unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在…

React井字棋游戏官方示例

在本篇技术博客中&#xff0c;我们将介绍一个React官方示例&#xff1a;井字棋游戏。我们将逐步讲解代码实现&#xff0c;包括游戏的组件结构、状态管理、胜者判定以及历史记录功能。让我们一起开始吧&#xff01; 项目概览 在这个井字棋游戏中&#xff0c;我们有以下组件&am…

交叉编译工具链的安装、配置、使用

一、交叉编译的概念 交叉编译是在一个平台上生成另一个平台上的可执行代码。 编译&#xff1a;一个平台上生成在该平台上的可执行文件。 例如&#xff1a;我们的Windows上面编写的C51代码&#xff0c;并编译成可执行的代码&#xff0c;如xx.hex.在C51上面运行。 我们在Ubunt…

jellyfin搭建服务器后,快解析端口映射让外网访问

Jellyfin是一款相对知名的影音服务器&#xff0c;是一套多媒体应用程序软件套装&#xff0c;可以有效的组织管理和共享数字媒体文件&#xff0c;不少伙伴喜欢用jellyin在本地自己主机上搭建自己的服务器。当本地搭建服务器后&#xff0c;面对动态IP和无公网IP环境困境下&#x…

【javaSE】面向对象程序三大特性之封装

目录 封装的概念 访问限定符 说明 访问private所修饰的变量的方法 封装扩展之包 包的概念 导入包中的类 注意事项 自定义包 基本规则 操作步骤 步骤一 ​编辑步骤二 ​编辑 步骤三 步骤四 步骤五 包的访问权限控制举例 常见的包 static成员 再谈学生类 s…

Vue中导入并读取Excel数据

在工作中遇到需要前端上传excel文件获取到相应数据处理之后传给后端并且展示上传文件的数据. 一、引入依赖 npm install -S file-saver xlsxnpm install -D script-loadernpm install xlsx二、在main.js中引入 import XLSX from xlsx三、创建vue文件 <div><el-uplo…

Aduino中eps环境搭建

这里只记录Arduino2.0以后版本&#xff1a;如果有外网环境&#xff0c;那么可以轻松搜到ESP32开发板环境并安装&#xff0c;如果没有&#xff0c;那就见下面操作&#xff1a; 进入首选项&#xff0c;将esp8266的国内镜像地址填入&#xff0c;然后保存&#xff0c;在开发板中查…

[STL]stack和queue使用介绍

[STL]stack和queue使用介绍 文章目录 [STL]stack和queue使用介绍stack使用介绍stack介绍构造函数empty函数push函数top函数size函数pop函数 queue使用介绍queue介绍构造函数empty函数push函数front函数back函数size函数pop函数 deque介绍 stack使用介绍 stack介绍 stack是一种…

C++中的static修饰类的成员变量和成员函数

回顾一下C语言中static的描述&#xff0c;我们知道&#xff1a; 当static修饰局部变量时&#xff0c;使局部变量的生命周期延长.static修饰全局变量时&#xff0c;将外部链接属性变成了内部链接属性&#xff0c;使全局变量的作用域只能在该源文件中执行.static修饰函数时&#…

时序预测 | Python实现NARX-DNN空气质量预测

时序预测 | Python实现NARX-DNN空气质量预测 目录 时序预测 | Python实现NARX-DNN空气质量预测效果一览基本介绍研究内容程序设计参考资料效果一览 基本介绍 时序预测 | Python实现NARX-DNN空气质量预测 研究内容 Python实现NARX-DNN空气质量预测,使用深度神经网络对比利时空气…

西安市未央区地方财政支出绩效管理研究_kaic

摘 要 目前传统的地方财政绩效管理研究普遍上主要集中在有关收入研究方面上&#xff0c;而对其支出的规模以及各类结构的研究较少。我国大部分地方财政政府的财政收入低下&#xff0c;财政支出效率有限&#xff0c;不能很好的为其地方经济提供较为稳定的社会支撑和经济保障。造…

6.1.tensorRT高级(1)-概述

目录 前言1. tensorRT高级概述总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 高级-概述 课程大纲可看下面的思维…

【C++】入门 --- 缺省参数函数重载

文章目录 &#x1f96e;一、缺省参数&#x1f355;1、基本概念&#x1f355;2、缺省参数的分类&#x1f6a9;全缺省参数&#x1f6a9;半缺省参数&#x1f6a9;缺省参数实用案例 &#x1f96e;二、函数重载&#x1f355;1、函数重载概念1️⃣参数类型不同2️⃣参数个数不同3️⃣…

MySQL 数据库 【增删查改(二)】

目录 一、表的设计 1、一对一 2、一对多 3、多对多 二、新增 三、查询 1、聚合查询 &#xff08;1&#xff09;聚合函数&#xff1a; &#xff08;2&#xff09; group by 子句 &#xff08;3&#xff09;having 2、联合查询 (1)内连接 (2)外连接 (3)自链接 (4)…