一、递归(recursion):程序调用自身的编程技巧。
递归满足2个条件:
1)有反复执行的过程(调用自身)
2)有跳出反复执行过程的条件(递归出口)
爬楼梯问题:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
n=2时,有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
对于第n阶台阶有多少种爬法这个问题,我们只需要知道n-1阶和n-2阶有多少种爬法就可以了(第n阶台阶,无非是通过n-1或n-2阶台阶爬上去的)。
class Solution:
def _climbStairs(self, n, climb):
if n == 0 or n == 1:
return 1
if n not in climb:
climb[n] = self._climbStairs(n - 1, climb) + self._climbStairs(n - 2, climb)
return climb[n]
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
climb = dict()
return self._climbStairs(n, climb)
我们通过一个dict记录我们递归过程中计算过的值,只要这个值我们计算过,那么直接返回就好了。
二、回溯
回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。
回溯法指导思想——走不通,就掉头。
设计过程:确定问题的解空间;确定结点的扩展规则;搜索。
1.八皇后问题
要在n*n的国际象棋棋盘中放n个皇后,使任意两个皇后都不能互相吃掉。规则:皇后能吃掉同一行、同一列、同一对角线的任意棋子。求所有的解。n=8是就是著名的八皇后问题了。
#include <iostream>
using namespace std;
#define MAX_LENGTH 50
int is_conflict(int *a, int n)
{
int flag = 0;
int i;
for (i = 0; i< n; i++)
{
if (a[i] == a[n] || a[i] - a[n] == i - n || a[i] - a[n] == n - i)//同列||同对角线||同反对角线
{
flag = 1;
break;
}
}
return flag;
}
void print_board(int *a, int n)
{
int i, j;
for (i = 0; i< n; i++)
{
for (j = 0; j< a[i]; j++)
{
cout << " ";
}
cout << "q";
for (j = a[i] + 1; j< n; j++)
{
cout << " ";
}
cout << "/n";
}
cout << "--------/n";
}
void init_board(int *a, int n)
{
int i;
for (i = 0; i< n; i++)
a[i] = 0;
}
int queen(int n)//i表示行,a[i]表示列
{
int count = 0;
int a[MAX_LENGTH];
init_board(a, n);
int i = 0;
while (1)
{
if (a[i]< n) // 如果没有超过棋盘范围,需要检查第n行与前n-1行是否冲突
{
if (is_conflict(a