51. N 皇后
分析
与leetcode79 走迷宫方法类似
联动leetcode 79
code
class Solution {
public:
vector<vector<string>> ans;
vector<string> res;
int dg[22], udg[22], col[11];
vector<vector<string>> solveNQueens(int n) {
for (int i = 0; i < n; i ++ )
res.push_back(string(n, '.'));
for (int i = 0; i < n; i ++ )
cout << res[i] << endl;
dfs(0, n);
return ans;
}
void dfs(int u, int n){
if (u == n){
ans.push_back(res);
return ;
}
for (int i = 0; i < n; i ++ ){
if (!dg[u + i] && !udg[i - u + n] && !col[i]){
res[u][i] = 'Q';
dg[u + i] = udg[i - u + n] = col[i] = 1;
dfs(u + 1, n);
res[u][i] = '.';
dg[u + i] = udg[i - u + n] = col[i] = 0;
}
}
}
};
LeetCode 52. N皇后 II
分析
递归树, 在递归中创建变量int res = 0
, 用于接受下一层递归返回的结果, 也方便将当前的结果返回到上一层.
递归结束条件: u == n
表示0~ n - 1层都放了八皇后, 表示一种方案合法, return 1;
code
class Solution {
public:
vector<bool> dg, udg, col;
int n;
int totalNQueens(int _n) {
n = _n;
int res = 0;
dg = udg = vector<bool> (2 * n);
col = vector<bool> (n);
return dfs(0);
}
int dfs(int u){
if (u == n){
return 1;
}
int res = 0;
for (int i = 0; i < n; i ++ ){
if (!dg[i + u] && !udg[i - u + n] && !col[i]){
dg[i + u] = udg[i - u + n] = col[i] = 1;
res += dfs(u + 1);
dg[i + u] = udg[i - u + n] = col[i] = 0;
}
}
return res;
}
};
53. 最大子序和
分析
code
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
vector<int> f(n);
f[0] = nums[0];
for (int i = 1; i < n; i ++)
f[i] = max(f[i - 1], 0) + nums[i];
int res = -1e9;
for (int i = 0; i < n; i ++ ) res = max(res, f[i]);
return res;
}
};
code(优化)
可以用一个变量last
保存nums[i] + max(f[i - 1], 0)
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int res = INT_MIN;
for (int i = 0, last = 0; i < nums.size(); i ++ ){
last = nums[i] + max(last, 0);
res = max(res, last);
}
return res;
}
};
54. 螺旋矩阵
分析
因为需要走n * m, 因此先循环n * m 次, 然后需要创建一个bool数组, 去记录哪些点已经走过了, 再创建上右下左方向, 并且指定起点.
在循环中, 现将当前点加入到答案, 再更新当前点, 如果当前点出界或者已经走过, 那么转换到下一个方向, 将下一个位置更新到当前点.
联动每日一题(AcWing 756. 蛇形矩阵)
code
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int n = matrix.size(), m = matrix[0].size();
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int x = 0, y = 0, d = 1;
vector<int> res;
bool st[n][m];
memset(st, 0, sizeof st);
for (int i = 0; i < n * m; i ++ ){
st[x][y] = true;
res.push_back(matrix[x][y]); // 加入到答案
int a = x + dx[d], b = y + dy[d]; // 更新当前点
if (a < 0 || a >= n || b < 0 || b >= m || st[a][b]) { // 如果不合法, 换方向
d = (d + 1) % 4;
a = x + dx[d], b = y + dy[d];
}
x = a, y = b;// 更新下一个坐标到当前点
}
return res;
}
};
55. 跳跃游戏
分析
可以证明一个点能跳跃的位置区间, 一定是连续的
证明: 假如存在一个点能跳跃的区间是不连续的, 那么存在如下图的情况
假定跳跃的位置不连续, 可以找到如图中所示的情况, 使得前一个位置不能跳到, 后一个位置能够跳到, 但是后一个位置由有前面的位置跳跃来的, 那么前面的位置少跳几步一定能够达到图中x点, 矛盾
由能跳跃的位置是连续的区间段, 因此可以从前往后扫描, 扫描的时候记录下, 当前可以跳跃的最右边是什么, 即所有前缀max(i + nums[i])
如果发现前面i - 1个点能跳跃到的最靠右的位置j
<i
, 那么说明i
永远到不了, return false;
否则, 用i + nums[i]
更新j
code
class Solution {
public:
bool canJump(vector<int>& nums) {
for (int i = 0, j = 0; i < nums.size(); i ++ ){
if (j < i) return false;
j = max(j, i + nums[i]);
}
return true;
}
};
56. 合并区间
分析
模板题
code
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& segs) {
vector<vector<int>> res;
sort(segs.begin(), segs.end());
int st = -2e9, ed = -2e9;
for (auto seg : segs){
if (ed < seg[0]){
if (st != -2e9) res.push_back({st, ed});
st = seg[0], ed = seg[1];
}else ed = max(ed, seg[1]);
}
if (st != -2e9) res.push_back({st, ed});
return res;
}
};
57. 插入区间
分析
当a[k][1] < b[0]
的时候, 左边区间与b没有交集
否则a[k][1] >= b[0]
说明当前区间右边界和b
区间左边界有交集, 还需要后面的区间与b有相交&& a[k][0] <= b[1]
code
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& a, vector<int>& b) {
vector<vector<int>> res;
int k = 0;
while (k < a.size() && a[k][1] < b[0]) res.push_back(a[k ++]);
if (k < a.size()){
b[0] = min(b[0], a[k][0]);
while (k < a.size() && a[k][0] <= b[1]) b[1] = max(b[1], a[k ++ ][1]);
}
res.push_back(b);
while (k < a.size()) res.push_back(a[k ++]);
return res;
}
};
58. 最后一个单词的长度
分析
做法1:
可以用stringstream
将字符串转化成cin
来处理
code
class Solution {
public:
int lengthOfLastWord(string s) {
stringstream ssin(s);
int res = 0;
string word;
while (ssin >> word) res = word.size();
return res;
}
};
code (双指针)
联动题型
AcWing 799. 最长连续不重复子序列
AcWing 800. 数组元素的目标和
class Solution {
public:
int lengthOfLastWord(string s) {
for (int i = s.size() - 1; i >= 0; i -- ){
if (s[i] == ' ') continue;
int j = i - 1;
while (j >= 0 && s[j] != ' ') j --;
return i - j;
}
return 0;
}
};
59. 螺旋矩阵 II
思路同54题
code
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n));
int d = 1;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int x = 0, y = 0;
bool st[n][n];
memset(st, 0, sizeof st);
for (int i = 1; i <= n * n; i ++ ){
res[x][y] = i;
st[x][y] = true;
int a = x + dx[d], b = y + dy[d];
if (a < 0 || a >= n || b < 0 || b >= n || st[a][b]){
d = (d + 1) % 4;
a = x + dx[d], b = y + dy[d];
}
x = a, y = b;
}
return res;
}
};
60. 排列序列
分析
考虑每位分别填什么数字,举个例子
n = 4, k = 10
1_ _ _, 后面总共有3! = 6种填法, 6 < 10, 那么第1位不能填1, 10 - 6 = 4.(表示在1的所有排列后, 还需要前进4)
2_ _ _, 后面总共排列6 > 当前需要步数4, 因此第1位填2,
2 1 _ _, 后面还有2个排列 < 4, 因此第2位不能填1, 4 - 2 = 2
2 3 _ _, 后面还有2个排列 <= 2, 因此第2位填写3,
2 3 1 _, 如果填1, 因为只有1个数, 小于步数2, 因此不能填1, 剩余步数2 - 1 = 1
2 3 4 1, 完成
当前位i
填写完毕后, 后面有(n - i - 1)!
种可能
联动题目
每日一题(忘了) 等待补充
code
class Solution {
public:
string getPermutation(int n, int k) {
string res;
vector<bool> st(10);
for (int i = 0; i < n; i ++ ){
int fact = 1;
// 当前位置是i的话, 因为下标从0开始, 实际填的是从1开始的 i + 1个数,
// 那么剩余 (n - (i + 1))! 排列
for (int j = 1; j <= n - i - 1; j ++ ) fact *= j;
// 先枚举当前位置填哪个没有填过的数
for (int j = 1; j <= n; j ++ ){
if (!st[j]){
if (fact < k) k -= fact;
else {
st[j] = true;
res += to_string(j);
break; // 重要, 每个位置只能填1次, 填完break;
}
}
}
}
return res;
}
};
code(next_permutation)
class Solution {
public:
string getPermutation(int n, int k) {
string res;
for (int i = 1; i <= n; i ++ ) res += to_string(i);
for (int i = 0; i < k - 1; i ++ ) next_permutation(res.begin(), res.end());
return res;
}
};