LeetCode【学习计划】:【数据结构】
36. 有效的数独
LeetCode: 36. 有效的数独
中 等 \color{#FFB800}{中等} 中等
请你判断一个
9 x 9
的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
1.数字1-9
在每一行只能出现一次。
2.数字1-9
在每一列只能出现一次。
3.数字1-9
在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
注意:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 空白格用
'.'
表示。
示例 1:
输入:board =
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
输出:true
示例 2:
输入:board =
[["8","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
输出:false
解释:除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
提示:
board.length == 9
board[i].length == 9
board[i][j]
是一位数字(1-9
)或者'.'
哈希表
可以为每一行、每一列和每一个3x3的宫设置一个哈希表,一共是9+9+9=27个哈希表。遍历九宫格的过程中,记录一个元素时需要在它所对应的行、列和宫的哈希表中记录,一个元素一共记录3次。
由于我们一般是按照一行一行地去遍历,所以可以把行哈希表缩减为一个,也就是每一行遍历完后重置哈希表。这样哈希表就变为了1+9+9=19个。
#include <vector>
using namespace std;
class Solution
{
public:
bool isValidSudoku(vector<vector<char>> &board)
{
int columns[9][9] = {};
int boxes[3][3][9] = {};
for (int i = 0; i < 9; i++)
{
int row[9] = {};
for (int j = 0; j < 9; j++)
{
if (board[i][j] != '.')
{
const int index = board[i][j] - '0' - 1;
if (row[index] == 1 || columns[j][index] == 1 || boxes[i / 3][j / 3][index] == 1)
return false;
row[index]++;
columns[j][index]++;
boxes[i / 3][j / 3][index]++;
}
}
}
return true;
}
};
复杂度分析
-
时间复杂度: O ( 1 ) O(1) O(1)。九宫格一共有81格,一共遍历81次,因此遍历的次数是确定的。
-
空间复杂度: O ( 1 ) O(1) O(1)。由于数独的大小固定,所以哈希表的大小也是固定的。
参考结果
Accepted
507/507 cases passed (12 ms)
Your runtime beats 95.57 % of cpp submissions
Your memory usage beats 73.07 % of cpp submissions (17.5 MB)
73. 矩阵置零
LeetCode: 73. 矩阵置零
中 等 \color{#FFB800}{中等} 中等
给定一个
m x n
的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
进阶:
- 一个直观的解决方案是使用
O(mn)
的额外空间,但这并不是一个好的解决方案。 - 一个简单的改进方案是使用
O(m + n)
的额外空间,但这仍然不是最好的解决方案。 - 你能想出一个仅使用常量空间的解决方案吗?
示例 1:
输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]
示例 2:
输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
提示:
m == matrix.length
n == matrix[0].length
1 <= m, n <= 200
- -231 <= matrix[i][j] <= 231 - 1
方法1:使用标记数组
假设matrix
的行数为m
,列数为n
,我们可以设置一个m
长的row
和n
长的col
作为标记数组,用于标记每一行、每一列是否有零出现。我们先完整遍历一次matrix
,记录哪里有零;最后根据row
和col
数组将matrix
对应的行和列全部置零。
#include <vector>
using namespace std;
class Solution
{
public:
void setZeroes(vector<vector<int>> &matrix)
{
const int m = matrix.size(), n = matrix[0].size();
bool *row = new bool[m]{}, *col = new bool[n]{};
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (matrix[i][j] == 0)
{
row[i] = true;
col[j] = true;
}
}
}
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (row[i] || col[j])
{
matrix[i][j] = 0;
}
}
}
}
};
复杂度分析
-
时间复杂度: O ( m n ) O(mn) O(mn)。最多完整遍历矩阵2次, O ( 2 m n ) = O ( m n ) O(2mn)=O(mn) O(2mn)=O(mn)。
-
空间复杂度: O ( m + n ) O(m+n) O(m+n)。主要为两个标记数组的开销。
参考结果
Accepted
164/164 cases passed (12 ms)
Your runtime beats 79.12 % of cpp submissions
Your memory usage beats 69.92 % of cpp submissions (12.8 MB)
方法2:使用2个标记变量
我们可以用矩阵的第一行和第一列来代替方法1中的标记数组,如果某一行某一列存在0
,那么就将原矩阵的第一行和第一列对应的位置置零,这样就能剩下两个额外的数组开销了,并且空间复杂度降为了
O
(
1
)
O(1)
O(1)。
不过这样一来,矩阵的第一行第一列原来有没有0
就不清楚了,如果第一行或第一列本来就有0
的话,最后要把它们单独置零,因此需要两个标记变量分别表示第一行和第一列原先是否含0
。
方法2的步骤如下:
- 标记第一行和第一列中是否含有零。
- 遍历整个数组,发现
0
时,更新第一行和第一列对应的位置为零。 - 通过第一行和第一列去更新整个数组
- 若第一行或第一列本身含有零,那么单独为第一行或第一列置零。
#include <vector>
using namespace std;
class Solution
{
public:
void setZeroes(vector<vector<int>> &matrix)
{
int m = matrix.size(), n = matrix[0].size();
bool row0Has0 = false, col0Has0 = false;
// 设第0行和第0列为标记量
for (int j = 0; j < n; j++)
{
if (matrix[0][j] == 0)
{
row0Has0 = true;
break;
}
}
for (int i = 0; i < m; i++)
{
if (matrix[i][0] == 0)
{
col0Has0 = true;
break;
}
}
// 更新标记量
for (int i = 1; i < m; i++)
{
for (int j = 1; j < n; j++)
{
if (matrix[i][j] == 0)
{
matrix[0][j] = matrix[i][0] = 0;
}
}
}
// 使用标记量去更新矩阵
for (int j = 1; j < n; j++)
{
if (matrix[0][j] == 0)
{
for (int i = 1; i < m; i++)
{
matrix[i][j] = 0;
}
}
}
for (int i = 1; i < m; i++)
{
if (matrix[i][0] == 0)
{
for (int j = 1; j < n; j++)
{
matrix[i][j] = 0;
}
}
}
if (row0Has0)
{
for (int j = 0; j < n; j++)
{
matrix[0][j] = 0;
}
}
if (col0Has0)
{
for (int i = 0; i < m; i++)
{
matrix[i][0] = 0;
}
}
}
};
复杂度分析
-
时间复杂度: O ( m n ) O(mn) O(mn)
-
空间复杂度: O ( 1 ) O(1) O(1)。我们只需要常量空间来存储若干变量。
参考结果
Accepted
164/164 cases passed (16 ms)
Your runtime beats 44.42 % of cpp submissions
Your memory usage beats 69.88 % of cpp submissions (12.8 MB)