堆排序具体思路以及实现(图解)

堆排序思想

堆排序:
  可以将数据看做一个完全二叉树,然后将其调整为大跟堆(升序),此时,根节点就可以保证是数据中的最大值,
  接下来,将根节点与最后一个节点交换,这时可以将最后一个节点剔除出排序(因为最后一个节点值已经有序),完全二叉树的节点个数 -= 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;
}

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值