算法第二章实践作业

寻找第k小的数 解答代码:

`#include
using namespace std;
//划分
int partition(int a[], int left, int right)
{
int pivot = a[left];
while (left < right)
{
while (left < right && a[right] >= pivot)
{
right--;
}
a[left] = a[right];
while (left < right && a[left] < pivot)
{
left++;
}
a[right] = a[left];
}
a[left] = pivot;
return left;
}
int find(int a[], int left, int right, int k)
{
if (left == right)
return a[left];

int pivotPoint = partition(a, left, right);
if (pivotPoint + 1 == k)
return a[pivotPoint];
else if (pivotPoint + 1 > k)
return find(a, left, pivotPoint - 1, k);
else
return find(a, pivotPoint + 1, right, k);
}
int main(){
int n;
int k;//第几小的数。
cin>>n>>k;
int a[10005]={};
for (int i = 0; i < n; i++)
{
cin>>a[i];
}
int re=find(a,0,n-1,k);
cout<<re;
}`
一、算法描述(自然语言)
找第k小的数可用分治实现。设区间为[left,right],取a[left]为基准pivot,双指针从两端扫描:右指针向左找比pivot小的元素填入左端,左指针向右找不小于pivot的元素填入右端,相遇后把pivot放回相遇处,此时a[p]为区间第p+1小的数。若p+1等于k,答案即a[p];
若p+1大于k,递归处理左半区[left,p-1];
否则递归处理右半区[p+1,right]。当left等于right时只剩一个元素,直接返回。

二、复杂度分析
复杂度取决于每次划分后剩余子问题的规模。最好情况是每次划分都近似把区间对半,递推式T(n)=T(n/2)+O(n),由主定理得O(n),线性时间就能拿到答案。最坏情况是数组已经有序或逆序,而基准总取左端元素,每次只能排除一个元素,递推式退化为T(n)=T(n-1)+O(n),累加为O(n²)。空间上递归深度最好O(logn)、最坏O(n)。实践中常用随机选取基准或三数取中,使期望复杂度回到O(n),避免被有序数据卡住。

三、对分治法的体会
学完这一章,我对分治最深的体会是:缩小问题规模往往比正面解决整个问题更重要。找第k小本可以先排序再取下标,代价O(nlogn),但分治只判断答案落在哪一半,另一半直接丢弃,平均只需O(n),这正是减治的威力。而能安全丢弃一半,靠的是划分给出的确定位次信息,也就是问题的可分解性与子问题的独立性。
同时性能高度依赖划分是否均衡,随机化、三数取中都是为此服务。
边界上递归出口、k与下标的对应关系稍有疏忽就会死循环或越界,需要格外小心。

posted @ 2026-10-13 22:45  Lssss177  阅读(3)  评论(0)    收藏  举报