循环与递归进阶用法
注意:避免栈溢出(出口的情况一定要罗列完毕)
例1:在n个球中取m个 n是总球数 m是被取球数。求可以有多少种取法?
准备使用递归解决问题,第一步该是构建框架。
如下:
public static void main(String[] args)
{
int k = fn(10, 3);//例如10个里面取3个
System.out.println(k);
}
此时大概知道需要构造出一个fn(),还需传入n,m。然后我们可以使用递归的想法得到,fn(n - 1, m - 1) + fn(n - 1, m)。最后构造出口,主要考虑被取球数大于/等于/为0所有的球
即m>n,n=m,m=0三种情况。
public static int fn(int n, int m)
{
if (n < m) return 0;//当取球数大于总球数时,没有取法
if (n == m) return 1;//当总球数与被取球数一致时,只有一种取法,即全取完
if (m == 0) return 1;//当被取球数为0时只有一种取法,即什么也不去取
//n个里有个特殊球x,取法划分,包不包含x
return fn(n - 1, m - 1) + fn(n - 1, m);
}
例2:n个元素的全排列(全排列即所有的排列的可能)
由例1的思路咱们先确定框架
public static void main(String[] args)
{
char[] data="ABCDEF".toCharArray();
fn(data,0);
}
然后我们想到可以定义k作为交换位置,以此为界,与其后面的元素交换,进行尝试。
可以得到有如下代码
for (int i = k; i <data.length ; i++)
{
{
char t=data[k];
data[k]=data[i];
data[i]=t;
}
fn(data,k+1);
}
但是数组循环之后,原有的顺序就被打乱了,此处应该有一个回溯的步骤,修改之后应该如下图。
//交换第一个数组元素,就是第K个与第i个交换
for (int i = k; i <data.length ; i++)
{
{//试探
char t=data[k];
data[k]=data[i];
data[i]=t;
}
fn(data,k+1);
{//回溯 恢复原来的位置
char t=data[k];
data[k]=data[i];
data[i]=t;
}
}
解决了上述问题后,问题实则已经解决了十之八九。所以到了该考虑出口的问题了。我们可以看出当k(交换位置)与data的长度相同时,就可以输出结果了。综合上述的分析我们可以定义有如下fn()。
public static void fn(char[] data,int k)
{
if (k==data.length)
{
for (int i = 0; i < data.length; i++)
{
System.out.print(data[i]+" ");
}
System.out.println("\t");//为了输出结果的美观
}
//交换第一个数组元素,就是第K个与第i个交换
for (int i = k; i <data.length ; i++)
{
{//试探
char t=data[k];
data[k]=data[i];
data[i]=t;
}
fn(data,k+1);
{//回溯 恢复原来的位置
char t=data[k];
data[k]=data[i];
data[i]=t;
}
}
}
例3:两个字符串公共子串的长度
思路还是如上,先定义有如下的框架。
public static void main(String[] args)
{
int k = fn("abc", "abcd");
System.out.println(k);
}
然后就需要想到定义fn(),传入s1,s2。当准备工作完成后,我们可以从输入的字符串中,看出s1,s2的首字母是相同的。所以可以先将其首字符进行比较,返回fn(s1.substring(1), s2.substring(1))+1;进行再次调用。我们也需要考虑其他情况的出现,所以其他情况可以将其归类成首字符之后的比较,可以将其写成 Math.max(fn(s1.substring(1), s2), fn(s1, s2.substring(1)));并将其返回出去。
关键是出口的发现,其实由我的输入,很容易看出s1与s2的长度相同时,两个字符串的公共子串长度便是0.所以得到如下fn()
public static int fn(String s1, String s2)
{
if (s1.length() == 0 || s2.length() == 0) return 0;
if (s1.charAt(0) == s2.charAt(0))
return fn(s1.substring(1), s2.substring(1))+1;
else
return Math.max(fn(s1.substring(1), s2), fn(s1, s2.substring(1)));
}
个人总结:确定框架之后,确定fn()这步实是难以把握。需要以练习,练出感觉。发现“出口”的意识还是欠缺。总之,继续学习,加强练习。