递归与分治

人盡茶涼 提交于 2019-11-29 21:50:12

一、递归与分治概述:

1、分治方法在于分和治。将一定规模的问题划分成性质相同的若干个小问题,分;对于每个小问题,进行所需要进行的操作,如排序等。

2、关于递归:

(1)递归中需要的成分是递归边界和递归规则,没有递归边界递归无法停止、无法进行。

(2)通过将各层的关系从小到大逐渐带入,可以求解出一个函数的非递归表达式。

(3)n该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

3、Ackerman函数:

Ackerman函数的定义为:

根据关系可以递推出来:

在m取不同的值的过程中,该函数的递推关系式也不同。只是一种无法用单一的非递归关系表达的函数。

4、在Ackerman函数中,定义它的逆拟函数α(n)为使A(x)>n成立的最小的x。

5、整数划分问题:

对于整数的不同划分,考虑以下几种不同的情况:(1)m>n:q(n,m)=q(n,n)

(2)m=n:q(n,m)=1+q(n,m-1)

(3)m<n:q(n,m)=w(n,m)+q(n,m-1)

又:w(n,m)=q(n-m,m)

(4)m=1:q(n,1)=1

6、汉诺塔问题:

思路:将前n-1个元素移动到c上,然后将第n个移动到b上,再将c上的n-1个移动到b上。

注意边界条件是:如果只有一个元素,直接move到b上。

void Move(int no,char a,char b){
    printf("Move %d from %c to %c.\n",no,a,b);
}
void Hanoi(int n,char a,char b,char c){
    if(n==1){
        Move(1,a,b);
    }
    else{
    Hanoi(n-1,a,c,b);
    Move(n,a,b);
    Hanoi(n-1,c,b,a);
    }

}

7、分治法的基本思想:将一个叫难解决的大问题分成k个规模较小的子问题,逐个解决,分而治之。如果问题的规模还不够小,就继续划分,直到可以直接解决这个小问题。将求出的小问题自下而上的合并,最终得到问题的解

8、递归的概念:

递归算法:直接或间接地调用自身的算法。

递归函数:用函数自身给出定义的函数。

使用递归技术使得算法的描述简捷且易于理解

10、分治法满足的要求:

(1)问题的规模缩小到一定的程度就可以解决

(2)该问题可以分成若干个规模较小的子问题

(3)该问题分解出的子问题的解可以合并成为问题的解

(4)问题没有重叠的子问题

11、分治法的基本步骤:

  divide-and-conquer(P)
  {
    if ( | P | <= n0) adhoc(P);  //解决小规模的问题, adhoc(P)是基本子算法
    divide P into smaller subinstances P1,P2,...,Pk;//分解问题
    for (i=1,i<=k,i++)
      yi=divide-and-conquer(Pi);  //递归的解各子问题
    return merge(y1,...,yk);  //将各子问题的解合并为原问题的解
  }

二、二分搜索技术

1、时间复杂性是O(logn)

2、核心代码:

int BinarySearch(int a[],int l,int r,int n){
    while(l<=r){
        mid=(l+r)/2;
        if(a[mid]==n){
            return mid;
        }
        else if(a[mid]>n){
           r=mid-1;
        }
        else l=mid+1;
    }
    return -1;
}

三、大整数的乘法

1、原始方法:XY的花费时间:n²

2、拆分成两块:

X = a 2n/2 + b     Y = c 2n/2 + d

XY = ac 2n + (ad+bc) 2n/2 + bd

其中ac等要花费的时间:(n/2)^2=n^2/4,有四个部分:有T(n)=4*T(n/2)+O(n)。

利用公式得到:

T(n)=O(n^2)

3、在拆成两块的情况下:XY=ac2^n+((a-b)(d-c)+ac+bd)+bd

则只有三个要计算的小部分:

利用公式得到的:T(n)=3T(n/2)+O(n)=O(n^1.59)

4、入果将整数拆分成更小的小块,就可以更好运行的效率

四、stressen矩阵乘法:

1、每计算一个c[i][j],就需要计算O(n^3),(即使矩阵的长和宽不是都等于n,但肯定可以表示成n的几分之几)

2、改进:将矩阵分割成均匀的四个,则有:

还是O(n^3)

3、构建几个辅助作用的矩阵:

得到的是

则有:

O(n^2.81)

 

五、最接近点对问题:

在一个平面中,想要去求两个最接近的点:基本的方法是蛮力O(n^2)

(一)一维情况下:

1、原始方法:(1)对一维的坐标进行排序O(nlongn)

(2)依次计算相邻两个点的距离O(n)

所以T(n)=O(nlongn)

2、想设计出一种用分治的、可以推广到二维的算法:

(1)将各个点分到s1、s2中去(O(n))

(2)分别查找s1,s2中的最短的距离(2T(n/2))

(3)(在线性时间内)找到s1,s2中最大和最小的点,计算二者的距离,与得到的s1,s2,中的最短的距离比较,选出来最小的(O(n))

注意此处:s1,s2中最大和最小的点肯定在中间位置的左边-d,与右边+D中,因为要不就里面没点;要不里面有点但是只是左边右边空间里面各有一个,可能导致这两点之间的距离才是整体里最小的,或者不。(如果这两个其中一边或两边中有一个以上的点,就与之前的结论相矛盾

;并且其中没有排序的过程)

可以得到时间复杂度:

T(n)=O(nlogn)

(二)二维情况下

T(n)=O(nlogn) 

六、棋盘覆盖问题

七、合并排序

1、流程

void MergeSort(Type a[ ], int left, int right)
   {
      if (left<right) {//至少有2个元素
      int i=(left+right)/2;  //取中点
      mergeSort(a, left, i);
      mergeSort(a, i+1, right);
      merge(a, b, left, i, right);  //将排好序的集合合并到数组b
      copy(a, b, left, right);    //复制回数组a
      }
   }

2、其中,取中点:O(1)

左边合并T(n/2),右边合并T(n/2)

合并O(n),复制回原来的数组O(n)

,T(n)=O(nlogn)

关于求解有不同的方法:

3、合并排序的新方法:从底向上两两合并/将原来的序列化分成若干个有序段(自然排序),之后进行两两合并

注意:自然排序最好的时间复杂度是O(n),因为原来已经排好了,只需要扫描一遍

.八、快速排序

1、快排的思路:

(1)选择第一个元素为基准

(2)在两头分别设置指针i和j,如果i的元素比基准还要小或者等于就继续向后,同理右边

找到不合适的二着就交换位置。

(3)如果i在j右边了,就让j指向的元素和基准元素交换位置。

2、关于时间复杂度:

(1)最坏情况下(逆序):

T(n)=O(n^2)

(2)平均情况下:

T(nlogn)

3、代码的框架:

template<class Type>
void QuickSort (Type a[ ], int p, int r)
{
      if (p<r) {
        int q=Partition(a,p,r);
        QuickSort (a,p,q-1); //对左半段排序
        QuickSort (a,q+1,r); //对右半段排序
        }
}

4、算法的改进:可改进之处是,可以在选择基准的时候随机的选择

template<class Type>
void QuickSort (Type a[ ], int p, int r)
{
      if (p<r) {
        int q=Partition(a,p,r);
        QuickSort (a,p,q-1); //对左半段排序
        QuickSort (a,q+1,r); //对右半段排序
        }
}

九、线性时间选择:

1、我们需要在n个无序的元素里面找出来第k小的元素。

2、基本思路:

selet中:

(1)以第一个元素为基准,进行(选择随机数的)一次的快排,返回拍好后基准元素的位置i

(2)①如果头到i的元素个数是小于等于k的,就在头到i这一段进行递归的select

②如果k大于前面的个数,就在后面这段进行查找select

(3)如果是只有一个元素了,就直接返回这个位置。

template<class Type>
Type RandomizedSelect(Type a[ ], int p, int r, int k)
{
      if (p==r) return a[p];
   //一次快速排序,随机选择基准元素,划分数组
     int i=RandomizedPartition(a,p,r), 
      j=i-p+1;        // j为a[p,i]中元素个数
      if (k<=j) return RandomizedSelect(a,p,i,k);
   //返回第k-j小元素
      else return RandomizedSelect(a,i+1,r,k-j);
}

3、时间复杂度:

=

最坏的情况:我想要找最小的,但总是随机选中一个最大的做划分,此时:

T(n)=O(n2)

4、改进:如果每次都可以找到一个数字,使得至少划分出来的个数为原来的b倍,则也可以确保时间复杂度为:O(n)

十、选择划分基准

1、

2、注意:(1)其中组别是从0到(p-q+4)=((p-q+1)-5)/5,(数组长度)

(2)在75长度以内的数组可以自行通过排序直接查找,效率有保障。

(3)其中最主要的有三步:

①每组进行排序并将中位数交换位置(c1n)

②在中位数中select选择中位数(T(5/n))

③在选择出的段中进行再一次的select(T(3n/4))

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!