C++ 数据结构超详细讲解顺序表

 更新时间:2022年03月25日 10:11:55   作者:琅時壹  
程序中经常需要将一组数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等。一组数据中包含的元素个数可能发生变化,顺序表则是将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示

(●’◡’●)

前言

线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串。

线性表在逻辑上是线性结构,也就是说连续的一条直线,但是在物理结构并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

本章我们来深度初体验顺序表

一、顺序表是什么

概念及结构

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

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

2.动态顺序表:使用动态开辟的数组存储

二、顺序表的实现

基本结构

typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;

接口实现

静态顺序表只适用于确定知道需要多少数剧的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以我们基本使用动态顺序表

typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
// 基本增删查改接口
// 顺序表初始化
void SeqListInit(SeqList* psl, size_t capacity);
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);
// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
// 顺序表尾删
void SeqListPopBack(SeqList* psl);
// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
// 顺序表头删
void SeqListPopFront(SeqList* psl);
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表销毁
void SeqListDestory(SeqList* psl);
// 顺序表打印
void SeqListPrint(SeqList* psl);

顺序表打印

普通的通过链表的数组打印

void SeqListPrint(SeqList* ps1)
{
	assert(ps1);

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

顺序表初始化

置空

void SeqListInit(SeqList* ps1)
{
	assert(ps1);
	ps1->a = NULL;
	ps1->size = 0;
	ps1->capacity = 0;
}

顺序表销毁

顺序表本质是数组,是一片连续的存储空间,头部操作置空即可

void SeqListDestory(SeqList* ps1)
{
	assert(ps1);
	free(ps1->a);
	ps1->a = NULL;
	ps1->capacity = ps1->size = 0;
}

动态扩容

由于是动态顺序表,就是为了控制长度来解决问题,对顺序表的操作大多离不开扩容

由于顺序表是连续的如果使用malloc会出现异地扩容的现象,realloc虽然也存在异地扩,但会返回一片连续空间的首地址.

void SeqListCheckCapacity(SeqList* ps1)
{
	assert(ps1);
	if (ps1->size == ps1->capacity)//满了
	{
		size_t newCapacity = ps1->capacity == 0 ? 4 : ps1->capacity * 2;//两倍扩容
		SLDateType* tmp = realloc(ps1->a, sizeof(SLDateType) * newCapacity);
		if (tmp == NULL)//扩容失败
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else//扩容成功
		{
			ps1->a = tmp;
			ps1->capacity = newCapacity;
		}
	}
}

在pos位置插入x

对头插尾插帮助极大

要判断是否可以插入以及有没有必要插入

过程图示

void SeqListInsert(SeqList* ps1, size_t pos, SLDateType x)
{
	if (pos > ps1->size)//如图
	{
		printf("越界:pos %d\n", pos);
		return;
	}
	SeqListCheckCapacity(ps1);//插入即要扩容
	size_t end = ps1->size;
	while (end > pos)
	{
		ps1->a[end] = ps1->a[end - 1];//数据后挪,腾出空间
		--end;
	}
	//end=pos
	ps1->a[pos] = x;
	ps1->size++;//添加数据,需要增长
}

删除pos位置

对头删尾删帮助极大

//注意,顺序表只注意size以前,size以后无硬性要求可以宽容对待

void SeqListErase(SeqList* ps1, size_t pos)
{
	assert(ps1);
	assert(pos < ps1->size);//同插入,需要有东西可删

	size_t begin = pos + 1;
	while (begin < ps1->size)//由后向前覆盖
	{
		ps1->a[begin - 1] = ps1->a[begin];
		++begin;
	}
	ps1->size--;
}

顺序表尾插

相当于上文在size处插入数据,调用即可

void SeqListPushBack(SeqList* ps1, SLDateType x)
{
	assert(ps1);
	SeqListInsert(ps1, ps1->size, x);
}

顺序表尾删

注意辨别size的位置

void SeqListPopBack(SeqList* ps1)
{
	assert(ps1);
	SeqListErase(ps1, ps1->size-1);
}

顺序表头插

void SeqListPushFront(SeqList* ps1, SLDateType x)
{
	assert(ps1);
	SeqListInsert(ps1, 0, x);
}

顺序表头删

//只需考虑size有效数据前

void SeqListPopFront(SeqList* ps1)
{
	assert(ps1);
	SeqListErase(ps1, 0);
}

查找数据x

int SeqListFind(SeqList* ps1, SLDateType x)
{
	assert(ps1);
	for (int i = 0; i < ps1->size; ++i)
	{
		if (ps1->a[i] == x)
		{
			return i;//返回下标
		}
	}
	return -1;//没找到
}


顺序表的缺点

1. 中间/头部的插入删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间

由此,前辈们总结出了链表

几道练手题

原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。题目链接

删除排序数组中的重复项。链接

合并两个有序数组.题目链接

总结

学习编程,解决问题,数据结构与算法正是非常好的工具。模拟实现正是对自身代码能力的锻炼提升,也对其有了更深的理解.竞赛的话,我认为要先对知识进行系统深度的学习,才可随机应变。不可盲目刷题,有些深层原理可能与做题所理解出的出入极大。

到此这篇关于C++ 数据结构超详细讲解顺序表的文章就介绍到这了,更多相关C++ 顺序表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言编写基于TCP和UDP协议的Socket通信程序示例

    C语言编写基于TCP和UDP协议的Socket通信程序示例

    这篇文章主要介绍了C语言编写基于TCP和UDP协议的Socket通信程序示例,其中TCP的客户端与服务器端采用多线程实现,需要的朋友可以参考下
    2016-03-03
  • 利用C语言实现经典多级时间轮定时器

    利用C语言实现经典多级时间轮定时器

    C语言是一门通用计算机编程语言,广泛应用于底层开发,这篇文章主要给大家介绍了关于利用C语言实现经典多级时间轮定时器的相关资料,需要的朋友可以参考下
    2021-07-07
  • C++多文件变量解析

    C++多文件变量解析

    大家注意不要在头文件中定义变量,在头文件中声明变量。定义放在对应的源文件中。其他地方只能用extern声明
    2013-10-10
  • C++实现航空订票程序

    C++实现航空订票程序

    这篇文章主要为大家详细介绍了C++实现航空订票程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • C++11新特性std::make_tuple的使用

    C++11新特性std::make_tuple的使用

    这篇文章主要介绍了C++11新特性std::make_tuple的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • c++判断文件是否存在的方法汇总

    c++判断文件是否存在的方法汇总

    这篇文章主要介绍了c++判断文件是否存在的方法汇总,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • C语言实现经典windows游戏扫雷的示例代码

    C语言实现经典windows游戏扫雷的示例代码

    今天我们会用C语言实现一个经典的windows小游戏:扫雷。扫雷是一款单机小游戏,每次通关最高难度的关卡都会开心好一阵。现在学会了C语言,总算可以自己实现扫雷了。话不多说,咱们开始吧
    2022-10-10
  • C++利用容器查找重复列功能实现

    C++利用容器查找重复列功能实现

    本文将详细介绍c++容器简介,c++容器的比较 与操作实例,需要了解更多的朋友可以参考下
    2012-11-11
  • c++ 调用python传输图片实例

    c++ 调用python传输图片实例

    今天小编就为大家分享一篇c++ 调用python传输图片实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • C++ 成员变量的初始化顺序问题详解

    C++ 成员变量的初始化顺序问题详解

    这篇文章主要介绍了C++ 成员变量的初始化顺序问题详解的相关资料,需要的朋友可以参考下
    2017-02-02

最新评论