網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
c語(yǔ)言循環(huán)加數(shù)組實(shí)現(xiàn)漢諾塔問(wèn)題_C 語(yǔ)言
作者:用戶882121311605lv-1 ? 更新時(shí)間: 2022-04-09 編程語(yǔ)言簡(jiǎn)介
漢諾塔問(wèn)題是學(xué)數(shù)據(jù)結(jié)構(gòu)與算法的時(shí)候會(huì)遇到的問(wèn)題,相信來(lái)看本文的讀者應(yīng)該都對(duì)漢諾塔問(wèn)題有基本的了解,理論上所有的遞歸都可以改成循環(huán),常規(guī)的做法是借助堆棧,但是我一想好像用循環(huán)加數(shù)組也可以實(shí)現(xiàn),于是就有了本文,實(shí)現(xiàn)聲明,本文最后出來(lái)的算法效率不高的,比直接用遞歸實(shí)現(xiàn)還要差很多,追求算法效率的同學(xué)就不用看這個(gè)了。
題目:假設(shè)有3個(gè)柱子,分別為A、B、C,A柱子上有數(shù)量為n個(gè)的空心圓盤(pán),從上到下序號(hào)分別為1...n,要求把A柱子中的n個(gè)圓盤(pán)移動(dòng)到C柱中,且序號(hào)大的盤(pán)子必須在在需要小的圓盤(pán)下方。
思路:如果只有1個(gè)圓盤(pán)需要移動(dòng),則直接從A柱移動(dòng)到C柱,如果有n個(gè)圓盤(pán)(n>1)需要移動(dòng),則需要先把n-1個(gè)圓盤(pán)從A柱移動(dòng)到B柱,再把第n個(gè)圓盤(pán)從A柱移動(dòng)到C柱,最后把原來(lái)的n-1個(gè)圓盤(pán)從B柱移動(dòng)到C柱。
例如:
將1個(gè)圓盤(pán)從A柱移動(dòng)到C柱只需要1個(gè)步驟:
1. 把圓盤(pán)1從A移動(dòng)到C
將2個(gè)圓盤(pán)從A柱移動(dòng)到C柱需要3個(gè)步驟:
1. 把圓盤(pán)1從A移動(dòng)到B
2. 把圓盤(pán)2從A移動(dòng)到C
3. 把圓盤(pán)1從B移動(dòng)到C
將3個(gè)圓盤(pán)從A柱移動(dòng)到C柱需要7個(gè)步驟:
1. 把圓盤(pán)1從A移動(dòng)到C
2. 把圓盤(pán)2從A移動(dòng)到B
3. 把圓盤(pán)1從C移動(dòng)到B
4. 把圓盤(pán)3從A移動(dòng)到C
5. 把圓盤(pán)1從B移動(dòng)到A
6. 把圓盤(pán)2從B移動(dòng)到C
7. 把圓盤(pán)1從A移動(dòng)到C
遞歸的漢諾塔解法(c語(yǔ)言)
可以看出下面的遞歸算法的時(shí)間復(fù)雜度為O(2^n),因?yàn)榇嬖谟姓{(diào)用2^n-2次遞歸調(diào)用和1次原生printf;而空間復(fù)雜度為O(n),因?yàn)檫\(yùn)行時(shí)棧中最多會(huì)同時(shí)保存n個(gè)函數(shù)的調(diào)用信息。
#include <stdio.h> #include <stdlib.h> #include <math.h> void towers(int n, char from, char to, char aux); int main() { ?? ?towers(3, 'A', 'C', 'B'); ?? ?return 0; } void showMove (int order, char from, char to) { ?? ?printf("move disk %d from %c to %c\n", order, from, to); } void towers(int n, char from, char to, char aux) { ?? ?if (n==1) { ?? ??? ?showMove(n, from, to); ?? ??? ?return; ?? ?} ?? ?towers(n-1, from, aux, to); ? ? ? ? showMove(n, from, to); ?? ?towers(n-1, aux, to, from); }
解釋一下代碼中參數(shù)的含義,void towers(int n, char from, char to, char aux)的意思是把n個(gè)圓盤(pán)從from柱子移動(dòng)到to柱子,中間可以借用aux柱子。
分析一下上面的遞歸執(zhí)行過(guò)程:
我們已經(jīng)知道把1個(gè)圓盤(pán)從from移動(dòng)到to的步驟是showMove(1, from, to)
;
而把2個(gè)圓盤(pán)從from移動(dòng)到to的步驟是,先照著移動(dòng)1個(gè)圓盤(pán)的操作,把前面1個(gè)圓盤(pán)從from移動(dòng)到aux,再把第2個(gè)圓盤(pán)從from移動(dòng)到to,最后按照移動(dòng)1個(gè)圓盤(pán)的操作,把剛才的1個(gè)圓盤(pán)從aux移動(dòng)到to。
同理,把3個(gè)圓盤(pán)從from移動(dòng)到to的步驟是,先照著移動(dòng)2個(gè)圓盤(pán)的操作,把前面2個(gè)圓盤(pán)從from移動(dòng)到aux,再把第3個(gè)圓盤(pán)從from移動(dòng)到to,最后按照移動(dòng)2個(gè)圓盤(pán)的操作,把剛才的2個(gè)圓盤(pán)從aux移動(dòng)到to。
所以,把n個(gè)圓盤(pán)的操作從from移動(dòng)到to的步驟是,先照著移動(dòng)n-1個(gè)圓盤(pán)的操作,把前面n-1個(gè)圓盤(pán)從from移動(dòng)到aux,再把第n個(gè)圓盤(pán)從from移動(dòng)到to,最后按照移動(dòng)n-1個(gè)圓盤(pán)的操作,把剛才的n-1個(gè)圓盤(pán)從aux移動(dòng)到to。
因此,我們可以記錄移動(dòng)1個(gè)圓盤(pán)的步驟,來(lái)得到移動(dòng)2個(gè)圓盤(pán)的步驟,通過(guò)移動(dòng)2個(gè)圓盤(pán)的步驟來(lái)得到移動(dòng)3個(gè)圓盤(pán)的步驟,...最后得到移動(dòng)n個(gè)圓盤(pán)的步驟。經(jīng)過(guò)分析可以知道移動(dòng)n個(gè)圓盤(pán)一共會(huì)有2^n-1個(gè)步驟
循環(huán)實(shí)現(xiàn)漢諾塔問(wèn)題(c語(yǔ)言)
為了代碼更加易懂,這里寫(xiě)了注釋,修改了部分變量命名,統(tǒng)一用數(shù)組保存步驟集合,最后才輸出。
可以看出這個(gè)算法的時(shí)間復(fù)雜度還是O(2^n),一共會(huì)執(zhí)行2^(n+1)-1次setMoveAction語(yǔ)句和2^n-1次,printf語(yǔ)句,比起直接用遞歸還要復(fù)雜一些,而空間復(fù)雜度也是O(2^n),屬于沒(méi)什么用的算法,就是隨便寫(xiě)寫(xiě)。
#include <stdio.h> #include <stdlib.h> #include <math.h> void towers(int n, char from, char to, char aux); int main() { ?? ?towers(3, 'A', 'C', 'B'); ?? ?return 0; } void showMove(int order, char from, char to) { ?? ?printf("move disk %d from %c to %c\n", order, from, to); } typedef struct { ?? ?int order; ?? ?char from; ?? ?char to; } MoveAction; void setMoveAction(MoveAction *p, int order, char from, char to) { ?? ?p->order = order; ?? ?p->from = from; ?? ?p->to = to; } char compare_map(char c, char left, char right) { ?? ?if (c == left) { ?? ??? ?return right; ?? ?} else if (c == right) { ?? ??? ?return left; ?? ?} ?? ?return c; } void towers(int n, char from, char to, char aux) { ?? ?MoveAction *actions, action; ?? ?int i, k, size; ?? ?char f, t; ?? ?actions = (MoveAction *)calloc(pow(2, n - 1) - 1, sizeof(MoveAction)); ?? ?setMoveAction(&actions[0], 1, from, to); ?? ?size = 1; ?? ?for (i = 2; i <= n; i++) ?? ?{ ?? ??? ?//本次循環(huán)會(huì)將把i個(gè)盤(pán)子從from移動(dòng)到to的步驟記錄到actions數(shù)組中 ?? ??? ?// 設(shè)size是把i-1個(gè)盤(pán)子從from移動(dòng)到to的步驟數(shù), ?? ??? ?// 則本次循環(huán)中集合{actions[x] | 0 <= x <= size -1 }就是size是把i-1個(gè)盤(pán)子從from移動(dòng)到aux的步驟集合, ?? ??? ?//而action[size]是把第i個(gè)盤(pán)子從from移到to, ?? ??? ?//而集合{actions[x] | size + 1 <= x <= 2*size }就應(yīng)該是把i-1個(gè)盤(pán)存從aux移動(dòng)到to的步驟集合 ?? ??? ?// 倒序,先求解集合{actions[x] | size + 1 <= x <= 2*size } ?? ??? ?for (k = 0; k < size; k++) ?? ??? ?{ ?? ??? ??? ?action = actions[k]; ?? ??? ??? ?f = compare_map(action.from, from, aux); ?? ??? ??? ?t = compare_map(action.to, from, aux); ?? ??? ??? ?setMoveAction(&actions[k + size + 1], action.order, f, t); ?? ??? ?} ?? ??? ?// 求解action[size] ?? ??? ?setMoveAction(&actions[size], i, from, to); ?? ??? ?// 求解集合{actions[x] | 0 <= x <= size -1 } ?? ??? ?for (k = 0; k < size; k++) ?? ??? ?{ ?? ??? ??? ?action = actions[k]; ?? ??? ??? ?f = compare_map(action.from, to, aux); ?? ??? ??? ?t = compare_map(action.to, to, aux); ?? ??? ??? ?setMoveAction(&actions[k], action.order, f, t); ?? ??? ?} ?? ??? ?size = size * 2 + 1; ?? ?} ?? ?for (i = 0; i < size; i++) ?? ?{ ?? ??? ?showMove(actions[i].order, actions[i].from, actions[i].to); ?? ?} }
原文鏈接:https://juejin.cn/post/7057423612227092511
相關(guān)推薦
- 2022-06-24 Python集合之set和frozenset的使用詳解_python
- 2022-11-20 Python必知必會(huì)之os模塊實(shí)例詳解_python
- 2022-12-09 Flutter?Widgets?MediaQuery控件屏幕信息適配_IOS
- 2023-11-20 python獲取當(dāng)前路徑所有文件
- 2022-07-29 Golang的鎖機(jī)制與使用技巧小結(jié)_Golang
- 2022-09-25 C++ sort比較函數(shù)的寫(xiě)法,最全面的總結(jié)
- 2023-03-28 Python中l(wèi)ist列表添加元素的3種方法總結(jié)_python
- 2022-07-02 python?np.arange?步長(zhǎng)0.1的問(wèn)題需要特別注意_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支