文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。
理解回溯
在我们的一生中,会遇到很多重要的岔路口。在岔路口上,每个选择都会影响我们今后的人生。有的人在每个岔路口都能做出最正确的选择,最后生活、事业都达到了一个很高的高度;而有的人一路选错,最后碌碌无为。如果人生可以量化,那如何才能在岔路口做出最正确的选择,让自己的人生“最优”呢?
我们可以借助前面学过的贪心算法,在每次面对岔路口的时候,都做出看起来最优的选择,期望这一组选择可以使得我们的人生达到“最优”。但是,我们前面也讲过,贪心算法并不一定能得到最优解。那有没有什么办法能得到最优解呢?
2004 年上映了一部非常著名的电影《蝴蝶效应》,讲的就是主人公为了达到自己的目标,一直通过回溯的方法,回到童年,在关键的岔路口,重新做选择。当然,这只是科幻电影,我们的人生是无法倒退的,但是这其中蕴含的思想其实就是回溯算法。
回溯的处理思想,有点类似枚举搜索,又被称为“暴力搜索”。我们枚举所有的解,找到满足期望的解。为了有规律地枚举所有可能的解,避免遗漏和重复,我们把问题求解的过程分为多个阶段。每个阶段,我们都会面对一个岔路口,我们先随意选一条路走,当发现这条路走不通的时候(不符合期望的解),就回退到上一个岔路口,另选一种走法继续走。
八皇后问题
在一个8x8的棋盘中,八个棋子(皇后)不能在同一行,同一列,对角线上相遇。第一幅图是满足要求的,第二幅图是不满足要求的。求八皇后所有的摆放方式。
我们把这个问题分成8个阶段。依次将八个棋子放到第一行、第二行…。在放置过程中不停地检查看当前位置是否满足条件。如果不满足就换另一种方式试。
public class EightQueens {public static void main(String[] args){new EightQueens().eightQueens();}public void eightQueens(){cal8queens(0);}private int[] result = new int[8];//下标表示第几行,值表示皇后在第几列private void cal8queens(int row) {if(row==8){printResult();}else{for(int j=0;j<8;j++){if(isOk(row,j)){result[row] = j;cal8queens(row+1);}}}}private boolean isOk(int row, int col) {//不在同一列int leftUp = col-1,rightUp=col+1;for(int i=row-1;i>=0;i--){if(i!=row && result[i]==col) return false;if(leftUp>=0 && result[i]==leftUp) return false;if(rightUp<8 && result[i]==rightUp) return false;leftUp++;rightUp--;}return true;}private void printResult() {for(int i=0;i<result.length;i++){System.out.println();for(int j=0;j<8;j++){if(j==result[i]){System.out.print("Q");}else{System.out.print("*");}System.out.print(" ");}}System.out.println();}
}
回溯法还可以适于练习的题目:0-1背包问题、正则表达式问题 。