一.请用自然语言或伪代码描述找第k小的数的分治算法
1.题目
设计一个平均时间为O(n)的算法,在n(1<=n<=1000)个无序的整数中找出第k小的数。
提示:函数int partition(int a[],int left,int right)的功能是根据a[left]~a[right]中的某个元素x(如a[left])对a[left]~a[right]进行划分,划分后的x所在位置的左段全小于等于x,右段全大于等于x,同时利用x所在的位置还可以计算出x是这批数据按升非降序排列的第几个数。因此可以编制int find(int a[],int left,int right,int k)函数,通过调用partition函数获得划分点,判断划分点是否第k小,若不是,递归调用find函数继续在左段或右段查找。
输入格式:
输入有两行:
第一行是n和k,0<k<=n<=10000
第二行是n个整数
输出格式:
输出第k小的数
输入样例:
在这里给出一组输入。例如:
10 4
2 8 9 0 1 3 6 7 8 2
输出样例:
在这里给出相应的输出。例如:
2
2.代码
#include<bits/stdc++.h>
using namespace std;
int a[100005];
void merg(int *a,int left,int mid,int right){
int* b = new int[right - left + 1]; //建立辅助空间,方便输出最终结果
int i=left;
int j=mid+1;
int k=0;
while(i <= mid && j <= right){
if(a[i]<=a[j]){
b[k++]=a[i++];
}
else{
b[k++]=a[j++];
}
}
while (i <= mid)
{
b[k++] = a[i++];
}
while (j <= right)
{
b[k++] = a[j++];
}
k = 0;
for (int i = left; i <= right; i++)
{
a[i] = b[k++];
}
delete[]b;
}
void mergsort(int *a,int left,int right){
if(left<right){
int mid=(left+right)/2;
mergsort(a,left,mid);
mergsort(a,mid+1,right); //不断划分为几个子问题
merg(a,left,mid,right); //单独处理子问题
}
}
int main(){
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++) cin>>a[i];
mergsort(a,0,n-1);
cout<<a[k-1];
}
3.运行结果

4.知识点
4.1分治法
将问题分解为更小的子问题,然后递归地解决这些子问题,最后将子问题的结果合并以得到原问题的解。
4.2归并排序法
(1)选基准
从数组中选一个元素作为基准。这个选择可以随便。
(2)分区
根据基准元素将数组重新排列,使得所有小于基准的元素都位于基准的左侧,所有大于或等于基准的元素都位于基准的右侧。
(3)递归
根据分区结果,确定基准元素的位置。
如果基准元素的位置正好是k-1,则基准元素就是第k小的数。
如果基准元素的位置小于k-1,则第k小的数位于基准元素的右侧子数组中,继续在右侧子数组中递归查找。
如果基准元素的位置大于k-1,则第k小的数位于基准元素的左侧子数组中,继续在左侧子数组中递归查找,并且k值要减去左侧子数组的大小加1。
二.分析该算法的最好时间复杂度和最坏时间复杂度
1.最好时间复杂度
在最好的情况下,每次选择的基准都是当前子数组中的最小值或最大值,这样每次划分都能将数组分成两个几乎相等的部分。在这种情况下,算法的时间复杂度接近于线性,即 O(n)。
2.最坏时间复杂度
在最坏的情况下,每次选择的基准都是当前子数组中的最小值或最大值,这样每次划分只能将数组分成一个元素和其余元素两部分。在这种情况下,算法的时间复杂度退化为 O(n^2)。
三.结合本章的学习,谈谈你对分治法的体会和思考
分治法是一种广泛应用的算法设计范式,其核心在于将复杂问题拆解为更小的、更易于管理的子问题。这些子问题随后会递归地被解决,而它们的解最终会被整合起来,形成原问题的完整解答。分治法直观易懂,能够广泛应用于多种场景,有效地简化问题并找到解决方案。由于分治法通常需要递归调用,这可能会增加算法实现的复杂度。
1万+

被折叠的 条评论
为什么被折叠?



