寻找最小的k个数

从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)的时间复杂度。

点赞