堆排序的非递归方法

堆是一种完全二叉树,第i个节点的左孩子是2 * i + 1,右孩子是 2 * i + 2,可以通过子节点求父节点:(i - 1)/ 2 。不管是左孩子还是右孩子都可以通过这种方式求的其父节点。对于一个数组,可以先将数组中的元素构建为一个大根堆,这样数组中的第一个数就是最大的值,将这个数与最后一个位置上的数进行交换,最后一个数就是最大值了,之后考虑对去掉这个元素的数组重新调整为一个大根堆,再将这个数与最后一个位置上的数进行排序,循环往复,直到只剩下一个元素。
比如要排序的数组为:5,7,0,6,8。
可以这样依次构建大根堆,将5和7放在如下图所示的二叉树位置:

在这里插入图片描述在放入第二个元素7的时候,将其与父节点的数5进行比较,7>5,交换位置,得到下图:
在这里插入图片描述放入第三个元素0,将其与父节点进行比较,将其作为7的右节点。
在这里插入图片描述下一个元素是6,6的父节点是5,比较后交换,6到达新位置后再次和它的父节点7比较,比较之后不交换。
在这里插入图片描述最后插入节点8,插入过程与上述过程类似。
在这里插入图片描述至此,将每个元素插入构造大根堆的过程完成,开始第二阶段,将最大元素取出,剩下元素构造大根堆,取出最大元素的循环,直至大根堆中只剩下最后一个元素。
首先将最大元素8与大根堆的最后一个元素6进行交换,然后将8取出,8是数组中最大的元素,6插入新的位置。
在这里插入图片描述将6的两个孩子节点与它进行比较,孩子节点中较大的那个与6进行交换,得到下面的结果:
在这里插入图片描述接着,将7与去除8之后新的大根堆最后一个元素5进行交换,得到下图所示的结果:
在这里插入图片描述5的左节点6比它大,进行交换:
在这里插入图片描述下一步,将6与0交换。
在这里插入图片描述最后,将0和5的值进行比较并交换。
在这里插入图片描述至此,堆排序完成。堆排序的时间复杂度为O(NlgN)。
堆排序的过程可以分为两个阶段:第一阶段是将数组中的每一元素插入形成大根堆的过程,这个过程的时间复杂为O(lgN)。因为假如完成二叉树的节点数为N,则其高度为lgN级别。则i位置上的数插入,要先遍历之前i-1位置的上的数,时间复杂度为O(lg(i-1)),最后加起来的值收敛在lgN级别。第二阶段,每排好一个数,都得过一个高度,相当于进行一次插入的过程,所以堆排序的时间复杂度为O(N
logN)。但是堆排序一般在工程上不常用,因为它是不稳定排序,且常数项比较大。
按照上述思路进行堆排序的C++程序如下:

#include <iostream>
using namespace std;
void heapInsert(int arr[], int index) ;
void heapify(int arr[], int index, int size);
void swap(int arr[], int a, int b)
{
	if(a == b) {
		return ;	
	}
	int c = arr[a];
	arr[a] = arr[b];
	arr[b] = c;
}
void heapSort(int arr[],int length) {
	if(arr == NULL || length < 2 ) {
		return ;
	}
	for(int i = 0; i < length; i++) {
		heapInsert(arr, i);
	}
	int heapSize = length;
	swap(arr, 0, --heapSize);
	while(heapSize > 0) {
		heapify(arr, 0, heapSize);
		swap(arr, 0 , --heapSize);
	}
}
void heapInsert(int arr[], int index) {
	while(arr[index] > arr[(index -1)/2]) {
		swap(arr, index, (index -1)/2);
		index = (index -1)/2;
	}
}
void heapify(int arr[], int index, int size) {
	int left = index *2 +1;
	while(left < size) {
		int largest = left + 1 < size && arr[left +1] > arr[left] ? left + 1 : left;//右孩子不越界,且右孩子大于左孩子的情况下,largest是右孩子的下标 
		largest = arr[largest] > arr[index] ? largest : index;//左右两个孩子较大的那个值如果比当前值大,大的那个孩子的下标变为了largest,否则,当前值的下标为largest。 
		if(largest == index) {
			break;
		}
		//某个孩子的值比当前值大,这个孩子的下标为largest 
		swap(arr,largest,index);
		index = largest;
		left = index * 2 + 1; 
	}
}
int main()
{
	int arr[] = {0, 3, 4, 4, 6, 5, 4};
	int len = sizeof(arr)/sizeof(arr[0]);
 	heapSort(arr, len);
	for(int i = 0; i < len; i++) {
		cout<<arr[i]<<endl;
	}
	return 0;	
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值