线性表之顺序表

news2024/9/27 21:21:10

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

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

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

个人主页: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)。
  • 扩容时会开新空间、拷贝数据、释放就空间,造成一定消耗。
  • 由于扩容的控制,可能会造成空间浪费。

💐顺序表的优点:

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

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

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

相关文章

idea 关于高亮显示与选中字符串相同的内容

dea 关于高亮显示与选中字符串相同的内容&#xff0c;本文作为个人备忘的同时也希望可以作为大家的参考。 依次修改File-settings-Editor-Color Scheme-General菜单下的Code-Identifier under caret和Identifier under caret(write)的Backgroud色值&#xff0c;可以参考下图。…

阿里云域名备案

最好的爱情&#xff0c;不是因为我们彼此需要在一起&#xff0c;而是因为我们彼此想要在一起。 阿里云的域名如何备案&#xff0c;域名备案和ICP备案一样吗&#xff1f;&#xff1f; 截至我所掌握的知识&#xff08;2021年9月&#xff09;&#xff0c;阿里云的域名备案和ICP备案…

【GoLang】基础语法(上)

Go基础语法(上) 文章目录 Go基础语法(上)01注释02变量定义初始化打印内存地址变量交换匿名变量变量的作用域 03常量iota 04基本数据类型布尔类型数字类型整型浮点型 字符与字符串 05数据类型转换06运算符算术运算符关系运算符逻辑运算符位运算符赋值运算符 07获取键盘输入 01注…

Java 设计模式 - 简单工厂模式 - 创建对象的简便之道

简单工厂模式是一种创建型设计模式&#xff0c;它提供了一种简单的方式来创建对象&#xff0c;而无需暴露对象创建的逻辑。在本篇博客中&#xff0c;我们将深入了解简单工厂模式的概念、实现方式以及如何在Java中使用它来创建对象。 为什么使用简单工厂模式&#xff1f; 在软…

PC音频框架学习

1.整体链路 下行播放&#xff1a; App下发音源→CPU Audio Engine 信号处理→DSP数字信号处理→Codec DAC→PA→SPK 上行录音&#xff1a; MIC拾音→集成运放→Codec ADC→DSP数字信号处理→CPU Audio Engine 信号处理→App 2.硬件 CPU PCH DSP(可选) Codec PA SPKbox MIC…

Vue 3 中的插槽(Slots)用法

插槽&#xff08;Slots&#xff09;是 Vue 组件中一种非常有用的功能&#xff0c;用于在父组件中向子组件传递内容。Vue 3 引入了 <script setup> 语法&#xff0c;使得组件的写法更加简洁和易读。在本篇博客中&#xff0c;我们将探讨在 Vue 3 中使用插槽的不同方式&…

数据结构与算法基础-学习-27-图之最短路径之Dijkstra(迪杰斯特拉)算法

一、最短路径应用案例 例如从北京到上海旅游&#xff0c;有多条路可以到目的地&#xff0c;哪条路线最短&#xff0c;哪条路线最省钱&#xff0c;就是典型的最短路径问题。 二、最短路径问题分类 最短路径问题可以分为两类&#xff0c;第一类为&#xff1a;两点间最短路径。第…

函数栈帧的创建和毁销【C语言版】

大家好&#xff0c;我是深鱼~ 【前言】前期学习的时候&#xff0c;我们可能有很多的困惑 比如&#xff1a; 局部变量是怎么创建的呢&#xff1f; 为什么局部变量的值是随机值&#xff1f; 函数是怎么传参的&#xff1f;传参的顺序是怎么样的&#xff1f; 形参和实参是什么关系…

上海亚商投顾:沪指放量大涨1.84% 证券股掀涨停潮

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 三大指数今日低开高走&#xff0c;沪指午后放量涨近2%&#xff0c;上证50盘中大涨超3%。大金融板块全线爆发&#…

Wireshark从下载到使用完整教程分享

surfshark如何下载呢&#xff1f;surfshark安卓&#xff0c;苹果以及电脑上使用的完整教程分享可以戳后面地址: https://qptool.net/shark.html Wireshark是一款流行的网络协议分析工具&#xff0c;用于捕获和分析网络数据包。它可以帮助网络管理员和安全专家监视和诊断网络问…

[每日习题]跳石板(动态规划) 手套(贪心)——牛客习题

hello,大家好&#xff0c;这里是bang___bang_&#xff0c;今天来记录2道习题跳石板和手套&#xff01; 目录 1️⃣跳石板 2️⃣手套 1️⃣跳石板 跳石板_牛客题霸_牛客网 (nowcoder.com) 描述 小易来到了一条石板路前&#xff0c;每块石板上从1挨着编号为&#xff1a;1、2、…

批量删除python代码中的注释

ctrlh&#xff0c;调出替换功能窗口 启用正则表达式&#xff0c;输入 (#.*) 点击替换就能删除全部的注释了

【图论】LCA(倍增)

一.LCA介绍 LCA通常指的是“最近共同祖先”&#xff08;Lowest Common Ancestor&#xff09;。LCA是一种用于解决树或图结构中两个节点的最低共同祖先的问题的算法。 在树结构中&#xff0c;LCA是指两个节点的最近层级的共同祖先节点。例如&#xff0c;考虑一棵树&#xff0c;…

16.Netty源码之ChannelPipeline

highlight: arduino-light 服务编排层:ChannelPipeline协调ChannelHandlerHandler EventLoop可以说是 Netty 的调度中心&#xff0c;负责监听多种事件类型&#xff1a;I/O 事件、信号事件、定时事件等&#xff0c;然而实际的业务处理逻辑则是由 ChannelPipeline 中所定义的 Cha…

《Elasticsearch 源码解析与优化实战》第5章:选主流程

《Elasticsearch 源码解析与优化实战》第5章&#xff1a;选主流程 - 墨天轮 一、简介 Discovery 模块负责发现集群中的节点&#xff0c;以及选择主节点。ES 支持多种不同 Discovery 类型选择&#xff0c;内置的实现称为Zen Discovery ,其他的包括公有云平台亚马逊的EC2、谷歌…

C++之普通函数指针/类成员函数指针/lambda回调函数总结(一百六十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

边写代码边学习之卷积神经网络CNN

1. 卷积神经网络CNN 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种深度学习神经网络的架构&#xff0c;主要用于图像识别、图像分类和计算机视觉等任务。它是由多层神经元组成的神经网络&#xff0c;其中包含卷积层、池化层和全连接…

算法与数据结构-二分查找

文章目录 什么是二分查找二分查找的时间复杂度二分查找的代码实现简单实现&#xff1a;不重复有序数组查找目标值变体实现&#xff1a;查找第一个值等于给定值的元素变体实现&#xff1a;查找最后一个值等于给定值的元素变体实现&#xff1a;查找最后一个小于给定值的元素变体实…

【雕爷学编程】MicroPython动手做(10)——零基础学MaixPy之神经网络KPU2

KPU的基础架构 让我们回顾下经典神经网络的基础运算操作&#xff1a; 卷积&#xff08;Convolution&#xff09;:1x1卷积&#xff0c;3x3卷积&#xff0c;5x5及更高的卷积 批归一化&#xff08;Batch Normalization&#xff09; 激活&#xff08;Activate&#xff09; 池化&…

玩一玩编程式 AOP

[toc] 平时我们项目中涉及到 AOP&#xff0c;基本上就是声明式配置一下就行了&#xff0c;无论是基于 XML 的配置还是基于 Java 代码的配置&#xff0c;都是简单配置即可使用。声明式配置有一个好处就是对源代码的侵入小甚至是零侵入。不过今天松哥要和小伙伴们聊一聊编程式的 …