超详细!!!顺序表的实现

顺序表的概念及结构

概念

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

顺序表与数组的区别

顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接⼝,并且顺序表的存储一定是连续的,只能从头开始连续存储。

顺序表的结构

  • 静态顺序表:
    使用定长数组存储元素,缺点是空间不好分配,空间给少了不够用,给多了造成空间浪费。
  • 动态顺序表:
    与静态顺序表相比,动态顺序表的空间可以自主申请空间,大大改善了静态顺序表空间难以分配的情况。

动态顺序表的实现

头文件 “SeqList.h”

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

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

#define SLDataType int

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

void SLInit(SL* ps);
void SLDestroy(SL* ps);

void SLPushBack(SL* ps,SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
void SLPopBack(SL* ps);

void SLErase(SL* ps, int pos);
void SLInsert(SL* ps, int pos, SLDataType x);

bool SLFind(SL* ps, SLDataType x);
int SLFindPos(SL* ps, SLDataType x);
void SLRevise(SL* ps, int pos, SLDataType x)void SLPrint(SL* ps);
bool SLIsEmpty(SL* ps);

定义结构体 SL

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

SLDataType* arr;用来存放顺列表的主体数据,SLDataType是我们利用define宏定义所定义的变量,为以后更好的利用顺序表做准备;
size表示有效数据空间个数;
capacity表示总的数据空间个数;
好,下面让我们一 一实现下面头文件中的函数。

源文件 SeqList.c

顺列表的实现

初始化顺列表 void SLInit(SL* ps)

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

将顺列表置为空的状态。

检查顺列表空间大小 void SLCheckCapacity(SL* ps)

void SLCheckCapacity(SL* ps)
{
	if (ps->capacity == ps->size)
	{
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
		assert(tmp);
		ps->arr = tmp;
		tmp = NULL;
		ps->capacity = newcapacity;
	}
}

如果满足ps->capacity == ps->size 那么说明空间不足,因为有效数据个数等于总的ps->arr空间存放数据个数,所以这个时候就需要为ps->arr开辟扩展新的空间,一般情况下,空间的扩展倍数是1.5倍或者2倍,这里以2倍为例。

int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;

如果ps->capacity的值为0,我们就不能利用倍数去扩展,而是为他直接开辟空间,所以ps->capacity == 0成立时,放回值为4,否则返回值为ps->capacity

SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
assert(tmp);
ps->arr = tmp;

新建一个SLDataType*tmp变量防止realloc开辟空间失败,返回空指针而导致ps->arr被置为NULL。

ps->capacity = newcapacity;

最后将新ps->arr的空间大小赋值给ps->capacity

尾插数据 void SLPushBack(SL* ps,SLDataType x)

void SLPushBack(SL* ps,SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->arr[ps->size++] = x;
}

我们在这里只需判断空间大小是否合适,再将数据插入即可,ps->size也要加一。

头插数据 void SLPushFront(SL* ps, SLDataType x)

void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	int i = 0;
	for (i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->size++;
	ps->arr[0] = x;
}

这里先判断空间大小,再将所有数据再数组中向后移动一位,再将所要添加的值x放到数组的首元素地址处,此时ps->size要记得加一。

打印顺序表 void SLPrint(SL* ps)

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

游历数组arr逐个打印。

数组指定位置插入 void SLInsert(SL* ps, int pos, SLDataType x)

void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	if (pos < 0 || pos > ps->size)
	{
		printf("输入错误!!!");
		return;
	}
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

先判断pos值是否输入正确,与顺序表空间是否足够。
这里我们只需要将要插入的数组坐标pos之后的数据向后移动一位,再将要插入的数据x放到arr[pos]即可。
在这里插入图片描述

销毁顺列表 void SLDestroy(SL* ps)

void SLDestroy(SL* ps)
{
	if (ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}

if (ps->arr) free(ps->arr);将顺序表内arr所申请空间释放,并将ps->size,ps->capacity置为0.

测试插入

#include"SeqList.h"
int main()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);//1 2
	SLPushFront(&s, 0);
	SLPushFront(&s, -1);//-1 0 1 2
	SLInsert(&s, 0, -2);//-2 -1 0 1 2
	SLInsert(&s, s.size, 3);//-2 -1 0 1 2 3
	SLInsert(&s, 3, 0);//-2 -1 0 0 1 2 3

	SLPrint(&s);
	SLDestroy(&s);
	return 0;
}

先尾插1,2再头插0,-1 此时顺序表为-1 0 1 2,再将-2插入到arr[0]此时顺序表为-2 -1 0 1 2,再将3插入到arr[5]此时顺序表为-2 -1 0 1 2 3,最后将0插入到arr[3]此时顺序表为-2 -1 0 0 1 2 3,程序运行结果如下:
在这里插入图片描述

判断有效数据的存在 bool SLIsEmpty(SL* ps)

bool SLIsEmpty(SL* ps)
{
	assert(ps);
	return (ps->size == 0);
}

若有效值ps->size0,则返回1.

头删数据 void SLPopFront(SL* ps)

void SLPopFront(SL* ps)
{
	assert(ps);
	assert(!SLIsEmpty(ps));
	int i = 0;
	for (i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

我们在处理头删数据的时候,assert(!SLIsEmpty(ps))先判断是否数据存在,若存在,可以让后面的数据对前面进行覆盖,再让ps->size减一即可,覆盖示意图如下:
在这里插入图片描述

尾删数据 void SLPopBack(SL* ps)

void SLPopBack(SL* ps)
{
	assert(ps);
	assert(!SLIsEmpty(ps));
	ps->size--;
}

我们尾删数据只需判断数据是否可删,若可删,我们也只要将ps->size--即可,因为我们在访问的时候,数组arr从0开始向后访问,而我们利用ps->size限制了arr数组从0访问的距离。

指定数组下标删除 void SLErase(SL* ps, int pos)

void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(!SLIsEmpty(ps));
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

这里我们只需要在满足删除条件的时候,找到pos的位置并将后面的数据向前推移即可,再将ps->size - -

测试删除

#include"SeqList.h"
int main()
{
	SL s;
	SLInit(&s);
	for (int i = 0; i < 8; i++)
	{
		SLPushBack(&s, i);//0 1 2 3 4 5 6 7 
	}
	SLPopBack(&s);
	SLPrint(&s);//0 1 2 3 4 5 6
	SLPopFront(&s);
	SLPrint(&s);//1 2 3 4 5 6
	SLErase(&s, 0);
	SLPrint(&s);//2 3 4 5 6
	SLErase(&s, s.size - 1);
	SLPrint(&s);//2 3 4 5
	SLErase(&s, 1);
	SLPrint(&s);//2 4 5
	SLDestroy(&s);
	return 0;
}

先利用SLPushBack(&s, i)循环十次来添加顺序表数据,在进行测试以上代码运行结果如下:
在这里插入图片描述

判断该数据是否存在 bool SLFind(SL* ps, SLDataType x)

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

遍历顺序表中的整个数组,若存在则返回true,否则返回false

查找并返回数据的位置 int SLFindPos(SL* ps, SLDataType x)

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

遍历顺序表中的整个数组,当顺序表中的数组元素与查找值相同时,返回数组此时的下标i,若不存在则返回-1

修改数据 void SLRevise(SL* ps, int pos, SLDataType x)

void SLRevise(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	if (pos >= ps->size && ps < 0)
	{
		printf("输入错误,无法修改\n");
		return;
	}
	ps->arr[pos] = x;
}

先判断pos是否合理,若合理,则通过pos直接进行修改。

测试查找与修改

#include"SeqList.h"
int main()
{
	SL s;
	SLInit(&s);
	for (int i = 0; i < 8; i++)
	{
		SLPushBack(&s, i);//0 1 2 3 4 5 6 7 
	}
	printf("%d\n", SLFind(&s,0));//1
	printf("%d\n", SLFind(&s,7));//1
	printf("%d\n", SLFind(&s,8));//0
	printf("%d\n", SLFindPos(&s, 0));//0
	printf("%d\n", SLFindPos(&s, 3));//3
	printf("%d\n", SLFindPos(&s, 7));//7
	printf("%d\n", SLFindPos(&s, 8));//-1
	printf("修改前:");
	SLPrint(&s);
	SLRevise(&s, SLFindPos(&s, 3), -3);//将3改为-3
	SLRevise(&s, SLFindPos(&s, 7), -7);//将7改为-7
	SLRevise(&s, SLFindPos(&s, 0), 10);//将0改为10
	printf("修改后:");
	SLPrint(&s);
	return 0;
}

运行结果如下:
在这里插入图片描述

结束语

本期是顺序表的基本功能实现,下期小编将为大家带来基于本期的顺序表所实现的可存储通讯录程序的实现。
那么,喜欢请多多关照把!!!

在这里插入图片描述

  • 67
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 41
    评论
点云处理算法是计算机视觉和图形学领域中的重要研究方向,它涉及到对三维空间中的点云数据进行处理、分析和应用。下面是一个详细的教程,介绍了点云处理算法的主要内容。 1. 点云数据表示: - 点云数据的表示方式有两种:有序点云和无序点云。有序点云是指点的顺序与其在物体表面上的位置一一对应,常见的有序点云格式包括PLY和XYZ等。无序点云是指点的顺序与其在物体表面上的位置无关,常见的无序点云格式包括XYZRGB和XYZNormal等。 2. 点云预处理: - 点云预处理包括去噪、滤波和采样等操作。去噪的目标是降低点云中的噪声数据,常见的去噪算法有统计滤波和曲面平滑等。滤波操作可以对点云进行平滑处理,常见的滤波算法有高斯滤波和均值滤波等。采样操作可以减少点云数据量,常见的采样算法有随机采样和体素采样等。 3. 点云配准: - 点云配准是将多个点云数据对齐到同一个坐标系中的过程。常见的配准算法有ICP(Iterative Closest Point)和特征匹配等。ICP算法通过迭代优化点云之间的最小距离来实现配准,而特征匹配算法则是通过提取点云的特征描述子进行匹配。 4. 点云分割: - 点云分割是将点云数据划分为不同的部分或者物体的过程。常见的分割算法有基于几何特征和基于深度学习的方法。基于几何特征的分割算法通常使用平面分割和曲面分割等技术,而基于深度学习的方法则是使用卷积神经网络对点云进行分类。 5. 点云重建: - 点云重建是将离散的点云数据还原为连续的曲面模型的过程。常见的重建算法有基于网格的方法和基于隐式函数的方法。基于网格的方法将点云映射到一个网格结构上,并使用插值技术生成曲面模型,而基于隐式函数的方法则是通过学习一个表示曲面的隐式函数来进行重建。 6. 点云应用: - 点云处理算法在许多应用领域有广泛的应用,包括三维建模、机器人导航、虚拟现实和增强现实等。在三维建模方面,点云处理算法可以从激光扫描数据中提取出物体的几何信息,用于建立三维模型。在机器人导航方面,点云处理算法可以帮助机器人感知周围环境,实现自主导航。在虚拟现实和增强现实方面,点云处理算法可以用于生成真实感的三维场景。 这些是点云处理算法的一些基本内容,希望对你有所帮助!如果还有其他问题,请继续提问。
评论 41
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒋志昂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值