1. 程式人生 > >資料結構實驗之排序四:尋找大富翁(堆排序)

資料結構實驗之排序四:尋找大富翁(堆排序)

資料結構實驗之排序四:尋找大富翁
Time Limit: 200 ms Memory Limit: 512 KiB
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

先說一下這題的思路吧,首先,這道題如果直接對所有資料進行隊排的話是會超時的,所以這裡有一個更加巧妙的方法,就是我們可以先把前m個數據直接放進堆裡,然後我們維護一個小頂堆,當輸入完m個數據之後每當我們輸入一個數據,我們拿這個資料與當前堆的堆頂(也就是堆裡的最小值)進行對比,如果大於堆頂,就讓把這個值賦值給堆頂,然後我們對這個堆進行調整,維護一個小頂堆,這樣當所有值都輸入完之後,這個小頂堆一定是最大的m個數,這時候我們只需要對這個堆進行排序就可以了。

堆排的思路(拿小頂堆來說,排序後是由大到小的順序):
1.我們知道的是這個小頂堆的堆頂一定是整個堆裡最小的值,我們把它和堆裡面最後一個元素進行交換。
2.這樣這個堆的最後一個元素就是最小的元素了。
3.然後我們把最後一個元素以外的堆再次調整為一個小頂堆。
4.重複上述操作。

#include<stdio.h>
#include<string.h>
int size,a[100000];		//size代表堆裡面有多少元素
void adjust(int n)		//調整函式(維護小頂堆)
{
    int i,p,t;
    for(i=1; i*2<=n; i=p)		//這裡的結束條件為當前節點沒有兒子
    {
        if(a[i*2+1]<a[i*2]&&i*2+1<=n)		//我們需要在左右兒子裡面找到一個
            p=i*2+1;						//較小的值,並與該節點比較,注意
        else
//這裡要判斷是否有右兒子 p=i*2; if(a[i]>a[p]) { t=a[i]; a[i]=a[p]; a[p]=t; } else break; } } void cp(int x) //僅僅對於這題而言可以這樣做,這裡是m個數據之後每輸入一個數據 { //就進行比較,如果大於堆頂元素就進行賦值操作並維護 if(x>a[1]) { a[1]=x; adjust(size); } } void hsort() //這裡是最後一步,輸入完所有資料之後,對這個小頂堆 { //進行堆排序,因為題目要求從大到小輸出 int i,t; for(i=size; i>=1; i--) { t=a[i]; //前面思路里提到的堆排步驟的實現 a[i]=a[1]; a[1]=t; adjust(i-1); } } int main() { int n,m,i,x; scanf("%d %d",&n,&m); for(i=1; i<=m; i++) { scanf("%d",&x); a[++size]=x; } adjust(size); //可能會想到,前m個數據根本不需要先調整,因為我們在後 for(i=m+1; i<=n; i++) //面插入一個數之後也會進行調整,所以這裡是多餘的,但 { //仔細一想的話,如果我們在這裡不進行調整的話,我們是無 scanf("%d",&x); //法保證後面的值與堆內最小值比較的。我們當然可以用插入函式 cp(x); //來避免此操作。 } hsort(); //堆排 for(i=1;i<=m;i++) { if(i==m) printf("%d\n",a[i]); else printf("%d ",a[i]); } return 0; }

寫的並不仔細,如果想看具體堆的操作可以看這裡