👂 Peter Pan - kidult. - 单曲 - 网易云音乐
👂 Bibliothèque(图书馆) - Jasing Rye - 单曲 - 网易云音乐
目录
[AC 标记数组](#AC 标记数组)
[AC 标记变量](#AC 标记变量)
[AC 模拟](#AC 模拟)
[AC 转置 + 翻转](#AC 转置 + 翻转)
[AC 辅助数组](#AC 辅助数组)
[🍃搜索二维矩阵 II](#🍃搜索二维矩阵 II)
[AC 二分](#AC 二分)
[AC Z字形查找](#AC Z字形查找)
🌼前言
分享一个,24届,现在大四学长的经历
大二绩点专业第一,稳拿保研资格,大三翘课一年,全力冲刺工作实习,想着两手抓,结果错失保研资格,只能全力备战秋招....
最后,作为唯一一个本科生,和一堆研究生竞争,笔试全国第二,综合表现第一,逆风翻盘,成功入职大厂
想说下他笔试全国第二的秘诀之一,hot100 刷了七八遍,总题量 500 左右,笔试时随便一道 medium,hard,5 ~ 15分钟 AC那么看来,我之前定的,大二结束前,++二刷 hot100 可能不太够++
那就大三再多刷两遍,无聊就刷刷
项目方面,他做了 webserver,6.824,另外还研究了 2 套源码,每套源码都写了十几篇 5000 字以上的博客记录下个项目,我准备做 ++muduo++ 数据库项目,理由如下
- 有个西邮的大二同学,打算和我一起做,但是他进度比我快一点
- 手上有 2 个 muduo 讨论群,可以和不同进度的 uu 一起交流
- 还有 1 套视频教程
- 认识几个做了 6.824,Tiny KV,muduo,6.s081 的佬,没事厚着脸皮请教请教
手头可选的项目:6.s081,6.824,Tiny KV,muduo,CMU 15445,rcore,ucore
🌼二分模板
二分,难点在于边界的处理,这里分享两个 yxc 的模板👇
++模板1 -- 整数二分++
视频 1:02分 ~ 1:14分 介绍模板1
cpp
while (l < r) {
int mid = (l + r + 1) >> 1; // 防止下取整死循环, 要 +1
if (...)
l = m; // 记住 l = m
else
r = m - 1;
}
++模板2 -- 整数二分++
cpp
while (l < r) {
int mid = (l + r) >> 1;
if (...)
r = m; // 上面没 +1 就 r = m
else
l = m + 1;
}
🎂矩阵置零
AC 标记数组
借助两个标记数组 r[], c[];r[3] = 1 表示第 3 行置 0;c[0] = 1 表示第 0 列置 0
注意:1)vector 要声明大小
时间O(mn) 空间O(m + n)
cpp
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
// vector 要声明大小
vector<int> r(m), c(n); // 行 / 列标记数组
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
if (matrix[i][j] == 0)
r[i] = 1, c[j] = 1; // 标记
for (int i = 0; i < m; ++i)
if (r[i] == 1)
for (int j = 0; j < n; ++j)
matrix[i][j] = 0;
for (int j = 0; j < n; ++j)
if (c[j] == 1)
for (int i = 0; i < m; ++i)
matrix[i][j] = 0;
}
};
AC 标记变量
用原矩阵第 0 行,第 0 列代替原本的 r[] 和 c[](matrix[i][0] = 0 或 matrix[0][j] = 0 进行标记),此时 第 0 行,第 0 列是否包含 0 没办法记录
只需要借助两个变量 r, c 记录
++注意++:一开始我担心,遍历过程会破坏原本的第 0 行,第 0 列,并不会,因为某个位置 (i, j) 为 0,必然会使整行整列为 0,那么的 matrix[0][j] 和 matrix[i][0] = 0 的标记,包含在里面
行是竖着下去的,列是横着往右的😂
时间 O(mn),空间 O(1)
cpp
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
int r = 0, c = 0; // 原来的第 0 行,第 0 列是否包含 0
// 遍历
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j) {
if (matrix[i][j] == 0 && i == 0)
r = 1; // 列
if (matrix[i][j] == 0 && j == 0)
c = 1; // 行
// 原矩阵第 0 行,第 0 列记录该行 / 列是否包含 0
if (matrix[i][j] == 0)
matrix[0][j] = 0, matrix[i][0] = 0; // 标记
}
// 置零
for (int i = 1; i < m; ++i)
if (matrix[i][0] == 0) // 第 i 行置 0
for (int j = 0; j < n; ++j)
matrix[i][j] = 0;
for (int j = 1; j < n; ++j)
if (matrix[0][j] == 0) // 第 j 列置 0
for (int i = 0; i < m; ++i)
matrix[i][j] = 0;
if (r == 1)
for (int j = 0; j < n; ++j)
matrix[0][j] = 0;
if (c == 1)
for (int i = 0; i < m; ++i)
matrix[i][0] = 0;
}
};
🚩螺旋矩阵
AC 模拟
坑....vector,初始声明大小后,++不要用 push_back(),只会在后面追加++....否则就会内存溢出
一道++模拟题++
通过维护 up, down, right, left,四个边界值,边界值每次碰壁都会收缩比如一开始走完上边,上边界++,达到收缩的目的
时间 O(m*n),空间 O(1)
cpp
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<int> ans(m*n);
// 边界值, 每次碰壁都会收缩
int left = 0, right = n - 1, up = 0, down = m - 1;
int i = 0, j = 0, cnt = 0;
ans[cnt++] = matrix[i][j]; // 插入起点
while (1) {
while (cnt < m*n) { // 向右
if (j < right)
j++;
else break;
ans[cnt++] = matrix[i][j];
}
up++; // 上边走完后,上边界收缩
while (cnt < m*n) { // 向下
if (i < down)
i++;
else break;
ans[cnt++] = matrix[i][j];
}
right--; // 右边走完,右边界收缩
while (cnt < m*n) { // 向左
if (j > left)
j--;
else break;
ans[cnt++] = matrix[i][j];
}
down--; // 下面走完,下边界收缩
while (cnt < m*n) { // 向上
if (i > up)
i--;
else break;
ans[cnt++] = matrix[i][j];
}
left++; // 左边走完,左边界收缩
if (cnt == m*n) break;
}
return ans;
}
};
🌼旋转图像
AC 转置 + 翻转
先矩阵转置(行列互换),再对称翻转
先矩阵转置(只需遍历对角线右侧)👇
(i, j),(j, i) 互换
再左右对称翻转,(i, j) 与 (i, n - j - 1) 互换,列的范围 < n/2 即可
时间 O(n^2),空间 O(1)
cpp
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
// 矩阵转置
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
// 左右对称翻转
for (int i = 0; i < n; ++i)
for (int j = 0; j < n / 2; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[i][n - j - 1];
matrix[i][n - j - 1] = temp;
}
}
};
AC 辅助数组
假设原矩阵 (i ,j)
新的列就是原矩阵的行 i,新的行就是原矩阵的 n - j - 1
所以新 (i, j) = 原 (n - j - 1, i)
时间 O(n^2),空间 O(n^2)
cpp
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
auto matrix_e = matrix; // 值拷贝
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
matrix_e[i][j] = matrix[n - j - 1][i];
// 最后要值拷贝回原数组
matrix = matrix_e;
}
};
🍃搜索二维矩阵 II
AC 二分
思路:遍历每一行,对该行二分
二分难点在于死循环,最好背背上面的模板,具体原因是,防止 L = R - 1 后进入死循环
时间 O(mlogn),空间 O(1)
cpp
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
for (int i = 0; i < m; ++i) {
int l = 0, r = n - 1; // 下标作为左右端点
while (l < r) { // 二分查找每一行
int mid = (l + r + 1) >> 1;
if (matrix[i][mid] <= target)
l = mid;
else if (matrix[i][mid] > target)
r = mid - 1;
}
// 退出 while 后 l == r
if (matrix[i][l] == target)
return true;
}
return false;
}
};
AC Z字形查找
利用好这两个性质👇
- 每行的元素从左到右升序排列
- 每列的元素从上到下升序排列
我们将 target 可能的范围,划分到当前元素 (i, j) 往左往下的长方形中
从右上角开始
如果 target 大于当前元素,有两种选择,往左 或 往下
考虑到行 / 列是有序的,只能往下 i++
如果 target 小于当前元素,只能往左 j--
时间 O(m + n),空间 O(1)
cpp
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
int i = 0, j = n - 1; // 右上角开始
while (i >= 0 && i < m && j >= 0 && j < n) {
if (target < matrix[i][j])
j--; // 目标值更小,只能往左
else if (target > matrix[i][j])
i++; // 目标值更大,只能向下
else
return true; // target ==
}
return false;
}
};