美文网首页ACM算法题
堆排序-数据结构实验之排序四:寻找大富翁

堆排序-数据结构实验之排序四:寻找大富翁

作者: YellowTag | 来源:发表于2018-09-23 12:06 被阅读0次

Problem Description

2015胡润全球财富榜调查显示,个人资产在1000万以上的高净值人群达到200万人,假设给出N个人的个人资产值,请你快速找出排前M位的大富翁。

Input

首先输入两个正整数N( N ≤ 10^6)和M(M ≤ 10),其中N为总人数,M为需要找出的大富翁数目,接下来给出N个人的个人资产,以万元为单位,个人资产数字为正整数,数字间以空格分隔。

Output

一行数据,按降序输出资产排前M位的大富翁的个人资产值,数字间以空格分隔,行末不得有多余空格。

Sample Input

6 3
12 6 56 23 188 60

Sample Output

188 60 56

Hint

请用堆排序完成。

思路

因为时间的要求
所以我们采用堆排序
堆排序最坏的情况时间复杂度为:O(N*log2N)

堆排序的原理参考:
https://www.cnblogs.com/chengxiao/p/6129630.html

视频演示:
https://www.bilibili.com/video/av18980178?from=search&seid=419020408467955213

堆排序代码:

void heapsort(int arr[]){
    //首先第一次调整大顶堆,从第一个非叶子节点,从右到左,从下到上 
     for(int i=m/2-1;i>=0;i--){//m 是数组元素个数
        heapadjust(arr,i,m);
     }
     for(int i=m-1;i>0;i--){
        swap(arr,0,i);
        heapadjust(arr,0,i);    //很明显i代表的是数组的长度
     } //交换首末元素,然后再重新调整 
 }
 void heapadjust(int arr[],int i,int len){
    int temp=arr[i];
    for(int k=2*i+1;k<len;k=2*k+1){//从子节点的左边开始,每一次迭代,都往下走一层 
        if(k+1<len&&arr[k]<arr[k+1]){//判断右子节点是否存在,且判断左子节点是否小于右子节点,是的话,k就指向右子节点
             k++;   
         }
        if(arr[k]>temp){
           arr[i]=arr[k];
           i=k;
        }
        else break;
     }
     arr[i]=temp;//这一步很好,减少了操作量,先不急着交换,因为已经知道下标和temp的值,我们到最后循环结束再放回来,就像快排里面的枢轴归位一样
      
     
 }
代码分析:
1.堆排序重要几步:
 >1.1 构建堆:  这一步其实就是一个数组,我们把这个数组看做一个堆(所以代码上没有什么内容)
 >2.2 对堆进行第一次调整:  假设我们要从小到大排列,那么就构造一个大顶堆
 >2.3 交换首末元素:  构造完大顶堆的意义就在于把最大的放到了根部,那么我们交换数组中第一个和最后一个,这样最后一个就是最大的
 >2.4 再次进行调整:  这时我们舍弃数组的最后一个元素就好,针对前面几个再构建大顶堆

>2.构建大顶堆的代码分析:

>1.我们先从这个完全二叉树(堆)的第一个(从右到左,从下到上的顺序)非叶子节点开始,所谓非叶子结点,简单来讲就是指还具有分支的节点,你把堆看成一棵树,没有分支的节点就叫做叶子
公式就是: i=n/2-1  
>2.接下来构造关于这个节点的大顶堆,我们要从它的左边的子节点开始,然后两个子节点比较,找到最大的,然后和这个节点比较,反正意思就是从这三个里面找到最大的那个,然后放在节点处
找子节点的公式  k=i/2+1
>3.然后就这样一步一步从右到左,从下到上构造,完成时,最大的就在顶部了
>4.当在最后一步,当构建第一个元素的顶堆时,我们要知道,它下面的几个元素的顶堆都是已经构造好了,唯一能破坏的就是,当这个元素小于它下面两个元素的其中一个时,交换,那么谁交换,i就指向了子节点的那个,然后再重新构造顶堆


好了,以上就是堆排序的主要内容,下面我们来看看这道题怎么破:

代码:

#include<stdio.h>
int a[12];
int m; 
 void heapsort(int arr[]);//long long 和 int的问题 
 void heapadjust(int arr[],int i,int len);
 void swap(int arr[],int i,int j);
 int main(){
    int n;  
    int k=0;
    int temp;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) {//注意题目要求,我们根本就不需要对n个数据进行排序,因为我们最后只要输出不大于10个的人数就行了 
        scanf("%d",&temp);
         if(k<m)
          a[k++]=temp;//先把m个数据读完了 
         else
            {
            int min=0;
            for(int i=1;i<m;i++)    
               if(a[min]>a[i])
                min=i;//找出m个里面最小的 
            if(a[min]<temp)
                a[min]=temp;//把最小的踢掉                             
             }
            }
    heapsort(a);
    for(int i=m-1;i>0;i--)
        printf("%d ",a[i]);                 
    printf("%d",a[0]);
    printf("\n");
 }
 void heapsort(int arr[]){
    //首先第一次调整大顶堆,从第一个非叶子节点,从右到左,从下到上 
     for(int i=m/2-1;i>=0;i--){
        heapadjust(arr,i,m);
     }
     for(int i=m-1;i>0;i--){
        swap(arr,0,i);
        heapadjust(arr,0,i);        
     } //交换首末元素,然后再重新调整 
 }
 void heapadjust(int arr[],int i,int len){
    int temp=arr[i];
    for(int k=2*i+1;k<len;k=2*k+1){//从子节点的左边开始,每一次迭代,都往下走一层 
        if(k+1<len&&arr[k]<arr[k+1]){//判断右子节点是否存在,且判断左子节点是否小于右子节点,是的话,k就指向右子节点
             k++;   //这个地方考虑到后面k再怎么往下走一层的问题,就有点难度了,但是大顶堆性质 还是不变,只是换了个形式,可能跟算法时间复杂度有关? 
         }
        if(arr[k]>temp){
           arr[i]=arr[k];
           i=k;
        }
        else break;
     }
     arr[i]=temp;//这一步很好,减少了操作量,先不急着交换,因为已经知道下标和temp的值,我们到最后循环结束再放回来,就像快排里面的枢轴归位一样
      
     
 }
 void swap(int arr[],int i,int j){
    int temp=arr[i];
    arr[i]=arr[j];
    arr[j]=temp;
}
 
 /*
 Author : kevis
 Status: accepted
 Time:188ms
 Memery:152kib
 */

是不是认为我们只要对所有的输入进行一个排序就好了?
如果这样做的话,我们看看数据最多有10^6个,再快的排序,也不可能在200ms时间内把这些数据排好
所以我们要做一个小trick:
题目要求是只要前十个就好了,那么我们先把这些数据里面的前十个找出来就好了,也就是说,我们创建一个10个元素的数组,然后把这些数据里面的最大的十个没有顺序的放到这个数组里面,这样子我们很快就得到了前十个,然后对这个10个进行排序就好了

这是一种很重要的思想!!!

如果排序的话,我们要对庞大的数据反反复复操作多次,而如果只要找最大那几个的话,我们只需要遍历一遍这些数据就好。

比如说我找一个最大的,那就一个个比,然后遍历一遍就能找到最大的,找两个最大的,就从输入里面得到一个数,然后遍历两个元素的数组,找到最小的那个,然后和输入的比较,然后看情况交换,这样子我们就可以忽略对那些小的元素的重复操作

注意我们要知道数组里面最小的那个再和输入比较,不是把输入直接和元素比较,然后小的话就交换,这样子是可能找不到最大的几个的

问题:

1.我们能用快排吗(为什么要用堆排序)
2.但是如果我们要找前1000个,或者更大,时间开销肯定会变大,有没有一个临界点就是我直接排序比先找到前几个会更好呢?

相关文章

  • 数据结构实验之排序四:寻找大富翁(堆排序)

    数据结构实验之排序四:寻找大富翁 Time Limit: 200MS Memory Limit: 512KB Pr...

  • 堆排序-数据结构实验之排序四:寻找大富翁

    Problem Description 2015胡润全球财富榜调查显示,个人资产在1000万以上的高净值人群达到2...

  • 堆排序

    转载:图解排序算法(三)之堆排序 预备知识 堆排序 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选...

  • 2018-06-30

    排序算法之堆排序 堆排序是利用堆的数据结构而设计的一种排序算法,堆排序是一种选择排序。可以利用数组的特点快速定位制...

  • 排序算法-堆排序

    参考: Java排序算法(五):堆排序 【算法与数据结构】图说堆排序 【数据结构】排序算法:希尔、归并、快速、堆排...

  • 算法与数据结构(六):堆排序

    title: 算法与数据结构(六):堆排序tags: [算法与数据结构, C语言, 堆排序]date: 2019-...

  • java堆排序

    什么是堆排序:图解堆排序堆排序:利用了堆这种数据结构堆数据结构:特殊的完全二叉树,因为具有以下的特点:1)每个结点...

  • 堆排序

    预备知识 堆排序 堆排序(heap sort)是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的...

  • 数组-堆排序

    采用堆排序方式对数组进行排序 堆排序百科:堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法.堆...

  • C++基础入门之模板堆排序(上):模板上的list的创造与操作

    整段源码链接C++的模板元堆排序 要点 组建数据结构list 组建对list的各种基本操作 堆排序中组建堆排序个个...

网友评论

    本文标题:堆排序-数据结构实验之排序四:寻找大富翁

    本文链接:https://www.haomeiwen.com/subject/tkifoftx.html