分治法:前m大的数

问题描述

给定一个数组包含n个元素,统计前m大的数并且把这m个数从大到小输

问题分析

如果先排序再找前m大的,时间复杂度为O(nlogn)

用分治处理:复杂度 O(n+mlogm)
思路:把前m大的都弄到数组最右边,然后对这最右边m个元素排序,
再输出
关键 :O(n)时间内实现把前m大的都弄到数组最右边

算法实现

/*
如何将前k大的都弄到最右边 
1)设key=a[0], 将key挪到适当位置,使得比key小的元素都在
key左边,比key大的元素都在key右边(线性时间完成) 

2) 选择数组的前部或后部再进行 arrangeRight操作



*/


#include<iostream>
#include<stdlib.h>
using namespace std;

//设k为a[0],将比k大的挪到k右边,返回k的位置下标 
int QuickArray(int a[],int first,int end){
    int i=first,j=end;
    int k=a[first];
    while(i<j){
        while(i<j&&a[j]>=k){
            j--;
        }
        if(i<j){
            a[i]=a[j];
            i++;
        }
        while(i<j&&a[i]<k){
            i++;
        }
        if(i<j){
            a[j]=a[i];
            j--;
        }
    }
    a[i]=k;
    return i;
}

//对前m大的数快速排序,也可以用系统自带的 qsort() ,O(nlogn)
void QuickSort(int a[],int first,int end){
    if(first<end){
        int key=QuickArray(a,first,end);
        QuickSort(a,key+1,end);
        QuickSort(a,first,key-1);   
    }
}

//分治法:将k两边的数分配好,时间复杂度O(n) 
void ArrangeRight(int a[],int first,int end,int k,int n){
    int key=QuickArray(a,first,end);
    int length=end-key; //长度不加元素a[key]
    if(length==k){
        return;
    }else if(length<k){
        ArrangeRight(a,first,key,k-length,n);
    }else if(length>k){
        ArrangeRight(a,key+1,end,k,n);
    }   
}

//test
int main(){
    int a[8]={2,7,1,8,2,4,5,9};
    int n=sizeof(a)/sizeof(int);
    int k=2;
    ArrangeRight(a,0,n-1,5,n);
    QuickSort(a,n-k+1,n-1);
    for(int i=n-1;i>=n-k;i--){
        cout<<a[i]<<" ";
    }
    cout<<endl;
    return 0;
}

运行结果

这里写图片描述

猜你喜欢

转载自blog.csdn.net/a_chen666/article/details/77712797