从一个无序数组中求出第K大/小的数
这个题目可以作为练习写大/小根堆的实现,不过貌似时间复杂度还是蛮高的。在洛谷上面一道模板题上面好像就超时了几个点,不知道是不是我实现的问题。那么除此之外,最容易想到的方法是先对该数组进行排序,然后取出第K或MAX-K数来。当选择使用快排的时候,时间复杂度是$O(nlogn)$。但还有一种更优的方法是利用快排划分出来的主元位置再递归寻找,这种方法叫作随机选择算法。它对任何输入都可以达到$O(n)$的期望时间复杂度。
#include <cstdlib> #include <algorithm> #include <cmath> //随机选择主元 其实也是快排的一种写法 int randPartition(int A[],int left,int right) { // rand()/RAND_MAX得到一个0~1的小数再乘以相差值right-left最后加到left四舍五入得到随机值 int p = round(1.0*rand()/RAND_MAX*(right-left)+ left); swap(A[p],A[left]); int temp = A[left]; while(left<right) // 跳出循环时left==right { while(left < right && A[right]>temp) right--; A[left] = A[right]; while(left < right && A[left]<=temp) left++; A[right] = A[left]; } A[left] = temp; return left; } //递归实现查询K值 int randSelect(int A[],int left,int right,int k) { if(left == right) return A[left]; int index = randPartition(A,left,right); int M = index-left+1; // 表示从左到右第M个数 if(M==k) return A[index]; if(M<k) randSelect(A,index+1,right,k-M); //注意 k-M else if(M>k) randSelect(A,left,index-1,k); }