##剑指offer 5.2 时间效率2 -最小的K个数

面试题30: 最小的K个数

解法一:最直观的解法莫过于将n个数按升序排列后输出前k个。但是就效率来看,这种方法并不是最理想的。

 一种改进方法是借助快速排序中对数组的划分,以第k个元素对数组进行划分,使得比第k个数字小的数字都在其左边,比其大的数字都在它的右边。O(N)

        算法的时间复杂度是O(n),是一种比较高效的解法。但是上述算法存在的问题是修改了原始数组数据,因此在不允许修改原始数据的情况下的应用就会受到限制。

void Swap(int &a, int &b) 
{ 
    int c = a; a = b; b = c; 
} 
 
int Partition(int data[], int length, int start, int end) 
{ 
    if(data == NULL || length <= 0) 
        return -1; 
 
    int index = start - 1; 
    for(int i = start; i < end; ++i) 
    { 
        if(data[i] < data[end]) 
        { 
            ++index; 
            swap(data[i], data[index]); 
        } 
    } 
    ++index; 
    Swap(data[index], data[end]); 
    return index; 
} 
 
void GetKLeastNumbers(int data[], int length, int result[], int k) 
{ 
    if(data == NULL || length <= 0 || result == NULL || k <= 0) 
        return; 
 
    int start = 0, end = length - 1; 
    int index = Partition(data, length, start, end); 
 
    while(index != k - 1) // 第k个数作为数组划分依据 
    { 
        if(index > k - 1) 
            index = Partition(data, length, start, index - 1); 
        else 
            index = Partition(data, length, index + 1, end); 
    } 
    for(int i = 0; i < k; ++i) 
        result[i] = data[i]; 
} 

解法二在不修改原始数据的前提条件下,我们可以创建一个大小为k的容器存放最小的k个数。再对n个整数进行遍历,如果容器中的数少于k个,则直接把读入的数存入容器;如果容器中的数大于等于k个,同时当前读入的数小于容器中最大的数,那么删除容器中最大的数,将该数读入容器,否则不做操作。

为了确保快速删除容器中最大的数,容器数据的存储可以考虑使用最大堆。由于最大堆的根结点的值大于它的子树中任意结点的值,因此可以在O(1)得到已有k个数中的最大者,删除和插入操作的时间则为O(lgk)。对n个数重复最大堆的插入、删除操作总的算法时间复杂度为O(nlgk)。

下面是使用STL multiset完成上述算法的实现代码。


上述两种方法都实现求最小k个数,虽然第二种方法比第一种方法慢一些,但是它并不修改原始数据,另外比较适用于海量数据处理的情形。因此两种方法各有优劣,实际应用时视具体情况确定算法的选用。

typedef multiset<int, greater<int> >  IntHeap;

///
// find k least numbers in a vector
///
void FindKLeastNumbers
(
      const vector<int>& data,               // a vector of data
      IntHeap& leastNumbers,                 // k least numbers, output
      unsigned int k                              
)
{
      leastNumbers.clear();

      if(k == 0 || data.size() < k)
            return;

      vector<int>::const_iterator iter = data.begin();
      for(; iter != data.end(); ++ iter)
      {
            // if less than k numbers was inserted into leastNumbers
            if((leastNumbers.size()) < k)
                  leastNumbers.insert(*iter);

            // leastNumbers contains k numbers and it's full now
            else
            {
                  // first number in leastNumbers is the greatest one
                  IntHeap::iterator iterFirst = leastNumbers.begin();

                  // if is less than the previous greatest number 
                  if(*iter < *(leastNumbers.begin()))
                  {
                        // replace the previous greatest number
                        leastNumbers.erase(iterFirst);
                        leastNumbers.insert(*iter);
                  }
            }
      }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值