一,递归的基本概念
递归(recursion):函数调用自身的编程技巧。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。
1,使用递归需要满足2个条件:
1)有反复执行的过程(调用自身)
2)有跳出反复执行过程的条件(递归出口,即终止条件),
2,递归的缺点:
递归算法解题的运行效率较低。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。
n! = n * (n-1) * (n-2) * ...* 1(n>0)
<span style="font-size:12px;">//阶乘 int recursive(int i) { int sum = 0; if (0 == i) return (1); else sum = i * recursive(i-1); return sum; }</span>
<span style="font-size:12px;">//河内塔 void hanoi(int n,int p1,int p2,int p3) { if(1==n) cout<<"盘子从"<<p1<<"移到"<<p3<<endl; else { hanoi(n-1,p1,p3,p2); cout<<"盘子从"<<p1<<"移到"<<p3<<endl; hanoi(n-1,p2,p1,p3); } }</span>
从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。如1,2,3三个元素的全排列为:
1,2,31,3,22,1,32,3,13,1,23,2,1
<span style="font-size:12px;">//全排列 inline void Swap(int &a,int &b) { int temp=a; a=b; b=temp; } void Perm(int list[],int k,int m) { if (k == m-1) { for(int i=0;i<m;i++) { printf("%d",list[i]); } printf("n"); } else { for(int i=k;i<m;i++) { Swap(list[k],list[i]); Perm(list,k+1,m); Swap(list[k],list[i]); } } }</span>
斐波纳契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、……这个数列从第三项开始,每一项都等于前两项之和。有趣的兔子问题:
一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?分析如下:第一个月小兔子没有繁殖能力,所以还是一对;两个月后,生下一对小兔子,总数共有两对;三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,总数共是三对;…… 依次类推可以列出下表:
<span style="font-size:12px;">//斐波那契 long Fib(int n) { if (n == 0) return 0; if (n == 1) return 1; if (n > 1) return Fib(n-1) + Fib(n-2); }</span>
分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。递归的解这些子问题,然后将各子问题的解合并得到原问题的解。
分治法所能解决的问题一般具有以下几个特征:
1) 该问题的规模缩小到一定的程度就可以容易地解决
2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3) 利用该问题分解出的子问题的解可以合并为该问题的解;
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
分治法的基本步骤:
分治法在每一层递归上都有三个步骤:
分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
合并:将各个子问题的解合并为原问题的解。
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
回溯算法是搜索算法中的一种控制策略。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解,如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则进入该子树,继续按深度优先的策略进行搜索。回溯算法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。回溯算法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯算法。
(1)针对所给问题,确定问题的解空间:
首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
<span style="font-size:12px;">回溯算法描述如下: procedure run (当前状态); var i:integer ; begin if 当前状态为边界 then begin if 当前状态为最佳目标状态 then 记下优结果; exit; { 回溯} end ;{then} for i ←算符最小值 to 算符最大值 do begin 算符i作用于当前状态,扩展出一个子状态;</span></span>
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
回溯算法是搜索算法中的一种控制策略。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解,如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则进入该子树,继续按深度优先的策略进行搜索。回溯算法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。回溯算法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯算法。
(1)针对所给问题,确定问题的解空间:
首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
<span style="font-size:12px;">回溯算法描述如下: procedure run (当前状态); var i:integer ; begin if 当前状态为边界 then begin if 当前状态为最佳目标状态 then 记下优结果; exit; { 回溯} end ;{then} for i ←算符最小值 to 算符最大值 do begin 算符i作用于当前状态,扩展出一个子状态;</span></span>