算法导论——堆排序

堆排序的平均性能是O(n*logn),虽然对于不同的数据性能并不稳定,但由于需要的辅助空间很小,所以是一个很常用的排序算法。
堆排序的基本原理是利用大(小)顶堆的特性,对于一个二叉树中的节点,假设所有节点的左右孩子节点值都不大(小)于父亲节点,那么这个二叉树就叫做大(小)顶堆。
对于一个大(小)顶堆,位于根节点的值一定是最大(小)的,所以堆排序的过程就是创建、调整大(小)顶堆的过程。
下面以大顶堆排序为例说明,有一个整型数组:
324 93 921 122 308 434 705 373 145 608 239 758
根据数组创建一棵二叉树:
heap_01
原则是:
(1) 根节点对应数组下标为0的数据;
(2) 当前节点下标为N,则左孩子节点的下标为2*N+1;
(3) 当前节点下标为N,则右孩子节点的下标为2*N+2;
由于叶子结点没有孩子节点,我们可以将叶子节点看做已经创建好的大顶堆,所以创建大顶堆就从最后一个非叶子结点开始,调整的算法为:
step 1: 父亲节点与左右孩子节点中最大的节点进行交换;
step 2: 如果交换后的孩子节点并非叶子结点而且不是大顶堆,则重复步骤step 1;
heap_02
heap_03
heap_04
heap_05
heap_06
heap_07
heap_08
最终完成的大顶堆如下图所示:
heap_09
数组中数据为:921 608 758 373 308 434 705 122 145 93 239 324
而之后排序的过程就是从大顶堆根节点取数据的过程,
heap_10
具体算法如下:
Step 1: 将根节点与最后一个叶子结点交换,并且将大堆节点个数减1;
Step 2: 从新的根结点开始重新调整堆为大顶堆;
Step 3: 从Step 1重新开始,直到堆中节点个数为2;
Step 4: 交换最后两个节点中的值;
C代码实现如下:
/* 创建大顶堆 */
int _create_max_heap(int* in, int len)
{
	int t_node;
 
	if(len%2 == 0)
		t_node = (len-1)/2;
	else
		t_node = (len-2)/2;
 
	while(t_node >= 0){
		_re_adjust_heap(in, t_node, len);
		t_node--;
	}
 
	return 0;
}
 
/* 调整大顶堆 */
int _re_adjust_heap(int* in, int pos, int len)
{
	if(len <= 2)
		return -1;
 
	while(pos < len){ int left, right; left = pos*2+1; right= pos*2+2; if(left >= len)
			break;
		if(right >= len && in[pos]<in[left]){ _exchange_with_left(in, pos, len); break; } if(in[pos] > in[left] && in[pos] > in[right])
			break;
		if(in[left] > in[pos] && in[left] > in[right]){
			_exchange_with_left(in, pos, len);
			pos = left;
			continue;
		}
		if(in[right] > in[pos] && in[right] > in[left]){
			_exchange_with_right(in, pos, len);
			pos = right;
			continue;
		}
	}
}
完整代码可以我已经push到github中,请访问:https://github.com/plumpyomi/algorithm.git

发表评论