取球问题
问题描述: 在n个球中, 任意取出m个(不放回), 求有多少种不同的取法.
求解思路:
从题目上看, 这个问题对于递归来说似乎没有突破口, 找不到合适的相似性? 这就要发挥我们特长 – 想象!
在进行想象之前需要先明确我们方法的参数 : int f (int n, int m) , n 个球中取m个 , f的返回值就是取法的种数
好, 我们想象这n个球取1个特殊球, 那么我们要么取到这个球, 要么取不到这个球
如果取到了这个特殊球, 则: f (n - 1, m - 1), 总数总是要减1, 因为取到了特殊球, 所以m-1,
反之我们没有取到特殊球, 则; f(n - 1, m), 总数还是减1, 但是m的值不变
此时我们就可以表达 f (n, m) = f (n - 1, m - 1) + f(n - 1, m)
递归的形式已经出来了, 现在就差递归出口了!
好, 看代码:
public static int f (int n, int m) {
//递归出口判断
if (n < m) {//不可能取到
return 0;
}
if (n == m) { //如果要取的球跟总球数相同, 那只有一种取法
return 1;
}
if (m == 0) { //还有, 如果取到最后, 那也只有一种取法, 返回1
return 1;
}
return f(n - 1, m - 1) + f(n - 1, m);// n 个球中取1个特殊球 取法划分: 取 | 不取
}
全排列问题
> 问题描述: 求n个元素的全排列(把所有的排列形式都打印出来)
求解思路:
不难发现, 有这么一个规律: 可以考虑把每个元素先放在开始这个位置, 把后面元素全排列
但是我们需要循环遍历后面的每个字符, 所以外层需要套层循环, 并且有个”关注点” 作为起始位置 , 在递归前, 将”关注点”的元素交换到遍历的位置, 而且递归后要把位置上字符再交换回来, 这就是我们通常讲的回溯.
接下来就要考虑出口问题了, 其实也不难想, 如果我们的要交换的位置到了最后是不是就要考虑交换了呢? 好, 我们思路缕清楚了.
看代码前我先介绍一种高速交换元素位置的办法(应该快吧 ?):
//一种用位运算的交换方法
private static void swap(char[] chs, int i, int j) {
if (i == j){
return;
}
chs[i] ^= chs[j];
chs[j] ^= chs[i];
chs[i] ^= chs[j];
}
接下就是我们的问题解决代码:
public static void f (char a[], int k) {
//递归出口, 如果关注点到了最后一个位置,就需要打印了
if (k == a.length-1){
System.out.println(Arrays.toString(a));
}
//从第k个开始到最后进行交换, 每交换一次开始递归, 递归结果返回后, 要再交换回来(回溯)
for (int i = k; i < a.length; i++) {
swap(a, k, i);
f(a, k + 1);//每递归一次就要, 就要把关注位置向前挪一位
swap(a, k, i);//一定不要忘记回溯
}
}