leetcode刷题:回溯算法15(N皇后)

46 篇文章 0 订阅
11 篇文章 0 订阅

第51题. N皇后

力扣题目链接

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

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

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

示例 1:

  • 输入:n = 4
  • 输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
  • 解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

  • 输入:n = 1
  • 输出:[[“Q”]]

思路:
设置标记棋盘数组,0为空位,1为皇后,2为不可下棋位置。即其他皇后攻击范围。
递归返回条件: 皇后数量达到最大值 n
因为棋盘每旋转90度又是一个一模一样的结果,所以可以用哈希去重。
每次下棋时判断这个位置是不是空位,是否在其他皇后攻击范围内,如果满足以上条件,跳过当前位置。否则就在这个位置下棋,当前位置横竖斜,呈米字型辐射本皇后攻击范围,赋值为2。开始递归。如果遇到不满足n皇后情况,回溯。

package com.programmercarl.backtracking;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @ClassName SolveNQueens
 * @Descriotion TODO
 * @Author nitaotao
 * @Date 2022/7/12 15:48
 * @Version 1.0
 * https://leetcode.cn/problems/n-queens/
 * 51. N 皇后
 **/
public class SolveNQueens {
    List<List<String>> result = new ArrayList<>();
    HashMap<String, List<String>> map = new HashMap<>();

    public List<List<String>> solveNQueens(int n) {
        //0为空 1为棋 2作为标记 表示不能放置棋子
        int[][] arr = new int[n][n];
        backtracking(arr, 0, n,0);
        for (String key : map.keySet()) {
            result.add(map.get(key));
        }
        System.out.println(result);
        return result;
    }

    /**
     * @param arr      棋盘
     * @param queueNum 当前皇后出现数量
     * @param n        应放置的皇后数量
     * @param startX   从之前走过的路接着走
     */
    public void backtracking(int[][] arr, int queueNum, int n,int startX) {
        //当 n 个皇后全部放置完毕,则返回
        if (queueNum == n) {
            String all = "";
            List<String> list = new ArrayList();
            for (int i = 0; i < arr.length; i++) {
                String s = "";
                for (int j = 0; j < arr.length; j++) {
                    if (arr[i][j] == 1) {
                        s += "Q";
                    } else {
                        s += ".";
                    }
                }
                all += s;
                list.add(s);
            }
            if (!map.containsKey(all)) {
                map.put(all, list);
            }
            return;
        }

        for (int i = startX; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                //简单判断一下当前位置是否可以下棋
                if (arr[i][j] != 0) {
                    continue;
                }
                boolean findQueue = false;
                //横向纵向不能有 1
                for (int k = 0; k < arr.length; k++) {
                    findQueue = arr[i][k] == 1 || arr[k][j] == 1;
                    if (findQueue) {
                        break;
                    }
                }
                if (findQueue) {
                    //如果这些位置有1,则有冲突
                    continue;
                }

                //左斜右斜不能有 1

                //左上
                int x = i - 1;
                int y = j - 1;
                while (x > -1 && y > -1) {
                    findQueue = arr[x][y] == 1;
                    if (findQueue) {
                        break;
                    }
                    x--;
                    y--;
                }
                if (findQueue) {
                    //如果这些位置有1,则有冲突
                    continue;
                }

                //左下
                x = i - 1;
                y = j + 1;
                while (x > -1 && y < n) {
                    findQueue = arr[x][y] == 1;
                    if (findQueue) {
                        break;
                    }
                    x--;
                    y++;
                }
                if (findQueue) {
                    //如果这些位置有1,则有冲突
                    continue;
                }

                //右上
                x = i + 1;
                y = j - 1;
                while (x < n && y > -1) {
                    findQueue = arr[x][y] == 1;
                    if (findQueue) {
                        break;
                    }
                    x++;
                    y--;
                }
                if (findQueue) {
                    //如果这些位置有1,则有冲突
                    continue;
                }

                //右下
                x = i + 1;
                y = j + 1;
                while (x < n && y < n) {
                    findQueue = arr[x][y] == 1;
                    if (findQueue) {
                        break;
                    }
                    x++;
                    y++;
                }
                // 如果 findQueue == true ,即当前位置横向、纵向或斜向出现 1 ,
                // 即出现了其他皇后,跳过
                if (findQueue) {
                    //如果这些位置有1,则有冲突
                    continue;
                }

                if (arr[i][j] == 0) {
                    //若为0,才能添加棋子,否则跳过
                    //此元素 横、纵、左斜、右斜元素标记为2,不可再写
                    for (int k = 0; k < arr.length; k++) {
                        arr[i][k] = 2;
                        arr[k][j] = 2;
                    }

                    //左上
                    x = i - 1;
                    y = j - 1;
                    while (x > -1 && y > -1) {
                        arr[x][y] = 2;
                        x--;
                        y--;
                    }

                    //左下
                    x = i - 1;
                    y = j + 1;
                    while (x > -1 && y < n) {
                        arr[x][y] = 2;
                        x--;
                        y++;
                    }

                    //右上
                    x = i + 1;
                    y = j - 1;
                    while (x < n && y > -1) {
                        arr[x][y] = 2;
                        x++;
                        y--;
                    }

                    //右下
                    x = i + 1;
                    y = j + 1;
                    while (x < n && y < n) {
                        arr[x][y] = 2;
                        x++;
                        y++;
                    }


                    // 在此处下棋
                    arr[i][j] = 1;
                    queueNum++;
                    //递归
                    backtracking(arr, queueNum, n, i + 1);
                    //回溯
                    queueNum--;
                    for (int k = 0; k < arr.length; k++) {
                        arr[i][k] = 0;
                        arr[k][j] = 0;
                    }

                    //左上
                    x = i - 1;
                    y = j - 1;
                    while (x > -1 && y > -1) {
                        arr[x][y] = 0;
                        x--;
                        y--;
                    }

                    //左下
                    x = i - 1;
                    y = j + 1;
                    while (x > -1 && y < n) {
                        arr[x][y] = 0;
                        x--;
                        y++;
                    }

                    //右上
                    x = i + 1;
                    y = j - 1;
                    while (x < n && y > -1) {
                        arr[x][y] = 0;
                        x++;
                        y--;
                    }

                    //右下
                    x = i + 1;
                    y = j + 1;
                    while (x < n && y < n) {
                        arr[x][y] = 0;
                        x++;
                        y++;
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        new SolveNQueens().solveNQueens(4);
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值