分治算法一:汉诺塔

一、分治算法与递归

(1)分治算法,是指将一个规模较大的问题分解成多个小规模的子问题进行处理;当问题规模足够小时,处理策略就相当简单明了了;
(2)若这些子问题有相似的处理方法,则可以使用递归结构处理;
(3)递归-三步走
递归出口:通常是问题规模最小时,最简处理策略;
递归过程:将大规模问题,逐步拆解成小问题的过程,递归的精髓皆在于此;
本层递归处理:各问题处理过程的衔接,还未理解透彻,处理比较麻烦。
(4)分治策略:
分解:将问题分解成若干个子问题;
治理:递归解决各子问题;
合并:将子问题的解合并成原问题的一个解。

二、汉诺塔问题

(1)问题说明:有三根柱子A、B、C,A柱上有n个大小不同的圆盘,大盘在下,小盘在上;需要将A柱上的盘子移动到C柱上,同样保持大盘在下小盘在上,移动过程中不允许大盘叠放在小盘上。
(2)分析:当仅A柱上仅有一个圆盘时,直接将其放到C柱上即可——最小规模问题,递归出口
若A上有n(n>1)个圆盘,则将其前n-1个圆盘移动到过渡柱B上,然后将其转化成最小规模问题处理——分解策略,递归过程
(3)因为盘子最后都要放到一个柱子上,且大盘在下小盘在上(默认没有相同大小的盘子),所以各个盘子都是唯一的

三、代码实现

// 查找x柱子上最底下一个圆盘的下标(当前x柱子上仅剩下一个圆盘,所以在current数组中有唯一下标)
int pickTopDisk(char *current, char x)
{
    int i = 0;
    while (current[i] != x) {
        i++;
    }
    return i;
}

/*
 * Description:三根木柱,将一根木柱上的圆盘借助中间木柱移动到另外一根木柱上,大圆盘不能放在小圆盘上
 * input: 各圆盘的位置数组current,圆盘数量num,圆柱位置标识A、B、C
 *        current的下标可以理解为各盘子的标记,下标越大盘子越大
 *        current的内容表示当前该盘子放置的位置,A-源柱子,B-过渡柱子,C-目标柱子
 * output: 动态输出圆盘移动步骤
 */
static int hanoiTower(char *current, int num, char A, char B, char C)
{
    // count表示移动次数
    static int count = 0;
    int i = 0;
    // 递归出口:当只有一个盘子时,直接将其移动到目标盘
    // 仅有一个盘子时,此时盘子应在A柱子上(单个递归过程),需要明确该盘子的大小,即在current中的下标,将该下标对应内容改成C柱即可
    if (num == 1) {
        // 当前current中,仅有一个A,获取其下标
        i = pickTopDisk(current, A);
        // 模拟从A柱移动到C柱的操作,并增加操作次数
        current[i] = C;
        count++;
        printf("move %d disk %d: %c -> %c \n", count, i + 1, A, C);
        return 0;
    }
    // 递归过程:当当前需要移动的盘子不只一个时,需要先将A柱的n-1个盘子移动到B盘(此时,A依旧是源柱,B成为目标柱,C为过渡柱)
    hanoiTower(current, num - 1, A, C, B);
    // 本层递归处理:通过上一步骤的处理后,A柱上只剩下最大一个盘,下标为n-1,将其移动到C上
    current[num - 1] = C;
    count++;
    printf("move %d disk %d: %c -> %c \n", count, num, A, C);
    // 此时,B上有很多A之前的盘子,A空了,C上有一个A之前最大的盘子
    // 下面将B作为源柱,A作为过渡,C依旧为目标柱,且需要移动的盘子数量减一(因为已经将最大的盘子移动到C柱了)
    hanoiTower(current, num - 1, B, A, C);
    return 0;
}

四、测试结果

测试代码:

#include <stdio.h>

#define DISK_NUM 4

// hanoiTower()函数实现

int main(void)
{
    char current[DISK_NUM] = {'A', 'A', 'A', 'A'};
    char A = 'A';
    char B = 'B';
    char C = 'C';
    hanoiTower(current, DISK_NUM, A, B, C);
    while (1);
    return 0;
}

测试结果:

posted @ 2021-01-24 15:11  Pangolin2  阅读(287)  评论(0编辑  收藏  举报