第八课.暴力递归

1.汉诺塔:打印n层汉诺塔从最左边移动到中间的全部过程

	public static void hanoi(int n) {
		if (n > 0) {
			func(n, "左", "中", "右");
		}
	}
	//1-i圆盘,目标是from->to,other是另外一个
	public static void func(int i, String start, String end, String other) {
		if (i == 1) {
			System.out.println("move 1 from " + start + " to " + end);
		} else {
			func(i - 1, start, other, end);
			System.out.println("move" + i + "from " + start + " to " + end);
			func(i- 1, other, end, start);
		}
	}

	public static void main(String[] args) {
		int n = 3;
		hanoi(n);
	}

2.打印一个字符串的全部字序列,包括空字符串
法一:

	public static void function(String str) {
		char[] chars = str.toCharArray();
		process(chars,0,new ArrayList<Character>());
	}
	//当前来到i位置,要和不要,走两条路
	//res之前的选择,所形成的路
	public static void process(char[] chs, int i, List<Character> res) {
		if(i==chs.length){
			printList(res);
			return;
		}
		List<Character> characters1 = copyList(res);
		process(chs,i+1,characters1);//不要当前字符
		List<Character> characters2 = copyList(res);
		characters2.add(chs[i]);
		process(chs,i+1,characters2);//要当前字符

	}

	public static void printList(List<Character> res) {
		for (Character re : res) {
			System.out.print(re);
		}
		System.out.println("\n");
	}

	public static List<Character> copyList(List<Character> list){
		List<Character> listA=new ArrayList<>();
		for (Character character : list) {
			listA.add(character);
		}
		return listA;
	}


	public static void main(String[] args) {
		String test = "abc";
		function(test);
	}

法二:

	public static void printAllSubsquence(String str) {
		char[] chars = str.toCharArray();
		process(chars,0);
	}
	//之前的选择,所形成的的结果,是str
	public static void process(char[] chs, int i) {
		if(i==chs.length){
			System.out.println(String.valueOf(chs));
			return;
		}
		process(chs,i+1);
		char temp=chs[i];//要当前字符的路
		chs[i]=0;
		process(chs,i+1);//不要当前字符的路
		chs[i]=temp;
	}
	public static void main(String[] args) {
		String test = "abc";
		printAllSubsquence(test);
	}

3.打印一个字符串的全排列,要求不出现重复的排列

	public static ArrayList<String> Permutation(String str) {
		if(str==null||str.length()==0){
			return null;
		}
		char[] chars = str.toCharArray();
		ArrayList<String> res=new ArrayList<>();
		process(chars,0,res);
		return res;
	}
	//str[i..]范围上,所有的字符,都可以在i位置上,后续都去尝试
	//str[0..i-1]范围上,是之前做的选择
	//把所有的字符串所形成的全排列,加入到res里去
	public static void process(char[] chs, int i, ArrayList<String> res) {
		if(i==chs.length){
			res.add(String.valueOf(chs));
			return;
		}
//		boolean visit[]= new boolean[26];//注释的部分就是去重的部分
		for(int j=i;j<chs.length;j++){
//			if(!visit[chs[j]-'a']){
//				visit[chs[j]-'a']=true;
				swap(chs,i,j);
				process(chs,j+1,res);
				swap(chs,i,j);
//			}
		}
	}

	public static void swap(char[] chs, int i, int j) {
		char tmp = chs[i];
		chs[i] = chs[j];
		chs[j] = tmp;
	}

	public static void main(String[] args) {
		String abc="abc";
		ArrayList<String> permutation = Permutation(abc);
		for (String s : permutation) {
			System.out.println(s);
		}
	}

4.给你一个栈,请逆序这个栈,不能申请额外的数据结构,只能使用递归函数

	public static void reverse(Stack<Integer> stack) {
		if(stack.isEmpty()){
			return;
		}
		int result=getAndRemoveLastElement(stack);
		reverse(stack);
		stack.add(result);
	}

	public static int getAndRemoveLastElement(Stack<Integer> stack) {
		int result=stack.pop();
		if(!stack.isEmpty()){
			int last = getAndRemoveLastElement(stack);
			stack.push(result);
			return last;
		}else{
			return result;
		}
	}

	public static void main(String[] args) {
		Stack<Integer> test = new Stack<Integer>();
		test.push(1);
		test.push(2);
		test.push(3);
		test.push(4);
		test.push(5);
		reverse(test);
		while (!test.isEmpty()) {
			System.out.println(test.pop());
		}
	}

5.规定1和A对应,2和B对应,3和C对应
一个数字字符比如‘111’,就可以转化为“AAA”,“KA”和“AK”。
给定一个只有数字字符组成的字符串str,返回有多少种转化的结果

	public static int number(String str) {
		if(str==null||str.length()==0){
			return 0;
		}
		return process(str.toCharArray(),0);
	}
	//i之前的位置,如何转化已经做过决定了
	//i...有多少种转化的结果
	public static int process(char[] chs, int i) {
		if(i==chs.length){
			return 1;
		}
		if(chs[i]=='0'){
			return 0;
		}
		if(chs[i]=='1'){//i自己作为单独的部分,后续有多少种方法
			int res =process(chs,i+1);
			if(i+1<chs.length){
				res+=process(chs,i+2);//(i和i+1)作为单独的部分,后续有多少种方法
			}
			return res;
		}
		if(chs[i]=='2'){
			int res=process(chs,i+1);//i自己作为单独的部分,后续有多少种方法
			if (i + 1 < chs.length && chs[i+1]<='6'&&chs[i+1]>='0') {
				res+=process(chs,i+2);//(i和i+1)作为单独的部分并且没有超过26,后续有多少种方法
			}
			return res;
		}
		return process(chs,i+1);
	}

6.给定两个数组w和v,两个数组长度相等,w[i]表示第i件商品的重量,v[i]表示第i件商品的价值。再给定一个数组bag,要求你挑选商品的重量一定不能超过bag,返回满足这个条件下,你能获得的最大价值。
法一:有alreadyValue

	public static int maxValue1(int[] weights, int[] values, int bag) {
		return process2(weights, values, 0, 0, 0,bag);
	}
		public static int process2(int[] weights, int[] values, int i, int alreadyweight, int alreadyValue, int bag) {
		if(alreadyweight>bag){
			return 0;
		}
		if(i==weights.length){
			return alreadyValue;
		}
		return 	Math.max(process2(weights,values,i+1,alreadyweight+weights[i],alreadyValue+values[i],bag),process2(weights,values,i+1,alreadyweight,alreadyValue,bag));
	}

法二:没有alreadyValue

	public static int maxValue1(int[] weights, int[] values, int bag) {

		return process1(weights, values, 0, 0, bag);

	}
	//i..的货物自由选择,形成的最大价值返回
	//重量永远不要超过bag
	//之前做的决定,所达到的重量,alreadyweight
	public static int process1(int[] weights, int[] values, int i, int alreadyweight, int bag) {
		if (alreadyweight > bag) {
			return 0;
		}
		if (i == weights.length) {
			return 0;
		}
		return Math.max(

				process1(weights, values, i + 1, alreadyweight, bag),

				values[i] + process1(weights, values, i + 1, alreadyweight + weights[i], bag));
	}

测试代码:

	public static int maxValue2(int[] c, int[] p, int bag) {
		int[][] dp = new int[c.length + 1][bag + 1];
		for (int i = c.length - 1; i >= 0; i--) {
			for (int j = bag; j >= 0; j--) {
				dp[i][j] = dp[i + 1][j];
				if (j + c[i] <= bag) {
					dp[i][j] = Math.max(dp[i][j], p[i] + dp[i + 1][j + c[i]]);
				}
			}
		}
		return dp[0][0];
	}
	public static void main(String[] args) {
		int[] weights = { 3, 2, 4, 7 };
		int[] values = { 5, 6, 3, 19 };
		int bag = 11;
		System.out.println(maxValue1(weights, values, bag));
		System.out.println(maxValue2(weights, values, bag));
	}

7.给定一个整型数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿。但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。

	public static int win1(int[] arr) {
		if(arr==null||arr.length==0){
			return 0;
		}
		return Math.max(f(arr,0,arr.length-1),s(arr,0,arr.length-1)); 
	}
	//先手
	public static int f(int[] arr, int i, int j) {
		if(i==j){
			return arr[i];
		}
		return Math.max(arr[i]+s(arr,i+1,j),arr[j]+s(arr,i,j-1));//注意调用的是s
	}
	//后手
	public static int s(int[] arr, int i, int j) {
		if(i==j){
			return 0;
		}
		return Math.min(f(arr,i+1,j),f(arr,i,j-1));//注意调用的是f,并且没有arr[i]+
	}

测试:

	public static int win2(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		int[][] f = new int[arr.length][arr.length];
		int[][] s = new int[arr.length][arr.length];
		for (int j = 0; j < arr.length; j++) {
			f[j][j] = arr[j];
			for (int i = j - 1; i >= 0; i--) {
				f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
				s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
			}
		}
		return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);
	}

	public static void main(String[] args) {
		int[] arr = { 1, 9, 1 };
		System.out.println(win1(arr));
		System.out.println(win2(arr));
	}

8.N皇后问题:
法一:正规解法

	public static int num1(int n) {//n个皇后
		if (n < 1) {
			return 0;
		}
		int[] record = new int[n];//record[i] -->i行的皇后,放在了第几列
		return process1(0, record, n);
	}
	//潜台词:record[0..i-1]的皇后,任何两个皇后一定不共行、不共列、不共斜线
	//目前来到第i行
	//record[0..i-1]表示之前的行,放了皇后的位置
	//n代表整体一共有多少行
	//返回值,摆完所有的皇后,所有合理的摆法有多少种
	public static int process1(int i, int[] record, int n) {
		if (i == n) {//终止行
			return 1;
		}
		int res = 0;
		for (int j = 0; j < n; j++) {//当前行在i行,尝试i行所有的列->j
			//当前i行的皇后,放在j列,会不会和之前(0..i-1)的皇后,共行共列或共斜线
			//如果是,认为无效
			//如果不是,认为无效
			if (isValid(record, i, j)) {
				record[i] = j;
				res += process1(i + 1, record, n);
			}
		}
		return res;
	}
	//record[0..i-1]需要看,record[i...]不需要看
	//返回i行皇后,放在j列,是否有效
	public static boolean isValid(int[] record, int i, int j) {
		for (int k = 0; k < i; k++) {//之前的某k行皇后
			if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) {//注意条件是||
				return false;
			}
		}
		return true;
	}

法二:位运算优化(没有掌握)

public static int num2(int n) {
		if (n < 1 || n > 32) {
			return 0;
		}
		int upperLim = n == 32 ? -1 : (1 << n) - 1;
		return process2(upperLim, 0, 0, 0);
	}

	public static int process2(int upperLim, int colLim, int leftDiaLim,
			int rightDiaLim) {
		if (colLim == upperLim) {
			return 1;
		}
		int pos = 0;
		int mostRightOne = 0;
		pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));
		int res = 0;
		while (pos != 0) {
			mostRightOne = pos & (~pos + 1);
			pos = pos - mostRightOne;
			res += process2(upperLim, colLim | mostRightOne,
					(leftDiaLim | mostRightOne) << 1,
					(rightDiaLim | mostRightOne) >>> 1);
		}
		return res;
	}

测试:

public static void main(String[] args) {
		int n = 14;

		long start = System.currentTimeMillis();
		System.out.println(num2(n));
		long end = System.currentTimeMillis();
		System.out.println("cost time: " + (end - start) + "ms");

		start = System.currentTimeMillis();
		System.out.println(num1(n));
		end = System.currentTimeMillis();
		System.out.println("cost time: " + (end - start) + "ms");

	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值