LC 51.N 皇后

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”]]

提示:

  • 1 ≤ n ≤ 9 1 \leq n \leq 9 1n9

解法一(回溯)

思路分析:

  1. 该题需要对放置皇后位置进行寻找,当找到的位置错误时,需要回退到确定错误位置前,即解决该题需要使用到回溯算法
  2. 即先确定回溯函数三要素:
    1. 确定函数的返回值和参数,因为该题需要记录所有可行的放置方案,所以不需要依靠返回值寻找最优,即返回值类型为void,同时对于参数,则需要count记录当前放置到第几行,n则记录最多放置多少行皇后
    2. 确定函数的结束条件,即当能够放完所有皇后count == n时,说明找到符合题意得放置方案,将其记录保存
    3. 确定函数得遍历过程,即从每一行得起始遍历到末尾,并判断当前位置是否能放置皇后,若不能放置则跳过,若能放置,则根据放置位置对周围位置做标记,并记录放置位置,然后继续寻找下一行皇后放置位置,寻找结束后,进行回溯,便于寻找第二可行方案。
  3. 其中,标记某位置不能放置的数组的类型,一定要是数值型,因为可能会同时有两个及多个皇后标记某位置不能放置,若不是数值型,当回溯时,会因为一个皇后放置位置回溯,导致其余皇后标记的位置失效,从而得到不正确的方案

实现代码如下:

class Solution {
	List<List<String>> ret = new LinkedList<>();
	List<String> path = new LinkedList<>();
	int[][] flag;	// 用于标记几个皇后导致某位置不能放置 当0个皇后时 即flag[i][j]=0
	boolean[][] ans;	// 记录放置皇后的位置
    public List<List<String>> solveNQueens(int n) {
		flag = new int[n][n];
		ans = new boolean[n][n];
		backtracking(0, n);
		return ret;
    }
	private void backtracking(int count, int n) {
		if (count == n) {
			// 找到符合题意的方案
			path.clear();	// 清除之前保存的方案
			for (int i = 0; i < n; ++i) {
				StringBuilder str = new StringBuilder();
				for (int j = 0; j < n; ++j) {
					if (ans[i][j])
						str.append('Q');
					else str.append('.');
				}
				path.add(str.toString());
			}
			ret.add(new ArrayList<>(path));
			return ;
		}
		for (int i = 0; i < n; ++ i) {
			if (flag[count][i] != 0)
				continue;	// 跳过不能放置皇后的位置
			doFlag(count, i, 1, n);	// 根据当前位置 对不能放置皇后的位置作标记
			ans[count][i] = true;	// 记录放置皇后的位置
			backtracking(count+1, n);
			ans[count][i] = false;	// 回溯
			doFlag(count, i, -1, n);
		}

	}
	// 根据皇后位置 做标记函数
	private void doFlag(int i, int j, int value, int n) {
		int k, l;
		// 标记行
		for (k = 0; k < n; ++ k)
			flag[i][k] += value;
		// 标记列
		for (k = 0; k < n; ++ k) {
			if (k == i) continue;	// 避免重复标记
			flag[k][j] += value;
		}
		// 当前位置到右下角
		k = i;	l = j;
		while (++k < n && ++l < n)
			flag[k][l] += value;
		// 当前位置到右上角
		k = i;	l = j;
		while (--k >= 0 && --l >= 0)
			flag[k][l] += value;
		// 当前位置到左下角
		k = i;	l = j;
		while (++k < n && --l >= 0)
			flag[k][l] += value;
		// 当前位置到左上角
		k = i;	l = j;
		while (--k >= 0 && ++l < n)
			flag[k][l] += value;
	}
}

提交结果如下:

解答成功:
执行耗时:2 ms,击败了89.69% 的Java用户
内存消耗:44.1 MB,击败了9.38% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n ! ) O(n!) O(n!)n表示皇后个数
  • 空间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值