一、堆

堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。在堆排序中使用到大顶堆

完全二叉树的性质
●如果按照层序遍历的方式给结点从0开始编号,则结点之间满足如下关系:
●如果i = 0;则结点i是二叉树的根,无双亲
●如果i > 0; 对于左孩子,则其双亲是结点i/2;对于右孩子,则其双亲是结点(i/2)-1;那么对于有n个结点的二叉树而言,非叶子结点的i值(也就是由左右孩子的结点)小于等于i/2。

【数据结构】堆排序

 

二、堆排序

堆排序的过程:
<1>构建大顶堆(自下而上调整):从非结点开始,比较左右孩子的大小,获得值比较大的孩子结点和父结点做比较,如果父结点大于此孩子结点,则进入下一次循环,否则交换父结点和孩子结点的值。
<2>得到序列的最大值:将大顶堆的根节点(最大值)与堆中的最后一个结点进行交换,下一次循环中,丢弃最后一个结点(i--),也就得到序列的最大值。
<3>再次调整成为大顶堆。

完整代码:

#include <stdio.h>
void Swap(int arr[],int a,int b)
{
	int temp;
	temp = arr[a];
	arr[a] = arr[b];
	arr[b] = temp;
}
void HeapSort(int arr[],int i,int len)
{
	int j = 2*i + 1;//i是一个子树中的根节点,j是子树中的左孩子
	for(j; j < len;j = i*2 +1)
	{
		if(arr[j+1] > arr[j] && j < len -1)
		{
			j++;//j指向孩子值比较大的一端
		}
		if(arr[i] >= arr[j])
		{
			break;
		}
		Swap(arr,i,j);
		i = j;
	}
}


void Heap(int arr[],int len)
{
	int i = len/2 - 1;//对于n个结点,非叶子结点的i值为n/2,这里的len是长度,所以要减一
	for(i;i >= 0;i--)
	{
		HeapSort(arr,i,len);//构建大顶堆,要自下而上调整;
	}

	for(int i = len -1;i >=0;i--)//i--,每次丢弃最后一个最大值元素,也就是每次循环得到一个最大值
	{
		Swap(arr,0,i);//将堆顶的最大值与最后一个元素交换
		HeapSort(arr,0,i);//将交换后的数据重新调整为大顶堆,因为下标为0的位置改变,所以每次从0号元素自顶向下开始调整;
	}
}
void Show(int arr[],int len)
{
	for(int i = 0;i < len;i++)
	{
		printf("%d ",arr[i]);
	}
}

int main()
{
	int arr[] = {8,39,4,809,89,1};
	int len = sizeof(arr)/sizeof(arr[0]);
	Heap(arr,len);
	Show(arr,len);
}

三、堆排序的时间复杂度

在构建堆的过程中,因为我们是完全二叉树从最下层最右边的非终端结点开始构建,将它与其孩子进行比较和若有必要的互换,对于每个非终端结点来说,其实最多进行两次比较和互换操作,因此整个构建堆的时间复杂度为O(n);

在正式排序时,第i次取堆顶记录重建堆需要用O(logi)的时间(完全二叉树的某个结点到根节点的距离为log2i + 1),并且需要取n - 1次堆顶记录,因此,重建堆的时间复杂度为O(nlogn)

所以总体来说,堆排序的时间复杂度为O(nlogn).

相关文章:

  • 2021-10-06
  • 2022-12-23
  • 2021-08-04
  • 2021-05-17
  • 2021-10-03
  • 2021-11-14
猜你喜欢
  • 2021-12-17
  • 2021-04-25
  • 2021-09-01
  • 2021-06-14
  • 2021-11-18
相关资源
相似解决方案