【41】回溯法——全排列 | N皇后问题(LC 46 | 51)

回溯算法详解

解决一个回溯问题,实际上就是一个决策树的遍历过程。

两种决策树的遍历:
在这里插入图片描述
如上,参数t为当前在决策树的层数。

子集树:针对在解向量中选择满足条件的子集),如背包问题;

排列树:针对解向量的排序问题,如全排列、八皇后问题。

全排列问题

问题描述

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

解题思想

很明显该问题是一个解向量的排序问题,可以直接套用遍历排列树的模板:

class Solution {
    
    
    public List<List<Integer>> permute(int[] nums) {
    
    
        List<List<Integer>> res = new ArrayList<List<Integer>>();

        List<Integer> path = new ArrayList<Integer>();
        for (int num : nums) {
    
    
            path.add(num);
        }

        int n = nums.length;
        backtrack(n, path, res, 0);
        return res;
    }

    public void backtrack(int n, List<Integer> path, List<List<Integer>> res, int t) {
    
    
        if (t >= n) {
    
    //到了最后一层
            res.add(new ArrayList<Integer>(path));
        }
        for (int i = t; i < n; i++) {
    
    
            Collections.swap(path, t, i);//swap(List list,int i,int j):交换集合中指定元素索引的位置
            backtrack(n, path, res, t + 1);
            Collections.swap(path, t, i);
        }
    }
}

时间复杂度:O(n!)
空间复杂度:O(n)

N皇后问题

问题描述

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

注:

1 <= n <= 9
皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。

解题思路

因为n*n的棋盘上要放n个皇后,又因为皇后不能在同一行同一列和同一对角线,所以必定每一行都要放一个皇后,那么假设皇后在列上的坐标为xi,xi表示第i行的皇后在第xi列,xi∈(0,…,n-1),由此,可以看出该问题是一个对解向量的排列问题,可套用排列树的模板。其实该问题也可用子集树求解。

与全排列问题不同的是,该题需要考虑剪枝问题,若不满足放置规则则剪枝:

针对第i行的皇后和第j行的皇后:
1)俩皇后不在同一列:xi != xj;
2)俩皇后不在同一对角线:| i-j | != | xi-xj|

class Solution {
    
    
    public List<List<String>> solveNQueens(int n) {
    
    
        List<List<String>> res = new ArrayList<List<String>>();
        int row[] = new int[n];//用row存储皇后在列上的排列顺序
        backtrack(res,row,n,0);
        return res;
    }

    public void backtrack(List<List<String>> res,int[] row,int n,int t){
    
    //遍历子集树
        if(t >= n)
            output(res,row,n);
        else{
    
    
            for (int i = 0; i < n; i++){
    
    
                row[t] = i;
                if(legal(row,t))
                    backtrack(res,row,n, t+1);
            }
        }
    }

    public boolean legal(int[] row,int t){
    
     //判断是否为有效的坐标
        for(int i=0;i<t;i++){
    
    
            if(Math.abs(i-t)==Math.abs(row[i]-row[t]) || row[i]==row[t])
                return false;
        }
        return true;
    }

    public void output(List<List<String>> res,int[] row,int n){
    
     //打印结果
        List<String> temp_res = new ArrayList<String>();
        for(int i=0;i<n;i++){
    
    
            String line = "";
            for(int j=0;j<n;j++){
    
    
                if(j == row[i])
                    line = line + 'Q';
                else
                    line = line + '.';
            }
            temp_res.add(line);
        }
        res.add(temp_res);
    }
}

时间复杂度:O(2^n)
空间复杂度:O(n)

猜你喜欢

转载自blog.csdn.net/qq_43424037/article/details/114293727
今日推荐