前言
本文隶属于专栏《LeetCode 刷题汇总》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构请见LeetCode 刷题汇总
正文
幕布
51. N 皇后
题解
3个set,i,row+i,row-i
import scala.collection.mutable.{ListBuffer, Set}
object Solution {
val col, diagonal1, diagonal2 = Set[Int]()
var N : Int = _
def solveNQueens(n: Int): List[List[String]] = {
N = n
val res = ListBuffer[List[String]]()
rec(res, ListBuffer(), 0)
res.toList
}
def rec(res: ListBuffer[List[String]], list: ListBuffer[String], row: Int): Unit = {
if (row == N) {
res += list.toList
return
}
for (i <- 0 until N if !col.contains(i) && !diagonal1.contains(row + i) && !diagonal2.contains(row - i)) {
val chars = Array.fill(N)('.')
chars(i) = 'Q'
list += new String(chars)
col += i
diagonal1 += row + i
diagonal2 += row - i
rec(res, list, row + 1)
list.remove(list.size - 1)
col -= i
diagonal1 -= row + i
diagonal2 -= row - i
}
}
}
52. N皇后 II
题解
DFS + 位运算剪枝
class Solution {
private int ans = 0;
public int totalNQueens(int n) {
dfs(n, 0, 0, 0, 0);
return ans;
}
private void dfs(int n, int row, int col, int ld, int rd) {
if (row >= n) {
ans++;
return;
}
int bits = ~(col | ld | rd) & ((1 << n) - 1); // 1
while (bits > 0) { // 2
int pick = bits & -bits; // 3
dfs(n, row + 1, col | pick, (ld | pick) << 1, (rd | pick) >> 1); //4
bits &= bits - 1; // 5
}
}
}
解释说明
(1 << n) - 1
生成了n个1.这里的1表示可以放置皇后
其实就是初始化了n个1,在不考虑皇后之间可以相互攻击的情况下,n个位置都可以放皇后
~(col | ld | rd)
三个变量分别代表了列以及两个斜线的放置情况。
这里的1表示的是不能放置皇后(就是相应的列或斜线上已经放置过皇后了)
因此 int bits = ~(col | ld | rd) & ((1 << n) - 1); 表示的是考虑了相应列、斜线后能放置皇后的位置。
举例
n=4时,初始化为1111,表示此时4个位置都可以放皇后
但是和~~(col | ld | rd)按位与后变为了0110
表示此时只有第2个和第3个位置是可以放皇后的。
bits > 0
当bits>0时,说明bits中还有1存在,就说明遍历还没有完成。
而在之后的循环体中,每遍历bits中的一个1,就会将其清0,这就是代码中注释部分 5 的语句。
pick
这里的 pick 就是取出了最后一位1,表示此时遍历的是这种情况。
假设 bits 为0110,取出最后一位1后,就变为0010,就是将皇后放在第3个位置。
row+1
不难理解,就是因为之前已经在row行放置了皇后了,现在应该搜索下一行可能的位置了。
col | pick
就是把目前所有放置皇后的列都计算出来了,比如最开始计算时col是0000,pick是0010,那么col | pick就是0010,意思就是第三列被放置过了。
(ld | pick) << 1
假设ld是0000,ld | pick就是0010,左移1位后变成了0100,意思就是下一行的第二列也不要放皇后了,因为在这一行的第三列我已经放过了,他们是位于一个斜线上的。
(rd | pick) >> 1
跟(ld | pick) << 1是一个含义
位运算
x & -x
这个操作可以保留最后一位1,而其他位都会清零
补码
正数 => 本身
负数 => 反码 + 1
举例
00110110 -> 00000010
变成负数->
10110110
反码->
11001001
补码->
11001010
按位与 00110110->
00000010
x & (x - 1)
最低位
1
最低位如果是1,减1后就变为了0,按位与后,其他位不发生变化,最低位清为0.
0
如果最低位是0,那么减1后,这个0就变为了1,并且向高位借位,直到遇到第一个1,这个1会因为之前的借位变成0,此时借位清0,更高位的数据就不会发生变化。这样按位与后,最低位的1就变成了0,而其他的数并没有变化。
53. 最大子序和
题解
max,cur
object Solution {
def maxSubArray(nums: Array[Int]): Int = {
var max, cur = nums(0)
for (i <- 1 until nums.length) {
cur = math.max(nums(i), cur + nums(i))
max = math.max(max, cur)
}
max
}
}
54. 螺旋矩阵
题解
Super Simple and Easy to Understand Solution
rowMin, rowMax, colMin, colMax,→ ↓ ← ↑
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> list = new ArrayList<>();
if(matrix == null || matrix.length == 0 || matrix[0].length == 0) return list;
int rowMin = 0, rowMax = matrix.length-1, colMin = 0, colMax = matrix[0].length-1;
while(rowMin <= rowMax && colMin <= colMax) {
for(int i=colMin; i<=colMax; i++) list.add(matrix[rowMin][i]);
rowMin++;
for(int i=rowMin; i<=rowMax; i++) list.add(matrix[i][colMax]);
colMax--;
if(rowMin > rowMax || colMin > colMax) break;
for(int i=colMax; i>=colMin; i--) list.add(matrix[rowMax][i]);
rowMax--;
for(int i=rowMax; i>=rowMin; i--) list.add(matrix[i][colMin]);
colMin++;
}
return list;
}
}
55. 跳跃游戏
题解
Java Solution easy to understand
if (curMax < i) return false; curMax 代表经过每一个点能到达的最远距离
class Solution {
public boolean canJump(int[] nums) {
int curMax = nums[0];
for (int i = 1; i < nums.length; i++) {
if (curMax < i) return false;
curMax = Math.max(curMax, i + nums[i]);
}
return true;
}
}