寻找最小的k个数(update)

类似编程之美中寻找最大的k个数

解法一:

题目没有要求最小的k个数有序,也没要求最后n-k个数有序。既然如此,就没有必要对所有元素进行排序。这时,咱们想到了用选择或交换排序,即:

1、遍历n个数,把最先遍历到的k个数存入到大小为k的数组中,假设它们即是最小的k个数;
2、对这k个数,利用选择或交换排序找到这k个元素中的最大值kmax(找最大值需要遍历这k个数,时间复杂度为

O(k)

);

3、继续遍历剩余n-k个数。假设每一次遍历到的新的元素的值为x,把x与kmax比较:如果

x < kmax

,用x替换kmax,并回到第二步重新找出k个元素的数组中最大元素kmax‘;如果

x >= kmax

,则继续遍历不更新数组。

每次遍历,更新或不更新数组的所用的时间为

O(k)



O(0)

。故整趟下来,时间复杂度为

n*O(k)=O(n*k)

解法二:快速选择算法

Quicksort can be modified to solve the selection problem, which we have seen in chapters 1 and 6. Recall that by using a priority queue, we can find the kth largest (or smallest) element in O(n + k log n). For the special case of finding the median, this gives an O(n log n) algorithm.

Since we can sort the file in O(nlog n) time, one might expect to obtain a better time bound for selection. The algorithm we present to find the kth smallest element in a set S is almost identical to quicksort. In fact, the first three steps are the same. We will call     this algorithm quickselect(叫做快速选择). Let |Si| denote the number of elements in Si(令|Si|为Si中元素的个数). The steps of quickselect are(快速选择,即上述编程之美一书上的,思路4,步骤如下):

1. If |S| = 1, then k = 1 and return the elements in S as the answer. If a cutoff for small files is being used and |S| <=CUTOFF, then sort S and return the kth smallest element.
    2. Pick a pivot element, v (- S.(选取一个枢纽元v属于S)
    3. Partition S - {v} into S1 and S2, as was done with quicksort.
(将集合S-{v}分割成S1和S2,就像我们在快速排序中所作的那样)

4. If k <= |S1|, then the kth smallest element must be in S1. In
this case, return quickselect (S1, k). If k = 1 + |S1|, then the pivot
is the kth smallest element and we can return it as the answer.
Otherwise, the kth smallest element lies in S2, and it is the (k - |S1| -
1)st smallest element in S2. We make a recursive call and return
quickselect (S2, k - |S1| - 1).
(如果k<=|S1|,那么第k个最小元素必然在S1中。在这种情况下,
返回quickselect(S1,k)。如果k=1+|S1|,那么枢纽元素就是第k个最小元素,即找到,直接返回它。否则,这第k个最小元素就在S2
中,即S2中的第(k-|S1|-1)个最小元素,我们递归调用并返回quickselect(S2,k-|S1|-1))。

In contrast to quicksort, quickselect makes only one recursive call
instead of two. The worst case of quickselect is identical to that of
quicksort and is O(n2). Intuitively, this is because quicksort‘s worst
case is when one of S1 and S2 is empty; thus, quickselect(快速选择) is not
really saving a recursive call. The average running time, however, is
O(n)(不过,其平均运行时间为O(N)。看到了没,就是平均复杂度为O(N)这句话). The analysis is similar to quicksort‘s and is left as an exercise.

The implementation of quickselect is even simpler than the abstract
description might imply. The code to do this shown in Figure 7.16. When
the algorithm terminates, the kth smallest element is in position k.
This destroys the original ordering; if this is not desirable, then a
copy must be made.

  • 选取S中一个元素作为枢纽元v,将集合S-{v}分割成S1和S2,就像快速排序那样

    • 如果k <= |S1|,那么第k个最小元素必然在S1中。在这种情况下,返回QuickSelect(S1, k)。
    • 如果k = 1 + |S1|,那么枢纽元素就是第k个最小元素,即找到,直接返回它。
    • 否则,这第k个最小元素就在S2中,即S2中的第(k - |S1| - 1)个最小元素,我们递归调用并返回QuickSelect(S2, k - |S1| - 1)。

此算法的平均运行时间为O(n)。

示例代码如下:

 1 //QuickSelect 将第k小的元素放在 a[k-1]
 2 void QuickSelect( int a[], int k, int left, int right )
 3 {
 4     int i, j;
 5     int pivot;
 6
 7     if( left + cutoff <= right )
 8     {
 9         pivot = median3( a, left, right );
10         //取三数中值作为枢纽元,可以很大程度上避免最坏情况
11         i = left; j = right - 1;
12         for( ; ; )
13         {
14             while( a[ ++i ] < pivot ){ }
15             while( a[ --j ] > pivot ){ }
16             if( i < j )
17                 swap( &a[ i ], &a[ j ] );
18             else
19                 break;
20         }
21         //重置枢纽元
22         swap( &a[ i ], &a[ right - 1 ] );
23
24         if( k <= i )
25             QuickSelect( a, k, left, i - 1 );
26         else if( k > i + 1 )
27             QuickSelect( a, k, i + 1, right );
28     }
29     else
30         InsertSort( a + left, right - left + 1 );
31 }
时间: 2024-08-03 00:19:56

寻找最小的k个数(update)的相关文章

寻找最小的K个数

寻找最小的K个数 题目描述:查找最小的K个数 题目:输入n个整数,输出其中最小的K个数 例如,输入1.2.3.4.5.6.7.8这8个数字,则最小的4个数字为1.2.3.4. 第一节.各种思路,各种选择 要求一个序列中最小的K个数,按照惯有的思维方式,很简单,先对这个序列从小到大排序,然后输出前面的最小的K个数即可: 至于选取什么样的排序方法,第一时间应该想到的是快速排序,我们知道,快速排序平均时间复杂度为O(nlogn),然后再遍历序列中前K个元素输出,即可,总的时间复杂度为O(nlogn +

寻找最小的k个数(四种方法)

1 使用从大到小的优先队列保存最小的K个数,每次取出K个数之后的其余数和堆顶元素比较,如果比堆顶元素小,则将堆顶元素删除,将该元素插入 void topK(int arr[],int n,int k) { if(k>n) return; priority_queue<int> q; for(int i=0;i<n;++i) { if(q.size()<k) q.push(arr[i]); else { if(arr[i]<q.top()) { q.pop(); q.pu

寻找最小的k个数(大顶堆方法)

题目描述:查找最小的k个元素,输入n个整数,输出其中最小的k个. 一般的排序方法,如快排,时间复杂度为O(n*logn+k); 大顶堆方法,时间复杂度为O(k+(n-k)*logk); 如果建立k个元素的最小堆的话,那么其空间复杂度势为O(N),而建立k个元素的最大堆的空间复杂度为O(k); 当面对海量数据处理的时候,大顶堆的方法是较为靠谱的,并且可以在面试时短时间内完成代码. 1 class Solution { 2 public: 3 void Swap(int &a,int &b)

O(n)时间复杂度求最小的k个数和第k小的数

//思路:使用快速排序的patition函数来进行处理 时间复杂度为O(n) #include<iostream> #include<cassert> using namespace std; int partition(int *ar, int len, int low, int high) { int temp = ar[low]; while(low < high) { while(low < high && temp < ar[high])

寻找最大或最小的K个数

题目描述 :在好几亿个数据中找出最大或最小的K个数. 分析:这几亿的数据肯定不能一起加载到内存中去,更不能对这些数据直接进行排序,因此我们这里讲用数据结构中的 堆 来解决这个问题. 假定这里要从100000个数据中找出最大的100个数据,这样是为了描述方便,我们这里直接用一个数组来存储这个100000个数据,如果数据多达好几亿,则只需将这些数据放入文件中进行读写即可,这里为了描述问题方便就这样假定. 步骤: 取出这些数据中前100个数据,然后用这些数据建立一个小堆: 从第101个数据开始,每读取

找到n中最小的k个数

题目:n个数中,求最小的前k个数. 这道题在各个地方都看到过,在国内出现的频率也非常高. 解法: 1. brute force. O(k*n)复杂度: 2. sort.O(k+n*lgn)复杂度: 3. 最大堆.每次替代的是大小为k的最大堆的最大值.O(k+(n-k)lgk)复杂度. 4. 最小堆.和sort类似,只是建堆后只求前k次.O(n+k*lgn)复杂度.在网上看到一个优化,就是pop出第k小的数(堆顶)的时候,最多只需要调整k-1层(不需要调到堆底).所以可以优化到O(n+k^2).当

寻找最大的K个数

编程之美有一道考察多种排序的题目,题目如下: 有一个长度为N的无序数组,假定其中的每一个元素都各不相等,求其中最大的K个数. 作者对于此题目结合各种排序算法给出了五种解法思路. 解法一: 使用快速排序或堆排序对它们元素进行排序,整个排序的时间复杂度为O(N*log2N),然后取出前K个,时间复杂度为O(K),总时间复杂度O(N*log2N)+O(K)=O(N*log2N).根据题目要求,并不需要后N-KN-K个数有序,而只需要寻找最大K的个数,因此可以用选择排序和交换排序进行部分排序来筛选最大的

编程之美2.5 寻找最大的K个数

在一个数组中寻找最大的K个数,我们首先说一种非常简单的方法,利用快速排序中的分割算法,即我们经常看见的partition.这个函数会返回一个 int 类型的值,这个值代表的是前一半数字和后一半数字的分割点,前一半数字都小于等于后一半数字(递增排序),所以,我们只要找到相对应的分割点,即可以找到最大的K个数,或者最小的K个数,这就是利用线性方法可以完成任务的方法. 首先,给出函数声明: int DutPartition(int*, int, int); void DutFindMaxKInArra

编程之美之2.5 寻找最大的K个数

[题目] 有很多无序的数,从中找出最大的K个数.假定他们都不相等. [解法一] 如果数据不是很多,例如在几千个左右,我们可以排一下序,从中找出最大的K个数.排序可以选择快速排序或者堆排序 [cpp] view plaincopy #include<stdio.h> #include<stdlib.h> int cmp(const void *a,const void *b){ return *(int *)a - *(int *)b; } int main(){ int n,k;