堆排序思想
堆排序:
可以将数据看做一个完全二叉树,然后将其调整为大跟堆(升序),此时,根节点就可以保证是数据中的最大值,
接下来,将根节点与最后一个节点交换,这时可以将最后一个节点剔除出排序(因为最后一个节点值已经有序),完全二叉树的节点个数 -= 1,
直到这颗完全二叉树的节点个数为1时,则完全有序。
堆排序流程
1.先将数组中的数据臆想成一个完全二叉树的结构。
2.将其调整为大跟堆,从最后一个非叶子节点开始调整,从右向左,从下向上。
3.将跟节点的值和最后一个节点的值进行交换,然后将当前的最后一个节点剔除出我们的排序即可。
4.重复2,3(只需调整最外层框),直至有效节点只剩下一个。
单次调整细节:
1.先将根节点的值取出来保存到tmp中。
2.这时,空白节点出现,则找到空白节点的左右孩子中的较大孩子节点。
3.判断这个较大的孩子节点的值和tmp谁大谁小,如果大于temp,则向上挪动。
4.孩子节点向上挪动之后,不能立即将tmp的值挪动回来,因为新的空白节点有可能会有孩子节点,
且新的空白节点的孩子节点值还大于tmp。
5.那什么时候tmp可以放回来:
(1)空白格子没有孩子,则触底,tmp可以放回来。
(2)空白格子的较大孩子值,小于tmp,tmp也可以放回来。
堆排序相关知识点
二叉树:每个节点最多由两个孩子(左孩子、右孩子)
表示形式:链式实现
顺序实现
最大堆:这棵完全二叉树上,每个子树上父节点的权值都要大于左、右孩子:根节点是最大的权值
完全二叉树:父节点的下标为i,左孩子的下标:2*i+1,右孩子下标:2*i+2
如果一个孩子的下标为i,则其父节点的下标为(i-1)/2
实现过程
具体实现
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define DATANUM 10
void Show(int* arr, int length)
{
for (int i = 0; i < length; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int IsOrder(int* arr, int length)
{
for (int i = 0; i < length - 1; i++)
{
if (arr[i] > arr[i + 1])
{
return 0;
}
}
return 1;
}
void Swap(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
//start:起始下标,end:结尾下标
void HeapAdjust(int* arr, int start, int end)//O(logn)
{
int tmp = arr[start];
//int par = start;//标记i的父节点下标
for (int i = (2 * start) + 1; i <= end; i = 2 * i + 1)
{
if (i + 1 <= end && arr[i] < arr[i + 1])//有右孩子且右孩子较大
{
i++;
}
if (arr[i] > tmp)//左孩子较大
{
arr[start] = arr[i];//出现了新的空白节点
}
else
{
break;
}
start = i;
}
arr[start] = tmp;
}
//空间复杂度:O(1);不稳定
void HeapSort(int* arr, int length)//O(logn)
{
assert(arr != NULL || length > 1);
if (arr == NULL || length < 2)
{
printf("HeapSort:Invalid Array\n");
return;
}
//1.先将数组中的数据臆想成一个完全二叉树的结构。(默认已完成)
//2.将其调整为大跟堆,从最后一个非叶子节点开始调整,从右向左,从下向上。
//建立大根堆
//i首先赋值为最后一个非叶子节点的下标
for (int i = (length - 1 - 1) / 2; i >= 0; i--)//从后往前多次调整O(n*logn)
{
HeapAdjust(arr, i, length - 1);
}
//3.将跟节点的值和最后一个节点的值进行交换,然后将当前的最后一个节点剔除出我们的排序即可。
int tmp = 0;
for (int i = 0; i < length - 1; i++)//O(logn * n)
{
tmp = arr[0];
arr[0] = arr[length - 1 - i];//每次都会有一个尾节点剔除出排序,记为-i
arr[length - 1 - i] = tmp;
//4.重复2,3(只需调整最外层框),直至有效节点只剩下一个。
HeapAdjust(arr, 0, length - 1 - i - 1);//length - 1 - i刚刚剔除出去的那个节点,length - 1 - i - 1这一趟需要剔除出去的节点
}
}
int main()
{
int arr[DATANUM];
for (int i = 0; i < DATANUM; i++)
{
arr[i] = rand() % 100;
}
printf("Before HeapSort:");
Show(arr, DATANUM);
HeapSort(arr, DATANUM);
if (IsOrder(arr, DATANUM))
{
printf("After HeapSort:");
Show(arr, DATANUM);
}
else
{
printf("HeapSort Failed\n");
}
return 0;
}