1. 递归
- 递归说白了就是程序是自身调用,通常情况下会让复杂的循环变的简单好理解,但由于会涉及到大量的函数调用,时间效率很低,容易造成栈的溢出。剑指说面试题中,在时间要求较低的情况下能用递归的可以用。很容易体现出思路。
- 数据结构中的树是最适合用递归的数据结构。
- 递归最重要的是设置好递归出口的条件。
- 递归算法的思路是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。
- 典型的递归算法:N!的阶乘,斐波那契数列等。(斐波那契数列使用动态规划更为合适。详解。)
2. 回溯
参考https://blog.csdn.net/zw6161080123/article/details/79985345
-
回溯法是一种穷举类型的算法,与其说它是一种算法,倒不如说它是一种试法。体现了算法效率与适用性之间的矛盾,二分查找效率很高,但适用性比较低。而穷举法效率最低,但几乎适用于所有问题。
-
要用回溯法解决问题,那首先要确定问题的状态空间树。看每一步选择有多少个可选值就可以了,第一步有8个可选值,那树第一层就有8个节点,第二步有5个可选值,那第一层每个节点都有5个分支,则第二层有8×5=40个节点,以此类推……到第n层一共有m1×m2×……×mn个节点,其中mi为第i步的可选值的个数。
-
在n层的状态空间树中,我们要得到一个解向量,每个分量对应每一步选择的结果,显然这个解向量的长度应该为n。
-
r代表层数,先从第一层r=0选择一个元素,看看是否满足要求,如果不满于,则取本层下一个元素,满足则r++,进入下一层。然后一直重复这样的操作。
-
如果到达了r = n-1层,说明到达状态空间树最后一层了,如果符合要求,则找到了一个解向量。如果没有,找本层的下一个。
-
如果一层中所有元素都找完了,都不符合要求怎么办呢?那就要回溯了。回溯就是返回上一层。如果最后r=0,第一层所有的都不符合,则无解向量。
-
回溯法解题一般步骤:
(1)针对所给问题,确定问题的解空间:首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。 -
回溯法一般都可以采用递归的方式,解空间找到了则就很好写了;但非递归的效率高,下面详细写一下。
1: int a[n],i; 2: 初始化数组a[]; 3: i = 1; 4: while (i > 0 (有路可走) and (未达到目标)) // 还未回溯到头 5: { 6: if(i > n) // i=n时叶结点符合要求,i变成n+1,说明找到了解向量 7: { 8: 搜索到一个解,输出; 9: } 10: else // 处理第i个元素 11: { 12: a[i]第一个可能的值; 13: while(a[i]在不满足约束条件且在搜索空间内) 14: { 15: a[i]下一个可能的值; 16: } 17: if(a[i]在搜索空间内) //剪枝函数,如果到这个情况下不符合题解,则避免无效搜索 18: { 19: 标识占用的资源; 20: i = i+1; //扩展下一个结点 21: } 22: else 23: { 24: 清理所占的状态空间; // 回溯 25: i = i –1; 26: } 27: }
-
经典的八皇后问题就适合以回溯算法解决:先从第一行第一个开始,找下一行符合的,再找下一行。
- 回溯递归:
class EightQueen { private: int array[8];//该数组保表示第i个皇后摆在第i行第array[i]列 public: void solution(