20天 hot 100 速通计划-day04
普通数组
238. 除自身以外数组的乘积
给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请不要使用除法,且在 O(*n*)
时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
提示:
2 <= nums.length <= 105
-30 <= nums[i] <= 30
- 保证 数组
nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> output(n, 1); // 初始化一个大小为n的输出数组,全部赋值为1
int left = 1; // 从左边累积乘积
int right = 1; // 从右边累积乘积
// 计算左边乘积
for (int i = 0; i < n; i++) {
output[i] *= left;
left *= nums[i];
}
// 计算右边乘积
for (int i = n - 1; i >= 0; i--) {
output[i] *= right;
right *= nums[i];
}
return output;
}
};
41. 缺失的第一个正数
给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]
输出:3
示例 2:
输入:nums = [3,4,-1,1]
输出:2
示例 3:
输入:nums = [7,8,9,11,12]
输出:1
提示:
1 <= nums.length <= 5 * 105
-231 <= nums[i] <= 231 - 1
代码参考力扣官方题解
标记法
我们对数组进行遍历,对于遍历到的数 x,如果它在 [1,N] 的范围内,那么就将数组中的第 x−1 个位置(注意:数组下标从 0 开始)打上「标记」。在遍历结束之后,如果所有的位置都被打上了标记,那么答案是 N+1,否则答案是最小的没有打上标记的位置加 1
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
// 将所有非正整数置为n+1
for (int& num : nums) {
if (num <= 0) {
num = n + 1;
}
}
// 将出现过的正整数标记为负数
for (int i = 0; i < n; ++i) {
int num = abs(nums[i]);
if (num <= n) {
nums[num - 1] = -abs(nums[num - 1]);
}
}
// 第一个未标记为负数的位置即为答案
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) {
return i + 1;
}
}
return n + 1; // 如果所有位置都标记为负数,则返回n+1
}
};
个人认为更易于理解的方法:交换法
通过交换实现原地哈希(数值为
i
的数映射到下标为i - 1
的位置)除了打标记以外,我们还可以使用置换的方法,将给定的数组「恢复」成下面的形式:
如果数组中包含 x∈[1,N],那么恢复后,数组的第 x−1 个元素为 x。
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n; ++i) {
while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
// 将nums[i] 放置到对应位置上
// nums[i]=1 → 将 nums[i] 的值放到 nums[0]
// nums[i]=2 → 将 nums[i] 的值放到 nums[1]
// 即交换 nums[i] 和 num[nums[i] - 1]
swap(nums[nums[i] - 1], nums[i]);
}
}
//交换完成后,理论上所有应该出现的正整数 i 会出现在下标为 i - 1 的位置
//换句话说,即理论上所有应该出现的正整数 i + 1 会出现在下标为 i 的位置
//漏了的就是缺失的第一个正数
for (int i = 0; i < n; ++i) {
if (nums[i] != i + 1) {
return i + 1;
}
}
//都没漏,证明缺失的正整数是 n + 1
return n + 1;
}
};
矩阵
73. 矩阵置零
给定一个 *m* x *n*
的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
示例 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
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
vector<int> column, row;
for(int i = 0; i < matrix.size(); i++){
for(int j = 0; j < matrix[0].size(); j++){
if(matrix[i][j] == 0){
//第i行要清零
row.push_back(i);
//第j列要清零
column.push_back(j);
}
}
}
//清零列
//遍历要清零的列
for(int i = 0; i < column.size(); i++){
//遍历行 ↓ ,遇到要清零的列置零
for(int j = 0; j < matrix.size(); j++){
matrix[j][column[i]] = 0;
}
}
//清零行
//遍历要清零的行
for(int i = 0; i < row.size(); i++){
//遍历列 → ,遇到要清零的行置零
for(int j = 0; j < matrix[0].size(); j++){
matrix[row[i]][j] = 0;
}
}
}
};
54. 螺旋矩阵
给你一个 m
行 n
列的矩阵 matrix
,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> result;
if (matrix.empty()) {
return result;
}
//行数
int m = matrix.size();
//列数
int n = matrix[0].size();
//左右游标 [左闭右开)
int left = 0, right = n;
//上下游标 [左闭右开)
int top = 0, bottom = m;
while (result.size() < m * n) {
// 从左到右
for (int i = left; i < right; i++) {
result.push_back(matrix[top][i]);
}
top++;
// 从上到下
for (int i = top; i < bottom; i++) {
result.push_back(matrix[i][right - 1]);
}
right--;
// 从右到左
if (top < bottom) {
for (int i = right - 1; i >= left; i--) {
result.push_back(matrix[bottom - 1][i]);
}
bottom--;
}
// 从下到上
if (left < right) {
for (int i = bottom - 1; i >= top; i--) {
result.push_back(matrix[i][left]);
}
left++;
}
}
return result;
}
};
48. 旋转图像
给定一个 n × n 的二维矩阵 matrix
表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
提示:
n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000
class Solution {
public:
// 旋转图像
// 纯技巧题,先主对角线翻转,再水平翻转,就是顺时针旋转 90 度
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
// 先进行主对角线翻转
// 对角线不用动,故起点为[i,i+1]
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
swap(matrix[i][j], matrix[j][i]);
}
}
// 再进行水平翻转
for (int i = 0; i < n; i++) {
for (int j = 0; j < n / 2; j++) {
swap(matrix[i][j], matrix[i][n - 1 - j]);
}
}
}
};