什么是堆

优先队列( (Priority Queue ):特殊的“ 队列” ,取出元素的顺序是依照元素的 优先权(关键字)大小,而不是元素进入队列的先后顺序。

优先队列的完全二叉树表示

堆,堆的创建,插入,删除,建立_算法与数据结构

堆的两个特性

结构性 :用数组表示的完全二叉树;
有序性 : 任一结点的关键字是其子树所有结点的最大值(或最小值)

  • 最大堆(MaxHeap) ”, 也称“ 大顶堆 ”:最大值
  • 最小堆( MinHeap) ”, 也称“ 小顶堆 ” :最小值
    堆,堆的创建,插入,删除,建立_算法与数据结构_02

堆的抽象数据类型描述

类型名称 : 最大堆 (MaxHeap)
数据对象集 : 完全二叉树 ,每个结点的元素值 不小于值其子结点的元素值
操作集 :最大堆H属于MaxHeap ,元素item属于ElementType ,主要操作有:

  • MaxHeap Create( int MaxSize ):创建一个空的最大堆。
  • Boolean IsFull( MaxHeap H ):判断最大堆H是否已满。
  • Insert( MaxHeap H, ElementType item ):将元素item插入最大堆H。 。
  • Boolean IsEmpty( MaxHeap H ):判断最大堆H是否为空。
  • ElementType DeleteMax( MaxHeap H ):返回H中最大元素(高优先级)

最大堆的操作

最大堆的创建

#define Elemtype int
typedef struct HNode {
	Elemtype *Data; /* 存储元素的数组 */
	int Size;          /* 堆中当前元素个数 */
	int Capacity;      /* 堆的最大容量 */
}*Heap;

MaxHeap CreateHeap(int MaxSize)
{ /* 创建容量为MaxSize的空的最大堆 */

	MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
	H->Data = (Elemtype *)malloc((MaxSize + 1)*sizeof(Elemtype));//堆元素从下标为1的位置开始存放
	H->Size = 0;
	H->Capacity = MaxSize;
	H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/
	return H;
}

把MaxData换成小于堆中所有元素的MinData,同样适用于创建最小堆。

最大堆的插入

先满足结构性,将要插入元素插到最后一个,再调整位置,满足有序性
首先将新插入的元素放在最后一个位置,在与其父节点比较,调整其位置。
堆,堆的创建,插入,删除,建立_算法与数据结构_03

bool Insert(MaxHeap H, Elemtype X)
{ /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */
	int i;

	if (IsFull(H))
	{
		printf("最大堆已满");
		return false;
	}
	i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
	for (; H->Data[i / 2] < X; i /= 2)
		H->Data[i] = H->Data[i / 2]; /* 上滤X */
	H->Data[i] = X; /* 将X插入 */
	return true;
}

H->Element[ 0 ] 是哨兵元素,它不小于堆中的最大元素。当插入的元素比任意元素都大,哨兵的存在使得H->Elements[0] > item; 顺环结束。
时间复杂度: O(log N)

最大堆的删除

取出根结点(最大值)元素,同时删除堆的一个结点。
用最后一个结点代替根结点,取根结点两孩子的较大的一个,若较大值大于此时的根节点,将该较大值移动至根结点,不断重复,直到找到最后一个结点该插入的位置。
堆,堆的创建,插入,删除,建立_算法_04

Elemtype DeleteMax(MaxHeap H)
{ /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
	int Parent, Child;
	Elemtype MaxItem, X;

	if (IsEmpty(H)) {
		printf("最大堆已为空");
		return ERROR;
	}

	MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */
	/* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
	X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
	for (Parent = 1; Parent * 2 <= H->Size; Parent = Child)
	{
		Child = Parent * 2;
		if ((Child != H->Size) && (H->Data[Child]<H->Data[Child + 1]))
			Child++;  /* Child指向左右子结点的较大者 */
		if (X >= H->Data[Child]) break; /* 找到了合适位置 */
		else  /* 下滤X */
			H->Data[Parent] = H->Data[Child];
	}
	H->Data[Parent] = X;
	return MaxItem;
}

最大堆的建立

建立最大堆:将已经存在的N个元素按最大堆的要求存放在一个一维数组中

方法1:通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去,其时间代价最大为O(N logN)

方法2:在线性时间复杂度下建立最大堆。

  1. 将N个元素按输入顺序存入,先满足完全二叉树的结构特性
  2. 调整各结点位置,以满足最大堆的有序特性

参考结点的删除,从最后一个结点的父节点开始,逐级向上,调整结点位置,调整到根结点时,即为大顶堆

/*----------- 建造最大堆 -----------*/
void PercDown(MaxHeap H, int p)
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
	int Parent, Child;
	Elemtype X;

	X = H->Data[p]; /* 取出根结点存放的值 */
	for (Parent = p; Parent * 2 <= H->Size; Parent = Child) {
		Child = Parent * 2;
		if ((Child != H->Size) && (H->Data[Child]<H->Data[Child + 1]))
			Child++;  /* Child指向左右子结点的较大者 */
		if (X >= H->Data[Child]) break; /* 找到了合适位置 */
		else  /* 下滤X */
			H->Data[Parent] = H->Data[Child];
	}
	H->Data[Parent] = X;
}

void BuildHeap(MaxHeap H)
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
	/* 这里假设所有H->Size个元素已经存在H->Data[]中 */
	int i;
	/* 从最后一个结点的父节点开始,到根结点1 */
	for (i = H->Size / 2; i>0; i--)
		PercDown(H, i);
}

线性时间复杂度
T(n)=O(n)

层数 节点数 最多交换的次数
倒数第2层 n/4 1
倒数第3层 n/8 2
倒数第4层 n/16 3
第1层 n/2k=1 log2n - 1=k-1

堆,堆的创建,插入,删除,建立_数据结构学习_05
完整c++代码

#include<iostream>
#include<stdlib.h>
using namespace std;

#define Elemtype int
#define MAXDATA 1000  /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */

typedef struct HNode {
	Elemtype *Data; /* 存储元素的数组 */
	int Size;          /* 堆中当前元素个数 */
	int Capacity;      /* 堆的最大容量 */
}*Heap;

typedef Heap MaxHeap; /* 最大堆 */
typedef Heap MinHeap; /* 最小堆 */

MaxHeap CreateHeap(int MaxSize)
{ /* 创建容量为MaxSize的空的最大堆 */

	MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
	H->Data = (Elemtype *)malloc((MaxSize + 1)*sizeof(Elemtype));
	H->Size = 0;
	H->Capacity = MaxSize;
	H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/
	return H;
}

bool IsFull(MaxHeap H)
{
	return (H->Size == H->Capacity);
}

bool Insert(MaxHeap H, Elemtype X)
{ /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */
	int i;

	if (IsFull(H))
	{
		printf("最大堆已满");
		return false;
	}
	i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
	for (; H->Data[i / 2] < X; i /= 2)
		H->Data[i] = H->Data[i / 2]; /* 上滤X */
	H->Data[i] = X; /* 将X插入 */
	return true;
}

#define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */

bool IsEmpty(MaxHeap H)
{
	return (H->Size == 0);
}

Elemtype DeleteMax(MaxHeap H)
{ /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
	int Parent, Child;
	Elemtype MaxItem, X;

	if (IsEmpty(H)) {
		printf("最大堆已为空");
		return ERROR;
	}

	MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */
	/* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
	X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
	for (Parent = 1; Parent * 2 <= H->Size; Parent = Child)
	{
		Child = Parent * 2;
		if ((Child != H->Size) && (H->Data[Child]<H->Data[Child + 1]))
			Child++;  /* Child指向左右子结点的较大者 */
		if (X >= H->Data[Child]) break; /* 找到了合适位置 */
		else  /* 下滤X */
			H->Data[Parent] = H->Data[Child];
	}
	H->Data[Parent] = X;
	return MaxItem;
}

/*----------- 建造最大堆 -----------*/
void PercDown(MaxHeap H, int p)
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
	int Parent, Child;
	Elemtype X;

	X = H->Data[p]; /* 取出根结点存放的值 */
	for (Parent = p; Parent * 2 <= H->Size; Parent = Child) {
		Child = Parent * 2;
		if ((Child != H->Size) && (H->Data[Child]<H->Data[Child + 1]))
			Child++;  /* Child指向左右子结点的较大者 */
		if (X >= H->Data[Child]) break; /* 找到了合适位置 */
		else  /* 下滤X */
			H->Data[Parent] = H->Data[Child];
	}
	H->Data[Parent] = X;
}

void BuildHeap(MaxHeap H)
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
	/* 这里假设所有H->Size个元素已经存在H->Data[]中 */
	int i;
	/* 从最后一个结点的父节点开始,到根结点1 */
	for (i = H->Size / 2; i>0; i--)
		PercDown(H, i);
}

int main()
{
	Heap heap = CreateHeap(20);
	int a[13] = { MAXDATA, 79, 66, 43, 83, 30, 87, 38, 55, 91, 72, 49, 9 };
	int len = sizeof(a) / sizeof(a[0]);

	heap->Data = a;
	heap->Size = len-1;
	BuildHeap(heap);
	for (int i = 1; i <= heap->Size; i++)
		cout << heap->Data[i]<<" ";
	cout << endl << endl;

	cout << DeleteMax(heap) << endl;
	for (int i = 1; i <= heap->Size; i++)
		cout << heap->Data[i] << " ";
	cout << endl << endl;

	if (Insert(heap, 99))
	{
		for (int i = 1; i <= heap->Size; i++)
			cout << heap->Data[i] << " ";
	}
}