s
e
l
e
c
t
:
select:
select: 我们每次会挑选一个主元
p
i
v
o
t
pivot
pivot 。这里有多种选择策略,这里我们选择挑取序列第一个。
p
a
r
t
i
t
i
o
n
:
partition:
partition: 根据
p
i
v
o
t
pivot
pivot 划分序列。保证
p
i
v
o
t
pivot
pivot 左边元素均比它小,右边元素均比它大 。
d
i
v
i
d
e
:
divide:
divide: 分治策略。我们根据给定的要求的第
k
k
k 小,如果
p
i
v
o
t
pivot
pivot 左边的元素大于等于
k
k
k 个,那么第
k
k
k 小的元素必然在
p
i
v
o
t
pivot
pivot 左侧,那么我们可以舍弃
p
i
v
o
t
pivot
pivot 的右侧元素。同理,如果如果
p
i
v
o
t
pivot
pivot 左边的元素小于
k
k
k 个,那么第
k
k
k 小的元素必然在
p
i
v
o
t
pivot
pivot 右侧,那么我们可以舍弃
p
i
v
o
t
pivot
pivot 左侧元素。
r
e
c
u
r
s
i
o
n
:
recursion:
recursion: 将
d
i
v
i
d
e
divide
divide 得到的序列继续递归。重复
d
i
v
i
d
e
divide
divide 的操作,直至只有一个元素,那么这个元素即是我们要求的第
k
k
k 小的元素。
3. 设计
分治策略找第K小元素算法:
#include<bits/stdc++.h>
using namespace std;constint N =1e5+10;int n,m,k;//找出第k小,就是说有k-1个元素比它小intFindKthMin(int a[],int l,int r,int k){if(l==r)return a[l];int i = l, j = r;//选取开头元素取出来然后右左分别插空int x = a[i];while(i < j){while(i<j && a[j]>=x)j--;
a[i]= a[j];while(i<j && a[i]<=x)i++;
a[j]= a[i];}//i作为分割点,i左边元素均比a[i]小,右边元素均比a[i]大
a[i]= x;//计算左区间有多少个int num = i-l+1;//如果左区间个数少于k个,那么第k小必然在右区间if(num < k)returnFindKthMin(a,i+1,r,k-num);//如果左区间个数大于等于k个,那么第k小必然在左区间returnFindKthMin(a,l,i,k);}voidrun(){
cin>>n>>k;int a[1000];for(int i=1;i<=n;i++){
cin>>a[i];}
cout<<FindKthMin(a,1,n,k)<<endl;}intmain(){run();return0;}
4. 分析
如果每次划分点都处在序列中间,该算法将具有线性的期望时间复杂度
平均时间复杂度为:
T
(
n
)
=
n
+
n
2
+
n
2
2
+
…
…
+
n
2
k
=
O
(
n
)
T(n) = n + \frac{n}{2} + \frac{n}{2^2} + …… + \frac{n}{2^k} = O(n)
T(n)=n+2n+22n+……+2kn=O(n)
但在最坏的情况下其时间复杂度为快速排序的最坏时间复杂度为
O
(
n
2
)
O (n^2)
O(n2)