1, Hanoi塔问题
public class Testing { static int dick = 3; public static void main(String[] args) { Testing.doTower(dick, 'A', 'B', 'C'); } private static void doTower(int topN, char from, char inter, char to) { if (topN == 1) { System.out.println("Move disk " + topN + " from tower " + from + " to tower " + to); } else { doTower(topN - 1, from, to, inter); System.out.println("Move disk " + topN + " from tower " + from + " to tower " + to); doTower(topN - 1, inter, from, to); } } }
2,乘方
计算x的y方。例如,计算2的8次方。通常的做法是用8个2相乘。
private static int power(int x, int y){
if (y == 1){
return x;
}
else{
x = x * power(x,y-1);
System.out.println("1"); //这种递归没有任何优势,从这句话可知,递归的运行了y-1次。和直接循环相乘没有区别。
return x;
}
}
其实我们在运算比如2的8次方时,完全可以首先计算2*2,然后利用递归加快这个过程:
普通算法:2*2*2*2*2*2*2*2 需要相乘8次。
改进算法:((2*2)^2)^2 仅需要相乘3次。也就是以2为底,8的对数。
private static int power(int x, int y){
if (y == 1){
return x;
}
else{
if (y%2 == 0){
x = power(x*x,y/2); //其实这里还可以理解成:2的8次方就等于4的4次方。
System.out.println("1");
return x;
}
else{
x = x*power(x,y-1); //当y是奇数时,还是需要用普通算法计算一次。之后y-1又是偶数了。
System.out.println("1"); //最终我们可以得知这种算法仅需要运行logY次。因此效率高的多。
return x;
}
}
}
我们甚至还可以用:
if(y%3 == 0){
System.out.println("b");
return power(x*x*x,y/3);
}
来进一步加快进程。
3,merge sort
merge排序的思想在于,将数组分成两个较小的有序数组,然后再将这两个有序数组合并在一起,就得到了排序后的整个数组。
那么如何得到两个较小的有序数组呢?就是将每个小组再划分为两个更小的有序数组,再merge。。。如此循环划分,直到数组中只剩下一个元素,即不需要再把组划分,也已经是有序数组了。
显而易见这里要用到递归了。
首先看看,没有递归的时候,如何将两个有序数组merge到一起:
public class mergeSort {
public static void main(String[] args) {
int[] a = {2,5,8,35,78,99,102,103,201};
int[] b = {5,7,17,32};
System.out.println(mergeSort(a,b) );
}
private static ArrayList mergeSort(int[] a, int[] b){
ArrayList c = new ArrayList();
int aInd=0;
int bInd=0;
int cInd=0;
while(aInd < a.length && bInd < b.length){
if (a[aInd] > b[bInd]){
c.add(cInd++, b[bInd++]);
}
else{
c.add(cInd++,a[aInd++]);
}
}
while(aInd == a.length && bInd < b.length){
c.add(cInd++,b[bInd++]);
}
while(bInd == b.length && aInd < a.length){
c.add(cInd++,a[aInd++]);
}
return c;
}
}
下面是merge sort的完整代码
public class mergeSort {
public static void main(String[] args) {
int[] initial = {5,2,7,8,102,45,36,22,201,43};
recSort(initial);
}
//recsort的任务就是将数组划分成2个更小的数组。
private static int[] recSort(int[] initial){
int[] begin;
int[] end;
if ( initial.length == 1 ){
return initial; //当数组被划分的最后只有一个元素时,就不需要再划分了。这就是基值条件。
}
else{
begin = Arrays.copyOfRange(initial, 0, initial.length/2); //begin数组代表数组的前半部分
end = Arrays.copyOfRange(initial, initial.length/2, initial.length);//end数组代表数组的后半部分
begin = recSort(begin); //让begin递归的被recsort划分。
end = recSort(end);
return mergeSort(begin, end);
//这句returen非常关键。当recsort循环到最内层的时候,mergeSort开始从栈里取出begin和end开始合并。
//同时合并的结果作为下一次mergeSort递归调用的输入参数。
//也就是,例如:上次合并的结果[2, 5] [7, 8, 102]这两个有序数组 成为了下次mergeSort的输入参数。
//这个例子有意思的地方是:我们使用recSort方法递归划分数组到最内层,使用mergeSort方法从最内层递归合并数组到最外层。从而这两个方法共同完成了递归调用。
}
}
//mergeSort方法和前面完全一致。关键在于如何调用。
private static int[] mergeSort(int[] a, int[] b){
int[] c = new int[a.length + b.length];
int aInd=0;
int bInd=0;
int cInd=0;
while(aInd < a.length && bInd < b.length){
if (a[aInd] > b[bInd]){
c[cInd++] = b[bInd++];
}
else{
c[cInd++] = a[aInd++];
}
}
while(aInd == a.length && bInd < b.length){
c[cInd++] = b[bInd++];
}
while(bInd == b.length && aInd < a.length){
c[cInd++] = a[aInd++];
}
System.out.println(Arrays.toString(c));
return c;
}
}
4,背包问题
背包问题有很多种形式,最简单的是:有5种重量的物体,1kg,2kg,5kg,10kg,20kg。现在将其中的一个或多个放入背包中,使背包的总重量为25kg。
一个物体只能出现一次。当找的一个可能的组合后返回reture,没有找到返回false。
5,概率问题
从5个字母(A,B,C,D,E)中选择出3个有多少种组合。注意这里是组合而不是排列,因此ABC和ACB是一样的组合。
递归的解决方法就是找个一个问题的中间态,因此这里我们可以考虑:
从5个字母中选出3个字母的组合等于=由A开头的所有组合+不是由A开头的所有组合
也可以表示为:
(5,3) = (4,2) + (4,3)
也就是f(n,k) = f(n-1,k-1) + f(n-1,k)
而基值条件为:
(i,1) -- 当k等于1时,有i种可能。例如从3个字母中挑1个出来,就有3种可能的组合。
(i,j) -- 当i和j相等时,有1种可能。例如从3个字母中挑3个字母出来,就只有一种组合。
代码非常的简单:
public class Testing {
public static void main(String arg[]){
System.out.println(selector1(5,3));
}
public static int selector1(int i, int j){
if(j == 1){
return i;
}
if(i == j){
return 1;
}
return selector1(i-1,j-1)+selector1(i-1,j);
}
}