数据结构---堆排序

1、叶子完全二叉树:节点必须按从左到右存放,少也要从右边节点开始少,不能从中间突然少一个节点

满二叉树:也是完全二叉树的特例

数据结构---堆排序_第1张图片数据结构---堆排序_第2张图片

大顶堆:父节点的值大于孩子节点                               小顶堆:父节点的值小于孩子节点

数据结构---堆排序_第3张图片                                                 

 叶子节点:

数据结构---堆排序_第4张图片

2、堆排序:默认从小到大排序,使用大顶堆

                    默认从大到小,使用小顶堆

(1)、先将数组中的数据想象成一个完全二叉树的结构 

数据结构---堆排序_第5张图片

 (2)将其调整为大顶堆,观察调整完毕的大顶堆,可得到根节点的值一定是所有值中最大值,现在已将找到了最大值,则可以将最大值与最后一个值的进行位置交换,最后一个位置记为最大值,相当于完成了一趟冒泡排序

        整规则:从最后一个非叶子节点开始调整(从右向左、从下到上),将其作为临时根节点去调整(非叶子节点一共有三个13 、12、21,最后一个非叶子节点是13)

数据结构---堆排序_第6张图片

先调整红色框,再橙色框,最后绿色框(依据从右向左、从下到上)

红色框调整:

步骤:(i)先将根节点的值取出来保存到tmp,

           (ii)需要找到当前空白节点的较大的孩子节点

           (iii)较大孩子节点和tmp比较,如果比tmp大,则将较大的孩子节点向上移动

           (iv)孩子节点移动后,不能直接将tmp插入倒原先较大孩子的位置上,因为它可能还有孩子节点的值比起大

           (v)那么tmp什么时候插入?

                    a:触底(较大的孩子节点没有孩子节点)

                    b:较大孩子节点的孩子节点值都小于tmp

(3)将最大值和最后一个节点的值进行交换,然后将尾节点剔除,不参与下一次排序

(4)重读(2)、(3),直到有序节点只剩一个,则结束。

        现在,如果继续调整为大顶堆,则不需要从内到外调整,只许将最大的那个框调整一次即可,因为只有一根节点为初始节点的框受到影响,可能不稳定,其他框不受影响。数据结构---堆排序_第7张图片

 例如,一次调整之后如上图,只有绿色不稳定,其他框依旧保持大顶堆状态。

调整流程图: 

数据结构---堆排序_第8张图片

 数据结构---堆排序_第9张图片

数据结构---堆排序_第10张图片

3、父节点位置为i,左右孩子节点的位置下标为2i+1,2i+2

     子节点位置为i,父节点的位置下标为   (i-1)/2  取整

堆排序代码:

//一次调整函数时间复杂度O(log2 n),空间复杂度
void HeapAdjust(int arr[],int start,int end)
{
	//assert
	int tmp = arr[start];
	for (int i = start * 2 + 1; i <= end;i = start * 2+1)//start*2+1相当于start节点的左孩子
	{
		if (iarr[i])//i tmp)
		{
			arr[start] = arr[i];
			start = i;//更改start的位置,i指向新的子孩子位置,体现在for循环语句3
		}
		else
		{
			break;//退出for循环,触发情况2
		}
	}
	arr[start] = tmp;
}
//堆排序,时间复杂度O(n*log2 n),空间复杂度O(1),不稳定
void HeapSort(int* arr, int len)
{
	//1、整体从最后一个非叶子节点由内到外调整
	//首先需要知道最后一个非叶子节点的下标,
	for (int i = (len - 1-1) / 2; i >= 0; i--)//因为最后一个非叶子结点是最后一个叶子结点的父节点,最后一个叶子节点的下标为len-1
	{
		
		HeapAdjust(arr, i,len-1);//调用一次调整函数,i为一次调整的父节点下标,这里第三个参数没有规律,则直接给最大值
	}
	//此时,已经调整为大顶堆

	//此时,当前根节点的值和当前最后一个节点的值进行交换,最后将当前尾节点剔除
	for (int i = 0; i < len - 1; i++)
	{
		int tmp = arr[0];
		arr[0] = arr[len - 1 - i];//当前根节点的值和当前最后一个节点的值进行交换
		arr[len - 1 - i] = tmp;

		HeapAdjust(arr, 0, (len - 1 - i) - 1);//此时只需要再次调用一次调整为大顶堆的一次函数.len-1-i是当前的尾节点,再给其-1,相当于提出当前最后一个尾节点
	}
}

void Show(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[] = { 12,2,39,88,4,6,25,232,62,221 };
	
	HeapSort(arr, sizeof(arr) / sizeof(arr[0]));
	Show(arr, sizeof(arr) / sizeof(arr[0]));

	return 0;
}

结果:

数据结构---堆排序_第11张图片

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