关于分治思想(基础)

今天学了关于分治算法的一点点,勉强能接受,就把自己理解的那一小部分整理整理,以后应该会完善

首先什么是分治思想?

其实就是把一个规模较大问题分解成若干个子问题,再递归地解决每子问题,最后合并子问题的解得到原问题的解。

Divide、Conquer、Combine。。。

由于它是一种思想,我就把能够体现出它的三个最最最基本而又狭隘的问题(因为我其他的不会)并附了简单的说明整理,以供理解:

 

1、二分查找

一般就是给你一个有序的数列,让你查找其中的一个数的问题。

我会用sort,简单又有效

gun

那我就可以用分治的思想来解决这种问题:

第一步:Divide,其实题目已经把它分了,就是提供一长串子串。

第二步:Conquer,就是运用递归的方法

分析一下,我们就先把这一串数字分成两半,数首位置为p1(=1),数位位置为p2,比较我们要搜索的数(n)和中间的数(mid)并比较,如果n==mid,直接输出mid的位置就行了(边界),如果n<mid,就让p2=mid,再进行下一步查找(这就体现了递归的思想),如果n>mid,就让p1=mid再下一步查找。

具体代码如下:

 1 #include<iostream>
 2 using namespace std;
 3 int n;//表示数的个数 
 4 int a[100];
 5 int m; //表示要搜索的数 
 6 int devide(int x,int y)
 7 {
 8     int mid=x+y>>1;
 9     if(a[mid]==m) cout<<"Find it!";
10     else
11     {
12         if(x>y) cout<<"Not Find!";
13         else
14         {
15             if(a[mid]<m) devide(mid+1,y);
16             if(a[mid]>m) devide(x,mid-1);
17         }
18     }
19 }
20 int main(){
21     cin>>n;
22     for(int i=1;i<=n;i++)
23     {
24         cin>>a[i];
25     }
26     cin>>m;
27     devide(1,n);
28     return 0;
29 }

 

2.归并(快速)排序

归并排序就是把一个无序数列通过分治的思想分成若干个有序的子序列,最后归并起来变成一个有序数列的快速有效(而且又麻烦)的由于sort排序的一种排序

 

 

 我们看,最开始的无序数列就是第一行,我们把它分成两个子序列,再把每个子序列分成若干个新的子序列,一直分分分(递归),直到子序列的元素个数为一后(就是最后一行),把这两个儿子比较大小并排序,再跟自己的兄弟排一下序(这会待会提及),一直这样下去,我就得到了一开始的数列的有序数列

那我们怎么把两个有序子序列归并成一个有序的父数列呢??

我们建两个指针分别指向两个子序列的最前面的数(也就是各自序列的最小值),比较两个指针指向的数,父序列的指针加1并等于较小的数,再把指向两个数中较小的指针往后移一位,继续比较并放置于数组中,直到比完为止,但是其实有个问题。就是肯定会有一个序列被提前拿完,另一个数组会剩下几个,无法放入数组中,针对这种情况,可以这样:

我们认为子序列a的个数是mid(也就是n/2),指针是top1,子序列b的个数是n(他们一人分一半父序列的长度n),指针是top2,最后要放入的数组(即父序列)为c,指针为top,比较完之后,会有剩下的那就这样处理

while(top1<=mid) c[top++]=a[top1++];

while(top2<=r) c[top++]=b[top2++];(当然,我也可以只用一个序列a,前序列是a[1]~a[mid],后序列是a[mid+1]~a[r]

因为我也不知道那个会剩下,所以两个都要来,由于只有一个序列会剩下,所以无需担心关于一个数列最后归并的数比另一个序列的数大或小的问题

具体代码如下

 

 1 #include<iostream>
 2 #define ll long long
 3 using namespace std;
 4 ll a[500001],b[500001];//一般要多开一点,最好开4倍
 5 ll n,ans;
 6 void seqence(ll l,ll r)
 7 {
 8     if(l==r) return;
 9     ll mid=l+r>>1,top1=l,top2=mid+1,top=l;
10     seqence(l,mid),seqence(mid+1,r);
11     while(top1<=mid&&top2<=r)//有一个子序列比较完了就结束,另外一个子数列有可能有剩余 
12     {
13         if(a[top1]<=a[top2]) b[top++]=a[top1++];
14         if(a[top2]<a[top1]) b[top++]=a[top2++];
15     }//top指向最大数,top++,相应的有较大的元素的数列指针加一,即指向下一个数继续比较
16     while(top1<=mid) b[top++]=a[top1++];
17     while(top2<=r) b[top++]=a[top2++];
18 //上面两步只有一种会进行,因为要不前面的子数列剩下,要不后面的剩下 
19     for(ll i=l;i<=r;i++)
20     {
21         a[i]=b[i];//给a数组赋b数组的值,因为我们还要进行新的一轮,要用新的排好了的数组a 
22     } 
23 }
24 int main()
25 {
26     cin>>n;
27     if(n==1)//我要特判一下,如果只输入一个数,那由于了分成两段,一定有一段是0,所以最后只会输出一个最小的0 
28     {
29         int x;
30         cin>>x;
31         cout<<x;
32         return 0;
33     }
34     for(int i=1;i<=n;i++)
35     {
36         cin>>a[i];
37     }
38     seqence(1,n);
39     for(int i=1;i<=n;i++)
40     {
41         cout<<a[i]<<" ";
42     }
43     return 0;
44 } 

 

 

3、快速幂

其实它也是用的二分的方式,道理是这样的

计算a的n次方,如果n是偶数(不为0),那么就先计算a的n/2次方,然后平方,然而如果n是奇数,那么就先计算a的n-1次方,再乘上a。

递归出口是a的0次方为1

其实就是一个分段函数:

代码如下

 1 int qpow(int a, int n)
 2 {
 3     if (n == 0)
 4         return 1;
 5     else if (n % 2 == 1)
 6         return qpow(a, n - 1) * a;
 7     else
 8     {
 9         int temp = qpow(a, n / 2);
10         return temp * temp;
11     }
12 }

 

好了,就先将到这吧,我去卷啦!!!

再见!

2022/3/15

 

posted @ 2022-03-15 16:49  你的小垃圾  阅读(412)  评论(0编辑  收藏  举报