[数据结构与算法]排序算法——图解堆排序

堆排序:堆排序作为一种高效的排序算法,最好、最坏以及平均时间复杂度都为O(NlogN)。

堆排序正如其名,是利用堆这种数据结构进行设计的一种排序算法,所以我们要先了解什么是堆。

堆:分为大顶堆小顶堆两种

大顶堆:每个结点的值都比它的左右孩子结点的值大。

小顶堆:每个结点的值都比它的左右孩子结点的值小。

对堆有了简单的认识即可,实际上我们在进行堆排序的时候既可以建立大顶堆进行排序,也可以建立小顶堆进行排序,对大顶堆进行排序过后会得到一个从小到大的数组,反之对小顶堆进行排序过后会得到一个从大到小的数组,建立的过程下面再详说,现在假设我们已经建立完成一个如上图的一个大顶堆。

大顶堆由于我们上面谈及得性质,即每个结点的值都比它的左右孩子结点的值大,所以不难得出一个结论:大顶堆最上面的数字肯定是整个数组最大的数字,现在数组的长度为5。

通过这个性质,我们先让最上面的数字和最后的一个数字进行交换,也就是8和1交换,变成如下

我们发现交换完了后,最大的元素(也就是8)已经跑到最后面去了,相当于跑到了数组的末尾了,那这个元素符合我们想要排序的结果的位置,而与之交换的1跑到顶端,这个图明显不符合大顶堆的性质,元素1并没有比它的左右结点(3和5)都大。于是我们需要对元素1进行下沉。

下沉:大顶堆的下沉,对比左右孩子结点哪个最大,然后用最大的那个孩子结点与父节点交换。例如上面就是1和5进行交换,1下沉到5的位置,得到如下

交换完成发现已经符合大顶堆的性质,于是停止下沉。

然后我们对除了8以外的元素进行重复上面得步骤,因为8已经在数组的最后面了,所以我们不需要动了,剩下的元素中最大的就是最顶端的5,此时我们应该要考虑的是除8以外的长度为4的数组,此时我们应该要假装8不在这个堆上。是的,你没有想错,其实我们每一轮下来最会把最大的元素交换到这次数组的后面,然后除了这个元素之外的所有元素中的最大元素都会通过下沉步骤被交换到最顶端。

所以不难得出下面的步骤应该是这样

2和5交换

2下沉

3和1交换

1下沉

2和1交换,结束

最后已经得出一个排序的数组,从上到下,从左到右:1,2,3,5,8。

总结上面,堆排序的过程其实就是:对数组建堆->最顶端的元素和末尾的元素进行交换->下沉。直到跑到最顶端为止。

下面为代码实现环节,代码分未三个模块,1:对数组建堆。2:对某个元素进行下沉。3:堆排序。

为了更好理解,我们先从第二个模块开始,对某个元素进行下沉。

代码如上所说,注意注释的内容即可

//下沉代码
void MaxHeapFixdown(int a[], int i, int n) // a为数组,对a[i]进行下沉,n时数组的长度
{
	int j, temp;
	temp = a[i];
	j = i * 2 + 1;                       //j的位置未a[i]的左孩子结点的位置
	while (j < n)
	{
		if (j + 1 < n&&a[j + 1] > a[j])   //比较左右孩子结点哪个更大,取最大的
			j++;
		if (a[j] < temp)                //如果左右孩子都比父节点小即可停止下沉
			break;
		a[i] = a[j];                   //父节点的值变成最大的那个孩子结点的值
		i = j;                         //跑到最大的那个孩子结点的位置
		j = i * 2 + 1;                 //重复上面的动作
	}
	a[i] = temp;
}

有了下沉代码,我们便可进行建堆操作

//建堆代码
void MakeMaxHeap(int a[], int n)        //a为数组,n是数组的长度
{
	for (int i = n / 2 - 1; i >= 0; i--)    //从第n/2-1这个位置开始对元素进行下沉,下沉完对前一个元素进行下沉,直到位置到i = 0
		MaxHeapFixdown(a, i, n);           //下沉代码
}

最后是堆排序

//排序代码
void MaxheapsortTodescendarray(int a[], int n)  //a为数组,n是数组的长度
{
	for (int i = n - 1; i >= 1; i--)       //对最后一个元素开始操作,直到第二个元素
	{ 
		swap(a[0], a[i]);                  //每个结尾的元素都与顶端进行交换
		MaxHeapFixdown(a, 0, i);           //交换完进行下沉,又形成新的大顶堆
	}
}

调用

int a[5] = { 5,1,8,2,3 };
cout << "初始数组:";
for (int i = 0; i < 5; i++)
    cout << a[i] << " ";
MakeMaxHeap(a, 5);     //建堆
cout << "最大堆为:";
for (int i = 0; i < 5; i++)
	cout << a[i] << " ";
cout << endl;
cout << "堆排序结果:";
MaxheapsortTodescendarray(a, 5);  //排序
for (int i = 0; i < 5; i++)
	cout << a[i] << " ";

运行结果

最后:对最大堆进行排序过后,数组是从小到大进行排列。小根堆和大根堆相反,结果时从大到小进行排列,可以自己进行尝试。

猜你喜欢

转载自blog.csdn.net/Uupton/article/details/82795234
今日推荐