要求:
1.请用自然语言或伪代码描述找第k小的数的分治算法
2.分析该算法的最好时间复杂度和最坏时间复杂度
3.结合本章的学习,谈谈你对分治法的体会和思考
作业:
1.分治算法(伪代码)
1.1.Parition()利用快速选择算法并返回第一个元素最后的位置。
j表示该开始最左边的数移了j-left个数。
int Partition(a[],left,right){
i=left,j=right+1;
while(i<j){
while(a[++i]<a[left]&&i<right);
while(a[--j]>a[left]);
if(i>=j){
break;
}
swap(a[i],a[j]);
}
x=a[left];
a[left]=a[j];
a[j]=x;
return j;
}
1.2.find()寻找数组中第K小的数
因为k是从1开始算起,而在a[]数组中是从0开始算起,所以判断划分点是不是第k小时,应该是k-1与mid比较。
如果相等的话,说明找到了第k小的数,就可以直接输出第k小的数的值
if(k-1==mid){
cout<<a[k-1];
}
如果k-1<mid的话,说明第k小的数在a[mid]的左边,所以向左边继续找。
else if(k-1<mid){
find(a,k,left,mid-1);
}
如果k-1>mid的话,说明第k小的数在a[mid]的右边,所以向右边继续找
else(k-1>mid){
find(a,k,mid+1,right);
}
完整的find()函数(伪代码):
void find(a[],k,left,right){
mid=Partition(a,left,right);
if(k-1==mid){
cout<<a[k-1];
}
else if(k-1<mid){
find(a,k,left,mid-1);
}
else{find(a,k,mid+1,right);}
}
int main(){
a[10001];
cin>>n>>k;
for(i=0;i<n;i++){
cin>>a[i];
}
left=0,right=n-1;
find(a,k,left,right);
return 0;
}
1.3.main()函数:
int main(){
int n,k;
int a[10001];
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>a[i];
}
int left=0,right=n-1;
find(a,k,left,right);
return 0;
}
2.时间复杂度
2.1.最好时间复杂度
find()函数最好的情况是第一次调用Parition函数()返回的mid就是k-1,所以时间复杂度为1*Parition()的时间复杂度,此时我们只要讨论Parition()函数的时间复杂度即可。
而Parition()在最好的情况时,每次划分都能直接找到第k小的元素,因此只需要一次划分,时间复杂度为O(n)。
所以最好时间复杂度为O(n)。
2.2.最坏时间复杂度
在最坏的情况下,每次Parition()的a[left]总是为最小值会最大值,那么每次调用Parition()后,问题的规模只减少了1。因此,如果要找到第K小的元素,还要执行k次划分,而每次划分的复杂度为O(n),所以总的时间复杂度为O(n*k),而最坏的情况下,k为n的话,总的时间复杂度就为O(n^2)。
所以最坏时间复杂度为O(n^2)。
3.体会和思考
分治法的基本做法是先将规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。然后递归地解决这些子问题,然后将这些子问题合并就可以得到原问题的解。