堆排序

    (二叉)堆是一个数组,它可以被看成一个近似的完全二叉树。树上的每一个结点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左到右填充。heap_size表示有多少个堆元素存储在该数组中。
    当根结点下标为0时,结点 iii 的左孩子、右孩子和父亲的下标为:

#define LEFT(i) (((i) << 1) + 1)
#define RIGHT(i) (((i) << 1) + 2)
#define PARENT(i) (((i)-1) >> 1)
图1 堆

    在最大堆中,最大堆性质是指除了根以外的所有结点 iii 都要满足:A[PARENT(i)]≥A[i]A[PARENT(i)] \ge A[i]A[PARENT(i)]A[i]。最小堆性质是指除了根以外的所有结点 iii 都有:A[PARENT(i)]≤A[i]A[PARENT(i)] \le A[i]A[PARENT(i)]A[i]

维护堆的性质

    MAX_HEAPIFY是用于维护最大堆性质的重要过程。它的输入为一个数组A和一个下标i。在调用MAX_HEAPIFY的时候,我们假定根结点为LEFT(i)和RIGHT(i)的二叉树都是最大堆,但这时A[i]有可能小于其孩子,这样就违背了最大堆的性质。MAX_HEAPIFY通过让A[i]的值在最大堆中“逐级下降”,从而使得以下标i为根节点的子树重新遵循最大堆的性质。图2图示了MAX_HEAPIFY的执行过程。

图2 MAX_HEAPIFY

建堆

    我们可以用自底向上的方法利用过程MAX_HEAPIFY把一个大小为 nnn 的数组 A[0..n]A[0..n]A[0..n] 转化为最大堆。自底向上从第一个非叶子结点开始,依次向前调用MAX_HEAPIFY。过程BUILD_MAX_HEAP对树中的非叶子结点都调用一次MAX_HEAPIFY。
    如果结点 iii 是叶子结点,即既没有左孩子也没有右孩子,满足{2i+1≥n2i+2≥n\begin{cases} 2i+1\ge n\\ 2i+2\ge n\end{cases}{2i+1n2i+2n,即 i≥[n/2]i \ge[n/2]i[n/2] 是叶子结点。第一个非叶子结点为 i=[n/2]−1i = [n/2]-1i=[n/2]1

堆排序算法

    初始时候,堆排序算法利用BUILD_MAX_HEAP将输入数组A建成最大堆,其中n为数组长度。因为数组中的最大元素总在根结点A[0]中,通过把它与A[n-1]进行互换,我们可以让该元素放在正确的位置。这时候,如果我们从堆中去掉结点n(这一操作可以通过减少heap_size的值来实现),剩余的结点中,原来根的孩子结点仍然是最大堆,而新的根节点可能会违背最大堆的性质。为了维护最大堆的性质,我们要做的是调用MAX_HEAPIFY(A,0),从而在A上构造一个新的最大堆。堆排序算法会不断重复这一过程,直到堆的大小降至2。

堆排序与top k(顺序统计)问题

    顺序统计问题是要求从n个元素中选出第k个最小元素。可以建立小根堆,依次进行k次堆排序子过程即可。
    以 Leetcode 215 题目为例,代码如下:

class Solution {
public:    
#define LEFT(i) (((i)<<1) + 1)
#define RIGHT(i) (((i)<<1) + 2)

int HEAPSIZE;

void MAX_HEAPIFY(vector<int>& nums, int i)
{
	int lf, rg;
	int largest = -1, tmp;
	while (largest != i)
	{
		lf = LEFT(i);
		rg = RIGHT(i);

		largest = i;
		if (lf < HEAPSIZE && nums[lf] > nums[largest])
			largest = lf;

		if (rg < HEAPSIZE && nums[rg] > nums[largest])
			largest = rg;

		if (largest != i)
		{
			tmp = nums[largest];
			nums[largest] = nums[i];
			nums[i] = tmp;

			i = largest;
			largest = -1;
		}
	}
}


void BUILD_MAX_HEAP(vector<int>& nums)
{
	HEAPSIZE = nums.size();
	for (int i = nums.size() / 2 - 1; i >= 0; i--)
		MAX_HEAPIFY(nums, i);
}

int findKthLargest(vector<int>& nums, int k) 
{
	int tmp;

	BUILD_MAX_HEAP(nums);
	for (int i = 1; i < k; i++)
	{
		tmp = nums[nums.size() - i];
		nums[nums.size() - i] = nums[0];
		nums[0] = tmp;

		HEAPSIZE--;
		MAX_HEAPIFY(nums, 0);
	}

	return nums[0];
}
  
};

参考

算法导论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值