堆排序来袭

说起堆排序就不得先说下简单选择排序,毕竟都是选择排序类,堆排序也是对选择排序的一种改进。

选择排序我的理解就是每趟找出某位置应该放的记录。直接上代码,拿代码感受吧:

void main()
{
	int arr[10] = {1,4,5,7,3,8,2,9,6,0};
	int len = sizeof(arr)/sizeof(arr[0]);
	for(int i=0;i<len-1;i++)
	{
		int minIndex = i;  //记录最小下标
		for(int j = i+1;j<len;j++)
		{
			if(arr[j] < arr[minIndex])
			{
				minIndex = j;
			}
		}
		if(minIndex != i)
		{
			int temp = arr[i];
			arr[i] = arr[minIndex];
			arr[minIndex] = temp;
		}
	}
}

 是不是感觉就是每趟找出i位置应该放的记录。它与初始状态下待排序的记录序列的排列情况无关。因为不管怎么样,都是去挨个遍历找最小下标,然后放到该趟应该放的位置。所以它的时间复杂度为O(n*2)。在交换的过程中某些相同的记录可能会顺序变化,所以它是一种不稳定的排序。

那么堆排序是怎么实现的的呢,在我看来堆排序无非就是建立大(小)根堆,然后进行不断地调整,调整的过程就是一种选择排序的过程,先上张粗图感受下如何建立大根堆吧:

它是从第一个非叶子节点开始调整的,找出他的左右孩子节点中的最大值,然后将其交换,交换之后继续从最大的孩子节点中执行相同操作,直到满足条件:所有的非叶子节点都比它左右孩子节点的值大。

来来来,上代码感受:

void swap(int *x, int *y)
{
	int tmp;
	tmp = *x;
	*x = *y;
	*y = tmp;
}
static void HeapAdjust(int *arr, int start, int len)
{
	for (int i = start * 2 + 1; i<len; i = i * 2 + 1)
	{
		if (i + 1<len && arr[i] < arr[i + 1]) //寻找对应孩子节点中的最大值
		{
			i++;
		}
		if (arr[start] < arr[i])      //判断该节点和孩子节点中最大值的大小
		{
            swap(&arr[start],&arr[i]);
		}
		else
		{
			break;
		}
		start = i;     //记录当前被调整的节点
	}
}

至此,堆排序已经完成一大半了,剩下的就是堆调整,来来上图感受:

图中拿顶部元素9和最后一个元素进行交换,交换之后,最后这个位置就不需要再进行变动了,是不是感觉和简单选择排序有点相似,一趟排序之后在固定位置放好该放的元素。来看下代码:

void HeapSort(int *arr, int len)    
{
	//1.建立大根堆
	for (int j = (len - 2) / 2; j >= 0; j--)  //从最后一个非叶节点开始调整
	{
		HeapAdjust(arr, j, len);    //一次只调整一个节点对应的那个分支树
	}
	swap(&arr[0], &arr[len - 1]);   //顶堆元素和最后一个元素交换

	//2.堆调整
	for (int k = len - 1; k>1; k--) //
	{
		HeapAdjust(arr, 0, k);    //自顶向下调整
		swap(&arr[0], &arr[k - 1]); //继续交换并从顶堆开始调整
	}
}

 堆调整就是自顶向下不断地对堆进行调整,交换堆顶和堆底最后一个元素,继续调整,,,

来分析下堆排序的复杂度:

(1)可以看出它主要复杂到建立初始堆和堆调整过程中的反复筛选上。分析堆排序对原始记录的排序状态并不敏感,无论是好是坏它的平均时间复杂度均为O(n*logn)

(2)空间复杂度,几乎可以不考虑,有没有发现上面的堆调整函数还存在小瑕疵,,,,没错,在比较的过程中发现其左右孩子比它自身大的时候,直接进行交换,这样可能交换的次数会很多,所以可以考虑用一个临时变量把初始的arr[start]记录,最后只进行一次交换。看下面代码:

static void HeapAdjust(int *arr, int start, int len)
{
	int tmp;
	tmp = arr[start];

	for (int i = start * 2 + 1; i<len; i = i * 2 + 1)
	{
		if (i + 1<len && arr[i] < arr[i + 1]) //寻找对应孩子节点中的最大值
		{
			i++;
		}

		if (tmp < arr[i])      //判断该节点和孩子节点中最大值的大小
		{
			arr[start] = arr[i];
		}
		else
		{
			break;
		}
		start = i;     //记录当前被调整的节点
	}

	arr[start] = tmp;   
}

还有最后一点:堆排序的比较和交换是跳跃式的,所以它是一种不稳定的排序,其次,在建立初始堆的所需比较的次数较多,它不适合待排序序列个数较少的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值