分治(Divide and Conquer)算法之归并排序

Posted spacerunnerZ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分治(Divide and Conquer)算法之归并排序相关的知识,希望对你有一定的参考价值。

顾名思义,分治问题由“分”(divide)和“治”(conquer)两部分组成,通过把原问题分为子
问题,再将子问题进行处理合并,从而实现对原问题的求解。我们在排序章节展示的归并排序就
是典型的分治问题,其中“分”即为把大数组平均分成两个小数组,通过递归实现,最终我们会
得到多个长度为1 的子数组;“治”即为把已经排好序的两个小数组合成为一个排好序的大数组,
从长度为1 的子数组开始,最终合成一个大数组。

分治策略Divide and Conquer

在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,通常是递归算法,就是 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换。

任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可…而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的。


分治策略的基本思想

分治算法是 将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。如果原问题可分割成 k 个子问题,1<kn,且这些子问题都可解并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的。由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。这自然导致递归过程的产生。分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。

熟悉的例子

二分查找: 给定已按升序排好序的 n 个元素a[0:n1],现要在这 n 个元素中找出一特定元素x

算法 1 binarysearch(T,x)
输入:排好序的数组 T ;数x
输出 j
1. l1;rn
2. while lr do
3.      ml+r2
4.      if T[m]=x then return m
5.      else if T[m]x then rm1
6.      else lm+1
7. return 0

分析:

1、该问题的规模缩小到一定的程度就可以容易地解决;
2、该问题可以分解为若干个规模较小的相同问题;
3、分解出的子问题的解可以合并为原问题的解;
4、分解出的各个子问题是相互独立的。

很显然此问题分解出的子问题相互独立,即在 a[i] 的前面或后面查找x是独立的子问题,因此满足分治法的第四个适用条件。

#include <iostream>
using namespace std;

// 查找成功返回value索引,查找失败返回-1
template <class T>
int binary_search(T array[],const T& value,int left,int right)
    while (right >= left) 
        int m = (left + right) / 2;
        if (value == array[m])
            return m;
        if (value < array[m])
            right = m - 1;
        else
            left = m + 1;
    
    return -1;


int main()

    int array[] = 0,1,2,3,4,5,6,7,8,9;

    cout << "0 in array position: " << binary_search(array,0,0,9) << endl;
    cout << "9 in array position: " << binary_search(array,9,0,9) << endl;
    cout << "2 in array position: " << binary_search(array,2,0,9) << endl;
    cout << "6 in array position: " << binary_search(array,6,0,9) << endl;
    cout << "10 in array position: " << binary_search(array,10,0,9) << endl;

    return 0;

算法复杂度分析

W(n)=W(n2)+1W(1)=1

每执行一次算法的while循环, 待搜索数组的大小减少一半。因此,在最坏情况下,while循环被执行了O(logn) 次。循环体内运算需要O(1) 时间,因此整个算法在最坏情况下的计算时间复杂性为O(logn) 。

上述算法就是用分治算法,它们的共同特点是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解

分治算法的一般性描述

对这k个子问题分别求解。如果子问题的规模仍然不够小,则再划分为k个子问题,如此递归的进行下去,直到问题规模足够小,很容易求出其解为止。将求出的小规模的问题的解合并为一个更大规模的问题的解,自底向上逐步求出原来问题的解。分治算法divide-and-conquer的伪码描述如下:

算法2: divideandconquer(P)
1. if(|P|c) S(P) // 解决小规模的问题
2. divide P into smaller subinstances P1,P2,,Pk //分解问题
3. for i=1 to k do
4. yidivideandconquer(Pi) //递归的解各子问题
5. ReturnMerge(y1,y2,以上是关于分治(Divide and Conquer)算法之归并排序的主要内容,如果未能解决你的问题,请参考以下文章

分治法 ( Divide And Conquer ) 详解

LeetCode分治法 divide and conquer (共17题)

图解排序算法之归并排序

图解排序算法之归并排序

图解排序算法之归并排序

图解排序算法之归并排序