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);
	}
}

Analysis:

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



你可能感兴趣的:(堆排序)