Sorting Algorithm-Heap Sort

Heap Sort-堆排序


Algorithm:

堆排序就是利用堆(假设是大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素的次大值。如此反复执行,便能得到一个有序序列了。
Ex:大顶堆定义(所有结点都大于等于其孩子)

重复上面的过程,就可以排好序了。
在程序中,需要 解决两个问题
1、如何构造大顶堆
2、堆顶元素和最后一个元素交换后,如何调整剩下的元素成为一个新的堆。

Code:

void Heap_Adjust(vector<int> &v, int i,int len)   //len为调整的长度
{
	int lchild = 2 * i + 1;         //左孩子节点
	int rchild = 2 * i + 2;         //右孩子节点
	int temp = i;
	int key = v[i];
	if (i < (len / 2))  //不是叶节点
	{
		if (lchild < len&&v[lchild] > v[temp])  //必须是v[temp]
			temp = lchild;
		if (rchild < len&&v[rchild] > v[temp])
			temp = rchild;
		if (temp != i)
		{
			v[i] = v[temp];
			v[temp] = key;
			Heap_Adjust(v, temp,len);  //避免调整后temp节点不是堆
		}
	}
}
void Heap_Sort(vector<int> &v)
{
	//Build Heap
	for (int i = v.size() / 2-1; i >= 0; i--)
		Heap_Adjust(v, i,v.size());
	//调整
	for (int k = v.size() - 1; k >= 0; k--)
	{
		int key = v[0];
		v[0] = v[k];
		v[k] = key;
		Heap_Adjust(v, 0,k);
	}
}

非递归,这里只写调整堆得代码:
void Adjust(vector<int> &A,int i,int n){
        int l=2*i+1;
        int r=2*i+2;
        while(l<n || r<n){
            int cur=i;
            if(l<n && A[l]<A[cur])
                cur=l;
            if(r<n && A[r]<A[cur])
                cur=r;
            if(cur != i){
                swap(A[cur],A[i]);
                l=cur*2+1;
                r=cur*2+2;
                i=cur;
            }   
            else
                break;
        }
    }



Analysis:

它的运行时间主要是消耗在初始构建堆和重建堆时的反复筛选上。
在构建堆的过程中,因为我们是完全二叉树从最下层右边的非终端结点开始构建,将它与其孩子进行比较和若有必要的互换,对于每个非终端结点,其实最多进行两次比较和互换操作,因此整个构建堆的时间复杂度为O(n)。
在正式排序时,第i次去堆顶记录重建堆需要用O(logi)的时间(完全二叉树的某个结点到根节点的距离为(log2i)+1),并且需要去n-1次堆顶记录,因此,重建堆的时间复杂度为O(nlogn)。
所以总体来说,堆排序的时间复杂度为O(nlogn)。由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度均为O(nlogn)。这在性能上显然远远好于冒泡、简单选择、直接插入的O(n^2)的时间复杂度。
空间复杂度上,它只有一个用来交换的暂存单元,也非常不错。不过由于记录的比较与交换是跳跃式进行,因此堆排序也是一种不稳定的排序方法。
另外,由于初始构建堆所需的比较次数较多,因此,它并不适合待排序序列个数较少的情况。

2017/5/13更新
如果排序几乎有序,可以使用堆排序,(因为归并排序和快速排序都不依赖数组的初始顺序)
Ex1:已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。
可以建立一个大小为k的小根堆(0~k-1),然后把堆顶元素取出,再对(1~k)调整,重复过程可以得到。

Ex2:请设计一个高效算法,判断数组中是否有重复值。必须保证额外空间复杂度为O(1)。
需要使用非递归的堆排序,(因为快速排序,归并排序的空间复杂度都不是常数级别的)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值