堆排序算法

数据结构:堆


(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树。树上的每一个结点对应数组中的一个元素。除最底层外,该树是完全充满的,而且是从左向右充满。二叉堆可以分两种形式:最大堆和最小堆。这两种堆中,结点的值都要满足堆的性质。最大堆要满足,除了根以外的所有结点都要满足:所有的父结点都大于或等于他的子结点

Arr[parent(i)]>=Arr[i]

也就是说,每个结点的值,最多与其父结点一样大,并且在任一子树中,该子树包含的所有结点的值都不大于该子树根结点的值(如下图所示为一个最大堆)。最小堆的组织方式正好相反:最小堆是所有结点的值都大于或等于他的父结点的值

Arr[parent(i)] <=Arr[i]

 

在堆排序算法中,我们使用的是最大堆。而最小堆通常用于构造优先队列。


堆的维护 


我们先看使用伪代码写的max_heapify()的过程:

max_heapify(Arr[],i)
    l=left(i)  //2i
    r=right(i)  //2i+1
    if l<=Arr.size and Arr[l]>Arr[i]
        largest=l
    else largest=i
    
    if r<=Arr.size and Arr[r]>Arr[largest]
        largest=r
    
    if largest != i
        exchange Arr[i] with Arr[largest]
        max_heapify(Arr,largest)



max_heapify()是用于维护堆最大堆的重要过程,他输入一个数组Arr和一个下标:i。我们假定:结点i的所有子结点都满足最大堆的性质,只有i结点不满足最大堆的性质,因为Arr[i]可能小于其孩子节点,这样就违背了最大堆的性质。调用max_heapify()过程后能使结点i在最大堆中逐级下降,从而使以下标i为根结点的子树重新遵守最大堆的性质。

在图示(a)中,Arr[2]违背了最大堆的性质。我们可以执行max_heapify(Arr,2)来恢复堆的性质。在执行程序的每一步中,从Arr[i]、Arr[left(i)]、Arr[right(i)]中选出最大的,并将其下标储存在largest中。如果Arr[i]是最大的,那么以i为根结点的子树已经是最大堆,程序结束。否则,最大元素是i的某个孩子节点,则交换Arr[i]和Arr[largest]的值。在交换之后Arr[i]都会大于它两个孩子结点的值,从而满足了最大堆的性质。在交换之后,下标largest的结点是原来的Arr[largest]的值,于是以该结点为根的子树又有可能会违反最大堆的性质。因此,需要对孩子树递归调用max_heapify();

维护堆的C语言代码:

void max_heapify(int arr[], int i, int size)
{
	int largest;
	int l = i * 2;
	int r = (i * 2) + 1;
	if (l<=size && arr[l]>arr[i]){
		largest= l;
	}
	else {
		largest= i;
	}

	if (r<=size && arr[r]>arr[largest]) {
		largest= r;
	}

	if (largest!= i){
		int temp = arr[i];
		arr[i] = arr[largest];
		arr[largest] = temp;
		max_heapify(arr, largest, size);
	}
}

建堆 


我们可以用自顶向上的方法利用过程max_heapify把一个大小为n=Arr.length的数组Arr[1...n]转换为最大堆。不难证明,Arr[(n/2)+1...n]的元素都是页结点,即他们没有子结点,他们都可以看成只包含一个元素的堆。我们可以利用build_max_heapify过程对树中的其他结点都调用一次max_heapify。伪代码实现过程如下:

build_max_heapify(Arr)
        for i=(Arr.size)/2 downto 1
                max_heapify(Arr,i)

因此,我们可以在线性时间内,把一个无序素组构造成为一个最大堆。


堆排序算法


讲了那么久,终于到重点了,我要开始装逼了:请大家拿好笔!!!!

上面我们讲到,我们建立了一个大顶堆,大顶堆的的特点就是,数组中最大的元素总是在Arr[1]中。我们可以把Arr[1]和Arr[n]互换,让最大的元素和最后一个元素互换位置,然后我们从堆中拿出最后一个元素(也就是最大的元素)。做法很简单,我们只需要把数组的Arr.size属性减去1即可。在剩余的结点中,只有一个元素不满足最大堆的性质,那就是我们刚刚换上去的Arr[1],这时候我们只需要调用一次max_heapify(Arr,1)维护堆,剩下的结点又编程了一个n-1的大顶堆了。这时我们又可以重新从Arr[1]取出最大值了,然后一直重复这个过程就能得到一个排好序得数组了,是不是感觉很简单?

伪代码实现过程:

heapsort(Arr)
    build_max_heap(Arr)  //建立一个大顶堆
    for i=Arr.length downto 2
        exchange Arr[1] with Arr[i]
        Arr.length=Arr.length-1
        max_heapify(Arr,1)

总体测试代码:

#include <iostream>
using namespace std;

void max_heapify(int arr[], int i, int size)
{
	int largest;
	int l = i * 2;
	int r = (i * 2) + 1;
	if (l <= size && arr[l] > arr[i]) {
		largest = l;
	}
	else {
		largest = i;
	}

	if (r <= size && arr[r] > arr[largest]) {
		largest = r;
	}

	if (largest != i) {
		int temp = arr[i];
		arr[i] = arr[largest];
		arr[largest] = temp;
		max_heapify(arr, largest, size);
	}
}

void build_max_heapify(int arr[], int size)
{
	for (int i = size / 2; i >= 1; i--) {
		max_heapify(arr, i, size);
	}
}

void sort_head(int Arr[], int size)
{
	build_max_heapify(Arr, size);
	int len = size;
	int temp;
	for (int i = size; i >= 2; i--)
	{
		temp = Arr[1];
		Arr[1] = Arr[i];
		Arr[i] = temp;
		max_heapify(Arr, 1, --len);
	}
}

int main()
{
	int Arr[11] = { 0,16,14,10,8,7,9,3,2,4,1 };//0号元素我们是不用得
	sort_head(Arr, 10); //堆排序
		for (int i = 1; i <= 10; i++) {
			cout << i << ":" << Arr[i] << endl;
		}

	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值