作业(二)分治

分治算法找第k小的数

分治算法找第k小的数的基本思路是利用快速选择

  1. 选择基准:从数组中随机选择一个元素作为基准
  2. 划分数组:将数组划分为三部分:
    • 小于基准的部分
    • 等于基准的部分
    • 大于基准的部分
  3. 判断位置
    • 如果小于基准的部分的大小等于k-1,则基准就是第k小的数。
    • 如果小于基准的部分的大小大于k-1,则在小于基准的部分递归查找第k小的数。
    • 如果小于基准的部分的大小小于k-1,则在大于基准的部分递归查找第k - (小于部分大小 + 1)小的数。

伪代码

int fenzhi(a[], left, right, k){
    if left == right;  // 数组中只有一个元素
        return a[left];}
    
    a = b(a[], left, right)
    size = a - left + 1

    if (size == k)// 基准正好是第k小的数
    {   return a[a]}
    else if (size > k){ // 第k小的数在左侧
        return fenzhi(arr, left, a - 1, k);}
    else // 第k小的数在右侧
        return quickSelect(a[], a + 1, right, k - size);

时间复杂度

最好时间复杂度:O(n),在每次划分时都能恰好将数组分成两半的情况下。

最坏时间复杂度:O(n²),在每次选择基准时都选到最小或最大元素,导致每次只减少一个元素。

分治法的体会

分治法通过将问题分解为更小的子问题,简化了复杂问题的解决方案。这个策略不仅提高了效率,还让算法更易于理解和实现。在查找第k小的数时,分治法充分利用了数组的局部结构,避免了完整排序,从而提升了性能。通过随机选择基准来减少最坏情况的发生,提高了算法的平均性能。这种方法在解决许多其他问题时也具有广泛的应用,如排序和搜索。

分治法是一种将大问题分割成规模较小的相同问题,各个击破、分而治之的编程思想,不是所有问题都能用分治法解决,能用分治法解决的问题需具备一定特征,如问题可分解为规模较小的相同问题、规模缩小到一定程度可容易解决、子问题的解可合并为原问题的解、子问题相互独立等[^1][^2]。 ### 完成方法 完成分治法头歌作业,首先要判断问题是否适合用分治法解决,可依据能用分治法解决问题的特征进行判断。若适合,就把大问题分解成若干规模较小的相同子问题,接着解决这些子问题,最后将子问题的解合并得到原问题的解。在分解和解决子问题时,可能会用到递归的方法,因为不断分解问题的过程通常是递归的过程[^2]。 ### 示例代码 以快速排序为例,快速排序是分治法的典型应用,以下是一段存在问题但可参考的快速排序代码: ```cpp #include <iostream> using namespace std; void quick_sort(int q[],int l,int r){ int i,j,basic; i=l;j=r;basic=q[l]; while(i<j){ while(q[i]<basic&&i<=r)i++; while(q[j]>=basic&&i<j)j--; swap(q[i],q[j]); }//完成一次基本分割 if(l<j-1) quick_sort(q,l,j-1);//左半边再分割 /*解决方案:增加左边界比有边界小这个条件 */ if(j<r) quick_sort(q,j,r);//右半边再分割 /*依然有问题:进行到这里r值不是整体边界了,左边分割时将j - 1不断传给r改变了r值了,现在的r值是左半边的有边界了,但是这里需要的是整体的右边界 怎么解决?快排学不会了 */ } int main(){ int q[100],n; cin>>n; for(int i=0;i<n;i++) cin>>q[i]; quick_sort(q,0,n-1); for(int i=0;i<n;i++) cout<<q[i]; return 0; } void swap(int a,int b){ int c; c=a; a=b; b=a; } ``` 不过这段代码存在问题,`swap` 函数没有正确交换数组元素,且递归调用时边界处理存在问题。以下是修正后的代码: ```cpp #include <iostream> using namespace std; // 交换数组中两个元素的位置 void swap(int& a, int& b) { int temp = a; a = b; b = temp; } // 快速排序函数 void quick_sort(int q[], int l, int r) { if (l >= r) return; int i = l - 1, j = r + 1, x = q[(l + r) / 2]; while (i < j) { do i++; while (q[i] < x); do j--; while (q[j] > x); if (i < j) swap(q[i], q[j]); } quick_sort(q, l, j); quick_sort(q, j + 1, r); } int main() { int q[100], n; cin >> n; for (int i = 0; i < n; i++) cin >> q[i]; quick_sort(q, 0, n - 1); for (int i = 0; i < n; i++) cout << q[i] << " "; return 0; } ``` ### 解题思路 在使用分治法解决问题时,要紧扣分治法的基本思想和适用问题的特征。以快速排序为例,先选择一个基准元素,将数组分为两部分,一部分元素小于基准,另一部分元素大于等于基准,这就是将大问题(对整个数组排序)分解为小问题(对两部分子数组分别排序)。然后递归地对这两部分子数组进行同样的操作,直到子数组规模足够小(如只有一个元素)可直接解决。最后,所有子数组都排好序,整个数组也就排好序了,实现了子问题解的合并。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值