当前位置: 首页 > article >正文

[数据结构]动态顺序表的实现与应用

文章目录

  • 一、引言
  • 二、动态顺序表的基本概念
  • 三、动态顺序表的实现
    • 1、结构体定义
    • 2、初始化
    • 3、销毁
    • 4、扩容
    • 5、缩容
    • 5、打印
    • 6、增删查改
  • 四、分析动态顺序表
    • 1、存储方式
    • 2、优点
    • 3、缺点
  • 五、总结
    • 1、练习题
    • 2、源代码

一、引言

想象一下,你有一个箱子(静态顺序表),它的大小是固定的,你只能在这个箱子里面放东西或者拿出来,如果东西放不下了,箱子就满了,没办法再放更多。但现在,我们有了一种更灵活的箱子 —— 动态顺序表。这个箱子不仅能放东西,还能根据需要自动变大或变小,非常方便。

不过,要想玩转这个神奇的箱子,我们得先对里面的 “东西” (数据)的存放方式有所了解,也就是得熟悉数组和指针。如果你对这些还不太熟悉,别担心,我已经为你准备了一篇文章作为参考:传送门。读完之后,你会发现它们其实并不复杂。


二、动态顺序表的基本概念

动态顺序表,简单来说,就是一个可以 “长大” 或 “缩小” 的箱子。它不像静态顺序表那样一开始就确定了大小,而是根据我们需要存放的东西的多少来动态地调整自己的大小。

当我们往动态顺序表里放东西时,如果箱子满了,它就会自动去找一个更大的箱子,然后把原来的东西和新东西一起放进去。这个过程就像是我们换了一个更大的家,然后把所有家具都搬进去一样。

反过来,如果我们从动态顺序表里拿走了很多东西,箱子变得空荡荡的,那它也会考虑换个小点的箱子住,毕竟节省空间也是一种美德嘛。

总之,动态顺序表就是这样一个既智能又灵活的箱子,它能够帮助我们更加高效地管理和存储数据。

请添加图片描述


三、动态顺序表的实现

1、结构体定义

首先,我们需要定义一个结构体来表示动态顺序表,这个结构体将包含指向数组元素的指针、当前存储的元素数量以及分配的空间大小。

typedef int DataType;
typedef struct {
    DataType* arr;//指向动态数组的指针
    int size;//当前存储的元素个数
    int capacity;//当前动态数组的容量
}Seq;

2、初始化

初始化动态顺序表时,我们需要为其分配一个初始的空间大小,并设置当前存储的元素数量为0。

void Init(Seq* ps, int capacity)
{
	capacity == 0 ? capacity = 2 : capacity;
	DataType* pos = (DataType*)malloc(sizeof(DataType) * capacity);
	if (pos == NULL)
	{
		fprintf(stderr, "内存分配失败\n");
		exit(EXIT_FAILURE);
	}

	ps->array = pos;
	ps->capacity = capacity;
	ps->size = 0;
}

3、销毁

使用完动态顺序表后,需要释放分配的内存,避免内存泄漏。

void Destroy(Seq* ps)
{
	if (ps == NULL)
		return;

	free(ps->array);
	ps->array = NULL;

	ps->capacity = 0;
	ps->size = 0;
}

4、扩容

当向动态顺序表中添加元素时,如果当前空间已满,则需要进行扩容操作。扩容通常会将容量加倍。

void Reserve(Seq* ps)
{
	if (ps->size == ps->capacity)
	{
		int capacity = ps->capacity == 0 ? 2 : ps->capacity * 2;
		DataType* pos = (DataType*)realloc(ps->array, capacity * sizeof(DataType));
		if (pos == NULL)
		{
			fprintf(stderr, "内存分配失败\n");
			exit(EXIT_FAILURE);
		}

		ps->array = pos;
		ps->capacity = capacity;
	}
}

5、缩容

当删除动态顺序表中的元素时,如果当前空间过大,则需要进行缩容操作。

void Shrink(Seq* ps)
{
	if (ps->capacity >= 64 && ps->size < ps->capacity / 2.5)
	{
		int capacity = ps->capacity / 2;
        DataType* pos = (DataType*)realloc(ps->array, capacity * sizeof(DataType));
		if (pos == NULL)
		{
			fprintf(stderr, "内存分配失败");
			exit(EXIT_FAILURE);
		}

		ps->array = pos;
		ps->capacity = capacity;
	}
}

5、打印

为了方便调试和查看动态顺序表中的内容,我们可以实现一个打印函数,将所有元素打印出来。

void Print(Seq* ps, void (*Pr) (DataType))
{
	for (int i = 0; i < ps->size; i++)
	{
		Pr(ps->array[i]);
	}
	printf("NULL\n");
}

6、增删查改

实现顺序表的基本操作,让顺序表更具有实用性。

void PushFront(Seq* ps, DataType data)
{
	Insert(ps, 0, data);
}

void PopFront(Seq* ps)
{
	Erase(ps, 0);
}

void PushBack(Seq* ps, DataType data)
{
	Insert(ps, ps->size, data);
}

void PopBack(Seq* ps)
{
	Erase(ps, ps->size - 1);
}

void Insert(Seq* ps, int x, DataType data)
{
	assert(x >= 0 && x <= ps->size);

    Reserve(ps);
	for (int i = ps->size - 1; i >= x; i--)
	{
		ps->array[i + 1] = ps->array[i];
	}
	ps->array[x] = data;
	ps->size++;
}

void Erase(Seq* ps, int x)
{
	assert(ps != NULL && ps->size > 0 && x >= 0 && x < ps->size);

	for (int i = x; i < ps->size - 1; i++)
	{
		ps->array[i] = ps->array[i + 1];
	}
    ps->size--;
}

int Find(Seq* ps, DataType data)
{
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->array[i] == data)
		{
			return i;
		}
	}
	return -1;
}

void Modify(Seq* ps, int x, DataType data)
{
	assert(ps != NULL && ps->size > 0 && x >= 0 && x < ps->size);

	ps->array[x] = data;
}

四、分析动态顺序表

1、存储方式

顺序表是线性表的一种,线性表的逻辑结构是连续的,物理结构是不一定连续的。顺序表使用数组进行存储,数组在内存中是连续的,所以顺序表的物理结构是连续的。

2、优点

顺序表在随机访问时具有很高的效率,因为数组在内存中是连续的,所以可以通过下标直接访问到对应的元素。

3、缺点

顺序表在插入和删除元素时,需要移动大量元素,所以效率较低。另外,顺序表的大小是固定的,如果需要扩容,需要重新分配内存,这也会带来一定的开销。


五、总结

1、练习题

  • 移除元素
  • 合并两个有序数组
  • 盛水最多的容器
  • 接雨水
  • 合并区间
  • 插入区间

2、源代码

对于动态顺序表的源代码我已经开源在GItee:传送门
建议读者,仔细阅读源代码,理解数据结构的设计思路,尝试修改和扩展功能以加深理解。通过编写测试案例来验证理解和代码的正确性。



http://www.kler.cn/a/318168.html

相关文章:

  • Unity3d 基于UGUI和VideoPlayer 实现一个多功能视频播放器功能(含源码)
  • LeetCode:1387. 将整数按权重排序(记忆化搜索 Java)
  • 【QSS样式表 - ⑥】:QPushButton控件样式
  • leetcode hot100除自身以外的数组的乘积
  • SAP抓取外部https报错SSL handshake处理方法
  • Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
  • 第二证券:“产业+科技” 中国并购重组市场持续升温
  • 【微服务即时通讯系统】——etcd一致性键值存储系统,etcd的介绍,etcd的安装,etcd使用和功能测试
  • Scikit-learn 识别手写数字
  • Qt:NULL与nullptr的区别(手写nullptr)
  • 数据处理与统计分析篇-day10-Matplotlib数据可视化
  • Leetcode 每日一题:Diameter of Binary Tree
  • DataWhale X 南瓜书学习笔记 task03笔记
  • vue3+Element-plus el-input 输入框组件二次封装(支持金额、整数、电话、小数、身份证、小数点位数控制,金额显示中文提示等功能)
  • rust属性宏
  • HTML段落,换行,水平线标签与其属性
  • c/c++八股文
  • MySQL 生产环境性能优化
  • 使用分布式调度框架时需要考虑的问题——详解
  • python 实现 P-Series algorithm算法
  • Seamless:Facebook推出的跨语言语音识别/翻译/合成大模型
  • 计算总体方差statistics.pvariance()
  • 通信工程学习:什么是VNF虚拟网络功能
  • 海思Hi3559av100 sdk开发环境搭建
  • 面试金典题2.3
  • 引用和指针的区别