输出前m的的数


例题一:输出前m大的数:

描述
给定一个数组包含n个元素,统计前m大的数并且把这m个数从大到小输出。
输入
第一行包含一个整数n,表示数组的大小。n < 100000。 第二行包含n个整数,表示数组的元素,整数之间以一个空格分开。每个整数的绝对值不超过100000000。 第三行包含一个整数m。m < n。
输出
从大到小输出前m大的数,每个数一行。

方法1:对全体的数字进行归并排序,输出前m大的数(O(nlogn))代码:

#include 
using namespace std;
int a[1000000],temp[1000000];
void Merge(int *a,int s,int e,int m,int *tmp){
    int pb = 0;
    int p1 = s,p2 = e;
    while(p1<=m&&p2<=e){
        if(a[p1]<a[p2]){
        
            tmp[pb++] = a[p1++];
        }else{
            tmp[pb++] = a[p2++];
        }
    }
    while(p1<=m){
        tmp[pb++] = a[p1++];
        
    }
    while(p2<=e){
        tmp[pb++] = a[p2++];
    }
    for(int i = 0;i1;i++){
        a[s+i] = tmp[i];
    }
}

void MergeSort(int *a,int s,int e,int *tmp){
    if(s < e)
    {
        int m = s+(e-s)/2;
        MergeSort(a,s,m,tmp);
        MergeSort(a,m+1,e,tmp);
        Merge(a,s,m,e,tmp);
    }
}

int main(){
    int n,m;
    while(cin>>n>>m){
        for(int i = 0;i){
            cin>>a[i];
        }
        MergeSort(a,0,n-1,temp);
        for(int i = n-m;i){
            if(i == n-m){
                cout<<a[i];
            }else{
                cout<<","<<a[i];
            }
        }
        cout<<endl;
    }
    return 0;
}

方法二:
用分治处理:复杂度 O(n+mlogm)
思路:把前m大的都弄到数组最右边,然后对这最右边m个元素排序,再输出
关键 :O(n)时间内实现把前m大的都弄到数组最右边
引入操作 arrangeRight(k): 把数组(或数组的一部分)前k大的都弄到最右边
如何将前k大的都弄到最右边
1)设key=a[0], 将key挪到适当位置,使得比key小的元素都在key左边,比key大的元素都在key右边(线性时间完成)——也就是使用一次快速排序的过程
2) 选择数组的前部或后部再进行 arrangeRight操作

a = k done 表示完成此过程
a > k 对此a个元素再进行arrangeRigth(k)
a < k 对左边b个元素再进行arrangeRight(k-a)

(这个地方引入图像加深思考)

 有a个数是比key大的,b个是比key更小的,现在开始比较前k个数在这个数组里面的分布,有三种情况,第一种情况就是k==a这个就是说明刚好前a个大刚好满足题目的要求,如果a>k说明只要对右边再次找arrangeRight(k),如果是a

#include 
using namespace std;
int a[1000000],temp[1000000];
void Merge(int *a,int s,int e,int m,int *tmp){
    int pb = 0;
    int p1 = s,p2 = e;
    while(p1<=m&&p2<=e){
        if(a[p1]<a[p2]){
        
            tmp[pb++] = a[p1++];
        }else{
            tmp[pb++] = a[p2++];
        }
    }
    while(p1<=m){
        tmp[pb++] = a[p1++];
        
    }
    while(p2<=e){
        tmp[pb++] = a[p2++];
    }
    for(int i = 0;i1;i++){
        a[s+i] = tmp[i];
    }
}

void MergeSort(int *a,int s,int e,int *tmp){
    if(s < e)
    {
        int m = s+(e-s)/2;
        MergeSort(a,s,m,tmp);
        MergeSort(a,m+1,e,tmp);
        Merge(a,s,m,e,tmp);
    }
}

void arrangeRight(int *a,int s,int e,int k){
    if(s>=e){
        return 0;
    }
    int t = a[s];
    int i =s,j = e;
    while(i != j){
        while(i < j && t <= a[j]){
            --j;
        }
        swap(a[i],a[j]);
        while(i= a[i]){
            i++;
        }
        swap(a[i],a[j]);
    }
    //这个i结束是在中间 
    int num = e-i+1;
    if(num == k){
        return;
    }
    if(num>k){
        arrangeRight(a,i+1,e,k);
    }
    if(num<k){
        arrangeRight(a,s,i-1,k-num);
    } 
    
    
    
}

int main(){
    int n,m;
    while(cin>>n>>m){
        for(int i = 0;i){
            cin>>a[i];
        }
        arrangeRight(a,0,n-1,m);
        MergeSort(a,n-m,n-1,temp);
        for(int i = n-m;i){
            if(i == n-m){
                cout<<a[i];
            }else{
                cout<<","<<a[i];
            }
        }
        cout<<endl;
    }
    return 0;
}

这个地方要注意一下,arrangeRight这个函数做的只是把所有前m大的数放在右边。一旦总数量到达了m就会return。只要数据规模足够大的话后面的速度就会比前面快很多。

代码参考:(92条消息) 输出前m大的数_Jamence的博客-CSDN博客

相关