leetcode51.N皇后——学习笔记

题目:力扣https://leetcode-cn.com/problems/n-queens/

class Solution {
    public List<List<String>> solveNQueens(int n) {
        Set<Integer> diagonal1 = new HashSet<Integer>();
        Set<Integer> diagonal2 = new HashSet<Integer>();
        Set<Integer> columns = new HashSet<Integer>();
        List<List<String>> ans = new ArrayList<List<String>>();
        int[] map = new int[n];
        Arrays.fill(map,-1);
        putQueen(ans,n,map,0,columns,diagonal1,diagonal2);
        return ans;
    }

    private void putQueen(List<List<String>> ans,int n,int[] map,int row,Set<Integer> columns,Set<Integer> diagonal1,Set<Integer> diagonal2){
        //出口
        if(row==n){
            List<String> temp = pack(map,n);
            ans.add(temp);
        }else{
            //判断
            for(int i=0;i<n;i++){
                if(columns.contains(i)){
                    continue;
                }
                int dia1 = row-i;
                if(diagonal1.contains(dia1)){
                    continue;
                }
                int dia2 = row+i;
                if(diagonal2.contains(dia2)){
                    continue;
                }
                //记录
                map[row] = i;
                columns.add(i);
                diagonal1.add(dia1);
                diagonal2.add(dia2);
                putQueen(ans,n,map,row+1,columns,diagonal1,diagonal2);
                //状态重置
                map[row] = -1;
                columns.remove(i);
                diagonal1.remove(dia1);
                diagonal2.remove(dia2);
            }
        }


    }
    //转换格式
    private List<String> pack(int[] map,int n){
        List<String> res = new ArrayList<String>();
        for(int i=0;i<n;i++){
            StringBuilder temp = new StringBuilder();
            for(int j=0;j<n;j++){
                if(j==map[i]){
                    temp.append("Q");
                }else{
                    temp.append(".");
                }
            }
            res.add(temp.toString());
        }
        return res;
    }
}

 

(补充:皇后的攻击范围是:皇后所在位置对应的行、列和两条对角线。因此,放置了一个皇后之后,其所在的行、列以及两道对角线则不能再放置下一个皇后。)

思路:但凡遇到需要用到递归方法的题目,现在的我还是不能独立完成,每次都需要研究一遍题解才能自己写出来。 这题类似于leetcode36.有效的数独,相比之下,我感觉这题稍稍简单于leetcode36。这题的递归比较清晰明了,每一行就是一层递归,每到新的一层遍历该层所有的格子,通过columns、diagonal1和diagonal2三个HashSet来判断当前格子是否可以放置新的皇后。若不可以,则continue继续遍历下一个格子;若可以则修改columns、diagonal1和diagonal2三个HashSet的状态以便后续判断。因为每一层只能放一个皇后,所以每放置完一个皇后就需要继续“递”到下一层去。若某一层所有格子都无法放置皇后,则表示当前的解不成立,需要回溯,将columns、diagonal1和diagonal2三个HashSet的状态重置(即将刚才记录的东西撤销,避免影响后续的判断),回溯到上一层遍历下一个格子。若出现row==n,即表示每一层都有皇后,并且已经放置到了最后一层,意味着当前解成立。此时,我们需要通过pack()方法,将map记录下来的解转换成List<String>的格式,然后将上述List装入List<List<String>>类型的ans中,执行完递归方法后,返回ans。

1.在主方法中声明后续需要用到的各个变量。diagonal1和diagonal2是表示当前格子对应的两道对角线,columns表示当前格子的所在列,ans是最后需要返回的参数,map是用于记录每一行皇后所在的位置(因为刚开始没有皇后在棋盘上,所以把所有的值默认设置为-1)。

Set<Integer> diagonal1 = new HashSet<Integer>();
Set<Integer> diagonal2 = new HashSet<Integer>();
Set<Integer> columns = new HashSet<Integer>();
List<List<String>> ans = new ArrayList<List<String>>();
int[] map = new int[n];
Arrays.fill(map,-1);

2.简单写一个转换格式的方法,用于最后得出解后将解转换成题目要求的格式。map[]是记录每一行的皇后位于那个格子,n是皇后的数量。

//转换格式
private List<String> pack(int[] map,int n){
    List<String> res = new ArrayList<String>();
    for(int i=0;i<n;i++){
        StringBuilder temp = new StringBuilder();
        for(int j=0;j<n;j++){
            if(j==map[i]){
                temp.append("Q");
            }else{
                temp.append(".");
            }
        }
        res.add(temp.toString());
    }
    return res;
}

3.写一个递归的方法。入参7个,列表ans,皇后数量n,皇后位置图map,当前所在的行数row(默认值为0),记录列上是否存在皇后的columns,记录对角线上是否存在皇后的diagonal1和diagonal2。

private void putQueen(List<List<String>> ans,int n,int[] map,int row,Set<Integer> columns,Set<Integer> diagonal1,Set<Integer> diagonal2){
    //......
}

4.定义递归出口。若row==n,则以为值当前解可以成立,调用pack()方法将答案转换成题目所要求的输出格式,加入到最后需要返回的列表ans中去。

if(row==n){
    List<String> temp = pack(map,n);
    ans.add(temp);
}

5.判断是否可以放置皇后。如果row尚未到达n值,则表示还有皇后没有放置完。因此需要判断当前位置是否可以放置皇后。通过contains()方法判断当前位置的columns、diagonal1和diagonal2中是否已经存在皇后(其中diagonal1和值用行下标与列下标的差表示,diagonal2的值用行下表和列下标的和表示。)。若已经存在皇后则当前位置不可以放置皇后,continue继续遍历下一个位置;反之,则可以放置皇后。

//判断
for(int i=0;i<n;i++){
if(columns.contains(i)){
continue;
}
int dia1 = row-i;
if(diagonal1.contains(dia1)){
continue;
}
int dia2 = row+i;
if(diagonal2.contains(dia2)){
continue;
}

6.记录当前位置放置皇后之后的相关信息。若经过上述判断,得出当前位置可以放置皇后,则需要进入“记录”的步骤。将当前格子的位置记录在map中,然后规定该列和对应的两道对角线不允许再出现皇后(至于行为什么没有提及,因为每放完一个皇后即刻递到下一层就已经完成了换行操作,所以不需要赘述。)通过putQueen()递到下一层,row值+1。

//记录
map[row] = i;
columns.add(i);
diagonal1.add(dia1);
diagonal2.add(dia2);
putQueen(ans,n,map,row+1,columns,diagonal1,diagonal2);

7.状态重置。若当前解row无法到达n值,则表示当前解不成立,需要回溯到上一层。回溯时需要将刚刚记录的东西撤销掉,即状态恢复。将当前位置在map上的标志改为初始值,将该位置对应的列和两道对角线存储的信息撤销,然后回到上一层继续遍历下一个位置。

//状态重置
map[row] = -1;
columns.remove(i);
diagonal1.remove(dia1);
diagonal2.remove(dia2);

8.在主方法中调用putQueen(),传入相关参数。执行完该递归方法后,返回ans即可。

putQueen(ans,n,map,0,columns,diagonal1,diagonal2);
return ans;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hokachi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值