基于快速排序的算法选择算法
快速选择算法,其本质思想就是基于快速排序的分治思想。
相信大家一定学习过快速排序,帮助大家回忆一下。
首先确定一个基准数,直接取 l + r >> 1 其实是比较方便的,因为不需要再交换基准数。
如下图:
以上是我的手写笔记,相信已经解释了什么是快排。
参考代码:
#include<iostream>
using namespace std;
const int N = 1000010;
int n ,m;
int a[N];
void sort(int l , int r){
if(l >= r) return ;
int tem = a[l + r >> 1 ] , i = l -1 , j = r + 1;
while(i < j ){
do i++ ; while(tem > a[i]);
do j-- ; while(tem < a[j]);
if(i < j){
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
sort(l, j);
sort(j + 1 , r);
}
int main(){
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%d", a + i);
sort(1 , n);
for(int i=1; i<=n; i++) printf("%d ", a[i]);
return 0;
}
我们来学习快选,所谓快选就是基于快排做的。
快选的问题就是快速的选出,第k小的数,或者第k大的数。
利用快排选出这样的一个数,需要O(nlogn) 的时间复杂度,但是其实并不需要这么高的时间复杂度。
因为,当我们排好,划分好左边,右边之后。其实我们就可以选择去哪个序列里面找了。
假设 , 序列有10个元素 , 要找第7小的数 , 那么明显将整个序列划分成左右之后 , 答案只可能在右边。
照着这样的思路 , 我们同样可以递归的做这个过程。每次都统计这个子序列里面有多少元素 , 然后对比一下这个第k个数,是不是可以判断大于左边序列个数了。
就像这样,但是因为我们统计的这个左序列的元素个数 , 伴随着我们不断地划分一个序列。这个序列的也会不断的变化 , 所以我们的第K 小的数也要根据区间发生变化,如果我们选择的是右边序列。那么我们就需要 (其中 为左子序列的长度), 重新确定,这个第k小的数在这个局部(右边序列的位置对应的值)。
所以我们有如下代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n , k;
int q[N];
int quick_sort(int l , int r , int k){
if(l == r) return q[l];
int i = l - 1 , j = r + 1 , x = q[l + r >> 1];
while(i < j){
while(q[++i] < x);
while(q[--j] > x);
if(i < j) swap(q[i] , q[j]);
}
int sl = j - l + 1;
if(k <= sl) return quick_sort(l , j , k);
return quick_sort(j + 1 , r , k - sl);
}
int main(){
scanf("%d%d" , &n , &k);
for (int i = 1; i <= n; i ++ )
scanf("%d", &q[i]);
int t = quick_sort(1 , n , k);
printf("%d" , t);
return 0;
}
该算法时间复杂度是O(n) 的 , 以下是证明:
假设提供的序列 s 长为 |s|
每次划分都将序列分成两份
那么我们由等差公式可以得到如下等式: