41. 缺失的第一个正数
桶排序:
- 若数字不为INT_MIN,则将该数字自减一次
- 使用桶排序,将每个数字放到对应索引的位置上
- 最后遍历一遍数组,若
nums[i] != i
,则返回i + 1
时间复杂度为 O ( n ) O(n) O(n),不使用额外空间,空间复杂度为 O ( 1 ) O(1) O(1)
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
if (!n) return 1;
for (auto& x : nums)
if (x != INT_MIN) x -- ;
for (int i = 0; i < n; i ++ ) {
//while循环每次会将一个数放在自己对应的位置上,从i由0 递增到 n 的过程中,最多执行n次
while (nums[i] >= 0 && nums[i] < n && nums[i] != i && nums[i] != nums[nums[i]])
swap(nums[i], nums[nums[i]]);
}
//再遍历一次返回正确答案
for (int i = 0; i < n; i ++ )
if (nums[i] != i)
return i + 1;
return n + 1;
}
};
42. 接雨水
class Solution {
public:
int trap(vector<int>& height) {
stack<int> stk;
int res = 0;
for(int i = 0; i < height.size(); i ++ ) {
int last = 0;
while(stk.size() && height[stk.top()] <= height[i]) {
res += (height[stk.top()] - last) * (i - stk.top() - 1);
last = height[stk.top()];
stk.pop();
}
if(stk.size()) res += (i - stk.top() - 1) * (height[i] - last);
stk.push(i);
}
return res;
}
};
43. 字符串相乘
模拟
- 使用两个vector数组A,B从后往前存放两个字符串数字的每一位
- 模拟乘法规则,使用第三个vector数组C存放两个数组对应位的乘积,
C[i + j] = A[i] * B[j]
,遍历两个数组每一位 - 将C中每个位上的数字取余再进位,去除前导零
- 将C数组中的数字逐个转换成字符串拼接
class Solution {
public:
string multiply(string num1, string num2) {
vector<int> A, B;
int n = num1.size(), m = num2.size();
for(int i = n - 1; i >= 0; i -- ) A.push_back(num1[i] - '0');
for(int i = m - 1; i >= 0; i -- ) B.push_back(num2[i] - '0');
vector<int> C(n + m);
for(int i = 0; i < n; i ++ )
for(int j = 0; j < m; j ++ )
C[i + j] += A[i] * B[j];
for(int i = 0, t = 0; i < C.size(); i ++ ) {
t += C[i];
C[i] = t % 10;
t /= 10;
}
int k = C.size() - 1;
while(k > 0 && !C[k]) k -- ;
string res;
while(k >= 0) res += C[k -- ] + '0';
return res;
}
};
44. 通配符匹配
class Solution {
public:
bool isMatch(string s, string p) {
int n = s.size(), m = p.size();
s = ' ' + s, p = ' ' + p;
vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
f[0][0] = true;
for(int i = 0; i <= n; i ++ )
for(int j = 1; j <= m; j ++ ) {
if(p[j] == '*') {
f[i][j] = f[i][j - 1] || i && f[i - 1][j];
} else {
f[i][j] = (s[i] == p[j] || p[j] == '?') && i && f[i - 1][j - 1];
}
}
return f[n][m];
}
};
45. 跳跃游戏 II
- 状态表示:
f
(
i
)
f(i)
f(i)表示走
i
步最远可以到达的距离 - 初始化:
f
(
0
)
=
0
f(0) = 0
f(0)=0 表示走
0
最远距离0
i
的每一次移动都可以到达 [ i + 1 , i + n u m s ( i ) ] [i+1, i+nums(i)] [i+1,i+nums(i)]这个区间中的任意位置,从1
遍历到n
的过程中,通过i
第一次可以到达j
时,j
的最少步数为i
的最少步数加1。- 状态转移:
f
(
i
)
=
f
(
j
)
+
1
f(i)=f(j) + 1
f(i)=f(j)+1,
j
为 j + n u m s [ j ] > = i j + nums[j] >= i j+nums[j]>=i的第一个值
class Solution {
public:
int jump(vector<int>& nums) {
int n = nums.size();
vector<int> f(n);
for(int i = 1, j = 0; i < n; i ++ ) {
while(j + nums[j] < i) j ++ ;
f[i] = f[j] + 1;
}
return f[n - 1];
}
};
双指针解法
- 定义指针
cur
代表当前位置和dis
代表最远可以到达的位置- 移动
cur
和dis
,记录最远可达位置nxt
- 答案+1
- 更新
dis
为nxt
- 移动
dis
大于n-1
视为可达,返回答案
class Solution {
public:
int jump(vector<int>& nums) {
const int n = nums.size();
int ans = 0, cur = 0, dis = 0;
while (dis < n - 1) {
int next = 0;
//dis标记目前的cur最远可以到达的位置
//超过dis的位置,当前的cur都无法到达
while (cur <= dis) {
//next记录最远当前cur最远可达的位置
next = max(next, cur + nums[cur]);
cur++;
}
ans++;
//更新dis
dis = next;
}
return ans;
}
};
46. 全排列
从前往后一位一位枚举,每次选择一个还没有被使用的数字,计算出所有的可能
class Solution {
vector<vector<int>> res;
vector<int> cur;
vector<bool> st;
public:
void dfs(vector<int>& nums, int u)
{
if(u == nums.size()) //枚举完所有数字
{
res.push_back(cur);
return;
}
//从前往后枚举
for(int i = 0; i < nums.size(); i ++ )
if(!st[i]) //选择还没有在路径数组中的数字
{
cur[u] = nums[i];
st[i] = true; //标记该数字
dfs(nums, u + 1);
st[i] = false;//回溯
}
}
vector<vector<int>> permute(vector<int>& nums) {
st.assign(nums.size(), false);
cur.resize(nums.size());
dfs(nums, 0);
return res;
}
};
47. 全排列 II
思路同46.,初始化的时候先排序一遍,枚举时再增加一个判断是否和上一个数字相同的条件即可
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<bool> st;
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
path = vector<int>(nums.size());
st = vector<bool>(nums.size());
dfs(nums, 0);
return ans;
}
void dfs(vector<int>& nums, int u) {
if(u == nums.size()) {
ans.push_back(path);
return;
}
for(int i = 0; i < nums.size(); i ++ ) {
if(!st[i]) {
if(i && nums[i - 1] == nums[i] && !st[i - 1]) continue;
st[i] = true;
path[u] = nums[i];
dfs(nums, u + 1);
st[i] = false;
}
}
}
};
48. 旋转图像
先沿着中轴上下交换,再沿着对角线一一交换,即可顺时针旋转90度
123 789 741
456 ----> 456 -----> 852
789 123 963
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = (int)matrix.size() - 1;
//上下交换
for(int i = 0, j = n; i < j; i ++ , j -- )
for(int k = 0; k <= n; k ++ )
swap(matrix[i][k], matrix[j][k]);
//沿着对角线交换
for(int k = 0; k <= n; k ++ )
for(int i = k, j = k; i >= 0; i -- , j -- )
swap(matrix[i][k], matrix[k][j]);
}
};
49. 字母异位词分组
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> hash;
for(auto& str : strs) {
string nstr = str;
sort(nstr.begin(), nstr.end());
hash[nstr].push_back(str);
}
vector<vector<string>> res;
for(auto &item : hash) res.push_back(item.second);
return res;
}
};
50. Pow(x, n)
比较典型的一道快速幂题目,可以使用位运算实现,也可以使用递归的做法实现,这里采用位运算的实现方式
class Solution {
public:
double myPow(double x, int n) {
//long long类型方便计算
typedef long long LL;
//记录指数幂是否为负数
bool is_minus = n < 0;
//res 记录答案
double res = 1;
//位运算,时间复杂度为O(log n)
for (LL k = abs(LL(n)); k; k >>= 1) {
//如果k的二进制串表示最末尾的一位为1,则res乘以x
if (k & 1) res *= x;
//x倍增
x *= x;
}
if (is_minus) res = 1 / res;
return res;
}
};