算法复习-递归与分治策略

分治(divide and conquer)策略的基本思想:

  将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。递归地解这些子问题,然后将各子问题的解合并得到原问题的解。

大致可以通过如下模式来描述:

  divide_and_conquer( P ){

      if(|P|<= n0) adhoc(P);

     divide P into smaller subinstances P1,P2,…,Pk;

    for( i = 1 ; i <= k;i++){

         yi = divide-and-conquer(Pi);

   }

   return merge(y1,y2,…yk);

}

其中|P|表示问题P的规模.(具体见王晓东的算法设计与分析或者算法导论中的相关内容)

以下为分之策略的典型算法:

1.归并排序(MergeSort)的基本思想是:将待排序元素分成大小大致相同的两个子集合,分别对两个子集合进行排序,最终将排好序的子集合合并成所要求的排好序的集合。

大致可以通过如下模式来描述:

void MergeSort(Type arr[],int l,int r){
                //MergeSort l…(l+r)/2,对l到(l+r)/2进行归并排序
                MergeSort(arr,l,(l+r)/2);

                //MergeSort (l+r)/2+1 … r,对(l+r)/2+1到r进行归并排序
                MergeSort(arr,(l+r)/2+1,r);

                //Merger,合并已经排好序的两个子序列,使整个序列有序。

                merge(arr,l,r);

}

具体代码如下:最后的合并我使用的事直接插入排序。

    template<class T>
    void MergeSort(T arr[],int l,int r){
        if(l < r){
               //MergeSort l...(l+r)/2
            MergeSort(arr,l,(l+r)/2);
            //MergeSort (l+r)/2+1 ... r
            MergeSort(arr,(l+r)/2+1,r);
            //Merger,direct insert sort
            for(int i = (l+r)/2+1 ; i <= r ; i++ ){
                T temp = arr[i];
                int j  = i-1;				//有序序列元素逐渐增多,待排序列元素逐渐减少。
                for(j; j >= 0 ; j--){
                    if(arr[j] > temp ){
                        arr[j+1] = arr[j];
                    }else{
                        break;	
                    }
                }
                arr[j+1] = temp;
            }
        }
    }

但其实,在合并部分使用直接插入有可能是件极糟糕的事情,在最坏情况下,合并操作(Merge)的时间复杂度为O(n^2),例如,无序序列{6,7,8,5 ,3,1,4,2}在最后一趟归并中形成了有序序列{5,6,7,8} 和{1,2,3,4},执行合并操作时,由于算法是直接插入,必然是属于最坏的情况。对于有n个元素的类似的无序序列,最坏情况下,其时间复杂度为O(n^2).

如果改变合并操作(Merge)的算法,则可保证合并操作在O(n)内完成,肯定能猜到,它就是合并两个有序链表的算法。

====2013年3月26日更新===========================================

归并操作过程:https://zh.wikipedia.org/wiki/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F#.E5.BD.92.E5.B9.B6.E6.93.8D.E4.BD.9C

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针达到序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

============================================================

此时,整个归并排序在最坏情况下所需的计算时间T(n)满足

             T(n) =                  {          O(1)                              n <= 1

                                          {         2T(n/2)+O(n)               n > 1

             解此递归方程可得  T(n)  = O(nlogn)   ,由于排序问题的计算时间下界为nlogn,故归并排序算法是一个渐进最优算法。

2.快速排序

快速排序的思想:

算法代码:

template<class T>
void quickSort(T arr[],int l,int r ){
    if(l < r){
        //一趟排序
        int t = l;
        T temp = arr[t];
        int i = l,j=r;
        while(i < j){
            for(;arr[j]>= temp && i<j ;j-- ){
            }
            if(arr[j]< temp && i<j){	//交换
                arr[t] = arr[j];
                t = j;
                i++;
            }
            for(;arr[i]<= temp && i<j ; i++){
            }
            if(arr[i] > temp && i<j){
                arr[t] = arr[i];
                t = i;
                j--;
            }
        }
        arr[i] = temp;
        t = i;
        //递归调用,分解问题
        quickSort(arr,l,t-1);
        quickSort(arr,t+1,r);
    }
}

时间复杂度:O(nlogn)

    原文作者:递归与分治算法
    原文地址: https://blog.csdn.net/gaoyingju/article/details/7538138
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞