算法基础分治

分治基本概念

把一个任务,分成形式与原任务相同,但规模更小的几个部分任务(通常是两部分)

分别完成,或者只需要选一部完成。然后再处理完成后的这一个或几个部分的结果实现整个任务的完成

对于一些枚举,可以考虑不从头尾枚举;中间二分分成两半,一半一半得来考虑

例子——称假币

思想:二分,二分接下去分(递归的二分查找)

典型分治:归并排序

  • 数组排序任务分解:
    • 把前一半排序(这个会递归,把前一半的前一半排序,把前一半的后一半排序)
    • 把后一半排序
    • 把两半归并到一个新的有序数组,然后拷贝到原数组,排序完成
#include<iostream>
using namespace std;
void Merge(int a[],int s,int m,int e,int tmp[]){
    //将数组a的局部a[s,m]和a[m+1,e]合并到tmp,并保证tmp有序,然后拷贝回a[s,m]
    //归并操作的时间复杂度是O(e-m+1)即o(n)
    int pb=0;
    int p1=s,p2=m+1;
    while(p1<=m&&p2<=e){
        if(a[p1]<a[p2]) tmp[pb++]=a[p1++];
        else            tmp[pb++]=a[p2++];
    }
    while(p1<=m){
        tmp[pb++]=a[p1++];
    }
    while(p2<=e){
        tmp[pb++]=a[p2++];
    }
    for(int i=0;i<e-s+1;i++){
        a[s+i]=tmp[i];
    }//拷贝操作
}//合并_algorithm中有merge算法
void MergeSort(int a[],int s,int e,int tmp[]){
    if(s<e){
        int m=s+(e-s)/2;
        MergeSort(a,s,m,tmp);
        MergeSort(a,m+1,e,tmp);
        Merge(a,s,m,e,tmp);
    }
    //这个递归到最后,会是两个数组只要一个元素,merge里本身有比较后才插入tmp里,因此会达到排序的效果
}//排序_algorithm中快速排序,sort
int main(){
    int a[10]={13,27,19,2,8,12,2,8,30,89};
    int b[10];
    int size=sizeof(a)/sizeof(int);
    MergeSort(a,0,size-1,b);
    for(int i=0;i<size;i++){
        cout<<a[i]<<",";
    }
    cout<<endl;
    return 0;
}

典型分治:快速排序

  • 数组排序任务:
    • 设K=a[0],将k挪到适当位置,使得比k小的元素在k的左边比k大的元素在k的右边;和k相等的,不关心的在k的左右出现均可,应在O(n)内完成
    • 将k左边的部分快速排序
    • 将k右边的部分快速排序(递归而且二分(元素个数不一定是平均二分)
    • 因此,运气最坏的时候,会达到O(n^2)
    • 如果十分在意,可以先把数组里的元素随机打乱
      • 可以用algorithm中的random_shuffle()函数
/*难点一:将K挪到适当位置,利用奇偶数次移动次数比较大小交换值*/
#include<iostream>
using namespace std;
void swAp(int &a,int &b){
    int tmp=a;
    a=b;
    b=tmp;
}//algorithm中有swap()函数
void QuickSort(int a[],int s,int e){
    if(s>=e){
        return;
    }
    int k=a[s];
    int i=s,j=e;
    while(i!=j){
        while(j>i&&a[j]>=k){
            j--;
        }
        swAp(a[i],a[j]);
        while(i<j&&a[i]<=k){
            i++;
        }
        swAp(a[i],a[j]);
    }//处理完之后,a[i]=k
    QuickSort(a,s,i-1);
    QuickSort(a,i+1,s);
}
int main(){
    int a[]={93,27,30,2,8,12,2,8,30,89};
    int size=sizeof(a)/sizeof(a[0]);
    QuickSort(a,0,size-1);
    for(int i=0;i<size;i++){
        cout<<a[i]<<" ";
    }
    cout<<endl;
    return 0;
}

例题——输出前m大的函数

思路:1.排序后输出,复杂度是O(nlogn)

​ 2.用分治处理,复杂度是O(n+mlogm)

当m比n小很多的情况下比较好用

分支处理思路:把前m大的都弄到数组的最右边,然后把这最右边m个元素排序,再输出

难点:前m大的都弄到数组的最右边,而且时间得是O(n)才有效

  • 引入操作arrangeright(K):把数组前K大都弄到最右边

  • 设k=a[0],将key挪到适当位置,使得比key小的元素都在key左边,比key大的都在key右边;

  • 选择数组的前部或后部在进行arrangeright操作

如果右边的a个,刚好等于m个,则done;

如果右边的a个,多于m个,则arrangeright(k)

如果右边的a个,小于m个,则对左边进行arrangeright(k-a)

#include<iostream>
#include<algorithm>
using namespace std;
int m;
void SwAp(int &a,int &b){
    int tmp=a;
    a=b;
    b=tmp;
}
void ArrangeRight(int *a,int s,int e,int k){
    if(s>=e){
        return;
    }
    int num=a[s];
    int i=s,j=e;
    while(i!=j){
        while(j>i&&a[j]>=num){
            j--;
        }
        SwAp(a[i],a[j]);
        while(j>i&&a[i]<=num){
            i++;
        }
        SwAp(a[i],a[j]);
    }
    if(e-i+1>k) return ArrangeRight(a,i+1,e,k);
    else if(e-i+1<k) return ArrangeRight(a,0,i-1,k-(e-i+1));
}
int main(){
    int n,num[100050];
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>num[i];
    }
    cin>>m;
    ArrangeRight(num,0,n-1,m);
    sort(num+(n-m),num+n);
    for(int i=0,j=n-1;i<m;i++,j--){
        cout<<num[j]<<endl;
    }
}

例题——求排列的逆序数

思路:1)求左半边逆序数和右半边逆序数

​ 2)再算有多少逆序是由左半边取一个数和右半边取一个数构成。要求O(n)

难点:在O(n)的复杂度里,实现2的种类数;

  • 要求左半边和右半边都是排好序的。比如,都是从大到小排序,这样左右半边都只需要从头到尾各扫一遍,就可以找出由两边各取一个数构成逆序;
  • 进行完第一步,第二步排好序是没关系的

10 8 7 3| 12 11 5 2(用i,j两个指针头,10>5的时候,j不变数右边剩下几个元素

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Caaaaaan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值