當然、這是一個經典的遞歸問題~
?? 想必來看這篇博文的同學對漢諾塔應該不會陌生了吧,
寫這篇博還是有初衷的:
之前學數據結構的時候自己看書、也上網上查了很多資料,資料都比較散、而且描述的不是很清楚,對于當時剛剛
接觸算法的我,要完全理解還是有一定難度。今天剛好有時間就整理了下思路、重寫分析了一下之前的疑惑的地方、
沒有透徹的地方便都豁然開朗了。所以迫不及待把我的想法記錄下來,和大家分享。
如果你也是和之前的我一樣對hanoi tower沒能完全消化,或者剛剛接觸漢諾塔,那希望我的這種理解方式能給你些
許幫助,如果你覺得已經完全掌握的比較牢靠了,那也可以看看,有好的idea可以一起分享;畢竟交流討論也是一種很好的
學習方式。
好了,廢話不多說,切入正題。
關于漢諾塔起源啊、傳說啊神馬的就不啰嗦了,我們直接切入正題:
問題描述:
有一個梵塔,塔內有三個座A、B、C,A座上有諾干個盤子,盤子大小不等,大的在下,小的在上(如圖)。
把這些個盤子從A座移到C座,中間可以借用B座但每次只能允許移動一個盤子,并且在移動過程中,3個座上的盤
子始終保持大盤在下,小盤在上。
描述簡化:把A柱上的n個盤子移動到C柱,其中可以借用B柱。

我們直接假設有n個盤子:
先把盤子從小到大標記為1、2、3......n
先看原問題三個柱子的狀態:
狀態0 A:按順序堆放的n個盤子。B:空的。C:空的。
目標是要把A上的n個盤子移動到C。因為必須大的在下小的在上,所以最終結果C盤上最下面的應該是標號為n的盤子,試想:
要取得A上的第n個盤子,就要把它上面的n-1個盤子拿開吧?拿開放在哪里呢?共有三個柱子:A顯然不是、如果放在C上
了,那么最大的盤子就沒地方放,問題還是沒得到解決。所以選擇B柱。當然,B上面也是按照大在下小在上的原則堆放的
**(記住:先不要管具體如何移動,可以看成用一個函數完成移動,現在不用去考慮函數如何實現。這點很重要)。**
很明顯:上一步完成后三個塔的狀態:
狀態1:? A:只有最大的一個盤子。B:有按規則堆放的n-1個盤子。C空的。
上面的很好理解吧,好,其實到這里就已經完成一半了。(如果前面的沒懂,請重看一遍。point:不要管如何移動!)
我們繼續:
這時候,可以直接把A上的最大盤移動到C盤,移動后的狀態:
中間狀態: A:空的。B:n-1個盤子。C:有一個最大盤(第n個盤子)
要注意的一點是:這時候的C柱其實可以看做是空的。因為剩下的所有盤子都比它小,它們中的任何一個都可以放在上面,也就是 ??? ? C柱上。
所以現在三個柱子的狀態:
中間狀態: A:空的。B:n-1個盤子。C:空的
想一想,現在的問題和原問題有些相似之處了吧?。。如何更相似呢?。顯然,只要吧B上的n-1個盤子移動到A,待解決的問題和原問題就相比就只是規模變小了
現在考慮如何把B上的n-1個盤子移動到A上,其實移動方法和上文中的把n-1個盤從A移動到B是一樣的,只是柱子的名稱換了下而已。。(如果寫成函數,只是參數調用順序改變而已)。
假設你已經完成上一步了(同樣的,不要考慮如何去移動,只要想著用一個函數實現就好),請看現在的狀態:
狀態2: A:有按順序堆放的n-1個盤子。B:空的。C:按順序堆放的第n盤子(可看為空柱)
就在剛才,我們完美的完成了一次遞歸。如果沒看懂請從新看一遍,可以用筆畫出三個狀態、靜下心來慢慢推理。
*我一再強調的:當要把最大盤子上面的所有盤子移動到另一個空柱上時,不要關心具體如何移動,只用把它看做一個函數可以完成即可,不用關心函數的具體實現。如果你的思路糾結在這里,就很難繼續深入了。*
到這里,其實 基本思路已經理清了。狀態2和狀態0,除了規模變小 ,其它方面沒有任何區別了。然后只要用相同的思維方式,就能往下深入。。。
好了,看看如何用算法實現吧:
定義函數Hanoi(a,b,c,n)表示把a上的n個盤子移動到c上,其中可以用到b。
定義函數move(m,n)表示把m上的盤子移動到n上
我們需要解決的問題正是 Hanoi (a,b,c,n)??? //上文中的狀態0
1、把A上的n-1個移動到B:? Hanoi (a,c,b,n-1);???????//?操作結束為狀態1
2、把A上的大盤子移動到C???????? move(a,c)
3、把B上的n-1移動到A Hanoi (b,c,a,n-1); //操作結束位狀態2(和狀態1相比只是規模變小)
如果現在還不能理解、請回過頭再看一遍、畢竟如果是初學者不是很容易就能理解的。可以用筆記下幾個關鍵的狀態,并且看看你有沒有真正的投入去看,獨立去思考了。
給出算法C代碼:

main()
{
int n;
printf("請輸入數字n以解決n階漢諾塔問題:\n");
scanf("%d",&n);
hanoi(n,'A','B','C');
}
void hanoi(char A,char B,char C,int n)
{
if(n==1)
{
printf("Move disk %d from %c to %c\n",A,C,n);
}
else
{
hanoi(A,C,B, n-1);
printf("Move disk %d from %c to %c\n",n,A,C);
hanoi(B,A,C,n-1,);
}
}
//以上代碼c-free5編譯通過。
//代碼出處:[http://www.cnblogs.com/yanlingyin/](http://www.cnblogs.com/yanlingyin/) 一條魚~

以上、如果有不對的地方、還希望您能指出。
我對遞歸的一點理解:
解決實際問題時、不能太去關心實現的細節(因為遞歸的過程恰恰是我們實現的方法)就像這個問題,如在第一步就過多的糾結于如何把n-1個盤子移動到B上、那么你的思路就很難繼續深入。只要看做是用函數實現就好,如果你能看出不管怎么移動,其實本質都一樣的時候,那么就能較快的得到結果了。就像這個案例,要注意到我們做的關鍵幾步都只是移動的順序有改變,其中的規則沒有改變,如
如果用函數表示的話,就只是參數調用的順序有所不同了。在遞歸的運用中、不用關心每一步的具體實現?,只要看做用一個函數表示就好。分析問題的時候,最好畫出自己的推理過程,得到有效的狀態圖。
思考問題講求思路的連貫性、力求盡快進入狀態,享受完全投入到一件事中的美妙感覺。
轉載請注明出處[http://www.cnblogs.com/yanlingyin/](http://www.cnblogs.com/yanlingyin/)
- 0-發現
- AndroidInterview-Q-A
- Android能讓你少走彎路的干貨整理
- LearningNotes
- temp
- temp11
- 部分地址
- 0-待辦任務
- 待補充列表
- 0-未分類
- AndroidView事件分發與滑動沖突處理
- Spannable
- 事件分發機制詳解
- 1-Java
- 1-Java-01基礎
- 未歸檔
- 你應該知道的JDK知識
- 集合框架
- 1-Java-04合集
- Java之旅0
- Java之旅
- JAVA之旅01
- JAVA之旅02
- JAVA之旅03
- JAVA之旅04
- JAVA之旅05
- JAVA之旅06
- JAVA之旅07
- JAVA之旅08
- JAVA之旅09
- java之旅1
- JAVA之旅10
- JAVA之旅11
- JAVA之旅12
- JAVA之旅13
- JAVA之旅14
- JAVA之旅15
- JAVA之旅16
- JAVA之旅17
- JAVA之旅18
- JAVA之旅19
- java之旅2
- JAVA之旅20
- JAVA之旅21
- JAVA之旅22
- JAVA之旅23
- JAVA之旅24
- JAVA之旅25
- JAVA之旅26
- JAVA之旅27
- JAVA之旅28
- JAVA之旅29
- java之旅3
- JAVA之旅30
- JAVA之旅31
- JAVA之旅32
- JAVA之旅33
- JAVA之旅34
- JAVA之旅35
- 1-Java-05辨析
- HashMapArrayMap
- Java8新特性
- Java8接口默認方法
- 圖解HashMap(1)
- 圖解HashMap(2)
- 2-Android
- 2-Android-1-基礎
- View繪制流程
- 事件分發
- AndroidView的事件分發機制和滑動沖突解決
- 自定義View基礎
- 1-安卓自定義View基礎-坐標系
- 2-安卓自定義View基礎-角度弧度
- 3-安卓自定義View基礎-顏色
- 自定義View進階
- 1-安卓自定義View進階-分類和流程
- 10-安卓自定義View進階-Matrix詳解
- 11-安卓自定義View進階-MatrixCamera
- 12-安卓自定義View進階-事件分發機制原理
- 13-安卓自定義View進階-事件分發機制詳解
- 14-安卓自定義View進階-MotionEvent詳解
- 15-安卓自定義View進階-特殊形狀控件事件處理方案
- 16-安卓自定義View進階-多點觸控詳解
- 17-安卓自定義View進階-手勢檢測GestureDetector
- 2-安卓自定義View進階-繪制基本圖形
- 3-安卓自定義View進階-畫布操作
- 4-安卓自定義View進階-圖片文字
- 5-安卓自定義View進階-Path基本操作
- 6-安卓自定義View進階-貝塞爾曲線
- 7-安卓自定義View進階-Path完結篇偽
- 8-安卓自定義View進階-Path玩出花樣PathMeasure
- 9-安卓自定義View進階-Matrix原理
- 通用類介紹
- Application
- 2-Android-2-使用
- 2-Android-02控件
- ViewGroup
- ConstraintLayout
- CoordinatorLayout
- 2-Android-03三方使用
- Dagger2
- Dagger2圖文完全教程
- Dagger2最清晰的使用教程
- Dagger2讓你愛不釋手-終結篇
- Dagger2讓你愛不釋手-重點概念講解、融合篇
- dagger2讓你愛不釋手:基礎依賴注入框架篇
- 閱讀筆記
- Glide
- Google推薦的圖片加載庫Glide:最新版使用指南(含新特性)
- rxjava
- 這可能是最好的RxJava2.x入門教程完結版
- 這可能是最好的RxJava2.x入門教程(一)
- 這可能是最好的RxJava2.x入門教程(三)
- 這可能是最好的RxJava2.x入門教程(二)
- 這可能是最好的RxJava2.x入門教程(五)
- 這可能是最好的RxJava2.x入門教程(四)
- 2-Android-3-優化
- 優化概況
- 各種優化
- Android端秒開優化
- apk大小優化
- 內存分析
- 混淆
- 2-Android-4-工具
- adb命令
- 一鍵分析Android的BugReport
- 版本控制
- git
- git章節簡述
- 2-Android-5-源碼
- HandlerThread 源碼分析
- IntentService的使用和源碼分析
- 2-Android-9-辨析
- LRU算法
- 什么是Bitmap
- 常見圖片壓縮方式
- 3-Kotlin
- Kotlin使用筆記1-草稿
- Kotlin使用筆記2
- kotlin特性草稿
- Kotlin草稿-Delegation
- Kotlin草稿-Field
- Kotlin草稿-object
- 4-JavaScript
- 5-Python
- 6-Other
- Git
- Gradle
- Android中ProGuard配置和總結
- gradle使用筆記
- Nexus私服搭建
- 編譯提速最佳實踐
- 7-設計模式與架構
- 組件化
- 組件化探索(OKR)
- 1-參考列表
- 2-1-組件化概述
- 2-2-gradle配置
- 2-3-代碼編寫
- 2-4-常見問題
- 2-9-值得一讀
- 8-數據結構與算法
- 0臨時文件
- 漢諾塔
- 8-數據-1數據結構
- HashMap
- HashMap、Hashtable、HashSet 和 ConcurrentHashMap 的比較
- 遲到一年HashMap解讀
- 8-數據-2算法
- 1個就夠了
- Java常用排序算法(必須掌握的8大排序算法)
- 常用排序算法總結(性能+代碼)
- 必須知道的八大種排序算法(java實現)
- 9-職業
- 閱讀
- 書單
- 面試
- 面試-01-java
- Java面試題全集駱昊(上)
- Java面試題全集駱昊(下)
- Java面試題全集駱昊(中)
- 面試-02-android
- 40道Android面試題
- 面試-03-開源源碼
- Android圖片加載框架最全解析(二),從源碼的角度理解Glide的執行流程
- 面試-07-設計模式
- 面試-08-算法
- 面試-09-其他
- SUMMARY
- 版權說明
- temp111