算法基础11-暴力递归思维、动态规划思维

本文深入探讨了暴力递归的思维模式,通过汉诺塔问题、字符串子序列、全排列和栈逆序等实例进行阐述。接着介绍了动态规划的基础模型,包括从左往右和范围上的尝试模型,并举例说明。文章最后提出了如何优化尝试策略和动态规划在面试中的应用。
摘要由CSDN通过智能技术生成

1 暴力递归、动态规划

1.1 暴力递归思维

暴力递归实质就是尝试

概念解释:

回溯-表示大问题被拆解为小问题,小问题返回给大问题信息,就是回溯

分治:大问题被拆解成小的子问题,就是分治

1、把问题转化为规模缩小了的同类问题的子问题

2、有明确的不需要继续进行递归的条件(base case)

3、有当得到了子问题的结果之后的决策过程

4、不记录每个子问题的解(如果记录每个子问题的解,就是我们熟悉的动态规划)

1.1.1 暴力递归下的尝试

1.1.1.1 例一:汉诺塔问题

打印n层汉诺塔从最左边移动到最右边的全部过程

汉诺塔圆盘移动,如果杆子上没有圆盘,可以移动到该杆,如果有圆盘则必须移动比该圆盘小的圆盘到该圆盘上

思路1:1、先想办法把1到N-1层圆盘移动到中间杆 2、再把N层的圆盘移动到最右侧的杆上 3、把1到N-1个圆盘从中间杆移动到最右侧。结束

思路2:忘掉左中右,理解为从from移动到to,from和to都有可能是左中右。所以定义from,to,other三个杆子。1、把1到N-1移动到other上。2、把第N层移动到to上。3、把1到N层从other移动到to上。结束

思路3:递归改非递归实现

N层汉诺塔,从左移动到右最优步数是2^N - 1 步。递归公式 T(N) = T(N-1) + 1 + T(N-1)。化简为等比数列,高中数学内容

尝试是有优劣之分的,譬如思路1和思路二。在动态规划章节,可以用动态规划优化我们的尝试到最优版本

package class11;

import java.util.Stack;

public class Code01_Hanoi {

        // 按照思路1的方法
	public static void hanoi1(int n) {
		leftToRight(n);
	}

	// 请把1~N层圆盘 从左 -> 右
	public static void leftToRight(int n) {
		if (n == 1) {
			System.out.println("Move 1 from left to right");
			return;
		}
		leftToMid(n - 1);
		System.out.println("Move " + n + " from left to right");
		midToRight(n - 1);
	}

	// 请把1~N层圆盘 从左 -> 中
	public static void leftToMid(int n) {
		if (n == 1) {
			System.out.println("Move 1 from left to mid");
			return;
		}
		leftToRight(n - 1);
		System.out.println("Move " + n + " from left to mid");
		rightToMid(n - 1);
	}

	public static void rightToMid(int n) {
		if (n == 1) {
			System.out.println("Move 1 from right to mid");
			return;
		}
		rightToLeft(n - 1);
		System.out.println("Move " + n + " from right to mid");
		leftToMid(n - 1);
	}

	public static void midToRight(int n) {
		if (n == 1) {
			System.out.println("Move 1 from mid to right");
			return;
		}
		midToLeft(n - 1);
		System.out.println("Move " + n + " from mid to right");
		leftToRight(n - 1);
	}

	public static void midToLeft(int n) {
		if (n == 1) {
			System.out.println("Move 1 from mid to left");
			return;
		}
		midToRight(n - 1);
		System.out.println("Move " + n + " from mid to left");
		rightToLeft(n - 1);
	}

	public static void rightToLeft(int n) {
		if (n == 1) {
			System.out.println("Move 1 from right to left");
			return;
		}
		rightToMid(n - 1);
		System.out.println("Move " + n + " from right to left");
		midToLeft(n - 1);
	}

        // 思路二:暴力递归 from to other
	public static void hanoi2(int n) {
		if (n > 0) {
			func(n, "left", "right", "mid");
		}
	}

	// 1~i 圆盘 目标是from -> to, other是另外一个
	public static void func(int N, String from, String to, String other) {
		if (N == 1) { // base
			System.out.println("Move 1 from " + from + " to " + to);
		} else {
			func(N - 1, from, other, to);
			System.out.println("Move " + N + " from " + from + " to " + to);
			func(N - 1, other, to, from);
		}
	}

	public static class Record {
		public boolean finish1;
		public int base;
		public String from;
		public String to;
		public String other;

		public Record(boolean f1, int b, String f, String t, String o) {
			finish1 = false;
			base = b;
			from = f;
			to = t;
			other = o;
		}
	}

        // 思路三:非递归实现
	public static void hanoi3(int N) {
		if (N < 1) {
			return;
		}
		Stack<Record> stack = new Stack<>();
		stack.add(new Record(false, N, "left", "right", "mid"));
		while (!stack.isEmpty()) {
			Record cur = stack.pop();
			if (cur.base == 1) {
				System.out.println("Move 1 from " + cur.from + " to " + cur.to);
				if (!stack.isEmpty()) {
					stack.peek().finish1 = true;
				}
			} else {
				if (!cur.finish1) {
					stack.push(cur);
					stack.push(new Record(false, cur.base - 1, cur.from, cur.other, cur.to));
				} else {
					System.out.println("Move " + cur.base + " from " + cur.from + " to " + cur.to);
					stack.push(new Record(false, cur.base - 1, cur.other, cur.to, cur.from));
				}
			}
		}
	}

	public static void main(String[] args) {
		int n = 3;
		hanoi1(n);
		System.out.println("============");
		hanoi2(n);
		System.out.println("============");
		hanoi3(n);
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值