栈的应用:递归
将函数的调用位置,局部变量和寄存器等信息作为1种称作激活记录的数据结构。(图P147和P148)
用通用方法将大型问题简化为类似性质但规模更小的问题。
一直递归直到将子问题简化到无需递归即可解答。
- 关键步骤:确定普遍使用的划分原则
- 停止规则:确定算法能够处理特例,例如什么不做时能正常返回。
- 递归函数执行关键步骤直到符合停止规则
将指令放在一个区域,将其他数据放在每个用户独立的区域
注意尾部递归:当函数最后执行的语句(不一定是程序的最后一句)是递归调用。可以通过while/do while循环改为迭代减少空间复杂度。
通过递归树来判断使用迭代还是递归:
- 当递归树不存在重复任务(例如斐波那契数列P150)且树很茂密,考虑使用迭代;
- 树的形式简单考虑迭代。
- 迭代(自底向上,由已知到未知)和栈能代替递归,递归(自顶向下,从未知分解为已知)能替代栈。
范例:
- 分而治之
- 树结构
回溯:
- 构造部分解,当与问题需求不一致时,移去最近构造的解并倒退,尝试另一种可能。
- 适合随着不断的测试能减少可能的数量
八皇后问题(皇后可以横/竖/对角线)
solve_form函数:从给定的皇后位置递归查找解答
Queens configuration:已选择的路径
configuration:棋盘上所有的位置
solve_form(Queens configuration)
检查Queens configuration是否违背规则,违背直接返回
if已有八个 打印结束
else
for_each configuration未选择的位置{
选择下一个皇后在configuration的位置
solve_form(Queens configuration);
移除
}
int main()
{
int board_size;//放置的皇后数量
cin>>board_size;
if(board_size<0 || board_size>max_board )
cout<<"…"<<endl;
else{
Queens_configuration(board_size);
solve_form(configuration);
}
}
//根据第count行第col列的格子是否能够放置
bool Queens::unguarded(int col)const;
//没违反则插入,count+1
bool Queens::insert(int col);
//已摆放的数量是否等于board_size
bool Queens::is_sovled()const;
solve_form(Queens_configuration)
{
if( configuration.is_solved() )
configuration.print();
else
for(int col=0;col<configuration.board_size;
++col)
if( configuration.unguarded(col) ){
configuration.insert(col);
solve_form(configuration);
configuration.remove(col);
}
}
const int max_board=0;
class Queens{
public:
Queens(int size);
bool is_solved()const;
void print()const;
//判断是否可以放置
bool unguarded(int col)const;
void insert(int col);
void remove(int col);
int board_size;
private:
int count;
bool queen_square[max_board][max_board];
};
Queens::Queens(int size)
{
board_size=size;
count=0;
for(int row=0;row<board_size;++row)
for(int col=0;col<board_size;++col)
queen_square[row][col]=false;
}
void Queens::insert(int col)
{
queen_square[count-1][col]=true;
}
bool Queens::unguarded(int col)const
{
int i;
bool ok=true;
for(i=0; ok&&i<count ;++i)
ok= !queen_square[i][col];
for(i=0; ok&&count-i>=0&&col+i<board_size ;++i)
ok= !queen_square[i][col];
return ok;
}
优化
- 建议1:将所有不能放置的数组同时标记
- 建议2:使用3个数组记录可以使用的格子
class Queens{
…
private:
int count;
bool col_free[max_board];
bool upward_free[max_board];
bool downward_free[max_board];
int queen_in_row[max_board];
};
bool Queens::unguarded(int col)const
{
return col_free[col]
&& upward_free[count+col];
&& downward_free[count+col+board_size-1];
}
游戏树:
2个玩家轮流选择123中的一个数字,并且不能选择上一轮的数字。2个玩家所选的数字之和等于8时的玩家获胜;使和大于8的玩家失败。
由于在合理的运行时间内无法探查所有的分支,只能通过评价函数返回利益的估计值。