堆的创建、性质、排序, 以及优先队列

(二叉)堆是一个数组,可以看成为一个近似的完全二叉树。树上的每一个节点对应数组中的一个元素,除了最底层外,其他层是完全充满的,而且是从左到右填充。表示堆的数组A包括两个属性,A.length表示数组元素的个数,A.heapsize表示有多少个堆元素存储在该数组中。
树的根节点是A[1],给定一个节点的下标i,可以得到它的父节点,左孩子和右孩子下标:

//父节点,下标向下取整
int parent(int i) 
{return i / 2;}
//左孩子节点
int left(int i)
{return 2 * i;}
//右孩子节点
int right(int i)
{return 2 * i + 1;}

二叉堆可以分成两种形式:最大堆最小堆。在最大堆中,除了根节点之外的所有节点 i 都要满足A[parent(i)] >= A[i],也就是说某个节点的值至多和其父节点一样大。在最小堆中正好相反。

堆的性质:1.高度为h的堆中,元素个数最少为2^(n), 最多为2^(n+1)-1;
2.含有n个元素的堆的高度为lgn(以2为底)向下取整。
3.当用数组表示存储n个元素的堆时,叶子节点的下标分别为(n/2+1),(n/2+2),…,n(均为向下取整)。因为,最后一个节点的父节点为(n/2),其后所有节点均为叶子节点。

维护堆的性质
maxHeapify用于维护堆的性质,通过让A[i]同左右孩子对比,使得小值在最大堆中逐级下降。

void maxHeapify(int A[], int heapSize, int i)
{
	int L = left(i);
	int R = right(i);
	int largest;

	if (L<=heapSize && A[L]>A[i])
		largest = L;
	else largest = i;
	if (R<=heapSize && A[R] > A[largest])
		largest = R;
	int temp;
	if (largest != i)
	{
		temp = A[largest];
		A[largest] = A[i];
		A[i] = temp;
		maxHeapify(A, heapSize, largest);
	}
}

在交换后,下标为largest的节点的值时原来的A[i],以其为根节点的子树可能会违反最大堆的性质,需要还需递归调用。这个函数的事件复杂度为O(lg n),根据堆性质,对于一个树高为h的节点来说,时间复杂度为O(h)。

建堆
自底向上建堆。

void buildMaxHeap(int A[], int heapSize)
{
	int i;
	for (i = heapSize / 2; i > 0; i--)
		maxHeapify(A, heapSize, i);
}

下标 i 从最后一个根节点开始,也就是(heapSize/2)。自底向上建堆是为了保证以当前节点 i 的左右孩子为根节点的子树是最大堆。

堆排序
时间复杂度为O(n * lg n)

void heapSort(int A[], int heapSize)
{
	buildMaxHeap(A, heapSize);
	int temp;
	for (int i = heapSize; i > 1; i--)
	{
		temp = A[i];
		A[i] = A[1];
		A[1] = temp;
		maxHeapify(A, i - 1, 1);
	}

}

将 A[1] 同 A[heapSize] 交换,在将heapSize减一,然后调整到最大堆。

优先队列
优先队列:一种用来维护由一组元素构成的集合S的数据结构,其中的每一个元素都有一个相关的值,称为关键字,最大优先队列操作包括:
insert(A,x):把元素插入到集合A中;
maximum(A):返回A中具有最大关键字的元素
extractMax(A):去掉并返回A中最大关键字的元素
increaseKey(A,x,k):将元素x的关键字值增加到k,假设k的值不小于x的原关键字值

最大优先队列:记录将要执行的各个作业以及它们之间的相对优先级。当一个作业完成或者被中断,调度器将调用extractMax(A)从所有的等待作业中,选出具有最高优先级的作业来执行。在任何时候,调度器可以调用insert(A,x)把一个新作业加入到队列中来

最小优先队列:可以用于基于事件驱动的模拟器,队列中保存要模拟的事件,每个事件都有一个发生时间作为其关键字。事件必须按照发生的时间顺序进行模拟,因为某一事件的模拟结果可能会触发对其他事件的模拟。在每一步,模拟程序调用extractMin(A)来选择下一个要模拟的事件。当一个新事件产生时,模拟器通过调用insert将其插入最小优先级对列中

优先队列可以用堆来实现。我们需要确定那个对象对应一个给定的优先队列元素。再用堆来实现优先队列时,需要在堆的每个元素里存储对应对象的句柄。句柄(如一个指针或一个整型数):的准确含义依赖于具体的应用程序。同样,在应用程序的对象中,也需要存储一个堆中对应元素的句柄。通常,这个句柄是数组的下标。由于在堆的操作过程中,元素会改变其在数组中的位置,因此,在具体的实现中,在重新确定堆元素位置时,我们也需要更新相应应用程序对象中的数组下标
相应代码

int heapMaximum(int A[], int heapSize)
{
	buildMaxHeap(A, heapSize);
	return A[1];
}
int heapExtractMax(int A[], int *heapSize)
{
	if (*heapSize < 1)
	{
		cout << "heap underflow";
		return INT_MIN;
	}
	int temp = A[1];
	A[1] = A[*heapSize];
	A[*heapSize] = temp;
	*heapSize -= 1;
	maxHeapify(A, *heapSize, 1);
	return temp;
}
void heapIncreaseKey(int A[], int i, int key)
{
	if (key < A[i])
	{
		cout << "new key is smaller than current key";
		return;
	}
	A[i] = key;
	int temp;
	while (i>1 && A[parent(i)] < A[i])
	{
		temp = A[i];
		A[i] = A[parent(i)];
		A[parent(i)] = temp;
		i = parent(i);
	}
}
void heapInsert(int A[], int* heapSize, int key)
{
	*heapSize += 1;
	A[*heapSize] = INT_MIN;
	heapIncreaseKey(A, *heapSize, key);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晴天stick

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值