zoukankan      html  css  js  c++  java
  • [转载] 递归问题整理

    不敢说是总结,就是把自己看到的一些递归相关题目整理一下,并按照自己的理解归下类~

    • 单路递归(一个递归过程中只有一个递归入口)
    • 多路递归(一个递归过程中有多个入口)
    • 间接递归(函数可通过其他函数间接调用自己)
    • 迭代递归(每次递归调用都包含一次循环递归)
    • 下面一一整理,注意许多题目都有更优解法,如DP,但是暂不讨论。

    先说说解递归的一般思路吧,把原问题分解为更小的子问题,再从子问题里慢慢寻找原问题的解。实际上递归是一种思路,解题时首先列出递归表达式,然后用程序语言的方式把他表现出来。往往递归都可转化为循环或者模拟调用栈来实现,但是递归表达更利于理解。

    一,单路递归(递归链)

    1,求n的阶乘(经典实例)

    int factorial(int n) { 
       if (n == 0) {  //基线条件(base case) 
          return 1; 
       } else { 
          return n * factorial(n - 1); //将问题规模逐渐缩小,或者说转化为更小更简单的子问题 
       } 
    }

    2,堆结构的维护(可参考数据结构相关书籍)

    上面的例题是比较简单的单路递归,类似的还有递归遍历目录下所有文件,下面介绍复杂一点的,不同的递归调用在不同的条件判断语句里,这也是单路递归:)

    3,迷宫问题(实际上属于深度优先搜索的范畴)

    通用解法:

    bool FindPathThroughMaze( Maze maze, Point position ){ //if the position has already been tried, don't try it again if( AlreadyTried( maze, position ) ) { return false;
         } //if this position is the exit, declare success if( ThisIsTheExit( maze, position ) ) { return true;
         } //Remember that this position has been tried RememberPosition( maze, position ); //check the path to the left, up, down, and to the right; if  //any path is successful, stop looking if( MoveLeft( maze, position, &newPosition ) ) { if( FindPathThroughMaze( maze, newPosition ) ) { return true;
             }
         } if( MoveUp( maze, position, &newPosition ) ) { if( FindPathThroughMaze( maze, newPosition ) ) { return true;
             }
         } if( MoveDown( maze, position, &newPosition ) ) { if( FindPathThroughMaze( maze, newPosition ) ) { return true;
             }
         } if( MoveRight( maze, position, &newPosition ) ) { if( FindPathThroughMaze( maze, newPosition ) ) { return true;
             }
         } //can't find the exit. return false;
    }
    4,POJ 2756 http://poj.grids.cn/problem?id=2756
    思路:
    • 这个题目要求树上任意两个节点的最近公共子节点。分析这棵树的结构不难看出,不论奇数偶数,每个数对2 做整数除法,就走到它的上层结点。
    • 我们可以每次让较大的一个数(也就是在树上位于较低层次的节点)向上走一个结点,直到两个结点相遇。
    • 如果两个节点位于同一层,并且它们不相等,可以让其中任何一个先往上走,然后另一个再往上走,直到它们相遇。
    • 设common(x, y)表示整数x 和y的最近公共子节点,那么,根据比较x 和y 的值,我们得到三种情况:
    • (1) x 与y 相等,则common(x, y)等于x 并且等于y
    • (2) x 大于y,则common(x, y)等于common(x/2, y)
    • (3) x 大于y,则common(x, y)等于common(x y/2)

    所以程序如下:

    #include  int common(int x, int y) { if(x == y) return x; if(x > y) return common(x / 2, y); return common(x, y / 2);
    } int main(void) { int m, n; scanf("%d%d", &m, &n); printf("%d/n", common(m, n)); return 0;
    }
    5,二分查找(经典例子,可查阅数据结构相关书籍)

    二,多路递归(递归树)

    1,树的前中后序遍历(经典例子,也形象地反映了多路递归过程的树状结构)

    2,合并排序,快速排序(都是将问题化解我两个更小的子问题去解决,也是树状结构,各节点是动态构造的)

    3,放苹果(POJ 1664)

    问题描述:

    把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法? 5,1,1和1,5,1 是同一种分法。(1<=M,N<=10)

    如输入M = 7, N = 3,应输出8

    (7 0 0 )(6 1 0 )(5 2 0 )(5 1 1 )(4 3 0 )(4 2 1 )(3 3 1 )(3 2 2)

    思路分析:

    • 所有不同的摆放方法可以分为两类:至少有一个盘子为空和所有盘子都不空。对于至少空着一个盘子的情况,则N 个盘子摆放M 个苹果的摆放方法数目与N-1 个盘子摆放M 个苹果的摆放方法数目相同。对于所有盘子都不空的情况,则N 个盘子摆放M 个苹果的摆放方法数目等于N 个盘子摆放M-N 个苹果的摆放方法数目。我们可以据此来用递归的方法求解这个问题。 
    • 设f(m, n) 为m 个苹果,n 个盘子的放法数目,则先对n 作讨论,如果n>m,必定有n-m 个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响;即if(n>m) f(m,n) =f(m,m)。当n <= m 时,不同的放法可以分成两类:即有至少一个盘子空着或者所有盘子都有苹果,前一种情况相当于f(m , n) = f(m , n-1); 后一种情况可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m , n) = f(m-n , n)。总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n) 。整个递归过程描述如下:
    int ways(int m, int n) { if (m == 0 || n == 1) //base case return 1; else if (m < n) return ways(m , m); else return ways(m, n - 1) + ways(m - n, n); //转化为更小更简单的子问题 }

    4,斐波那契数(递归方法是很搓的,勿用)

    int Fib( int n ){ if( n <  2 ) return; else return Fib(n-2) + Fib(n-1);
    }
    5,最大子段和(最优解法是DP)
    求a[1:n]的最大子段和可分解为a[1:n/2], a[n/2+1,n]的最大子段和情况,有如下3种情况

    a[1:n]的最大子段和与a[1:n/2]的最大子段相等;

    a[1:n]的最大子段和与a[n/2+1,n]的最大子段相等;

    a[1:n]的最大子段和经过a[1:n/2]与a[n/2+1,n],这种情况最大子段和等于两个最大子段和相加;

    代码:

    int MaxSubSum(int *a, int left, int right)
    { int sum = 0; if(left == right)
            sum = a[left] > 0 ? a[left] : 0; else { int center = (left + right) / 2; int leftsum = MaxSubSum(a, left, center); int rightsum = MaxSubSum(a, center+1, right); int s1 = 0; int lefts = 0; for(int i = center; i >= left; i--) {
                    lefts += a[i]; if(lefts > s1)
                        s1 = lefts;
            } int s2 = 0; int rights = 0; for(int i = center + 1; i <= right; i++){
                    rights += a[i]; if(rights > s2)
                        s2 = lefts;
            }
            
            sum = s1 + s2;
            
            sum = (sum < leftsum ? leftsum : sum);
            sum = (sum < rightsum ? rightsum : sum);
        } return sum;
    }

    三,间接递归(类似递归链)

    1,递归下降解释器(解释器用的比较多,以后我会写篇相关文章) 
    原理就是exp1()->exp2()->exp3()->………..->exp1(),其中按优先级,越后面的表达式优先级越高,然后递归又从exp1()调用。

    四,迭代递归(类似图的深度优先遍历)

    1,图的深度优先遍历

    通用深度优先搜索框架:

    void dfs(int deep, State curState){ if (deep > Max) {//深度达到极限  if (curState == target) //找到目标 { //... }
    } else { for (i = 1; i <= totalExpandMethod; i++)
            {
    	   dfs(deep + 1, expandMethod(curState, i));
            }
    }
    }
    2,广义水仙花数
    问题描述:一个三位数abc如果满足abc = a^3 + b^3 + c^3 那么就把这个数叫做水仙花数。

    如果一个N位数所有数码的N次方的和加起来等于这个数字本身,我们把这样的数叫做广义水仙花数,容易看出来水仙花数是N = 3的广义水仙花数现在,我们的任务是,输入一个m (m < 7) ,让你求出所有满足N = m的广义水仙花数。

    3 (153 370 371 407)

    5 (54748 92727 93084)

    方法:数据规模很小,可以直接枚举所有情况,然后判断是否满足条件。

    难点:循环层数不确定

    于是我们现在的问题是,怎么实现这个m重循环?

    答案是:递归。

    完整代码:

    #include#includeusing namespace std; int m; int Pow(int x, int n)
    { int res = 1; while (n--) res *= x; return res;
    } void dfs(int deep, int curNum, int curSum)
    { if (deep > m) //类似于base case { if (curNum == curSum) printf("%d/n", curNum);
    	} else if (deep <= m)
    	{ int start = (deep == 1); //第一位不为0 for (int i = start; i <= 9; i++)
    			dfs(deep + 1, curNum * 10 + i, curSum + Pow(i, m)); //缩小问题规模 }
    } int main()
    { while (scanf("%d", &m), m)
    	{
    		dfs(1, 0, 0);
    	} return 0;
    }

    3,全排列(可网上查阅。。)
  • 相关阅读:
    鼠标移入图片放大效果
    搜索框
    将下拉菜单放入input框中
    轮播图2
    V-demo item.vue
    v-demo1 List.vue
    v-demo1 add.vue
    v-demo1 app.vue
    3D轮播图
    封装代码
  • 原文地址:https://www.cnblogs.com/youxin/p/3285731.html
Copyright © 2011-2022 走看看