汉诺塔问题--递归版

问题:有三个柱子放在左中右三个位置,左边柱子上有n个碟子(都不一样大)从上到下按从小到大的顺序叠起来放置,最底下是第n个碟子,最大。现要将n个碟子从左边柱子移动到右边柱子。要求:每次只能移动一个碟子;大碟子不能压在小碟子上;只能往相邻的柱子移动。求移动碟子的最少次数,并输出移动次数最少时的每个移动过程。
《汉诺塔问题--递归版》

递归版本

n个碟子的问题可以这么考虑:先将n-1个碟子从左柱子依次移动到中间、右边的柱子,然后将最底下的碟子移到中间,再将n-1个碟子移动回左边,第n个碟子移到右边。如此一来,规模为n的问题就变成了n-1规模的问题,可以采用递归进行求解。

#include<string>
#include<iostream>
using std::string;
using std::cout;
using std::endl;

//用于打印每个移动步骤,num为碟子序号,origin为移动前所在的柱子,dest为移动后所在的柱子
void printStep(int num, string origin, string dest) {
    cout << "Move " << num << " from " << origin << " to " << dest << endl;
}

printStep用于打印每个移动步骤,注意由于移动只能发生在相邻的柱子之间,此函数的发出地,终点也应该是相邻的

//将num个碟子从origin移动到dest的函数,left,mid,right为左中右三个柱子的名称
//函数返回移动的总次数
int process(int num, string left, string mid, string right, string origin, string dest) {
    //假设输入的出发点和终点位置不同
    //递归基,如果只有一个碟子,直接将其移动到目的柱子处即可
    if (num == 1) {
        if (origin == mid || dest == mid) {
            printStep(num, origin, dest);
            return 1;
        }
        else
        {
            printStep(num, origin, mid);
            printStep(num, mid, dest);
            return 2;
        }
    }
    int sum = 0;//用于记录移动总次数
    //移动分多种情况,从mid移动到两边,从两边移动到中间,或者从一端移动到另一端
    if (origin == mid) {
    //从mid移动到某一端时,先将最上面的n-1个移动到dest对面的另一端,记为another
        string another = (dest == left ? right : left);
        sum += process(num - 1, left, mid, right, mid, another);
        //然后将第n个碟子移到目的地
        printStep(num, origin, dest);
        //最后将处于另一端的n-1个碟子移到目的地
        sum += process(num - 1, left, mid, right, another, mid);
        sum += process(num - 1, left, mid, right, mid, dest);
    }
    else if (dest == mid) {
    //如果从某一端移动到中间柱子,同理,先将n-1个碟子移到另一端,再将第n个碟子移到中间,最后将n-1个碟子移到中间
        string another = (origin == left ? right : left);
        sum += process(num - 1, left, mid, right, origin, dest);
        sum += process(num - 1, left, mid, right, dest, another);
        printStep(num, origin, dest);
        sum += process(num - 1, left, mid, right, another, dest);
    }
    else {
    //从一端移到另一端
        sum += process(num, left, mid, right, origin, mid);
        sum += process(num, left, mid, right, mid, dest);
    }
    return sum;
}

最后的HanoiTower1函数,利用了process函数进行递归。

int HanoiTower1(int num, string left, string mid, string right) {
    if (num < 1)
        return 0;
    else if (num == 1) {
        printStep(num, left, mid);
        printStep(num, mid, right);
        return 2;
    }
    int sum = 0;
    //前n-1个碟子移动到右柱子
    sum += process(num - 1, left, mid, right, left, mid);
    sum += process(num - 1, left, mid, right, mid, right);
    //第n个碟子移到中间
    printStep(num, left, mid);
    //前n-1个碟子移动到左柱子
    sum += process(num - 1, left, mid, right, right, mid);
    sum += process(num - 1, left, mid, right, mid, left);
    //第n个碟子移到右边
    printStep(num, mid, right);
    sum += 2;//加上两次第n个碟子的移动
    //化为了n-1规模的子问题
    sum += HanoiTower1(num - 1, left, mid, right);
    return sum;
}

主函数

int main()
{
    int x=HanoiTower1(2, "L", "M", "R");
    cout << "Total steps num is: " << x << endl;
    return 0;
}

递归部分写得比较啰嗦,为了便于理解,其实优化后的代码HanoiTower1函数可以写得很简单。

引用自《程序员代码面试指南》

    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/xipitu/article/details/81331192
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞