线性表之顺序表

在计算机科学中,数据结构是非常重要的基础知识之一。数据结构为我们提供了组织和管理数据的方法和技巧,使得我们可以高效地存储、检索和操作数据。而顺序表作为数据结构中最基本、最常用的一种存储结构,也是我们学习数据结构的第一步。

本文将介绍顺序表的概念和结构,通过实现顺序表的接口,我们将学习如何操作顺序表中的元素。同时,我们也会探讨顺序表的优点和缺点,以便更好地理解顺序表的适用场景和局限性。

通过阅读本文,你将对顺序表有一个清晰的认识,并且能够使用顺序表来解决实际问题。让我们开始学习顺序表吧!

个人主页:Oldinjuly的个人主页

收录专栏:数据结构

欢迎各位点赞👍收藏⭐关注❤️

 

🌹1.线性表

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

  • 存储结构上又分为顺序存储结构和链式存储结构。

顺序存储是将数据元素按照顺序依次存放在一块连续的内存空间中。数组就是一种顺序存储结构。

链式存储则是通过使用指针将数据元素存储在离散的内存空间中,并通过指针将这些离散的内存空间连接起来。

 

🌹2.顺序表的概念和结构

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

顺序表又分为:

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

2.动态顺序表:使用动态开辟的空间存储元素

🌹3.顺序表接口实现

头文件的类型和函数接口声明:

#pragma once

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

#define DEFAULT_CAP 4//起始容量4

typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;
	int size;
	int capacity;
}SL;

void SLInit(SL* ps);//顺序表初始化
void SLDestory(SL* ps);//顺序表销毁
void SLPrint(SL* ps);//顺序表打印
void SLCheckCapacity(SL* ps);//容量检查,扩容函数

void SLPushBack(SL* ps, SLDataType x);//顺序表尾插
void SLPopBack(SL* ps);//顺序表尾删
void SLPushFront(SL* ps, SLDataType x);//顺序表头插
void SLPopFront(SL* ps);//顺序表头删
void SLInsert(SL* ps, int pos, SLDataType x);//顺序表插入
void SLErase(SL* ps, int pos);//顺序表删除

前提补充:

这里的参数都是结构体指针,原因已经说吐了,具体见C语言结构体章节。

前面C语言所写的通讯录其实就是顺序表。

💐3.1 顺序表初始化

void SeqListInit(SL* ps)
{
	ps->a = (SLDataType*)malloc(sizeof(SLDataType) * DEFAULT_CAP);
	if (ps->a == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	ps->capacity = DEFAULT_CAP;
	ps->size = 0;
}
  • 起始容量大小为4。
  • malloc后要对返回值进行检查。
  • 空间开辟失败后,要退出程序exit,不能简单的返回return。

💐3.2 顺序表销毁

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

每次释放过一块空间后最好置空NULL

💐3.3 顺序表打印

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

💐3.4 顺序表尾插

在此之前写介绍容量检查和扩容函数:

动态顺序表相对静态顺序表最大的优点就是能够进行扩容,那么频繁扩容的话,最好封装成函数。

void CheckCapacity(SL* ps)
{
	//满了扩容
	if (ps->capacity == ps->size)
	{
		SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->size * 2);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}

		ps->a = tmp;
		ps->capacity *= 2;
	}
}

注意几点:

  • realloc函数的返回值不能直接给ps->a,因为如果扩容失败的话会造成内存泄漏。所以会用tmp临时指针变量来保存返回值,然后进行检查,无误后赋值给ps->a。
  • realloc扩容之后不能释放ps->a,原因:realloc底层有异地扩容和原地扩容,如果异地扩后free,会造成重复释放,原地扩后free,会导致realloc白干。
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);

	SLCheckCapacity(ps);

	ps->a[ps->size] = x;
	ps->size++;
}

💐3.5 顺序表尾删

void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);

	ps->size--;
}

注意几点:

ps->size--后要不要把ps->a[ps->size-1]置零?

答:不要,直接--顺序表是访问不到的,而且置零也没意义。

ps->size--后要不要把删除的那部分free掉?

答:不是不要,是不能释放,malloc出什么指针,就要free掉什么指针,不能只释放一部分。

删除和插入不同的是,只要扩容成功,插入是能一直插的,但是顺序表中没有元素后不能删除。所以要检查是否删完了。

两种检查方式:

温柔的检查方式:

if(ps->size==0)
	return;

暴力的检查方式:

assert(ps->size > 0);

💐3.6 顺序表头插

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

	CheckCapacity(ps);

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

注意一下边界控制就行。

💐3.7 顺序表头删

void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);

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

💐3.8 顺序表查找

查找一般结合后面的插入删除使用,查找的返回值做下标pos参数

int SLFind(SL* ps, SLDataType x)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}

💐3.9 顺序表插入

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

	CheckCapacity(ps);

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

💐3.10 顺序表删除

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--;
}

🌹4.关于顺序表的问题和思考

💐顺序表的缺点:

  • 中间和头部的插入删除效率不高,时间复杂度为O(N)。
  • 扩容时会开新空间、拷贝数据、释放就空间,造成一定消耗。
  • 由于扩容的控制,可能会造成空间浪费。

💐顺序表的优点:

  • 尾插尾删的效率高。
  • 下标的访问和修改效率高。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QT: 完成服务器的实现

1> 思维导图 2> 手动完成服务器的实现&#xff0c;并具体程序要注释清楚 Widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器类 #include <QTcpSocket> //客户端类 #include <QMessageBox> //…

jMeter使用随记

参数化BodyData 先制作参数文件 再设置一个csv data set config 最后在body data里面写上参数${xxxxx}

Stable diffusion 和 Midjourney 怎么选?

通过这段时间的摸索&#xff0c;我将和你探讨&#xff0c;对普通人来说&#xff0c;Stable diffusion 和 Midjourney 怎么选&#xff1f;最重要的是&#xff0c;学好影视后期制作对 AI 绘画创作有哪些帮助&#xff1f;反过来&#xff0c;AI 绘画对影视后期又有哪些帮助&#xf…

【docker】docker部署nginx

目录 一、步骤二、示例 一、步骤 1.搜索nginx镜像 2.拉取nginx镜像 3.创建容器 4.测试nginx 二、示例 1.搜索nginx镜像 docker search nginx2.拉取nginx镜像 docker pull nginx3.创建容器&#xff0c;设置端口映射、目录映射 # 在root目录下创建nginx目录用于存储nginx数据…

error:0308010C:digital envelope routines::unsupported(Vue2报错)

原因:node.js版本过高&#xff0c; 解决方案&#xff0c;在终端输入以下命令 set NODE_OPTIONS--openssl-legacy-provider 然后再package.json里面添加一行 "dev_t": "set NODE_OPTIONS\"--openssl-legacy-provider\" & npm run dev\n" 然后…

【Linux命令200例】用ln创建链接文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜活的实操案例对各个命令进行深入…

windows 删除无法删除的文件

有两种原因&#xff1a; 文件被占用文件无权限 解决方案 通用解决方案是进入安全模式进行删除 安全模式&#xff1a; 不会启动非必要的进程有最高的系统权限 进入系统配置 安全引导&#xff0c;重启 删除文件 修改系统配置为正常启动 重启

[JavaWeb]SQL介绍-DDL语句

SQL介绍-DDL语句 一.SQL简介1.简介2.SQL通用语法3.SQL语言的分类 二.DDL-操作数据库与表1.DDL操作数据库2.DDL操作表①.查询表(Retrieve)②.创建表(Create)③.修改表(Update)④.删除表(Delete) 一.SQL简介 1.简介 SQL: Structured Query Language–结构化查询语言用来操作关系…

PCB封装设计指导(十五)验证封装的正确性

PCB封装设计指导(十五)验证封装的正确性 封装建立好之后,我们需要验证封装是否能够正常的放入PCB文件中,最好最直接的办法就是直接放入PCB中来验证。 具体操作如下 任意新建一个空白的PCB文件点击File 选择NEW

JAVA基础多线程-模拟线程死锁以及预防和避免死锁

引言 线程死锁描述的是这样一种情况&#xff1a;多个线程同时被阻塞&#xff0c;它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞&#xff0c;因此程序不可能正常终止。 一&#xff0c;模拟死锁 示例代码&#xff1a; public class LockT1 {Object o …

SciencePub学术 | 物联网类重点SCIEEI征稿中

SciencePub学术 刊源推荐: 物联网类重点SCIE&EI征稿中&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; 物联网类重点SCIE&EI 【期刊简介】IF&#xff1a;7.5-8.0&#xff0c;JCR1区&#xff0c;中科院1/2区TOP&#xff1b; 【出版社…

【JAVASE】顺序和选择结构

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 顺序和选择 1. 顺序结构2. 分支结构2.1 …

Could not locate supplied template: react+ts搭建

1. reactts创建 我们在是用下create-react-app之前要下载一下 npm install create-react-app -g使用一下命令创建ts的react框架 create-react-app my-app --scripts-versionreact-scripts-ts 2. 遇见问题 我们用以上创建之后会提示一段代码选择“Y”之后发现我们创建的项目…

LangChain Agents深入剖析及源码解密上(一)

LangChain Agents深入剖析及源码解密上(一) LangChain Agents深入剖析及源码解密上 Agent工作原理详解 本节会结合AutoGPT的案例,讲解LangChain代理(Agent)为核心的内容。我们前面已经谈了代理本身的很多内容,也看了绝大部分的源代码,例如:ReAct的源代码,还有mrkl的源代…

windows11打不开任务管理器,

目录 第一章、win11系统任务管理器打不开&#xff1f;第二章、解决方式修改注册表 友情提醒&#xff1a; 先看文章目录&#xff0c;大致了解文章知识点结构&#xff0c;点击文章目录可直接跳转到文章指定位置。 第一章、win11系统任务管理器打不开&#xff1f; Win11任务管理…

Python web实战 | 用Docker部署Django项目

1. Docker与Django 为什么要使用Docker来部署Django项目&#xff1f;这是因为Docker提供了一个独立、一致的环境&#xff0c;让开发者可以在任何地方运行他们的应用程序&#xff0c;而不用担心环境配置问题。这就像是你有一个可以装下所有工具和材料的宝箱&#xff0c;无论你走…

AI数字人:金融数字化转型的“关键先生”

今年年初ChatGPT的火热&#xff0c;在全球掀起一阵生成式AI&#xff08;AIGC&#xff09;热潮。国外的OpenAI、国内的百度等企业&#xff0c;都在AIGC上强力布局。 各种应用场景中&#xff0c;AIGC助力的数字人引起了市场注意。 事实上&#xff0c;数字人不是个新鲜事。早在1…

JUC并发工具类

一、ReentrantLock 特点&#xff1a;独占、可重入、公平/非公平、可中断、支持多个条件变量 1、常用api ReentrantLock实现了Lock接口&#xff0c;Lock类规范定义了如下方法 lock()&#xff1a;获取锁&#xff0c;调用该方法的线程会获取锁&#xff0c;当锁获得后&#xff0…

数字身份、分布式存储、跨链技术等将如何推动Web3数据的发展?

Web3数据是基于区块链技术、去中心化、可信任的数据&#xff0c;具有较高的安全性和可信度。随着Web3.0时代的到来&#xff0c;Web3数据将会在金融、物联网、医疗、教育、政务等领域发挥重要的作用。其中&#xff0c;数字身份、分布式存储、跨链技术等将会是Web3数据发展的重要…

Unity 资源与源码

Unity场景资源 Unity场景资源 Unity 路径编辑运行插件 Unity AssetBundle资源反编译