牛客网剑指offer第29题——最小的k个数
题目:
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,
分析:
思路一:我们可以利用STL库的sort函数,先将数组排序(时间复杂度O(nlogn),再选前K个数。
代码如下:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { vector<int>res; if(k == 0 || input.size() == 0 || k > input.size()) return res; sort(input.begin(),input.end()); for(int i =0;i < k;i++) res.push_back(input[i]); return res;
代码很简单,而又朴实无华。
但是很明显有些浪费,因为我们并不关心其余N-K个元素的顺序,上述做法反而做了多余的工作。
那么更好的思路是:采用快速排序的思想:找前K个值。
毫无疑问的是,快速排序的思想是很朴素的:让正确的元素出现在正确的位置:什么意思?就是:如果一个数;它前面的所有元素都小于它(我们并不关心它前面的元素是否拍好序),其后面额元素都大于它,那么这个数就处在正确的位置;举例:3 1 2 4 7 9 6,对于数字4,它前面的元素都小于它,它后面的元素都大于它,因此4出于正确的位置(正确的位置的含义是:即使这个数组完全排好序,这个数的位置索引仍然是不变的)。那么上述问题就变成了:我们让一组数据的前K个数,使得他们都处在一个正确的位置。
1 vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { 2 vector<int>res; 3 if(k == 0 || input.size() == 0 || k > input.size()) 4 return res; 5 int start =0; 6 int end = input.size()-1; 7 int bounder = partition(input,start,end); 8 while(bounder != k-1) 9 { 10 if(bounder < k-1) 11 start = bounder+1; 12 else 13 end = bounder-1; 14 bounder = partition(input,start,end); 15 } 16 for(int i =0;i < k;i++) 17 res.push_back(input[i]); 18 return res; 19 } 20 int partition(vector<int>& input, int l,int r) 21 { 22 int tmp = input[l]; 23 while(l < r) 24 { 25 while( l < r && input[r] > tmp) r--; 26 swap(input[l],input[r]); 27 while(l < r && input[l] <= tmp) l++; 28 swap(input[l],input[r]); 29 } 30 input[l] = tmp; 31 return l; 32 }
解释代码:
第20——32行的代码:称之为partition,其作用是:对于一个随机的数组vector,我们处理其最左端的数据。也就是input[0],最终使得input[0]处在一个正确的位置(也就是让其前面的数比它小,后面的数比它大),并返回这个位置。
我们来解读一下:这个位置bounder表达的含义:这个位置前的数都比这个位置的数小,后面的数都比它大;换句话说:前面的bounder-1个是就是这个数组前bounder-1个最小的数!!!
现在主程序也就是第2行到第18行的代码,如果bounder大于k了,说明bounder前面的数多于k个,很明显我们要缩小partition的范围;而bounder小于k,说明bounder前面的数还不够k个,我们需要进一步搜索。总而言之,我们需要使得找到的bounder正好使得bouder 前面的数和bounder处的数。一共有k个。