数据结构初阶 —— 堆

目录

一,堆的概念

二,向下调整法(数组)

三,向上调整法(数组)

四,堆的创建(建堆)

建堆时间复杂度

五,堆排序  

堆排序时间复杂度推算

六,堆的实现 

七,TOP-K问题


堆(heap),二叉树的顺序存储结构;

向下/上调整时间复杂度:O(logN);

建堆时间复杂度:O(N);

堆排序时间复杂度:O(NlogN);

一,堆的概念

  • 如有个集合K={ k_{0}k_{1}k_{2},...,k_{n-1}},把它的所有元素按完全二叉树顺序存储方式存储在一个一维数组中;
  • 并满足 k_{i}<=k_{2*i+1} 且 k_{i}<=k_{2*i+2} (k_{i}>=k_{2*i+1} 且 k_{i}>=k_{2*i+2}),i=0、1、...,则称为小堆(大堆);

小堆

  • k_{i}<=k_{2*i+1} 且 k_{i}<=k_{2*i+2} ,即所有节点小于等于孩子;
  • 根节点最小,叫做最小堆或小根堆;

大堆

  • k_{i}>=k_{2*i+1} 且 k_{i}>=k_{2*i+2} ,即所有节点大于等于孩子;
  • 根节点最大,叫做最大堆或大根堆;

堆的性质

  • 堆中某个节点的值,总是不大于或不小于其父节点的值;
  • 根一定是最值(最大值或最小值);
  • 堆总是一颗完全二叉树,适合顺序结构存储;

二,向下调整法(数组)

  • 前提条件为,此二叉树的左右子树需是一个堆,只有根节点不满足堆要求;
  • 从根开始,与左右子节点中的最小值比较并交换,依次类推,直到比子节点小或到叶子节点终止(假设为小堆);
  • 时间复杂度,O(logN);
int array[] = {27,15,19,18,28,34,65,49,25,37};

//向下调整法,小堆
void Swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}
//arr数组,n数组大小,parent元素序号
void AdjustDown(int* arr, int n, int parent)
{
	int child = 2 * parent + 1;

	//无孩子节点或父亲小于孩子,即终止
	while (child < n)
	{
		if (child + 1 < n && arr[child + 1] < arr[child])
			child++;
		
		if (arr[parent] > arr[child])
		{
			Swap(arr + parent, arr + child);
			parent = child;
			child = 2 * parent + 1;
		}
		else
			break;
	}
}

三,向上调整法(数组)

  • 前提条件为,此二叉树的左右子树需是一个堆,只有最后节点不满足堆要求;
  • 从最后开始,与父节点比较并交换,依次类推,直到比父亲小或到根节点终止(假设为大堆);
  • 时间复杂度,O(logN);
//向上调整法
//arr数组,child元素序号
void AdjustUp(int* arr, int child)
{
	int parent = (child - 1) / 2;

	//大堆
	//根节点或父亲大于孩子,即终止
	while (child > 0)
	{
		if (arr[parent] < arr[child])
		{
			Swap(arr + parent, arr + child);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

四,堆的创建(建堆)

  • 即对数据如数组(可看为完全二叉树),将其构建为堆;
  • 方法为,从倒数第一个非叶子节点的子树开始调整,直到根节点为止;
  • 即每次调整时,使用向下调整法;
  • 建堆时间复杂度,O(N),下面有证明;
  • 注,也可使用向上调整法建堆,但初始化堆的个别元素位置可能不一样;
int arr[] = {1,5,3,8,7,6}; 
//建堆-向下调整法
void Heap(int* arr, int n)
{
	int i = (n - 1 - 1) / 2; //最后节点的父节点
	for (i; i >= 0; i--)
	{
		AdjustDown(arr, n, i);
	}
}
//建堆-向上调整法
void Heap(int* arr, int n)
{
	int i = 1; 
	for (i; i < n; i++)
	{
		AdjustUp(arr, i);
	}
}

建堆时间复杂度

五,堆排序  

  • 升序,建大堆(将最大的数换到最后,在将剩下的数向下调整下,选出次大数,依次类推);
  • 降序,建小堆(将最小的数换到最后,在将剩下的数向下调整下,选出次小数,依次类推);
  • 整体的时间复杂度,O(N*logN)

注:

  • 如升序建小堆(或降序建大堆)的话,先建堆O(N)选出最值后,剩余部分只能继续建堆O(N-1),以此类推,时间复杂度=N+(N-1)+...+2+1=O(N^2);
  • 如此这样,效率较低,还不如直接遍历,建堆的价值未体现;
//建堆排序
void HeapSort(int* arr, int n)
{
	//建堆 - O(N)
	int i = (n - 1 - 1) / 2; //最后节点的父节点
	for (i; i >= 0; i--)
	{
		AdjustDown(arr, n, i);
	}

	//排序 - O(N*log(N))
	int end = n - 1;
	while (end)
	{
		Swap(arr, arr + end);
		AdjustDown(arr, end, 0);
		end--;
	}
}

堆排序时间复杂度推算
  • 总时间复杂度 = 建堆时间复杂度O(N) + 调整时间复杂度O(Nlog(N)) = O(Nlog(N))

        假设刚好为满二叉树,建完堆后,随后首尾节点交换获取最值,在调整剩下节点,获取剩下节点最值,依次类推;

六,堆的实现 

//堆
typedef int HPDataType;

typedef struct Heap
{
	HPDataType* data;
	int size;
	int capacity;
}Heap;

//初始化
void HeapInit(Heap* php, HPDataType* arr, int n);
//释放销毁
void HeapDestroy(Heap* php);

//插入数据并保持堆
void HeapPush(Heap* php, HPDataType* x);
//删除堆顶数据并保持堆
void HeapPop(Heap* php);

//返回堆顶数据
HPDataType HeapTOP(Heap* php);
//返回堆节点个数
int HeapSize(Heap* php);
//判断释放为空
bool HeapEmpty(Heap* php);

注:完整接口实现

七,TOP-K问题

直接建堆排序:

  • O(N*log(N)),数据过大的话,内存可能不足;

建堆并pop出前K个数:

  • O(N+K*log(N)),数据过大的话,内存可能不足;

建K个元素的堆:

  • 前K个最大元素,建小堆(前K个最小元素,建大堆);
  • 将剩下的数依次与堆顶比较,大即入堆,最后此堆即为最大的K个数;
  • O(N*log(K)),通常K远小于N;
//TOP-K,前K个最大值,建小堆
void TOPK(int* arr, int n, int k)
{
	Heap hp;
	//建堆
	HeapInit(&hp, arr, k);

	int i = k;
	for (i; i < n; i++)
	{
		//替换
		if (arr[i] > HeapTop(&hp))
		{
			HeapPop(&hp);
			HeapPush(&hp, arr[i]);
		}
	}

	HeapPrint(&hp);
	HeapDestroy(&hp);
}

### 数据结构的存储方式深入解析 数据结构的存储方式主要取决于具体的数据结构类型及其应用场景。以下是几种常见数据结构的存储方式解析: #### B树的存储方式 B树是一种自平衡的多叉树数据结构,广泛用于数据库和文件系统的实现中。它的存储特点在于节点可以容纳多个键值和指针[^1]。每个内部节点包含若干个指向子节点的指针以及分隔这些指针的键值。这种设计使得B树能够在磁盘上高效地进行数据存取操作,因为单次I/O操作可以加载较大的节点。 在实际应用中,例如Elasticsearch中的`_source`字段也采用了类似的思路来存储完整的JSON文档结构体[^3]。虽然这不是严格意义上的B树,但它体现了复杂数据结构在持久化存储时的设计理念——即通过优化存储布局减少访问成本。 #### 数组与矩阵的存储方式 在编程语言如MATLAB中,数组和矩阵是最基本也是最常用的线性数据结构之一。它们通常采用连续内存区域来进行存储[^2]。这意味着如果有一个大小固定的n维数组,则该数组的所有元素都会被分配在同一片相邻的物理地址空间内。这样的安排极大地提高了缓存命中率并简化了随机访问模式下的寻址逻辑。 然而需要注意的是,当涉及到稀疏矩阵或者不规则形状的数据集时,简单的密集型表示法可能不再适用;此时可以选择压缩列/行存储(CSR/CSC)等形式替代传统二维表格形式以节省资源消耗。 #### 初级数据结构笔记总结 针对初学者而言,《店小二初阶数据结构笔记》提供了一个很好的入门视角[^4]。尽管其中并未特别强调高级主题比如外部记忆模型下不同层次间切换开销等问题,但对于理解基础概念仍然非常有帮助. #### MySQL 中 B-Tree 的特殊考虑 对于关系型数据库管理系统 (RDBMS),像MySQL这样基于表结构的产品来说,B-tree 或者变种 b+tree 是索引机制的核心组成部分[^5]. 这些树木状的数据组织允许快速查找记录位置的同时还支持范围扫描等功能特性. 不同于纯理论描述版本,在真实世界部署当中还需要考虑到诸如事务隔离级别影响等因素带来的额外负担. ```sql -- 创带有B-Tree索引的简单例子 CREATE TABLE example ( id INT NOT NULL, name VARCHAR(50), PRIMARY KEY(id) ); ``` 上述SQL语句定义了一张新表,并为其主键设置默认使用的B-Tree类型的唯一索引来加速检索过程。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值