几个经典的递归问题

以下内容来自维基百科:
递归(英语:Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法重复事物的过程。例如,当两面镜子相互之间近似平行时,镜中嵌套的图像是以无限递归的形式出现的。也可以理解为自我复制的过程。
维基百科的几个例子有助于理解递归

  1. 从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?“从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?‘从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……’”
  2. 一只狗来到厨房,偷走一小块面包。厨子举起杓子,把那只狗打死了。于是所有的狗都跑来了,给那只狗掘了一个坟墓,还在墓碑上刻了墓志铭,让未来的狗可以看到:“一只狗来到厨房,偷走一小块面包。厨子举起杓子,把那只狗打死了。于是所有的狗都跑来了,给那只狗掘了一个坟墓,还在墓碑上刻了墓志铭,让未来的狗可以看到:‘一只狗来到厨房,偷走一小块面包。厨子举起杓子,把那只狗打死了。于是所有的狗都跑来了,给那只狗掘了一个坟墓,还在墓碑上刻了墓志铭,让未来的狗可以看到……’”
  3. 大雄在房里,用时光电视看着未来的情况。电视画面中的那个时候,他正在用时光电视,看着未来的情况。电视画面中的电视画面的那个时候,他正在用时光电视,看着未来的情况……

递归的两个要点
1.递归基:特殊情况下,一般为递归的出口。
2. 递归表达式:重复计算的那部分。

一.求阶乘
有数学表达式
n ! = n ( n 1 ) ( n 2 ) 2 1 n! = n\cdot (n-1)\cdot (n-2)\cdot \cdot \cdot 2\cdot 1
可以很容易写出递归的代码

 public static int fact(int n) {
        if (n <= 1)
            return 1;
        return n * fact(n - 1);
    }

二. 求斐波那契数列
有数学表达式
f ( n ) = { 1 , n = 1 1 , n = 2 f ( n 1 ) + f ( n 2 ) , n > 2 f(n)= \left\{ \begin{aligned} 1 &&, n=1\\ 1&&, n=2\\ f(n-1)+f(n-2)&&,n>2\\ \end{aligned} \right. ,可以很容易写出递归的代码

public static int fib(int n) {
        if (n == 1 || n == 2) {
            return 1;
        }
        return fib(n - 1) + fib(n - 2);
    }

三. 数组求和
这也可以看成一个递归问题,要求数组中n个数的和可以转化为开始第一个数与剩余n-1个数的和的和。

  • 基准情况:开始的那个数为数组的最后一个数,则直接返回这个数。
  • 递归:开始第一个数与剩余n-1个数的和的和。
 /**
   * 数组求和
   * @param arr   -数组
   * @param begin -数组起始位置
   * @return
   */
 public static int sum(int[] arr, int begin) {
        if (begin == arr.length - 1)
            return arr[begin];
        else if (begin >= arr.length) {
            throw new ArrayIndexOutOfBoundsException("数组下标越界……");
        } else {
            return arr[begin] + sum(arr, begin + 1);
        }
    }

四. 反转字符串

  • 递归分析:将问题分解为反转最后一个字符与剩余的字符串。
  • 基准情况:只剩下一个字符是,直接返回字符
  • 递归:求解最后一个字符与剩余的字符串
/**
  * 字符串反转
  * @param src  -原字符串
  * @param end  -最后一个字符
  * @return 反转后的字符串
  */
    public static String reverse(String src, int end) {
        if (end == 0) {
            return src.toCharArray()[end] + "";
        }
        return src.toCharArray()[end] + reverse(src, end - 1);
    }

五. 最大公约数
这是一个数学问题,利用辗转相除法。

  • 辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数相除余数的最大公约数。例如,252和105的最大公约数是21(252 = 21 × 12; 105 = 21 × 5);因为 252 − 105 = 21 × (12 − 5) = 147 ,所以147和105的最大公约数也是21。在这个过程中,较大的数缩小了,所以继续进行同样的计算可以不断缩小这两个数直至其中一个变成零。这时,所剩下的还没有变成零的数就是两数的最大公约数。

  • 基准情况:两个数其中一个变成零。

  • 递归:求较小的数和较大的数与较小的数的余数的最大公约数

/**
  * 最大公约数
   * @param m
   * @param n
   * @return
   */
    public static int gcd(int m, int n) {
        if (n == 0) {
            return m;
        }
        return gcd(n, m % n);
    }

六. 插入排序递归法

 /**
   * 插入排序递归法
   * @param arr -数组
   * @param k  -数组前k个元素
   */
    public static void insertionSort(int arr[],int k){
        if(k==0)
            return;
        insertionSort(arr,k-1);
        int tmp = arr[k];
        int i = k - 1;
        while(i>=0 && arr[i]>=tmp){
            arr[i+1]=arr[i];
            --i;
        }
        arr[i+1] = tmp;
    }

七. 汉诺塔问题

public static void printHanoTower(int n,String from,String to, String help){
        if(n==1){
            System.out.println("move "+n+" from "+from+" to " + to);
            return;
        }
        printHanoTower(n-1,from,help,to);
        System.out.println("move "+n+" from "+ from +" to "+to);
        printHanoTower(n-1,help,to, from);
    }
发布了13 篇原创文章 · 获赞 5 · 访问量 684

猜你喜欢

转载自blog.csdn.net/qq_43657590/article/details/103946933
今日推荐