程序员面试金典 - 面试题 08.06. 汉诺塔问题

题目难度: 中等

原题链接

今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:

  1. 每次只能移动一个盘子;
  2. 盘子只能从柱子顶端滑出移到下一根柱子;
  3. 盘子只能叠在比它大的盘子上。

请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。

你需要原地修改栈。

示例 1:

  • 输入:A = [2, 1, 0], B = [], C = []
  • 输出:C = [2, 1, 0]

示例 2:

  • 输入:A = [1, 0], B = [], C = []
  • 输出:C = [1, 0]

提示:

  • A 中盘子的数目不大于 14 个。

题目思考

  1. 有什么方法来模拟整个过程?

解决方案

思路
  • 这个题目乍一看无从下手, 我们很难考虑所有细节, 得出中间某个状态下的盘子分布情况
  • 但我们可以换个角度思考, 将问题从大化小, 不再考虑中间细节:
    • 假设当前 A 有 n 个从上到下依次减小的盘子, B 和 C 都是空的 (即初始情况)
    • 我们可以通过某种手段, 将 A 上面的 n-1 个小盘子移动到 B, 然后将最下面最大的盘子移动到 C, 最后再将 B 的 n-1 个小盘子移动到 C
    • 这样就完成了将 A 的 n 个盘子移动到 C 的整个过程
  • 归纳总结上述过程, 我们不难发现, 从 A 到 C 移动 n 个盘子, 从 A 到 B 移动 n-1 个小盘子和从 B 到 C 移动 n-1 个小盘子三者具有相同的特点: 1. 目标柱子为空; 2. 都有额外一个空的柱子用于移动. 唯一不同的只是移动的盘子的数目
  • 所以我们可以将这一过程提取出来作为一个函数, 额外加入一个参数表示移动的盘子数目, 然后递归调用即可实现上述过程
  • 最后注意递归函数需要设置出口, 上述过程在 n=1 时就是简单的将 A 的栈顶盘子弹出并压入 C 中, 不再需要递归调用, 这也就是递归出口
  • 下面代码对应的就是上面整个分析, 并加入了必要的注释, 方便大家理解
复杂度
  • 时间复杂度 O(2^N): 移动 N 个盘子时需要两次递归调用移动 N-1 个盘子的函数, 所以总时间是 N 个 2 相乘, 即 2^N
  • 空间复杂度 O(N): 递归栈的消耗, 递归深度就是 N, 表示从 N 一路递归调用到递归出口 1 的过程(栈中保存 N 个函数)
代码
Python 3
class Solution:
    def hanota(self, A, B, C) -> None:
        """
        Do not return anything, modify C in-place instead.
        """
        # 递归+额外参数显式指定移动的盘子数+注意递归调用时的参数顺序
        def move(source, temp, target, n):
            # move函数表示source移动n个盘子到target的过程 (temp为空)
            if n == 1:
                # 递归出口, 只需要移动一个盘子, 直接弹出source的栈顶元素并压入target即可
                target.append(source.pop())
                return
            # 此时要移动超过1个盘子, 我们可以分为以下三个步骤:
            # 1. 先把source上面n-1个小盘子移动到temp
            move(source, target, temp, n - 1)
            # 2. 然后将source最大的盘子移动到target
            target.append(source.pop())
            # 3. 最后再递归调用该函数, 将temp的n-1个小盘子移动到target
            move(temp, source, target, n - 1)

        move(A, B, C, len(A))

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值