Top-k 问题有多种解法。LeetCode #215 #347
k次冒泡法 就是冒泡排序了,执行看次冒泡操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 vector <int > kBubble(vector <int >& nums, int k){ bool swapped; int n = nums.size(); int topK = k; do { if (topK==0 ) break ; swapped= false ; for (int i=1 ;i<n;i++){ if (nums[i-1 ]>nums[i]){ swap(nums[i-1 ], nums[i]); swapped = true ; } } n--; topK--; }while (swapped); vector <int > res(nums.rbegin(), nums.rbegin()+k); return res; }
测试:
1 2 3 4 5 6 7 8 9 10 int main (int argc, char ** argv) { vector <int > nums = {2 ,5 ,8 ,3 ,4 ,5 ,9 ,12 ,45 ,78 ,340 ,5 ,2 }; vector <int > res = kBubble(nums, 3 ); for (auto item:res) cout <<item<<" " ; cout <<endl ; return 0 ; }
返回:
1 2 3 4 340 78 45 340 78 45 12 9 8 5 5 5 4 3 2 2
时间复杂度:O(N*K)
快排中的Partition操作法 先看,快排中的partition操作返回第k大的元素。其思路是,每次partition返回的值是一个索引值index,它对应的元素是在排好的序列中的正确位置。对于下面的例子,元素个数为9,k为2。一次partition操作后判断这个索引值index=4,对应元素5,5被放到它应该在的位置。而且9-4==5 != k,所以5不是第k个元素。
1 2 3 4 k=2 5 4 3 2 1 8 9 6 7 4 3 2 1 [5 ] 8 9 6 7
则此时第2大的元素在以5为界的右半边,所以对右半边进行partition操作。假如返回index为7,而9-7==k,所以找到第2大元素8.
上述是这个问题的关键 。
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 int partitionOpe (vector <int >& nums, int l, int r) { swap(nums[l], nums[rand()%(r-l+1 )+l]); int p = nums[l]; int j = l; for (int i=j+1 ;i<=r;i++){ if (nums[i]<p){ swap(nums[i], nums[++j]); } } swap(nums[l], nums[j]); return j; } int kthItem (vector <int >& nums, int k) { int n = nums.size(); int l=0 , r=n-1 , index=0 ; index = partitionOpe(nums,l,r); while (true ){ if (n-index>k){ l = index+1 ; index = partitionOpe(nums,l,r); } else if (n-index<k){ r = index-1 ; index = partitionOpe(nums,l,r); } else if (n-index==k){ return nums[index]; } } }
测试对象[5,7,3,8,2,9,1,6,4]
, 返回第3大元素,结果:
进而Top-k问题,就是将此时的数组返回7之后的所有元素(包括7)即可。
时间复杂度:
O(N(1+1/2+1/4+1/8+…)), 根据数列和的极限,1+1/2+1/4+1/8+… 趋近于2,所以partition操作的top-k问题时间复杂度为O(2 N).
注意 :
此法可以处理含有重复元素的数组。
不要忘了快排递归终止条件。
附件 快排 注意递归终止条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int partitionOpe (vector <int >& nums, int l, int r) { swap(nums[l], nums[rand()%(r-l+1 )+l]); int p = nums[l]; int j = l; for (int i=j+1 ;i<=r;i++){ if (nums[i]<p){ swap(nums[i], nums[++j]); } } swap(nums[l], nums[j]); return j; } void findPSplit (vector <int >& nums, int l, int r) { if (l>=r) return ; int p = partitionOpe(nums, l, r); findPSplit(nums, l, p-1 ); findPSplit(nums, p+1 , r); return ; } void quickSort (vector <int >& nums) { findPSplit(nums, 0 , nums.size()-1 ); return ; }
优先队列法 找到第k的元素:
1 2 3 4 5 6 7 8 9 10 11 int findKthLargest (vector <int >& nums, int k) { priority_queue<int > pq; for (auto itr=nums.begin(); itr!=nums.end(); itr++) pq.push(*itr); for (int i=1 ;i<k;i++) pq.pop(); return pq.top(); }
如果是求Top-k:
1 2 3 4 5 6 7 8 9 10 11 12 13 vector <int > topKQueue(vector <int >& nums, int k){ priority_queue<int > pq; for (auto item: nums) pq.push(item); vector <int > res; for (int i=0 ; i<k; i++){ res.push_back(pq.top()); pq.pop(); } return res; }
储存pop的结果得到top-k问题结果。
时间复杂度,与数据结构有关。