数组中求第K大数的实现方法

所属分类: 软件编程 / C 语言 阅读数: 104
收藏 0 赞 0 分享
问题:有一个大小为n的数组A[0,1,2,…,n-1],求其中第k大的数。
该问题是一个经典的问题,在《算法导论》中被作为单独的一节提出,而且其解决方法很好的利用了分治的思想,将时间复杂度控制在了O(n),这多少出乎我们的意料,此处暂且不表。
该问题还可以变形为:有一个大小为 n的数组A[0,1,2,…,n-1],求其中前k大的数。
一字之差,原问题是“第k大”,变形的问题是“前k大”,但是平均时间复杂度却都可以控制在O(n),这不由得让人暗暗称奇。

我们先分析原问题:有一个大小为 n的数组A[0,1,2,…,n-1],求其中第k大的数。
我们先取特例,令k=1,那么就是取最大的数,只要扫描一遍数组就可以确定该值,如果k=2,则扫描两边数组就可以确定第二大的数,依此类推下去,时间复杂度是O(k*n),如果k跟n是一个数量级,那么时间复杂度就是O(n*n)了,显然不是最优的解法。

考虑分治法,难点在于如何将该问题分解为两个子问题。
快速排序最基础的一步:
随机取某一个数x,将其与数组末尾元素交换,然后将比其小的数交换至前,比其大的数交换至后。
这一步使某一数组的快速排序问题分解成两个子数组的排序问题,现在我们就依此来解决取第k大的数这个问题。
设数组下表从0开始,至n-1结束。
1、 随机取某个数,将其与数组末尾元素交换。
a)        idx=rand(0,n-1);生成[0,n-1]间的随机数。
b)        Swap(array[idx], array[n-1]);
2、 用末尾元素x,将比x小的数交换至前,比x大的数交换至后,并返回此时x在数组中的位置mid。
3、 如果mid==n-k,那么返回该值,这就是第k大的数。
如果mid>n-k,那么第k大的数在左半数组,且在左半数组中是第k-(n-mid)大的数。
如果mid<n-k,那么第k大的数在右半数组,而且仍然是第k的数。
复制代码 代码如下:

#include "iostream"
using namespace std;
int random_partion(int *p, int n)
{
     int idx=rand()%n;
     swap(p[idx], p[n-1]);
     int i=-1;    //i表示最后一个小于p[n-1]的元素的位置
     int j=0;     //j用来扫描数组
     for(j=0; j<n; j++)
     {
            //将小于p[n-1]的数交换到前半部分
            if(p[j]<p[n-1])
            {
    swap(p[++i], p[j]);
            }
     }
     swap(p[++i], p[n-1]);
     return i;
}
int getMaxK(int *p, int n, int k)
{
 int mid;
     if(k<=0)
            return -1;
     if(n<k)
            return -1;
  mid=random_partion(p, n);   //对原数组进行一次划分
     if(mid == n-k)      //如果mid==n-k,那么返回该值,这就是第k大的数
   return p[mid];
     else if(mid<n-k)
   return getMaxK(p+mid+1, n-mid-1, k);  //如果mid<n-k,那么第k大的数在右半数组,而且仍然是第k大数
     else
   return getMaxK(p, mid, k-(n-mid));   //如果mid>n-k,那么第k大的数在左半数组,且在左半数组中是第k-(n-mid)大的数
}
int main(void)
{
 int num,a[] = {12012, 3, 945, 965, 66, 232, 65, 7, 8, 898, 56, 878, 170, 13, 5};
 num=getMaxK(a, 15, 4);
 printf("%d\n",num);
 system("pause");
 return 0;
}

更多精彩内容其他人还在看

从汇编看c++中变量类型的深入分析

本篇文章是对c++中的变量类型进行了详细的分析介绍。需要的朋友参考下
收藏 0 赞 0 分享

从汇编看c++的默认析构函数的使用详解

本篇文章是对c++中默认析构函数的使用进行了详细的分析介绍。需要的朋友参考下
收藏 0 赞 0 分享

基于c++中的默认拷贝函数的使用详解

本篇文章对c++中默认拷贝函数的使用进行了详细的分析介绍。需要的朋友参考下
收藏 0 赞 0 分享

解析c++中的默认operator=操作的详解

本篇文章是对c++中的默认operator=操作的应用进行了详细的分析介绍。需要的朋友参考下
收藏 0 赞 0 分享

解析c++中参数对象与局部对象的析构顺序的详解

本篇文章是对c++中参数对象与局部对象的析构顺序进行了详细的分析介绍,需要的朋友参考下
收藏 0 赞 0 分享

深入c++中临时对象的析构时机的详解

本篇文章对c++中临时对象的析构时机进行了详细的分析介绍,需要的朋友参考下
收藏 0 赞 0 分享

解析内存对齐 Data alignment: Straighten up and fly right的详解

对于所有直接操作内存的程序员来说,数据对齐都是很重要的问题.数据对齐对你的程序的表现甚至能否正常运行都会产生影响
收藏 0 赞 0 分享

深入内存对齐的详解

本篇文章是对内存对齐进行了详细的分析介绍,需要的朋友参考下
收藏 0 赞 0 分享

深入C语言把文件读入字符串以及将字符串写入文件的解决方法

本篇文章是对C语言把文件读入字符串以及将字符串写入文件的方法进行了详细的分析介绍,需要的朋友参考下
收藏 0 赞 0 分享

深入Windows下的回车是回车换行(\r\n)还是换行回车(\n\r)的详解

本篇文章对Windows下的回车是回车换行(\r\n)还是换行回车(\n\r)进行了详细的分析介绍,需要的朋友参考下
收藏 0 赞 0 分享
查看更多