例题:输出前m大的数

题目描述

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

题目分析

思路:用分治处理,把前m大的都弄到数组最右边,然后对这最右边m个元素排序,再输出。关键就是如何在O(n)时间内把前m大的都弄到数组最右边。

引入操作arrangeRight(k): 把数组前k大的都弄到最右边。

  • 设key=a[0], 将key挪到适当位置,使得比key小的元素都在key左边,比key大的元素都在key右边(线性时间完成)
  • 选择数组的前部或后部再进行arrangeRight操作
    • a = k done
    • a > k 对最右边a-1个元素再进行arrangeRigth(k)
    • a < k 对左边b个元素再进行arrangeRight(k-a)

20-输出前m大的数_算法基础

代码实现

#include<iostream>
using namespace std;
typedef int ElementType;
//用治处理  O(n+mlog(m))
//用O(n)把m大个数挪到右边,然后对这m个数排序再输出
void QuickSort(ElementType a[], int left, int right)
{
    if (left >= right)
        return;
    int privot = a[left];
    int low = left, high = right;
    while (low != high)
    {
        while (high > low && a[high] >= privot) --high;
        swap(a[low], a[high]);
        while (low < high && a[low] <= privot) ++low;
        swap(a[low], a[high]);
    }
    QuickSort(a, left, low - 1);
    QuickSort(a, low + 1, right);
}

//把数组前k大的都弄到最右边
void arrangeRight(ElementType* a, int left, int right, int k)
{
    if (left >= right)
        return;
    if (k == right - left + 1)
        return;
    int low = left;
    int high = right;
    int privot = a[left];

    while (low != high)
    {
        while (low < high && a[high] >= privot) --high;
        swap(a[low], a[high]);
        while (low < high && a[low] <= privot) ++low;
        swap(a[low], a[high]);
    }
    if (right - low + 1 == k)             //如果右边的数刚好为e-i+1为k个
        return;
    else if (right - low + 1 > k)         //右边的数大于k个
    {
        arrangeRight(a, low + 1, right, k);
    }
    else                                //右边的数小于k个,需要左边取出k - (e-i+1)个
    {
        arrangeRight(a, left, low - 1, k - right + low - 1);
    }
}

void printM(ElementType* a, int n, int m)
{
    arrangeRight(a, 0, n - 1, m);           //O(N)时间内先把m大的都弄到数组最右边
    QuickSort(a, n - m, n - 1);             //对这m个数排序
    while (m--)
    {
        cout << a[--n] << " ";
    }
    cout << endl;
}

int main()
{
    int a[] = { 11, 4, 14, 6, 1, 12, 8, 9, 3, 13, 7, 10, 0, 5, 2, 15 };
    int len = sizeof(a) / sizeof(a[0]);
    printM(a, len, 6);
    return 0;
}

复杂度分析:

将前m大的都弄到数组最右边的时间:
T(n) = T(n/2) + a*n
= T(n/4) + a*n/2 + a*n
= T(n/8) + a*n/4 + a*n/2 + a*n
= …
= T(1) + … + a*n/8 + a*n/4 + a*n/2 + a*n
< 2*a*n
即O(n)

将前m大的元素排序,复杂度为mlogm,所以整体的时间复杂度为:复杂度O(n+mlogm)