汉诺塔递归算法

起源:

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

抽象为数学问题:

  如下图所示,从左到右有A、B、C三根柱子,其中A柱子上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间只有一个原则:一次只能移到一个盘子且大盘子不能在小盘子上面,求移动的步骤和移动的次数

hanoi.jpeg

 

解:(1)n == 1

             第1次  1号盘  A---->C       sum = 1 次

       (2)  n == 2

             第1次  1号盘  A---->B

             第2次  2号盘  A---->C

             第3次  1号盘  B---->C        sum = 3 次

  (3)n == 3

        第1次  1号盘  A---->C

        第2次  2号盘  A---->B

        第3次  1号盘  C---->B

        第4次  3号盘  A---->C

        第5次  1号盘  B---->A

        第6次  2号盘  B---->C

        第7次  1号盘  A---->C        sum = 7 次

不难发现规律:1个圆盘的次数 2的1次方减1

       2个圆盘的次数 2的2次方减1

                         3个圆盘的次数 2的3次方减1

                         。  。   。    。   。 

                         n个圆盘的次数 2的n次方减1

 故:移动次数为:2^n - 1

有两种解法,一种是递归,一种是栈。先来看递归的实现

  • 将 n - 1 个圆盘从 A 移动到 C(借助 B)
  • 将第 n 个圆盘从 A 移动到 B
  • 将 n - 1 个圆盘从 C 移动到 B(借助 A)

移动次数为 2 的 n 次方 - 1

let count = 0
function move(number, from, to, depend) {console.log(`将第 ${number} 号圆盘从 ${from} 移动到 ${to}`)count++
}
// 将 n 个圆盘从 a 移动到 b 借助 c
function hanoi(n, a, b, c) {if (n === 0) {return}hanoi(n - 1, a, c, b) // 将 n -1 个圆盘从 a 移动到 c,借助 bmove(n, a, b) // 将第 n 个圆盘从 a 移动到 bhanoi(n - 1, c, b, a) // 将 n -1 个圆盘从 c 移动到 b,借助 a
}
hanoi(3, 'A', 'B', 'C')
console.log('移动次数', count)
// 将第 1 号圆盘从 A 移动到 B
// 将第 2 号圆盘从 A 移动到 C
// 将第 1 号圆盘从 B 移动到 C
// 将第 3 号圆盘从 A 移动到 B
// 将第 1 号圆盘从 C 移动到 A
// 将第 2 号圆盘从 C 移动到 B
// 将第 1 号圆盘从 A 移动到 B
// 移动次数 7

重构上面,使用一个函数

function hanoiRecursion(n, a, b, c, moves = []) {if (n === 0) {return moves}hanoiRecursion(n - 1, a, c, b, moves) // 将 n -1 个圆盘从 a 移动到 c,借助 bmoves.push([a, b]) // move(n, a, b) // 将第 n 个圆盘从 a 移动到 bhanoiRecursion(n - 1, c, b, a, moves) // 将 n -1 个圆盘从 c 移动到 b,借助 areturn moves
}
let moves = hanoiRecursion(3, 'A', 'B', 'C')
console.log('移动路径', moves)
console.log('移动次数', moves.length)
// // 移动路径
// // [
// //  ["A", "B"], ["A", "C"], ["B", "C"], ["A", "B"],
// //  ["C", "A"], ["C", "B"], ["A", "B"]
// // ]
// // 移动次数 7

使用栈其实也需要使用递归,只是我们通过 3 个栈,表示三个圆柱,可以实时看对应的效果

// 栈类
class Stack {constructor() {this.count = 0;this.items = {};}// 向栈中插入元素push(element) {this.items[this.count] = element;this.count++;}isEmpty() {return this.count === 0;}size() {return this.count;}get length() {return this.count;}// 从栈中弹出元素pop() {if (this.isEmpty()) {return undefined;}this.count--;const result = this.items[this.count];delete this.items[this.count];return result;}peek() {if (this.isEmpty()) {return undefined;}return this.items[this.count - 1];}clear() {this.items = {};this.count = 0;// 或者// while(!this.isEmpty()) {//   this.pop()// }}toString() {if (this.isEmpty()) {return "";} else {let i = 0,len = this.count,result = "";while (i < len) {result += this.items[i];if (i !== len - 1) {result += ",";}i++;}return result;}}
}function hanoi(n, source, dest, depend, a, b, c, moves = []) {if (n === 0) {return}// 将 n - 1 个圆盘从 source 移动到 dependhanoi(n - 1, source, depend, dest, a, c, b, moves) moves.push([a, b])// 将第 n 个圆盘从 source 移动到 destdest.push(source.pop()) // 将 n - 1 个圆盘从 depend 移动到 desthanoi(n - 1, depend, dest, source, c, b, a, moves) 
}
function hanoiStack(n) {let source = new Stack()let dest = new Stack()let depend = new Stack()let count = nwhile (count) {source.push(count--)}let moves = []console.log('source: ', source)hanoi(n, source, dest, depend, 'A', 'B', 'C', moves)console.log('source: ', source)console.log('dest: ', dest)console.log('depend: ', depend)return moves
}
console.log(hanoiStack(3))
// source:  Stack { count: 3, items: { '0': 3, '1': 2, '2': 1 } }
// source:  Stack { count: 0, items: {} }
// dest:  Stack { count: 3, items: { '0': 3, '1': 2, '2': 1 } }
// depend:  Stack { count: 0, items: {} }
// [
//   [ 'A', 'B' ],
//   [ 'A', 'C' ],
//   [ 'B', 'C' ],
//   [ 'A', 'B' ],
//   [ 'C', 'A' ],
//   [ 'C', 'B' ],
//   [ 'A', 'B' ]
// ]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247257.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java编程基础阶段笔记 day 07 面向对象编程(上)

​ 面向对象编程 笔记Notes 面向对象三条学习主线 面向过程 VS 面向对象 类和对象 创建对象例子 面向对象的内存分析 类的属性&#xff1a;成员变量 成员变量 VS 局部变量 类的方法 方法的重载 可变个数形参 面向对象&#xff1a;封装性 访问权限修饰符 构造方法&…

Vue/Angular中父窗口新开的子窗口关闭的时候刷新父窗口

最近遇到一个项目需求&#xff1a;Angular中父窗口新开的子窗口提交完信息关闭的时候刷新父窗口。 知识点&#xff1a; window.opener 概述 返回打开当前窗口的那个窗口的引用&#xff0c;例如&#xff1a;在window A中打开了window B&#xff0c;B.opener 返回 A. 语法 …

HDU 6631 line symmetric(枚举)

首先能想到的是至少有一对相邻点或者中间间隔一个点的点对满足轴对称&#xff0c;那么接下来只需要枚举剩下的点对是否满足至多移动一个点可以满足要求。 第一种情况&#xff0c;对于所有点对都满足要求&#xff0c;那么Yes。 第二种情况&#xff0c;有一个点不满足要求&#x…

学习数字图像处理经验谈

一、面向应用&#xff1a;层层分解、抓住要点 我们学习数字图像处理的最终目的还是应用&#xff0c;不管是用它来研制产品还是研发项目抑或是研究课题&#xff0c;都要用数字图像处理的理论、方法和技术来解决实际问题。在此过程中&#xff0c;提高效率是非常重要的&#xff0c…

react-router-dom@6获取路由传参

目录 参数获取 1、子路由形式携带 2、问号(?)形式参数 3、事件跳转传参 router/index.tsx import App from "App"; import Home from "pages/Home"; import List from "pages/List"; import Detail from "pages/Detail"; import…

koa洋葱模型

Koa 和 Express 都会使用到中间件 Express的中间件是顺序执行&#xff0c;从第一个中间件执行到最后一个中间件&#xff0c;发出响应如上图 Koa是从第一个中间件开始执行&#xff0c;遇到 next 进入下一个中间件&#xff0c;一直执行到最后一个中间件&#xff0c;在逆序&#x…

koa后端允许跨域

举个例子 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewport" content"widthdevice-width…

python面向对象之继承

面向对象之继承 什么是面向对象的继承&#xff1f; 继承&#xff08;英语&#xff1a;inheritance&#xff09;是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B&#xff0c;就把这个A称 为“B的子类别”&#xff0c;而把B称为“A的父类别”也可以称“B是…

美国正面临“人才泡沫”破裂危机?

&#xff08;Jason Lane和Kevin Kinser/文&#xff09;最近&#xff0c;与教育有关的种种问题在美国社会引起了广泛讨论。首先巨额的学生贷款问题&#xff1a;根据美联储纽约分行在2012年11月发布的一份报告&#xff0c;全美学生贷款总额已经达到420亿美元&#xff0c;其中新增…

ngrx学习笔记

什么是ngrx ngrx是Angular基于Rxjs的状态管理&#xff0c;保存了Redux的核心概念&#xff0c;并使用RxJs扩展的Redux实现。使用Observable来简化监听事件和订阅等操作。 在看这篇文章之前&#xff0c;已经假设你已了解rxjs和redux。 有条件的话请查看官方文档进行学习理解。 所…

.slice(0)

高手代码里看到.slice(0)&#xff0c;查了下这样写的好处&#xff1a; 1.对原数组进行深拷贝&#xff0c;这样进行一系列操作的时候就不影响原数组了&#xff1b; 2.将类数组对象转化为真正的数组对象&#xff1a;var anchorArray [].slice.call(document.getElementsByTagN…

亲历2013年TED大会:全球最潮灵感大会

本文转自&#xff1a;http://mooc.guokr.com/opinion/436837/ 本文由《外滩画报》供稿 文/华琪&#xff08;发自美国&#xff09; 编辑/吴慧雯 什么是TED的世界&#xff1f;在这里&#xff0c;你可以轻易发现各种名人出没的痕迹&#xff0c;和各个领域里最具远见卓识和创造…

Java生鲜电商平台-电商会员体系系统的架构设计与源码解析

Java生鲜电商平台-电商会员体系系统的架构设计与源码解析 说明&#xff1a;Java生鲜电商平台中会员体系作为电商平台的基础设施&#xff0c;重要性不容忽视。我去年整理过生鲜电商中的会员系统&#xff0c;但是比较粗&#xff0c;现在做一个最好的整理架构. 设计电商会员体系需…

为什么要上大学?

为了让自己成为更有意思的人。 &#xff08;文&#xff0f;美国圣母大学哲学教授 Gary Gutting&#xff09;再不久&#xff0c;千千万万的大学生又将走完一个学期。他们中的很多人以及他们的家人&#xff0c;可能为刚刚过去的几个月或是几年投入了相当可观的时间、努力以及金钱…

打造“神犇”是教育的未来吗?

这年头&#xff0c;品学兼优、身怀特长的“神犇”&#xff0c;拼的不仅是天赋异禀和后天努力&#xff0c;更是身后爹妈的钱包&#xff0c;而本该实现社会公平的教育&#xff0c;反而加速和凝固了社会的不公。 高等教育的终极目标真的是造就学业超人吗&#xff1f;《纽约时报》刊…

AntD 官网样例 InputRef报错原因

在官网可编辑表格typescript样例里 const inputRef useRef<InputRef>(null); InputRef项目报错原因是ant design的版本问题! antd 4.19版本重写了input 可通过InputRef来使用input组件的ref

AntD 可编辑行表格

本地数据代码模板自用,官网例子改改 // 编辑行的自定义表格 import React, { useState } from "react"; import {Table,Input,InputNumber,Popconfirm,Form,Typography,Divider, } from "antd";interface Item {key: string;name: string;age: number;add…

三心二意,助你好运?

经验说&#xff1a;做事要专心致志。 实验说&#xff1a;专心致志常常让人缺少一双发现的眼睛。 专心致志从来都被当做一个美德来歌颂。从来我们就认为要想成为伟大的人就必须要像牛顿老师那样把钟当成吃的放到锅里煮才行&#xff0c;至少至少也得有能在集市上看书的本事。否则…

React Antd Upload自定义上传customRequest

单独的上传图片接口要传参,action方式不太适合,需要使用自定义上传customRequest覆盖 公司代码不可弄,就发一个可用的demo例子 import React, { useState } from "react"; import { render } from "react-dom"; import "antd/dist/antd.css"; i…

HDU 4109 Instrction Arrangement

题目链接&#xff1a;https://vjudge.net/problem/HDU-4109 题目大意 有 N 个指令&#xff0c;标号从 0 ~ N - 1&#xff0c;和 M 个指令间的先后关系&#xff0c;每个关系都有一个权值 w&#xff0c;表示后一个指令在前一个指令开始时间之后 w 纳秒才开始执行。现在要并发执行…