<数据结构学习>排序——堆排序

本文详细介绍了如何使用堆排序算法对整数序列按关键字递减顺序进行排序,包括堆的定义、堆化过程、堆排序函数以及示例代码。

目录

题目描述

 问题分析

问题解决


题目描述

用堆排序算法按关键字递减的顺序排序。

输入:待排序记录数(整数)和待排序记录(整数序列);

输出:建堆结果和建堆后第一、第二次筛选结果。(注:待排序记录数大于等于3)

测试输入期待的输出时间限制内存限制额外进程
测试用例 1以文本方式显示
  1. 6↵
  2. 11↵
  3. 12↵
  4. 16↵
  5. 14↵
  6. 15↵
  7. 10↵
以文本方式显示
  1. 16 15 11 14 12 10 ↵
  2. 15 14 11 10 12 ↵
  3. 14 12 11 10 ↵
1秒64M0
测试用例 2以文本方式显示
  1. 9↵
  2. 9↵
  3. 8↵
  4. 7↵
  5. 6↵
  6. 5↵
  7. 4↵
  8. 3↵
  9. 2↵
  10. 1↵
以文本方式显示
  1. 9 8 7 6 5 4 3 2 1 ↵
  2. 8 6 7 2 5 4 3 1 ↵
  3. 7 6 4 2 5 1 3 ↵
1秒64M0

 问题分析

首先要了解一下堆的含义。

大顶堆需要满足两个条件

1、完全二叉树

2、父结点数值大于两个子结点数值

堆可以存储在数组中,数组下标和堆的结点下标有一一对应关系,对应公式如下:

设某父结点在数组中下标为i,则在数组中其左孩子结点下标为2*i+1,其右孩子结点下标为2*i+2

同理若已知孩子结点在数组中下标为i,则其父结点数组中下标为(i-1)/ 2

问题解决

有了堆的基础知识,我们可以尝试用代码实现建堆和堆排序

定义全局变量和交换函数

int n;
vector<int> num;
void swapp(int x,int y) //传下标即可
{
    int temp;
    temp=num[x];
    num[x]=num[y];
    num[y]=temp;
}

 接下来建立堆化函数,确保当前树形为堆

void heapify(int n,int i)  //首先建立堆化函数,确保当前树形为堆
{
    if(i>=n) return; //递归终止条件
    int n1=i*2+1; //求得当前结点的两个孩子在数组中的下标
    int n2=i*2+2;
    int maxx=i;  //假设当前父结点比两子结点都大
    if(n1<n && num[maxx]<num[n1]) //如果有子结点大于父结点,就更新下标maxx的值
    {
        maxx=n1;
    }
    if(n2<n && num[maxx]<num[n2])
    {
        maxx=n2;
    }
    if(maxx!=i)  //如果maxx被更改,就把两个子结点中最大的子结点和父结点交换
    {
        swapp(maxx,i);
        heapify(n,maxx); //被交换的子结点那棵子树不一定还是堆,要再进行堆化
    }
}

这里需要注意的是判断子结点是否大于父结点时一定要加上n1<n和n2<n的判断条件,排除父结点为叶子结点的情况,保证子结点也在数组范围内。

void heap_sort()  //堆排序函数
{
    int l=n-1;  //拿到最后一个结点的下标
    int par=(l-1)/2; //求其父结点,即为需要堆化的最后一个(子)树根结点
    for(int i=par;i>=0;i--) //在需要堆化的结点范围内逐步堆化,因为是完全二叉树,下标连续-1即可
    {
        heapify(n,i);
    }
    for(int j=0;j<n;j++) //输出建堆结果
    {
        cout<<num[j]<<" ";
    }
    cout<<endl;
 
    swapp(0,n-1);  //交换根节点和最后一个结点
    heapify(n-1,0);   //剪断最后一个结点(不用真的剪断,堆化总结点数-1即可)
    for(int j=0;j<n-1;j++) //输出第一轮排序结果
    {
        cout<<num[j]<<" ";
    }
    cout<<endl;

    swapp(0,n-2);
    heapify(n-2,0);
    for(int j=0;j<n-2;j++) //同理,输出第二轮排序结果
    {
        cout<<num[j]<<" ";
    }
    cout<<endl;

}

如果抛开本题,想要输出最后的排序结果,只需改动此函数的最后一部分即可。

void heap_sort()
{
    int l=n-1;
    int par=(l-1)/2;
    for(int i=par;i>=0;i--)
    {
        heapify(n,i);
    }
    for(int j=0;j<n;j++) //输出建堆结果
    {
        cout<<num[j]<<" ";
    }
    cout<<endl;

    for(int k=n-1;k>=0;k--)  //n-1轮排序,每次剪断最后一个结点
    {
        swapp(0,k);
        heapify(k,0);
    }
    for(int j=0;j<n;j++) //输出排序结果
    {
        cout<<num[j]<<" ";
    }
    cout<<endl;
}

附本题完整代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int n;
vector<int> num;
void swapp(int x,int y) //传下标即可
{
    int temp;
    temp=num[x];
    num[x]=num[y];
    num[y]=temp;
}
void heapify(int n,int i)
{
    if(i>=n) return;
    int n1=i*2+1;
    int n2=i*2+2;
    int maxx=i;
    if(n1<n && num[maxx]<num[n1])
    {
        maxx=n1;
    }
    if(n2<n && num[maxx]<num[n2])
    {
        maxx=n2;
    }
    if(maxx!=i)
    {
        swapp(maxx,i);
        heapify(n,maxx);
    }
}
void heap_sort()
{
    int l=n-1;
    int par=(l-1)/2;
    for(int i=par;i>=0;i--)
    {
        heapify(n,i);
    }
    for(int j=0;j<n;j++) //输出建堆结果
    {
        cout<<num[j]<<" ";
    }
    cout<<endl;

    swapp(0,n-1);
    heapify(n-1,0);
    for(int j=0;j<n-1;j++) //输出第一轮排序结果
    {
        cout<<num[j]<<" ";
    }
    cout<<endl;

    swapp(0,n-2);
    heapify(n-2,0);
    for(int j=0;j<n-2;j++) //输出第二轮排序结果
    {
        cout<<num[j]<<" ";
    }
    cout<<endl;
    // for(int k=n-1;k>=0;k--)  //n-1轮排序,每次剪断最后一个结点
    // {
    //     swapp(0,k);
    //     heapify(k,0);
    // }
    // for(int j=0;j<n;j++) //输出排序结果
    // {
    //     cout<<num[j]<<" ";
    // }
    // cout<<endl;
}

int main()
{
    cin>>n;
    int q=n;
    while (q--)
    {
       int t;
       cin>>t;
       num.push_back(t);
    }
    heap_sort();
    return 0;
}

参考视频

堆排序(heapsort)_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1Eb41147dK?spm_id_from=333.337.search-card.all.click

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值