顺序表的实现超详细

一、顺序表的概念

● 顺序表是一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。
在这里插入图片描述
顺序表一般可以分为:

  1. 静态顺序表:使用定长数组存储元素。
//把 int 重命名为SLDataType,方便后期数据类型的修改
typedef int SLDataType; 
//capacity初始大小为3个(int)
#define N 50 

typedef struct SeqList
{
	SLDataType data[N]; //用来存放数据的数组指针
	int size; //当前顺序表中有效数据的个数
}SeqList;
  1. 动态顺序表:使用动态开辟的数组存储。
//把 int 重命名为SLDataType,方便后期数据类型的修改
typedef int SLDataType; 
//capacity初始大小为3个(int)
#define INIT_CAPACITY 3 

typedef struct SeqList
{
	SLDataType* data; //用来存放数据的数组指针
	int size; //当前顺序表中有效数据的个数
	int capacity; //顺序表的总大小
}SeqList;
二、顺序表的基本实现
1.顺序表的定义

使用结构体来创建一个顺序表

//把 int 重命名为SLDataType,方便后期数据类型的修改
typedef int SLDataType; 
//capacity初始大小为3个(int)
#define INIT_CAPACITY 3 

typedef struct SeqList
{
	SLDataType* data; //用来存放数据的数组指针
	int size; //当前顺序表中有效数据的个数
	int capacity; //顺序表的总大小
}SeqList;
2.顺序表的初始化

初始化顺序表为3个空间大小

void SeqListInit(SeqList* ps)
{
	//使用malloc为data初始化为3个空间
	ps->data = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);
	//如果开辟失败则打印字符串和错信息
	if (ps->data == NULL)
	{
		perror("SeqListInit");
		return;
	}
	ps->size = 0; //size是顺序表的有效数据,初始为0
	//上面为顺序表开辟了3个空间,所以这里capacity初始为3个空间大小
	ps->capacity = INIT_CAPACITY; 
}

3.顺序表的销毁

动态开辟的空间都需要销毁

void SeqListDestroy(SeqList* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->capacity = ps->size = 0;
}
4.顺序表的打印
void SeqListPint(SeqList* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("\n");
}
5.增加容量(空间不够则增容)

如果已经插入了3个数据,则空间已经满了,继续增加会非法访问内存,这时我们需要增加容量,我们是按照总容量大小的2倍来增加容量的,因为使用realloc开辟的空间,如果要增加的这个空间太大,realloc可能会异地扩容,realloc异地扩容的消耗大,所以我们每次增加总容量的2倍。

void check_capacity(SeqList* ps)
{
	//当size==capacity时,则代表顺序表的空间满了,需要扩容
	if (ps->size == ps->capacity)
	{
		//每次开辟的空间都是总空间的2倍
		SLDataType* tmp = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * ps->capacity * 2);
		//如果开辟失败则打印字符串和错信息
		if (tmp == NULL)
		{
			perror("check_capacity");
			return;
		}
		
		ps->data = tmp; //把tmp开辟的空间给data
		ps->capacity *= 2;  //扩完容,把capacity的容量也修改成总空间大小的2倍
		printf("增容成功\n");
	}
}
6.查找指定位置

给定一个要查找的值,返回这个值下标就行了

int SeqListFind(SeqList* ps, SLDataType x)
{
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->data[i] == x)
		{
			return i;
		}
	}
	return -1;
}
7.任意位置插入

给定一个下标,在下标位置插入一个新的值,下标的值和下标后的值都要往后移动一个位置

在这里插入图片描述

void SeqListInsert(SeqList* ps, int pos, SLDataType x)
{
	//当pos=0时,相当于头插,当下标为size时,相当于尾插
	assert(pos>=0 && pos<=ps->size);
	check_capacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->data[end + 1] = ps->data[end];
		end--;
	}
	ps->data[pos] = x;
	ps->size++;
}
8.任意位置删除

给定一个下标,从pos下标开始,把后面的元素向前覆盖一个位置
在这里插入图片描述

void SeqListErase(SeqList* ps, int pos)
{
	assert(pos>=0 && pos < ps->size);
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->data[begin-1] = ps->data[begin];
		begin++;
	}
	ps->size--;
}
9.尾部插入

1.尾部插入比较简单,我们只需要找到有效元素最后一个位置+1就可以插入了
2.也可以调用SeqListInsert任意位置插入函数
在这里插入图片描述

void SeqListPushBack(SeqList* ps, SLDataType x)
{
	//方法1:
	//assert(ps);
	//check_capacity(ps);
	//ps->data[ps->size] = x;
	//ps->size++;

	//方法2:调用SeqListInsert任意位置插入函数
	SeqListInsert(ps, ps->size , x);
}
10.尾部删除

1.尾部删除也比较简单,只需要找到size位置然后减1
2.也可以调用SeqListInsert任意位置删除函数

void SeqListPopBack(SeqList* ps)
{
	//方法1:
	//断言size不能为空,为空则会报错,不为空则什么也不做
	//assert(ps->size > 0);
	//ps->size--;

	//方法2:调用SeqListErase任意位置删除函数
	SeqListErase(ps, ps->size-1);
}
11.头部插入

1.把顺序表中所有数据向后移动,然后把data[0]首元素位置插入新值
2.也可以调用SeqListErase任意位置删除函数
在这里插入图片描述

void SeqListPushFront(SeqList* ps, SLDataType x)
{
	//方法一:
	//check_capacity(ps);
	//int end = ps->size - 1;
	//while (end >= 0)
	//{
	//	ps->data[end + 1] = ps->data[end];
	//	end--;
	//}
	//ps->data[0] = x;
	//ps->size++;


	//方法2:调用SeqListInsert任意位置插入函数
	SeqListInsert(ps, 0, x);
}
12.头部删除

1.头部删除就是把顺序表中所有元素向前移动一个位置
2.也可以调用SeqListErase任意位置插入函数

void SeqListPopFront(SeqList* ps)
{
	//方法1:
	//assert(ps->size > 0);
	//int begin = 1;
	//while (begin < ps->size-1)
	//{
	//	ps->data[begin - 1] = ps->data[begin];
	//	begin++;
	//}
	//ps->size--;

	//方法2:调用SeqListErase任意位置删除函数
	SeqListErase(ps,0);
}
13.整体代码

SeqList.h

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

//把 int 重命名为SLDataType,方便后期数据类型的修改
typedef int SLDataType; 
//capacity初始大小为3个(int)
#define INIT_CAPACITY 3 

typedef struct SeqList
{
	SLDataType* data; //用来存放数据的数组指针
	int size; //当前顺序表中有效数据的个数
	int capacity; //顺序表的总大小
}SeqList;

void SeqListInit(SeqList* ps);

void SeqListDestroy(SeqList* ps);

void SeqListPint(SeqList* ps);

void SeqListPushBack(SeqList* ps, SLDataType x);
void SeqListPopBack(SeqList* ps);

void SeqListPushFront(SeqList* ps, SLDataType x);
void SeqListPopFront(SeqList* ps);

// 顺序表查找
int SeqListFind(SeqList* ps, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"

void SeqListInit(SeqList* ps)
{
	//使用malloc为data初始化为3个空间
	ps->data = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);
	//如果开辟失败则打印字符串和错信息
	if (ps->data == NULL)
	{
		perror("SeqListInit");
		return;
	}
	ps->size = 0; //size是顺序表的有效数据,初始为0
	//上面为顺序表开辟了3个空间,所以这里capacity初始为3个空间大小
	ps->capacity = INIT_CAPACITY; 
}


void SeqListDestroy(SeqList* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->capacity = ps->size = 0;
}

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

void check_capacity(SeqList* ps)
{
	//当size==capacity时,则代表顺序表的空间满了,需要扩容
	if (ps->size == ps->capacity)
	{
		//每次开辟的空间都是总空间的2倍
		SLDataType* tmp = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * ps->capacity * 2);
		//如果开辟失败则打印字符串和错信息
		if (tmp == NULL)
		{
			perror("check_capacity");
			return;
		}
		
		ps->data = tmp; //把tmp开辟的空间给data
		ps->capacity *= 2;  //扩完容,把capacity的容量也修改成总空间大小的2倍
		printf("增容成功\n");
	}
}

void SeqListPushBack(SeqList* ps, SLDataType x)
{
	//方法1:
	//assert(ps);
	//check_capacity(ps);
	//ps->data[ps->size] = x;
	//ps->size++;


	//方法2:调用SeqListInsert任意插入函数
	SeqListInsert(ps, ps->size , x);
}


void SeqListPopBack(SeqList* ps)
{
	//方法1:
	//断言size不能为空,为空则会报错,不为空则什么也不做
	//assert(ps->size > 0);
	//ps->size--;


	//方法2:调用SeqListInsert任意位置删除函数
	SeqListErase(ps, ps->size-1);
}


void SeqListPushFront(SeqList* ps, SLDataType x)
{
	//方法一:
	//check_capacity(ps);
	//int end = ps->size - 1;
	//while (end >= 0)
	//{
	//	ps->data[end + 1] = ps->data[end];
	//	end--;
	//}
	//ps->data[0] = x;
	//ps->size++;


	//方法2:调用SeqListErase任意位置插入函数
	SeqListInsert(ps, 0, x);
}


void SeqListPopFront(SeqList* ps)
{
	//方法1:
	//assert(ps->size > 0);
	//int begin = 1;
	//while (begin < ps->size-1)
	//{
	//	ps->data[begin - 1] = ps->data[begin];
	//	begin++;
	//}
	//ps->size--;

	//方法2:调用SeqListErase任意位置删除函数
	SeqListErase(ps,0);
}

// 顺序表查找
int SeqListFind(SeqList* ps, SLDataType x)
{
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->data[i] == x)
		{
			return i;
		}
	}
	return -1;
}
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDataType x)
{
	//当pos=0时,相当于头插,当下标为size时,相当于尾插
	assert(pos>=0 && pos<=ps->size);
	check_capacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->data[end + 1] = ps->data[end];
		end--;
	}
	ps->data[pos] = x;
	ps->size++;
}
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos)
{
	assert(pos>=0 && pos < ps->size);
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->data[begin-1] = ps->data[begin];
		begin++;
	}
	ps->size--;
}

test.c

enum Optin
{
	EXIT,
	SLBACKPUSH,
	SLBACKPOP,
	SLFRONTPUSH,
	SLFRONTPOP,
	SLINSERT,
	SLERASE,
	SLEFIND,
	SLPRINT
};



void menu()
{
	printf("**************************************************************\n");
	printf("*********      1.尾插            2.尾删             ***********\n");
	printf("*********      3.头插            4.头删             ***********\n");
	printf("*********      5.任意位置插入     6.任意位置删除      ***********\n");
	printf("*********      7.查找元素的下标   8.打印             ***********\n");
	printf("*********      0/-1.退出                           ***********\n");
	printf("*************************************************************\n");
}


int main()
{
	int option = 0;
	SeqList p;
	SeqListInit(&p);
	while (option != -1)
	{
		menu();
		printf("请输入要选择的选项:");
		scanf("%d", &option);
		if (option == EXIT)
		{
			printf("请依次输入要尾插的数据,以-1结束:");
			int x = 0;
			while (x != -1)
			{
				scanf("%d", &x);
				SeqListPushBack(&p, x);
			}
			SeqListPint(&p);
		}
		else if (option == SLBACKPUSH)
		{
			int x = 0;
			printf("请输入要尾删的个数:");
			scanf("%d", &x);
			while (x--)
			{
				SeqListPopBack(&p);
			}
			SeqListPint(&p);
		}
		else if (option == SLBACKPOP)
		{
			int x = 0;
			printf("请依次输入要头插的数据,以-1结束:");
			while (x != -1)
			{
				scanf("%d", &x);
				SeqListPushFront(&p, x);
			}
			SeqListPint(&p);
		}
		else if (option == SLFRONTPOP)
		{
			int x = 0;
			printf("请输入要头删的个数:");
			scanf("%d", &x);
			while (x--)
			{
				SeqListPopFront(&p);
			}
			SeqListPint(&p);
		}
		else if (option == SLINSERT)
		{
			int x = 0;
			int y = 0;
			printf("请输入要任意插入位置的下标:");
			scanf("%d", &x);
			printf("请依次输入你要插入的数据:");
			scanf("%d", &y);
			SeqListInsert(&p, x, y);
			SeqListPint(&p);
		}
		else if (option == SLERASE)
		{
			int x = 0;
			printf("请输入你要任意删除位置的下标:");
			scanf("%d", &x);
			SeqListErase(&p, x);
			SeqListPint(&p);
		}
		else if (option == SLEFIND)
		{
			int x = 0, y = 0;
			printf("请输入你要查找下标的个数:");
			scanf("%d", &x);
			printf("请依次输入你要查找下标的数字:");
			while (x--)
			{
				scanf("%d", &y);
				int ret = SeqListFind(&p, y);
				printf("%d ", ret);
			}
			printf("\n");
		}
		else if (option == SLPRINT)
		{
			SeqListPint(&p);
		}
		else if (option == -1)
		{
			printf("退出顺序表\n");
		}
		else
		{
			printf("无此选项,请重新输入\n");
		}
	}
}

打印结果:
在这里插入图片描述

14.顺序表的优缺点

优点:
1.支持随机访问(用下标访问),需要随机访问结构支持算法可以很好的适用;
2.cpu高速缓存命中率更高;

缺点:
1.头部中部插入删除时间效率低O(N);
2.连续的物理空间,空间不够了以后需要增容;
a.增容有一定程度的消耗;
b.为了避免频繁增容,我们一般按照2倍去增加,用不完会存在一定空间的浪费;

这篇文章到这就结束啦,不管你喜不喜欢,都看到这了,点完赞再走吧🤭

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

川子767

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

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

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

打赏作者

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

抵扣说明:

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

余额充值