【转】递归算法的解题思路二

转自 http://blog.csdn.net/yzx226/article/details/6195999

递归算法的解题思路:

首先步骤分解,写出最后一次递归(n=1)的计算公式,然后是倒数第二次(n=2),n=3....,最后归纳出递归算法

如第二题:fn(1)=1;f(2)=1;f(3)=f(1)+f(2);----> f(n)=f(n-2)+f(1),那么很容易就写出这个递归函数

f(n)={n<=2?1:fn(n-2)+f(n-1)}

再如第五题:
设f()=0,是一次失败的组合,f()=1是一次成功的组合
f(n,sum),n:轮次,sum:本轮及本轮之后应打中的总环数
 
f(1,sum),sum<0||sum>10,则返回0;
                sum<=10,这说明最后一枪只要打中sum环,就能满足题设,返回1,即一次组合情况
f(2,sum),sum<0||sum>20,则返回0;
                sum==20,这说明最后两枪只要打都中10环,就能满足题设,返回1
                sum<20,如果倒数第二枪打中x环[0,10],最后一枪打中sum-x环,也就能满足题设,返回1
注意这里,上一句就可以描述为当本轮打中x环的情况下,会几轮能打中sum-x环会用多少种情况,也即f(n-1,sum-x)种情况
我这个递归算法中,还可以加上一个数组参数用来记录前几轮的中靶情况,这样就能打印出每种组合
 
在递归算法中,当递归层次很深时,要考虑空间复杂度,尽量减少新变量,所以我的算法中,多用了ref方式。在面试可以忽略这种情况,加快解题速度。
 
另外,多数递归算法都可以拆解成非递归的循环算法,因为这样会减少递归函数的入栈出栈。在实际运用中,要综合考虑运行工况(CPU、内存、算法被调用的频度,递归层数等),也就是空间与时间的取舍。
 
以上为个人观点,请各位指教。
 
应用实例:

1、编写一个方法用于验证指定的字符串是否为反转字符,返回true和false。请用递归算法实现。(反转字符串样式为"abcdedcba")

2、一列数的规则如下: 1、1、2、3、5、8、13、21、34...... 求第30个是多少

3、一列数的规则如下: 1、12、123、1234、12345、123456......,求第n个数的递归算法(n<=9)。
4、将一整数逆序,如987654321变为123456789。

5、一个射击运动员打靶,靶一共有10环,连开10枪打中90环的可能行有多少种?

以上的前提:不能用数组 或转成字符串处理,也不能用内置函数,如C#的幂函数(Math.Pow)

 

 

[c-sharp] view plaincopy
  1. namespace RecursionAlgorithms  
  2. {  
  3.     class Program  
  4.     {  
  5.         private static bool fn1(ref string str, ref int from, ref int to)  
  6.         {  
  7.             if (from >= to) return true;  
  8.             if (str[from++] != str[to--]) return false;  
  9.             return fn1(ref str, ref from, ref to);  
  10.         }  
  11.         private static int fn2(int i)  
  12.         {  
  13.             return i <= 2 ? 1 : fn2(i - 2) + fn2(i - 1);  
  14.         }  
  15.         private static long fn3(long x, ref long n)  
  16.         {  
  17.             return (x <= 1) ? x : fn3(x - 1, ref n) + x * (n *= 10);  
  18.         }  
  19.         private static long fn4(long x, ref long n)  
  20.         {  
  21.             return (x < 10) ? x : fn4(x / 10, ref n) + (x % 10) * (n *= 10);  
  22.         }  
  23.         private static long fn5(int n, int sum)  
  24.         {  
  25.             if ((n == 1 && sum <= 10) || (sum == n * 10)) return 1;  
  26.             if (sum > n * 10 || sum < 0) return 0;  
  27.             long ok = 0;  
  28.             for (int i = 0; i <= 10; i++)  
  29.             {  
  30.                 ok += fn5(n - 1, sum - i);  
  31.             }  
  32.             return ok;  
  33.         }  
  34.         static void Main(string[] args)  
  35.         {  
  36.             string[] strs = { """a""aa""aba""abba""abcba""ab""abc""abca" };  
  37.             for (int i = 0; i < strs.Length; i++)  
  38.             {  
  39.                 string str = strs[i];  
  40.                 int from = 0, to = str.Length - 1;  
  41.                 Console.WriteLine("{0} is {1}", str, fn1(ref str, ref from, ref to));  
  42.             }  
  43.             for (int i = 1; i <= 30; i++) Console.Write("{0}:{1} /t", i, fn2(i));  
  44.             long n = 1, m = 1, t = 0;  
  45.             for (int i = 0; i <= 9; i++, n = m = 1)  
  46.             {  
  47.                 Console.Write("/n {0} ==> {1}", t = fn3(i, ref n), fn4(t, ref m));  
  48.             }  
  49.             Console.WriteLine("/n{0}种可能性", fn5(10, 90));  
  50.         }  
  51.     }  
  52. }  
    续:

递归算法设计的基本思想是:对于一个复杂的问题,把原问题分解为若干个相对简单类同的

子问题,继续下去直到子问题简单到能够直接求解,也就是说到了递推的出口,这样原问题就有递推

得解。

  关键要抓住的是:

  (1)递归出口

  (2)地推逐步向出口逼近

  三、具体说明

  1.汉诺塔

  这是递归的超经典的例子,几乎每本程序设计书上谈到递归都会介绍。具体情景不再赘述。以我

上述的方法观之:(1)递归的出口在于disk数为一的时候

  (2)向出口逼近:如果不是一,是n ,则我们先挪动上面n-1块disk,等上面挪完,即递归返回

的时候,我们挪动最底下的disk.

  仅仅如此,一个貌似十分复杂的问题就解决了,因为挪动那n-1块disk的时候,会继续向上减少,

直到disk的数量为一为止。下面给出java程序编码(已测试过,运行正常):

  import javax.swing.JOptionPane;

  public class Hanoi {

  private static final String DISK_B = "diskB";

  private static final String DISK_C = "diskC";

  private static final String DISK_A = "diskA";

  static String from=DISK_A;

  static String to=DISK_C;

  static String mid=DISK_B;

  public static void main(String[] args) {

  String input=JOptionPane.showInputDialog("please input the number of the disks you

want me move.");

  int num=Integer.parseInt(input);

  move(num,from,mid,to);

  }

  private static void move(int num, String from2, String mid2, String to2) {

  if(num==1){

  System.out.println("move disk 1 from "+from2+" to "+to2);

  }

  else {

  move(num-1,from2,to2,mid2);

  System.out.println("move disk "+num+" from "+from2+" to "+to2);

  move(num-1,mid2,from2,to2);

  }

  }

  }

  2.这是一个排列的例子,它所做的工作是将输入的一个字符串中的所有元素进行排序并输出,例

如:你给出的参数是"abc" 则程序会输出:

  abc

  acb

  bac

  bca

  cab

  cba

  (1)算法的出口在于:low = high 也就是现在给出的排列元素只有一个时。

  (2)算法的逼近过程:先确定排列的第一位元素,也就是循环中i所代表的元素,

  然后 low+1 开始减少排列元素,如此下去,直到 low = high

  

    public static void permute(String str) {
        char[] strArray = str.toCharArray();
        permute(strArray, 0, strArray.length - 1);
    }

    public static void permute(char[] list, int low, int high) {
        int i;
        if (low == high) {
            String cout = "";
            for (i = 0; i <= high; i++)
                cout += list[i];
            System.out.println(cout);
        } else {
            for (i = low; i <= high; i++) {
                char temp = list[low];
                list[low] = list[i];
                list[i] = temp;
                permute(list, low + 1, high);
                temp = list[low];
                list[low] = list[i];
                list[i] = temp;
            }
        }
    }

  3。这是一个组合的例子,与上述的例子相似,只是它所做的工作是,输出所给字符串中制定数

目的元素的组合种类

  (1)程序出口在于n=1,此时只要输出目标数组的所有元素即可

  (2)逼近过程,当n>1 的时候,我们先取第一个元素放入目标数组中,然后n-1,如此

下去,最后出来。

import javax.swing.JOptionPane;

public class Combination {

    public static void main(String[] args) {
        String input = JOptionPane
                .showInputDialog("please input your String: ");
        String numString = JOptionPane
                .showInputDialog("please input the number of your Combination: ");
        int num = Integer.parseInt(numString);
        Combine(input, num);
    }

    private static void Combine(String input, int num) {
        char[] a = input.toCharArray();
        String b = "";
        Combine(a, num, b, 0, a.length);

    }

    private static void Combine(char[] a, int num, String b, int low, int high) {
        if (num == 0) {
            System.out.println(b);
        } else {
            for (int i = low; i < a.length; i++) {
                b += a[i];
                Combine(a, num - 1, b, i + 1, a.length);
                b = b.substring(0, b.length() - 1);
            }
        }
    }
}

 

 

posted @ 2013-03-22 14:39  muzhi121  阅读(650)  评论(0)    收藏  举报