【算法題】06-用棧來解決漢諾塔問題

用棧來解決漢諾塔問題

題目

修改漢諾塔問題的遊戲規則:限制不能從最左側的塔直接移動到最右側,也不能從最右側直接移動到最左側,而是必須通過中間。求當塔有N層的時候,打印最優移動和最優移動總步數。java

要求:ide

  • 方法一:遞歸的方法
  • 方法二:非遞歸的方法,用棧來模擬漢諾塔的三個塔

思路

方法一:遞歸的方法

首先,若是隻剩最上層的塔須要移動,則有以下處理:code

  1. 若是但願從左移動到右,打印Move 1 from left to right
  2. 若是但願從中移動到左,打印Move 1 from mid to left
  3. 若是但願從中移動到右,打印Move 1 from mid to right
  4. 若是但願從右移動到中,打印Move 1 from right to mid
  5. 若是但願從左移動到右,打印Move 1 from left to mid Move 1 from mid to right
  6. 若是但願從右移動到左,打印Move 1 from right to mid Move 1 from mid to left

以上就是遞歸的終止條件,也就是隻剩上層塔時的打印過程。遞歸

多層塔的時候。遊戲

若是剩下N層塔,從最上到最小依次爲1~N-1,則有以下判斷部署

  1. 若是剩下的N層塔都在左,但願所有移到中,則有三個步驟it

    1)將1~N-1層塔先所有從左移動到右,交給遞歸過程io

    2)將第N層塔從左移到中class

    3)將1~N-1層塔所有從右移到中,明顯交給遞歸過程方法

  2. 若是把剩下的N層塔從中移到左,從中移到右,從右移到中過程與上述相同

  3. 若是剩下的N層都在左,但願所有移動到右。

    1)將1~N-1層塔先所有從左移動到右,交給遞歸

    2)將第N層塔從左移動到中

    3)將1~N-1層塔從右移到左

    4)N層從中移到右

    5)最後將1~N-1層塔所有從左移到右,交給遞歸過程

  4. 若是剩下全在右,但願移到左,同上

public static int hanoiProblem1(int num, String left, String mid,
			String right) {
		if (num < 1) {
			return 0;
		}
		return process(num, left, mid, right, left, right);
	}

	public static int process(int num, String left, String mid, String right,
			String from, String to) {
		//只有一層須要移動的時候
		if (num == 1) {
			//若是from或to有一個是mid,說明是從右往中,或從左往中,直接移動便可。
			if (from.equals(mid) || to.equals(mid)) {
				System.out.println("Move 1 from " + from + " to " + to);
				return 1;
			} else {
				//不然說明是從左到右,或從右到左,都須要兩步,先到中間,再到目的地
				System.out.println("Move 1 from " + from + " to " + mid);
				System.out.println("Move 1 from " + mid + " to " + to);
				return 2;
			}
		}
		//當層數大於1的時候,且有一個是到中間
		if (from.equals(mid) || to.equals(mid)) {
			//若是是從左到中,或者中到左 another就爲右,不然就爲左,就是說如今參與的是左中,那麼剩下的一個是右,同理右中
			String another = (from.equals(left) || to.equals(left)) ? right : left;
			//遞歸處理n-1層,從當前的from到另外一端,即從作到右,或從右到左
			int part1 = process(num - 1, left, mid, right, from, another);
			//將第N層塔從左移到中或從右移到中
			int part2 = 1;
			System.out.println("Move " + num + " from " + from + " to " + to);
			//將1~N-1層塔所有從右移到中,或者從左移動中
			int part3 = process(num - 1, left, mid, right, another, to);
			return part1 + part2 + part3;
		} else {
			//從左到右或者從右到左  遞歸處理n-1層
			//將1~N-1層塔先所有從左移動到右或從右移到左
			int part1 = process(num - 1, left, mid, right, from, to);
			//將第N層塔從左移動到中 或從右移到中
			int part2 = 1;
			System.out.println("Move " + num + " from " + from + " to " + mid);
			//將1~N-1層塔從右移到左 或相反
			int part3 = process(num - 1, left, mid, right, to, from);
			//N層從中移到右
			int part4 = 1;
			System.out.println("Move " + num + " from " + mid + " to " + to);
			//最後將1~N-1層塔所有從左移到右,交給遞歸過程
			int part5 = process(num - 1, left, mid, right, from, to);
			return part1 + part2 + part3 + part4 + part5;
		}
	}

方法二:非遞歸,用棧來模擬

將左、中、右三個地點抽象成棧,LS、MS、RS,那麼棧的操做就能夠當作是:某一個棧(from)把棧頂元素彈出,而後壓入到另外一個棧裏(to),做爲另外一個棧(to)的棧頂。

  1. 遊戲的第一個動做必定是L->M,這是顯而易見的。

  2. 在走出最小部署過程當中的任什麼時候刻,四個動做只有一個動做不違反小壓大和相鄰不可逆原則,另外三個必定會違反

    上述第二點證實:

    假設前一步的動做是L->M:

    1. 根據小壓大的原則,L->M的動做不會重複發生
    2. 根據相鄰不可逆原則,M->L的動做也不應發生
    3. 根據小壓大的原則,M->R和R->M只會有一個達標

    假設前一步的動做是M->L:

    1. 根據小壓大原則,M->L的動做不會重複發生
    2. 根據相鄰不可逆原則L->M也不會發生
    3. 根據小壓大原則,M->R和R->M只會有一個達標

    假設前一步的動做是M->R:

    1. 根據小壓大的原則,M->R不會重複發生
    2. 根據相鄰不可逆原則,R->M也不會發生
    3. 根據小壓大的原則,L->M和M->L只會有一個達標

    假設前一步的動做是R->M:

    1. 根據小壓大的原則,R->M的動做不會重複發生
    2. 根據相鄰不可你原則,M->R的動做也不應發生
    3. 根據小壓大原則,L->M和M->L只會 有一個達標

    如上,每一步只會有一個動做達標,那麼只要每走一步都根據這兩個原則考察全部的動做就能夠,哪一個達標走哪一個。

public static enum Action {
		No, LToM, MToL, MToR, RToM
	}

	public static int hanoiProblem2(int num, String left, String mid, String right) {
		//左棧
		Stack<Integer> lS = new Stack<Integer>();
		//中棧
		Stack<Integer> mS = new Stack<Integer>();
		//右棧
		Stack<Integer> rS = new Stack<Integer>();
		lS.push(Integer.MAX_VALUE);
		mS.push(Integer.MAX_VALUE);
		rS.push(Integer.MAX_VALUE);
		//從num開始由大到小依次入左棧
		for (int i = num; i > 0; i--) {
			lS.push(i);
		}
		Action[] record = { Action.No };
		int step = 0;
		//若是右棧的個數不等於num+1說明尚未移動完
		while (rS.size() != num + 1) {
			step += fStackTotStack(record, Action.MToL, Action.LToM, lS, mS, left, mid);
			step += fStackTotStack(record, Action.LToM, Action.MToL, mS, lS, mid, left);
			step += fStackTotStack(record, Action.RToM, Action.MToR, mS, rS, mid, right);
			step += fStackTotStack(record, Action.MToR, Action.RToM, rS, mS, right, mid);
		}
		return step;
	}
相關文章
相關標籤/搜索