数据结构----顺序表

news2024/9/20 1:21:31

在学习顺序表之前,我们先来了解一下数据结构。

数据是什么呢?

我们在生活中常见的名字,数字,性别等都属于数据。

结构又是什么呢?

在计算机中,结构就是用来保存数据的方式。

总的来说,数据结构就是计算机存储和组织数据的方式。

1.顺序表

顺序表是数据结构中的一种,也就是说顺序表是一种用来存储和组织数据的一种结构,并且顺序表的底层本质就是数组,只不过顺序表是在数组的的基础上,增加了增删查改的方法,也就是说顺序表里面已经提供了对数据进行增删查改的现成的方法。只不过顺序表里面的增删查改的方法需要我们通过代码来实现,实现顺序表里面的增删查改的代码之后,以后我们实现对通讯录里面数据的增删查改的操作直接用就行了。

同时顺序表是线性表的一种。

1.1线性表

线性表就是具有相同特性的的数据结构的集合。

数据结构是否具有相同特性是从物理结构和逻辑结构来判断。

物理结构:数据在内存中存储的样子。由于我们无法得知计算机是如何给数据分配内存的,所以数据结构有连续和不连续的两种。

逻辑结构:逻辑结构一定是连续的。逻辑结构是我们抽象想象的。

比如排队的时候,我们排对的时候可能排的不是一条直线,但是我们还是得一个一个按序付钱,虽然看起来是不连续的,但在逻辑上我们将其抽象想象成线性的。

前面我们提到,顺序表的底层结构就是数组,而数据又是一块连续的空间,所以顺序表在物理结构和逻辑结构上都是连续的。

 

2.顺序表的分类

我们将顺序表分为了两种,分别为静态顺序表动态顺序表。

2.1 静态顺序表

静态顺序表简单来说就是一个定长的数组。

如下图

1255d2824f2c4bb0af1ed8d5b340029a.png

此时Seqlist就是一个静态顺序表,该顺序表能储存的数据个数大小已经确定,其只能存储100个数据。

2.1动态顺序表

动态顺序表就是一个未定长的数组,也就是动态顺序表里面的数组能存储数据的个数是未定的,但是动态顺序表能存储的数据个数是可以根据实际情况实现动态改变的。

d73fc86872a74f9581c4c3e79924a40d.png

那上面两种顺序表哪种更好用呢?

肯定是动态顺序表。因为它能够根据实际情况来动态实现增容或删除数据。

3.动态顺序表的的实现

由于顺序表的属性有点多,所以我们要用结构体来实现顺序表。

当我们实现动态顺序表的时候我们要分多个文件来实现。如下图

1594fdd1785540ceb1043213e09e269c.png

3.1 动态顺序表的创建和声明

typedef int SLDataType;  //方便以后修改要存储的数据类型
struct SeqList
{
	SLDataType* arr;
	int size;  //有效的数据个数顺序表里面
	int capacity;  //顺序表的总大小
};
typedef struct SeqList SL; 

在一个头文件里面实现动态顺序表的声明。这里我们要将顺序表里面要存储的数据类型重命名为SLDataType,是为了以后方便修改要存储的数据类型,并且也将顺序表重命名为SL,方便后面的操作。

3.2动态顺序表的初始化

在.c源文件实现初始化

void SLInit(SL* sl)
{
	sl->arr = NULL;
	sl->size = 0;
	sl->capacity = 0;
}

ec01091d4d25487b99bf074eaf2240bc.png

需要注意的是,我们进行传参的时候要将sl的地址传过去。我们知道形参是实参的拷贝,我们对形参的改变是不会影响实参的,不会影响实参,初始化就会失败所以我们要将sl的地址传过去,通过指针来实现初始化 。

8ffdfe40345e49adba5f2798db992441.png

通过调试,我们发现顺序表初始化成功了。

3.2 销毁动态顺序表

因为后面涉及到扩容等问题,要用到realloc函数,申请空间,完成程序之后我们要将申请的空间还给计算机,我们这里先把顺序表的销毁讲了,方便以后操作。

void SLBreak(SL* sl)
{
	if (sl->arr)
	{
		free(sl->arr);
	}
	sl->size = 0;
	sl->capacity = 0;
}

3.3 数据插入

将数据插入顺序表中有两种方法,分别是尾插和头插。

3.3.1 尾插

什么是尾插呢?

尾插就是从顺序表的尾部插入数据。如下图

63fc300e4f8548609e1fb5c35d602782.png

此时的size==4,我们可能就会想到所以只要将ps->arr[size]改为想要插入的之就行了 。写出以下代码

void SLPushBack(SL* sl, SLDataType x)
{
	sl->arr[sl->size] = x;
}

但事情并没有那么简单。

我们要知道插入数据时,首先要清楚顺序表内有没有足够的空间来存储插入的数据呢?一开始我们已经将顺序表的内存大小设置为0,这时侯肯定是没有空间插入数据的,所以这时候我们要向内存申请空间,并且以后我们要根据实际情况来实现扩容,所以这时候最好的选择是通过realloc函数来申请空间。

接着再来分析,我们要申请多大的空间呢?

一搬来说,是原来capacity的两倍或者三倍是最好的的选择。

但原来的capacity为0,所以这时候要用上三目操作符。

尾插代码如下

void SLPushBack(SL* sl, SLDataType x)
{
	assert(sl);
	if (sl->size == sl->capacity)
	{
		//实现扩容
		int SLNewCapacity = sl->capacity == 0 ? 4 : 2 * sl->capacity;
		SLDataType* tmp =(SLDataType*) realloc(sl->arr, SLNewCapacity * sizeof(SLDataType));
         //判断空间是否申请成功
		if (tmp==NULL)
		{
			perror("realloc fail");
			exit(1);
		}
		//代码运行到这里,空间就申请成功了
		sl->arr = tmp;
		sl->capacity = SLNewCapacity;
	}
	sl->arr[sl->size] = x;
	sl->size++;
}

易错点:扩容那里后面是两个==。

 我们需要注意的是我们申请空间成功时返回的地址不能直接传给原来顺序表的数组,因为申请空间会存在失败的情况,空间申请失败就会退出程序,并销毁该空间了。假设我们之前的顺序表已经存有数据,但因为空间申请失败也把原来的空间释放掉,那么原来的数据也没了,这就功亏一篑了,所以我们要设计一个中间变量存储申请空间返回的地址,有这个中间变量来判断空间是否申请成功。

由于我们插入了数据,不要忘了让size的个数进行增加。

3.3.2 头插

什么是头插呢?

头插就是从顺序表的头部插入一个数据。

下面来分析一下如何实现头插。

59731d29ed6244f49c61eaa16f847859.png

假设上图是我们要进行头插的一个数组?

我们要将一个数据插入1的位置,那么我们就要将原有的数据向后移一位。如下图

2a69ca80fd8e4aaab72e8a5a79e65afa.png

既然插入数据,我们就涉及到空间申请了,这里的空间申请于尾插的一莫一样,既然一样,那我们干脆把空间申请的那部分代码单独写为一个函数,需要的时候调用这个函数就行了。

空间申请的代码如下

void SLSpace(SL* sl)
{
	assert(sl);
	if (sl->size == sl->capacity)
	{
		//实现扩容
		int SLNewCapacity = sl->capacity == 0 ? 4 : 2 * sl->capacity;
		SLDataType* tmp = (SLDataType*)realloc(sl->arr, SLNewCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(1);
		}
		//代码运行到这里,空间就申请成功了
		sl->arr = tmp;
		sl->capacity = SLNewCapacity;
	}
}

尾插代码

void SLPushHead(SL* sl, SLDataType x)
{
	assert(sl);
	//调用空间申请函数
	SLSpace(sl);
	//实现顺序表原数据向后移1位
	for (int i = sl->size; i > 0; i--)
	{
		sl->arr[i] = sl->arr[i - 1];//arr[1]=arr[0]
	}
	sl->arr[0] = x;
	sl->size++;
}

运行效果如下图,头插的数据为5。

4b6c1f2b18a745ffb76c090d28b52099.png

3.4.数据删除

既然有数据的插入,那么就有数据的删除。删除我们也分为尾删头删的两种。

3.4.1 尾删

什么是尾删?

尾删就是将顺序表里面的的最后一个元素删除掉。

465d00ffed8f4f8389eb8f20bb27e750.png

如上图,尾删将4给除掉了。

代码实现

void SLPopBack(SL* sl)
{
	assert(sl);
	assert(sl->size);  //检测顺序表不为空
	sl->size--;
}

尾删我们直接让size( 顺序表中的有效个数)减减就行了。我们没必要将要尾删的数据改为其他,我们只要求尾删的操作不影响后面的增删查改的操作就行了。

运行效果

c4d2832165374ad595bdc6f56d295d42.png

3.4.2 头删

什么是头删呢?

头删就是将顺序表的第一个元素删除掉 。

eb4632f13a9b4a068f8da951c8fb4764.png

头删如上图所示,也就是将第一个元素除外其他元素向前移动一位。

代码实现 

void SLPopHead(SL* sl)
{
	assert(sl);
	assert(sl->size);
	//实现数据向前移动一位
	for (int i = 0; i<sl->size-1; i++)
	{
		sl->arr[i] = sl->arr[i + 1];  //arr[2]=arr[3]
	}
	sl->size--;
}

运行效果

30b1ec68d75a449db11afb822975d488.png

3.5 在指定位置插入数据

分析如何实现?

思路:将指定位置的数据及以从后向前的顺序向后移动一位。

如下图

4cd8c68bfc26492596a41558b1a42245.png

代码实现

 

void SLPopPos(SL* sl, int pos, SLDataType x)
{
	assert(sl);
	assert(sl->size && pos <= sl->size);
	//插入数据之前要申请空间
	SLSpace(sl);
	for (int i = sl->size; i > pos; i--)
	{
		sl->arr[i] = sl->arr[i - 1];
	}
	sl->arr[pos] = x;
	sl->size++;
}

运行效果

31b8e5fdbb4f44fb9bba6ace53a2b049.png

3.6 在指定位置删除数据

 思路

将指定位置之后的数据向前移动一位。

如下图

195180e0bd55478a92ad80eaaacb2731.png

代码实现

void SLPopPos(SL* sl, int pos)
{
	assert(sl&&sl->size);
	assert(pos && pos < sl->size);
	for (int i = pos; i<sl->size-1; i++)
	{
		sl->arr[pos] = sl->arr[pos + 1];
	}
	sl->size--;
}

运行效果

84665d2439864f168b556732e63dd126.png

3.7 查找数据

代码实现

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

}

运行效果

a6e21b25e3fe44cfbb2d3e58974795de.png

 这里就结束了对顺序表的介绍

下面给大家列处全部代码

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
int main()
{
	SL sl;  //创建一个结构体变量
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(sl);
	SLPushHead(&sl, 5);
	SLPrint(sl);
	SLPopBack(&sl);
	SLPrint(sl);
	SLPopHead(&sl);
	SLPrint(sl);
	SLPushPos(&sl, 1, 3);
	SLPrint(sl);
	SLPopPos(&sl, 2);
	SLPrint(sl);
	int find = SLFind(&sl, 3);
	if (find < 0)
	{
		printf("不存在");
	}
	else
	{
		printf("数据位于下标为%d",find);
	}
	//要在顺序表销毁之前完成增删查改的操作
	SLBreak(&sl);
	
	return 0;
}

SeqList.h

#include <assert.h>
typedef int SLDataType;  //方便以后修改要存储的数据类型
struct SeqList
{
	SLDataType* arr;
	int size;
	int capacity;
};
typedef struct SeqList SL; 
void SLInit(SL* sl);
void SLPrint(SL sl);
void SLBreak(SL* sl);
void SLPushHead(SL* sl, SLDataType x);
void SLPushBack(SL* sl, SLDataType x);
void SLPopBack(SL* sl);
void SLPopHead(SL* sl);
void SLPushPos(SL* sl, int pos, SLDataType x);
void SLPopPos(SL* sl, int pos);
int SLFind(SL* sl, SLDataType x);

Seqlist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void SLInit(SL* sl)
{
	sl->arr = NULL;
	sl->size = 0;
	sl->capacity = 0;
}
void SLSpace(SL* sl)
{
	assert(sl);
	if (sl->size == sl->capacity)
	{
		//实现扩容
		int SLNewCapacity = sl->capacity == 0 ? 4 : 2 * sl->capacity;
		SLDataType* tmp = (SLDataType*)realloc(sl->arr, SLNewCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(1);
		}
		//代码运行到这里,空间就申请成功了
		sl->arr = tmp;
		sl->capacity = SLNewCapacity;
	}
}
void SLPrint(SL sl)
{
	for (int i = 0; i < sl.size; i++)
	{
		printf("%d ", sl.arr[i]);
	}
	printf("\n");
}
void SLBreak(SL* sl)
{
	if (sl->arr)
	{
		free(sl->arr);
	}
	sl->arr = NULL;
	sl->size = 0;
	sl->capacity = 0;
}
void SLPushBack(SL* sl, SLDataType x)
{
	SLSpace(sl);
	sl->arr[sl->size] = x;
	sl->size++;
}
void SLPushHead(SL* sl, SLDataType x)
{
	assert(sl);
	//调用空间申请函数
	SLSpace(sl);
	//实现顺序表原数据向后移1位
	for (int i = sl->size; i > 0; i--)
	{
		sl->arr[i] = sl->arr[i - 1];//arr[1]=arr[0]
	}
	sl->arr[0] = x;
	sl->size++;
}
void SLPopBack(SL* sl)
{
	assert(sl);
	assert(sl->size);  //检测顺序表不为空
	sl->size--;
}
void SLPopHead(SL* sl)
{
	assert(sl);
	assert(sl->size);
	//实现数据向前移动一位
	for (int i = 0; i<sl->size-1; i++)
	{
		sl->arr[i] = sl->arr[i + 1];  //arr[2]=arr[3]
	}
	sl->size--;
}
void SLPushPos(SL* sl, int pos, SLDataType x)
{
	assert(sl);
	assert(sl->size && pos <= sl->size);
	//插入数据之前要申请空间
	SLSpace(sl);
	for (int i = sl->size; i > pos; i--)
	{
		sl->arr[i] = sl->arr[i - 1];
	}
	sl->arr[pos] = x;
	sl->size++;
}
void SLPopPos(SL* sl, int pos)
{
	assert(sl&&sl->size);
	assert(pos && pos < sl->size);
	for (int i = pos; i<sl->size-1; i++)
	{
		sl->arr[pos] = sl->arr[pos + 1];
	}
	sl->size--;
}
int SLFind(SL* sl, SLDataType x)
{
	assert(sl);
	for (int i = 0; i < sl->size; i++)
	{
		if (sl->arr[i] == x)
		{
			return i;
		}
	}
	return -1;

}

 感谢观看。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

相关文章

【抽代复习笔记】13-群(七):变换群引理

引理&#xff1a;考虑等边三角形123—— 这个等边三角形的对称性可用(1),(12),(13),(23),(123),(132)表示&#xff0c;其中&#xff1a; (1)表示这个等边三角形绕着其中心点旋转360/720/.../360n&#xff0c;得到的图形与原图形完全重合的旋转对称变换&#xff1b; (12)表示这…

selenium_使用XPATH定位

selenium_使用XPATH定位 """需求&#xff1a;1. 使用绝对路径定位 用户名 输入 admin2. 暂停2秒钟3. 使用相对路径定位 密码框 输入 123方法&#xff1a;driver.find_element_by_xpath() """# 导包 from selenium import webdriver from time im…

Elasticsearch常用查询语法及RestClient操作

DSL Query基本语法 1&#xff0c;查询所有数据matchall&#xff08;当然并不是会显示所有数据&#xff09; #查询所有数据 GET /索引名/_search {"query": {"查询类型": {"查询条件":"条件值"}} }2&#xff0c;全文搜索检索-分词搜索…

第07-4章 网络层详解

7.1 网络层协议 IP协议ARP&#xff08;地址解析协议&#xff09;RARP&#xff08;反向地址解析协议&#xff09;ICMP&#xff08;互联网控制消息协议&#xff09; 7.2 IP协议详解 7.2.1 IP协议功能 寻址和路由传递服务&#xff08;不可靠&#xff0c;尽最大努力&#xff0c…

牛客小白月赛91 ----- Bingbong的回文路径 ---- 题解

Bingbong的回文路径&#xff1a; 题目描述&#xff1a; 思路解析&#xff1a; 现在有一棵树&#xff0c;树上每个结点上都有一个小写字母&#xff0c;那么如果唯一确定了x和y两个结点&#xff0c;那么就唯一确定了一个字符串路径&#xff08;最短路径&#xff09;。 -现在给出…

linux内核初始化成功后是如何过渡到android初始化的

Android用的linux内核&#xff0c;以完成OS该有的功能&#xff0c;例如&#xff0c;文件系统&#xff0c;网络&#xff0c;内存管理&#xff0c;进程调度&#xff0c;驱动等 &#xff0c;向下管理硬件资源向上提供系统调用。另一些Android特有驱动也放在内核之中。 当linux内核…

Vue3+TS版本Uniapp:封装uni.request请求配置

作者&#xff1a;前端小王hs 阿里云社区博客专家/清华大学出版社签约作者✍/CSDN百万访问博主/B站千粉前端up主 封装请求配置项 封装拦截器封装uni.request 封装拦截器 uniapp的封装逻辑不同于Vue3项目中直接使用axios.create()方法创建实例&#xff08;在create方法中写入请求…

ChatGPT基础(三) 让ChatGPT回答质量提高十倍的提示词模版

上篇文章介绍了ChatGPT使用提示词的一些方法策略和如何优化我们的提示词。这里呢&#xff0c;我介绍一下参照大佬的方法总结的一个提示词的一个用法的模板。使用这个模板之后&#xff0c;我们的提问和获得答案的效率和收集素材的完整度能提高很多。 首先我介绍一下这个模板&am…

实战|哈尔滨等保2.0 Linux主机测评过程之身份鉴别

一、身份鉴别 a)应对登录的用户进行身份标识和鉴别&#xff0c;身份标识具有唯一性&#xff0c;身份鉴别信息具有复杂度要求并定期更换。 输入 more /etc/shadow,得知系统所有用户&#xff0c;此语句字段格式有九段。 第一字段&#xff1a;用户名&#xff08;也被称为登录名…

Nature Communications 构筑了具备优异形状记忆功能的聚合物材料

2024年2月29日&#xff0c;华东理工大学化学与分子工程学院、费林加诺贝尔奖科学家联合研究中心曲大辉教授团队在形状记忆功能聚合物材料研究中取得新进展&#xff0c;相关研究成果发表于《自然通讯》&#xff0c;这项研究取得了在形状记忆功能聚合物材料领域的新进展。研究团队…

国内ai人工智能软件大全

很多人一直在寻找一个稳定且可靠的全球AI大模型测试平台&#xff0c;希望它不仅真实可信&#xff0c;而且能提供稳定、快速的服务&#xff0c;不会频繁出现故障或响应缓慢。迄今为止&#xff0c;我已经尝试了国内外至少10个不同的服务站点。不幸的是&#xff0c;这些站点总是存…

【UE 材质】雨滴效果

在上一篇博客&#xff08;【UE 材质】表面湿润效果&#xff09;的基础上继续实现物体表面附加雨滴的效果 效果 步骤 1. 下载所需纹理 2. 创建一个材质并打开&#xff0c;添加如下节点&#xff0c;我们将纹理的RG通道输出的值和1组成一个三维向量&#xff0c;作为基本的法线效…

Shapley量化调峰成本?高比例可再生能源电力系统的调峰成本量化与分摊模型程序代码!

前言 在能源安全、环境污染和气候变化的大背景下&#xff0c;大力发展可再生能源是应对全球气候变化&#xff0c;实现“碳达峰、碳中和”和可持续发展的重大需求。截至2020年底&#xff0c;中国风电总装机容量为281GW&#xff0c;风力发电466.5TWh&#xff0c;同比增长约15%&a…

Reddit数据API 获取reddit的帖子、评论、按关键字搜索

近期调研发现 iDataRiver平台 https://www.idatariver.com/zh-cn/ 提供开箱即用的Reddit数据采集API&#xff0c;是目前用下来最方便简单的API&#xff0c;可以抓取 reddit 公开数据&#xff0c;例如 subreddit 中的帖子、按关键字搜索以及文章评论等&#xff0c;供用户按需调用…

【算法】实验室2024第一次考核复盘

【算法】实验室2024第一次考核复盘 本篇博客将遵循从易到难的原则&#xff0c;依次解析这几道考核题目。 原题链接&#xff1a; 125. 验证回文串 - 力扣&#xff08;LeetCode&#xff09; 使用两个指针i和j分别从字符串的两端开始&#xff0c;i从左往右&#xff0c;j从右往左。…

MySQL如何避免全表扫描?

MySQL如何避免全表扫描&#xff1f; 这篇文章解释了何时以及为什么MySQL会执行全表扫描来解析查询&#xff0c;以及如何避免在大型表上进行不必要的全表扫描。 何时会发生全表扫描 MySQL使用全表扫描&#xff08;在EXPLAIN输出中的type列显示为ALL&#xff09;来解析查询的几…

继东风一汽通信后,天磊咨询再次与东风集团达成深度业务合作

&#xff08;天磊咨询总经理&#xff1a;刘文喜&#xff09; 在风起云涌的市场激战中&#xff0c;天磊咨询凭借其出类拔萃的专业实力与服务品质&#xff0c;犹如一颗璀璨明星般脱颖而出&#xff0c;成功与赫赫有名的东风集团达成业务合作。这一合作的达成&#xff0c;不单彰显…

用java实现PDF的下载

1.下载PDF模版 2.导入依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.2.5</version><type>pom</type></dependency> 3.完整代码 package com.by.controller…

数据存储的大小端和测试程序

1.引言 计算机和嵌入式产品中数据的存储方式分为大端和小端两种方式。为什么呢&#xff1f;因为对于大多数设备都需要存储超过1个字节的数据单元。比如&#xff0c;你要表达0x12345678这个数字。使用1个字节的存储空间是肯定完成不了的。因此&#xff0c;你必须使用4个字节的空…

Postgres数据库中的死锁是如何产生的,如何避免和解决?

文章目录 死锁的产生原因如何避免死锁如何解决死锁示例代码查询死锁信息终止事务 在Postgres数据库中&#xff0c;死锁是一种特殊的情况&#xff0c;其中两个或多个事务相互等待对方释放资源&#xff0c;从而导致它们都无法继续执行。这种情况通常发生在多个事务尝试以不同的顺…