基础数据结构之八大排序算法(七)
⑦堆排序
时间复杂度:O(nlogn) 其中的循环与平方有关,则时间复杂度为O(nlongn)
空间复杂度:O(1) 问题规模与其额外辅助变量无关
稳定性:不稳定 存在跳跃交换
(以顺序表作为待排序对象)
1.在学习堆排序前需要先了解一些关于树的基本知识:
(1)树:
(2)二叉树:
【每个节点最多只能分出两个节点】
(3)叶子节点:
【树最外层的节点】
(4)完全二叉树:
(5)满二叉树:
【每一层的节点都满了】
(6)大顶堆:
【父节点的值大于孩子节点的值】
{ 大顶堆的根节点的值一定是所有节点里最大的 }
(7)小顶堆:
【父节点的值小于孩子节点的值】
{ 大顶堆的根节点的值一定是所有节点里最小的 }
2.基本思想:
以数组arr[]={21,7,8,1,12,9,66}为例:
【1】将所有数组臆想成一个完全二叉树:
【2】将这个完全二叉树化为一个大顶堆(从小到大排序)
具体调整方法:
{ 1.将根节点的值保存一份 }
{ 2.找当前节点的左右孩子中较大的孩子 }
{ 3.这个较大的孩子和根节点比较,如果大于根节点则向上走 }
{ 4.原先较大孩子的节点不可能还有大于根节点的节点 }
{ 5.不能直接插入根节点,需要继续向下判断 }
{ 6.那什么时候插入(1.触底了 2.左右孩子都小于根节点)}
(先调整最后一个非叶子节点)
(从右向左调整第二个非叶子节点)
(从下向上调整第三个非叶子节点)
(此时已经成为一个大顶堆)
【3】将大顶堆的根节点的值和最后一个节点交换,并将交换后的最后一个节点剔除,循环这个过程,直到只剩下一个节点
3.代码实现:
<1>.所需头文件和宏替换:
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<stdbool.h>
#define ar arr[]={21,7,8,1,12,9,66}
#define ELEM_TYPE int
int ar;
<2>.顺序表的建立:
typedef struct Sqlist
{
ELEM_TYPE* data;
int length;
int SIZE;
}Sqlist,*PSqlist;
<3>.顺序表的初始化:
void Init_Sqlist(PSqlist L)
{
L->data = arr;
L->length = 0;
L->SIZE = sizeof(arr) / sizeof(arr[0]);
}
<4>.打印
void Show(PSqlist L)
{
for (int i = L->length; i < L->SIZE; i++)
{
printf("%d ", L->data[i]);
}
printf("\n\n");
}
<5>.每一次排序的调整:
void HeapAdjust(PSqlist L, int start, int end)
{
int tmp = L->data[start];
for (int i = start * 2 + 1; i <= end; i = start * 2 + 1)
{
if (i<end && L->data[i]<L->data[i + 1]) // i < L->SIZE-1 代表右孩子存在,i来保存较大的那个孩子的下标
{
i++;
}
if (L->data[i] > tmp)
{
L->data[start] = L->data[i];//如果孩子的值大于起始的值,则起始的位置放入孩子的值
start = i; //因为L->data[i]向上放,所以L->data[i]变为了空白格子,让start指向空白格子。
}
else
{
break;
}
}
//此时无论是通过for循环结束退出的(触底,没有孩子节点了退出),或是通过break推退出的(左右孩子都小于tmp)退出的
//将tmp插入到start空白格子处
arr[start] = tmp;
}
<6>.堆排序:
void HeapSort(PSqlist L)
{
//将全部数据看成一个完全二叉树,再将这个完全二叉树化为一个‘大顶堆’(从小到大排)
for (int i = ((L->SIZE - 1 - 1) / 2); i >= 0; i--) //L->SIZE-1为最后一个数据的下标,(L->SIZE-1-1)/2 为最后一个‘非叶子节点’的下标
{
HeapAdjust(L, i, L->SIZE - 1);
}
//根节点的值和最后一个节点的值交换,再将最后一个元素剔除,再继续将剩下的数据排成大顶堆,重复这三步
for (int i = 0; i < L->SIZE - 1; i++)
{
int tmp = L->data[0];
L->data[0] = L->data[L->SIZE-1-i];
L->data[L->SIZE - 1-i] = tmp;
HeapAdjust(L, 0, ((L->SIZE - 1 - i)-1));//L->SIZE-1-i是最后一个元素的下标,再减1就将最后一个数据剔除
}
}
<7>.主函数:
int main()
{
Sqlist head;
Init_Sqlist(&head);
printf("初始数据为:");
Show(&head);
HeapSort(&head);
printf("堆排序后的数据为:");
Show(&head);
return 0;
}
<8>.运行结果:
希望这篇博客能帮助大家更好的学习和理解二路归并排序
感谢阅读!