最大最小堆(min-max heap)

做项目的时候听说了最大最小堆这种数据结构,于是就来学习一下。

最大最小堆算是二叉堆的变形,结构与二叉堆类似,只是节点的排列顺序有所不同。相较于二叉堆,最大最小堆能够同时支持最大值或最小值的查询( O ( 1 ) O(1) O(1))和删除( O ( log ⁡ n ) O(\log n) O(logn))操作。

相较于用两个二叉堆或用平衡树实现相同的功能,最大最小堆唯一的优势大概就是只需要开一个大小为 n n n 的数组。

性质

最大最小堆的结构和二叉堆一样,都是一棵满二叉树。若令根节点深度为 0 0 0,则满足在以深度为偶数的节点为根的子树中,根节点为子树的最小值,否则为子树的最大值。上述条件等价于,深度为偶数的点小于父亲且大于祖父,深度为奇数的点大于父亲且小于祖父(假设键值两两不同)。

那么根节点就是最小值,根节点两个儿子的较大值就是最大值。

操作

主要的操作分为上移和下移,时间复杂度均为 O ( log ⁡ n ) O(\log n) O(logn)

min_shift_down(index) 函数在假设编号为 index 的节点的子树中,除根以外均满足最大最小堆的性质,且根节点的深度为偶数,并把根节点下移调整来满足性质。

void min_max_heap::min_shift_down(int index)
{
   
	if (!has_child(index)) return;	//若没有孩子就退出
	//将根节点调整为它和两个孩子中的较小值,调整后子树仍满足性质
	int c = min_child(index);
	if (data[c] < data[index]) swap(c, index);
	//根据孙子情况讨论
	if (!has_grandchild(index)) return;
	int grand = min_grandchild(index);
	if (data[index] > data[grand]) swap(index, grand), min_shift_down(grand);
}

min_shift_up(index) 函数在假设整个堆若不看编号为 index 的节点,均满足最大最小堆的性质,且 index 的深度为偶数,并把 index 上移调整来满足性质。

void min_max_heap::min_shift_up(int index)
{
   
	if (!has_parent(index)) return;	//若没有父亲则退出
	if (!has_grandparent(index))	//若没有祖父,则只需要让父亲满足条件
	{
   
		int p = parent(index);
		if (data[p] < data[index]) swap(p, index);
	}
	else		//否则需要根据祖父情况进行讨论
	{
   
		int grand = grandparent(index);
		//若祖父不满足性质,则交换后递归处理
		if (data[index] < data[grand]) swap(index, grand), min_shift_up(grand);
		else
		{
   
			int p = parent(index);
			//若父亲不满足性质,则交换后递归处理
			if (data[index] > data[p]) swap(index, p), max_shift_up(p);
		}
	}
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是 C 代码实现的最小最大堆: ```c #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct { int* arr; int size; int capacity; } MinMaxHeap; void swap(MinMaxHeap* heap, int i, int j) { int temp = heap->arr[i]; heap->arr[i] = heap->arr[j]; heap->arr[j] = temp; } void trickle_down(MinMaxHeap* heap, int i, int is_min) { int child1 = 2 * i + 1; int child2 = 2 * i + 2; int max_child = child1; if (child2 < heap->size && heap->arr[child2] > heap->arr[child1]) { max_child = child2; } int min_child = child1; if (child2 < heap->size && heap->arr[child2] < heap->arr[child1]) { min_child = child2; } int extreme_child = is_min ? min_child : max_child; if (extreme_child < heap->size && heap->arr[extreme_child] > heap->arr[i]) { swap(heap, i, extreme_child); trickle_down(heap, extreme_child, !is_min); } } void trickle_up(MinMaxHeap* heap, int i, int is_min) { int parent = (i - 1) / 2; if (parent < 0 || parent == i) { return; } if ((!is_min && heap->arr[i] > heap->arr[parent]) || (is_min && heap->arr[i] < heap->arr[parent])) { swap(heap, i, parent); trickle_up(heap, parent, !is_min); } } void build_heap(MinMaxHeap* heap, int* arr, int size) { heap->arr = arr; heap->size = size; heap->capacity = size; for (int i = size / 2 - 1; i >= 0; i--) { trickle_down(heap, i, i == 0); } } void insert(MinMaxHeap* heap, int value) { if (heap->size == heap->capacity) { return; } heap->arr[heap->size] = value; trickle_up(heap, heap->size, heap->size % 2 == 1); heap->size++; } int remove_root(MinMaxHeap* heap) { if (heap->size == 0) { return -1; } int root = heap->arr[0]; swap(heap, 0, heap->size - 1); heap->size--; trickle_down(heap, 0, 1); return root; } void print_heap(MinMaxHeap* heap) { for (int i = 0; i < heap->size; i++) { printf("%d ", heap->arr[i]); } printf("\n"); } void test_heap() { MinMaxHeap heap; int arr[MAX_SIZE]; for (int i = 1; i <= 15; i++) { insert(&heap, i); } printf("Initial heap: "); print_heap(&heap); for (int i = 1; i <= 5; i++) { int root = remove_root(&heap); printf("Removed root: %d, new heap: ", root); print_heap(&heap); } printf("Adding elements 16-20: "); for (int i = 16; i <= 20; i++) { insert(&heap, i); } print_heap(&heap); printf("Removing all elements: "); while (heap.size > 0) { int root = remove_root(&heap); printf("%d ", root); } printf("\n"); } int main() { test_heap(); return 0; } ``` 在此实现中,最小最大堆用一维数组表示,其节点按照从左到右、从上到下的顺序编号。实现使用了两个递归函数 `trickle_down()` 和 `trickle_up()` 来维护堆的顺序性,当插入或删除元素时,需要使用这两个函数来重新组织堆。 在创建和打印最小最大堆时,使用了 `build_heap()` 和 `print_heap()` 函数。在该实现中,最小最大堆在递归堆时与它在 `arr[]` 数组中的位置有关系。这些关系是: - 一个节点的左子节点位于 `2*i+1` 处,右子节点位于 `2*i+2` 处。 - 一个节点的父节点位于 `(i-1)/2` 处。 此外,由于最小最大堆每个节点都可以有 2 或 3 个子节点,因此需要在 `trickle_down()` 和 `trickle_up()` 函数中考虑这个特殊情况。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值