• 数据结构之递归


    在这篇文章里,我们主要讨论和递归相关的话题。递归是数据结构中解决复杂问题时非常有效的一种思考方式,对于一个复杂的问题,我们可以从中抽取一个可以通过迭代方式解决的子问题,而子问题是一个容易解决的问题。在使用递归时,有两个问题需要注意:1)抽取递归体;2)确定递归边界条件以及处理方式。

      下面我们来看相关的题目。

    • 斐波那契数列 我想这应该是最常见的递归的例子了,我们可以使用递归和非递归两种方式来实现它。 首先来看非递归的方式:
       
       1 public static int fibo2(int n)
       2 {
       3     if (n < 0) return -1;
       4     if (n == 0) return 0;
       5     
       6     int a = 0,b = 1,c;
       7     for (int i = 2; i <= n; i++)
       8     {
       9         c = a + b;
      10         a = b;
      11         b = c;
      12     }
      13     return b;
      14 }
       

      接着是使用递归方式:

       
      1 public static int fibo1(int n)
      2 {
      3     if (n < 0) return -1;
      4     if (n == 0) return 0;
      5     if (n == 1) return 1;
      6     return fibo1(n - 1) + fibo1(n - 2);
      7 }
       
    • 对于一个N*N的矩阵,从左上角开始遍历,每次只能横着走或者竖着走,问一共有多少种方式可以到达矩阵的右下角。 思路:可以把矩阵看成是N行N列的数据结构,初始时,对角线距离为N行、N列,当走一步之后,距离变为(N行、N-1列)或者是(N-1行、N列),直到距离变为(0行、0列)后,表明已经走到了右下角。
       
       1 public static void printAllPath(int n)
       2 {
       3     int[][] matrix = Matrix.initMatrix(n, 50);
       4     Matrix.printMatrix(matrix);
       5     ArrayList<Integer> path = new ArrayList<Integer>();
       6     walk(matrix, n, 0, 0, path);
       7 }
       8 
       9 private static void walk(int[][] matrix, int n, int row, int column, ArrayList<Integer> path)
      10 {
      11     path.add(matrix[row][column]);
      12     if (row == n - 1 && column == n -1)
      13     {
      14         StringBuffer sb = new StringBuffer();
      15         for(int value : path) sb.append(value).append("->");
      16         System.out.println(sb.substring(0, sb.length() - 2));
      17     }
      18     if (row < n -1)
      19     {
      20         ArrayList<Integer> path1 = (ArrayList<Integer>)path.clone();
      21         walk(matrix, n, row + 1, column, path1);
      22     }
      23     if (column < n - 1)
      24     {
      25         ArrayList<Integer> path2 = (ArrayList<Integer>)path.clone();
      26         walk(matrix, n, row, column + 1, path2);
      27     }
      28 }
       
      执行结果(首先生成一个矩阵,然后列出各种可能方式)
       
      22    17    35    
      16    3    38    
      46    39    4    
      
      22->16->46->39->4
      22->16->3->39->4
      22->16->3->38->4
      22->17->3->39->4
      22->17->3->38->4
      22->17->35->38->4
       
    • 列出给定集合的所有子集 思路:按照子集的定义,对于长度为N的集合来说,它的子集长度是从1到N-1,我们可以先假设求长度为M的子集,那应该怎么做呢?首先可以循环遍历集合,取出某一个元素,然后计算除去该元素的集合的长度为M-1的子集,然后递归,直到最后要找长度为1的子集为止。 然后按照上述思路,将M从1开始一直设置到N-1,就可以得到所有的子集。 需要注意的是,在递归的过程中,会产生重复的子集,需要删除这些子集。
       
       1 public static void getAllSubSet(int n)
       2 {
       3     int[] arrValue = Matrix.initArray(n, 50);
       4     StringBuffer sb = new StringBuffer();
       5     for(int value : arrValue) sb.append(value).append("->");
       6     System.out.println(sb.substring(0, sb.length() - 2));
       7     
       8     ArrayList<Integer> list = new ArrayList<Integer>();
       9     ArrayList<ArrayList<Integer>> subsets = new ArrayList<ArrayList<Integer>>();
      10     for(int i = 1; i < n; i++)
      11     {
      12         subset(arrValue, i, i, list, subsets);
      13     }
      14 }
      15 
      16 private static void subset(int[] arrValue, int subSetLength, int left, ArrayList<Integer> list, ArrayList<ArrayList<Integer>> subsets)
      17 {
      18     if (left == 0 || list.size() == subSetLength)
      19     {
      20         boolean bExist = false;
      21         for(ArrayList<Integer> temp : subsets)
      22         {
      23             if (temp.size() == list.size())
      24             {
      25                 int j = 0;
      26                 for (int value : list)
      27                 {
      28                     if (!temp.contains(value))break;
      29                     j++;
      30                 }
      31                 if (j == subSetLength) 
      32                 {
      33                     bExist = true;
      34                     break;
      35                 }
      36             }
      37         }
      38         if (!bExist)
      39         {
      40             StringBuffer sb = new StringBuffer();
      41             for(int value1 : list) sb.append(value1).append("->");
      42             System.out.println(sb.substring(0, sb.length() - 2));
      43             subsets.add(list);
      44         }
      45         
      46     }
      47     for (int temp : arrValue)
      48     {
      49         if (list.contains(temp)) continue;
      50         ArrayList<Integer> listTemp = (ArrayList<Integer>)list.clone();
      51         listTemp.add(temp);
      52         subset(arrValue, subSetLength, left - 1, listTemp, subsets);
      53     }
      54 }
       

      执行结果(假设一个长度为4的集合)

       
      35->6->40->33
      
      35
      6
      40
      33
      35->6
      35->40
      35->33
      6->40
      6->33
      40->33
      35->6->40
      35->6->33
      35->40->33
      6->40->33
       
    • 求给定字符串的所有可能组合(假设字符串没有重复字符) 思路:这个题目和上面的题目非常类似,只不过这个题目求的是对于长度为N的集合,我们要列出长度为N的“子集”。注意要去除重复组合。
       
       1 public static void perm(String value)
       2 {
       3     char[] arrChars = value.toCharArray();
       4     char[] arrAlready = new char[arrChars.length];
       5     ArrayList<char[]> all = new ArrayList<char[]>();
       6     permRecursive(arrChars, arrChars.length, arrAlready, all);
       7 }
       8 
       9 private static void permRecursive(char[] arrChars, int left, char[] arrAlready, ArrayList<char[]> all)
      10 {
      11     if (left == 0)
      12     {
      13         boolean bExist = false;
      14         for(char[] arrTemp : all)
      15         {
      16             int j=0;
      17             for (int i = 0; i < arrChars.length; i++)
      18             {
      19                 if (arrTemp[i] != arrAlready[i]) break;
      20                 j++;
      21             }
      22             if (arrChars.length == j)
      23             {
      24                 bExist = true;
      25                 break;
      26             }
      27         }
      28         if (!bExist)
      29         {
      30             all.add(arrAlready);
      31             for(char ch:arrAlready)System.out.print(ch);
      32             System.out.println();
      33         }
      34     }
      35     for(char ch : arrChars)
      36     {
      37         int i = 0;
      38         for(i = 0; i < arrChars.length - left; i++)
      39         {
      40             if (ch == arrAlready[i]) break;
      41         }
      42         if (i == arrChars.length - left) 
      43         {
      44             arrAlready[arrChars.length - left] = ch;
      45             char[] arrTemp = arrAlready.clone();
      46             permRecursive(arrChars, left - 1, arrTemp, all);
      47         }
      48     }
      49 }
       

      执行结果(以“abc”为例)

      abc
      acb
      bac
      bca
      cab
      cba
    • 给出N个'('和')',列出所有可能的合法组合 思路:依然使用递归的套路,需要注意已输出的'('的数目不能小于')'的数目。
       
       1 public static void printPar(int n)
       2 {
       3     char[] arrResult = new char[2*n];
       4     printParRecursive(n, n, arrResult, 0);
       5 }
       6 
       7 private static void printParRecursive(int lCount, int rCount, char[] arrResult, int totalCount)
       8 {
       9     if (lCount == 0 && rCount == 0)
      10     {
      11         System.out.println(arrResult);
      12     }
      13     if (lCount > 0)
      14     {
      15         arrResult[totalCount] = '(';
      16         printParRecursive(lCount - 1, rCount, arrResult, totalCount + 1);
      17     }
      18     if (rCount > lCount)
      19     {
      20         arrResult[totalCount] = ')';
      21         printParRecursive(lCount, rCount - 1, arrResult, totalCount + 1);
      22     }
      23 }
       

      执行结果(假设N=4)

       
      (((())))
      ((()()))
      ((())())
      ((()))()
      (()(()))
      (()()())
      (()())()
      (())(())
      (())()()
      ()((()))
      ()(()())
      ()(())()
      ()()(())
      ()()()()
       
    • 硬币组合,假设我们有25美分、15美分、5美分以及1美分的硬币,硬币的数目不限,对于指定的N美分,请列出所有可能的组合。 思路:典型的递归,假设已经有25美分,那么需要找出N-25的所有组合,同样需要找出N-15、N-5、N-1的组合。
       
       1 public static void combine(int n)
       2 {
       3     ArrayList<Integer> list = new ArrayList<Integer>();
       4     ArrayList<ArrayList<Integer>> all = new ArrayList<ArrayList<Integer>>();
       5     combinRecursive(n, list, all);
       6 }
       7 
       8 private static void combinRecursive(int n, ArrayList<Integer> list, ArrayList<ArrayList<Integer>> all)
       9 {
      10     if (n == 0)
      11     {
      12         Collections.sort(list);
      13         boolean bExist = false;
      14         for(ArrayList<Integer> temp : all)
      15         {
      16             if (temp.size() == list.size())
      17             {
      18                 int j = 0;
      19                 for(int i = 0; i < temp.size(); i++)
      20                 {
      21                     if (temp.get(i) != list.get(i)) break;
      22                     j++;
      23                 }
      24                 if (j == temp.size())
      25                 {
      26                     bExist = true;
      27                     break;
      28                 }
      29             }
      30         }
      31         if (!bExist)
      32         {
      33             all.add(list);
      34             StringBuffer sb = new StringBuffer();
      35             for(int value1 : list) sb.append(value1).append("->");
      36             System.out.println(sb.substring(0, sb.length() - 2));
      37         }
      38     }
      39     
      40     if (n >= 25)
      41     {
      42         ArrayList<Integer> temp = (ArrayList<Integer>)list.clone();
      43         temp.add(25);
      44         combinRecursive(n - 25, temp, all);
      45     }
      46     if (n >= 10)
      47     {
      48         ArrayList<Integer> temp = (ArrayList<Integer>)list.clone();
      49         temp.add(10);
      50         combinRecursive(n - 10, temp, all);
      51     }
      52     if (n >= 5)
      53     {
      54         ArrayList<Integer> temp = (ArrayList<Integer>)list.clone();
      55         temp.add(5);
      56         combinRecursive(n - 5, temp, all);
      57     }
      58     if (n >= 1)
      59     {
      60         ArrayList<Integer> temp = (ArrayList<Integer>)list.clone();
      61         temp.add(1);
      62         combinRecursive(n - 1, temp, all);
      63     }
      64 }
       

      执行结果(假设N=25)

       
      25
      5->10->10
      1->1->1->1->1->10->10
      5->5->5->10
      1->1->1->1->1->5->5->10
      1->1->1->1->1->1->1->1->1->1->5->10
      1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->10
      5->5->5->5->5
      1->1->1->1->1->5->5->5->5
      1->1->1->1->1->1->1->1->1->1->5->5->5
      1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->5->5
      1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->5
      1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1->1
       

      最后,欢迎大家提出更多和递归相关的面试题目,我们可以一起讨论。

  • 相关阅读:
    spring-boot BUG 集锦
    彻底卸载mysql
    mybatis -generator 逆向工程
    内网穿透工具使用
    Spring 笔记
    maven项目 @Resource报错 ava.lang.NoSuchMethodError: javax.annotation.Resource.lookup()Ljava/lang/String;
    log4j配置及异常、解决办法
    from…import * 语句与 import 区别
    python:__file__
    python安装yaml
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/3444570.html
Copyright © 2020-2023  润新知