回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。用回溯算法解决问题的一般步骤为:

1、定义一个解空间,它包含问题的解。

2、利用适于搜索的方法组织解空间。

3、 以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。

问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性。


基本思想:回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该(死)结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索


n皇后问题回溯法求解:

public class Queen {
    
    int QUEEN_COUNT = 8;    //随便你定义几个皇后了,你可以循环产生a个到b个皇后的解
    static final int EMPTY = 0;  //如果count[x][y] == EMPTY ,则可以放置皇后;反之,其正上方或斜上方必己放置皇后
    int[][] count = new int[QUEEN_COUNT][QUEEN_COUNT]; //
    int[] QueenIndex = new int[QUEEN_COUNT]; //第index行的皇后放置位置是QueenIndex [index]
    int resultCount = 0; //记录皇后放置方法的数量
    
    public void putQueenIndex(int index) {
        int row = index;
        for (int col = 0; col < QUEEN_COUNT; col++) {
            if (count[row][col] == EMPTY) { //该位置可以放置皇后
                for (int iRow = row+ 1; iRow < QUEEN_COUNT; iRow++) { //增加该位置的正下面/斜下面的count,使之不为0
                    count[iRow][col]++;
                    if ((col - iRow + row) >= 0) {
                        count[iRow][col - iRow + row]++;
                    }
                    if ((col + iRow - row) < QUEEN_COUNT) {
                        count[iRow][col + iRow - row]++;
                    }
                }
                QueenIndex[row] = col;
                if (row == QUEEN_COUNT - 1) { //第QUEEN_COUNT个皇后己放置好,打印出这种皇后布局
                    print(++resultCount);
                } else { //继续放置下一行的皇后
                    putQueenIndex(row + 1);
                }
                for (int iRow = row+ 1; iRow < QUEEN_COUNT; iRow++) { //回溯,在此行的皇后不放此列col ,恢复该位置的正下面/斜下面的count
                    count[iRow][col]--;
                    if ((col - iRow + row) >= 0) {
                        count[iRow][col - iRow + row]--;
                    }
                    if ((col + iRow - row) < QUEEN_COUNT) {
                        count[iRow][col + iRow - row]--;
                    }
                }
            }
        }
        if (row == 0) { 
            System.out.println(QUEEN_COUNT + "皇后共有 " + resultCount + " 个解");
        }
    }
    
    public void print(int n) { //打印皇后布局
        System.out.println(QUEEN_COUNT + "皇后的第 " + n + " 个解:");
        for (int i = 0; i < QUEEN_COUNT; i++) {
            for (int j = 0; j < QUEEN_COUNT; j++) {
                System.out.print(QueenIndex[i] == j ? " * " : " - ");
            }
            System.out.println();
        }
        System.out.println();
    }
    
    public static void main(String[] args) {
        new Queen().putQueenIndex(0);
    }
}

代码运行结果:

n皇后问题(回溯)_n皇后   回溯法 n皇后问题(回溯)_n皇后   回溯法 _02


回溯法求解的一般模式(n皇后)

void Trial(int i,int n){
     if(i>n)  输出棋盘的当前布局;//合法布局
     else for(j=1;j<=n;++j){
         在第i行j列放置一枚棋子;
         if(当前布局合法) Trial(i+1,n);
         移走第i行j列的棋子;
     }
}

更普遍的,用递归函数来实现回溯法:

void Backtrack(int t){
   if(t>n) Output(x);//搜索到叶节点,输出得到的可行解(t表示递归深度)
   else
    //f、g分别表示在当前扩展结点处未搜索过的子树的起始编号和终止编号
       for(int i=f(n,t);i<=g(n,t);i++){
           x[t]=h(i);//h(i)表示在当前扩展结点处x[t]的第i个可选值
           if(Constraint(t)&&Bound(t)) Backtrack(n+1);
           //Constraint(t)和Bound(t)分别表示当前扩展结点处的约束函数和限界函数,不满足条件时,须剪枝
       }
}