从n个书中找出最小的k个数。
解法有如下几种:
(1)全部排序之后找前k个,时间为O(n*n)
(2)部分排序。维护一个大小为k的数组a[k],遍历n个数的过程中更新数组a。时间为O(k*n)
(3)维护一个容量为k 的大顶堆,遍历过程中更新堆。时间为O(nlogk)。
(4)下面着重介绍一种平均时间复杂度为O(n)的算法,线性选择算法。该算法的思想类似于快速排序。
选取数组A中一个元素作为主元(pivot)v,用快速排序的过程将A(除v)分成两个集合A1和A2。
- 如果k<=|A1|,那么第k个最小元素在A1内,此时返回quickSelect(A1, k)
- 如果k=1+|A1|,那么主元就是最小的第k个元素,返回
- 否则,这第k个最小元素在A2中,即A2中的第k-|A1|-1个最小元素,此时返回quickSelect(A2, k-|A1|-1)
class QuickSelectMinK { public: void quickSelect(int arr[], int k, int left, int right) { if(left < right) { int pivot = arr[left]; int i = left, j = right - 1; for(;;) { while(arr[++i] > pivot){} while(arr[--j] < pivot){} if(i < j) { swap(arr[i], arr[j]); } else{ break; } } //重置主元?? swap(arr[i], arr[right-1]); if( k <= i ) { quickSelect(arr, k, left, i - 1); }else if( k > i + 1 ) { quickSelect(arr, k, i + 1, right); } } else { //如果left > right, 利用插入排序调整一下 InsertSort(arr + left, right - left + 1); } } }
此快速选择算法类似于快速排序的划分方法。利用主元把数组划分为A1和A2两个部分,A1<= pivot <=A2 。如果要找的k个元素小于A1 的元素个数,则返回A1中较小的k个元素;否则返回A1中所有元素和A2中较小的k-|A1|个元素,平均情况下能做到O(n)的时间复杂度。