本篇主要是围绕着分治算法的概念、思想、策略以及步骤四个方向叙述,同时通过汉诺塔游戏的讲解,促进大家对分治算法的理解。


一、基本概念  

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

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


二、基本思想及策略

分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。

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


三、使用步骤

(1)使用分治法的基本步骤:

1. 分解

将原问题分解为若干规模较小,相互独立,与原问题相同的子问题。

2. 解决

若干子问题较小而容易被解决则直接解决,否则再继续分解为更小的子问题,直到容易解决。

3. 合并

将已求解的各个子问题的解,逐步合并为原问题的解。

(2)分治算法能解决的问题,一般需要满足下面这几个条件:

1. 原问题与分解成的小问题具有相同的模式;

2. 原问题分解成的子问题可以独立求解,子问题之间没有相关性,这一点是分治算法跟动态规划的明显区别;

3. 具有分解终止条件,也就是说,当问题足够小时,可以直接求解;

4. 可以将子问题合并成原问题,而且这个合并操作的复杂度不能太高,否则就起不到减小算法总体复杂度的效果了。


四、分治算法最佳实践-汉诺塔

(1)汉诺塔的传说

汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64 片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

假如每秒钟一次,共需多长时间呢?移完这些金片需要5845.54 亿年以上,太阳系的预期寿命据说也就是数百亿年。真的过了5845.54 亿年,地球上的一切生命,连同梵塔、庙宇等,都早已经灰飞烟灭。

(2)汉诺塔游戏的演示和思路分析:

如果是有一个盘, A->C

如果我们有n >= 2 情况,我们总是可以看做是两个盘1.最下边的盘2. 最上面的(所有盘)看成一个盘

1. 先把最上面的所有盘A->B

2. 把最下边的盘A->C

3. 把B 塔的所有盘从B->C

(3)代码实现

package com.qf.math;

public class Hanoitower {
    public static void main(String[] args) {
        char [] arr={'A','B','C'};
        hanoitower(4,'A','B','C');
    }

    public static void hanoitower(int num,char a,char b,char c){
        if (num==1){
            //盘数为1,做一次搬迁,从A移动到C柱
            System.out.println("第1个盘从"+a+"移动到"+c+"盘");
        }else{
            //盘数大于1
            //先从a盘最上面的盘值最下面的盘之间的所有盘,移动到b盘,C盘作为中间盘
            hanoitower(num-1,a,c,b);

            //把最底下的那个盘,从a盘移动到c盘
            System.out.println("第"+num+"个盘从"+a+"移动到"+c+"盘");

            //把b盘的所有盘,移动到c盘上
            hanoitower(num-1,b,a,c);
        }

    }
}


点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)