算法题合集
Sheldon的刷题笔记
字符串处理
1694. 重新格式化电话号码
- 字符串模拟
class Solution {
public:
string reformatNumber(string number) {
string ans = "";
for(int i = 0,num = 0; i < number.size(); i++)
{
if(!isdigit(number[i])) continue;
ans += number[i];
num ++;
if(num == 3)
{
num = 0;
ans += '-';
}
}
int n = ans.size();
if(ans[n - 1] == '-') ans.pop_back();//可以被3整除的情况
if(n > 2 && ans[n - 2] == '-') swap(ans[n - 2],ans[n - 3]);//剩下4个
return ans;
}
};
38. 外观数列
class Solution {
public:
string countAndSay(int n) {
string s = "1";
for(int i = 0; i < n - 1; i++)
{
string ns = "";//每一次循环的当前值
for(int left = 0; left < s.size();)
{
int right = left;//找到终点
while(right < s.size() && s[right] == s[left]) right++;
ns += to_string(right - left);//该字符个数
ns += s[left];//该字符
left = right;
}
s = ns;
}
return s;
}
};
49. 字母异位词分组
- 哈希+排序
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> hash;
for(auto str:strs)
{
string key = str;
sort(key.begin(),key.end());
hash[key].push_back(str);
}
vector<vector<string>> res;
for(auto item:hash) res.push_back(item.second);
return res;
}
};
DFS
200. 岛屿数量
思路:
- 这是一道深搜的题
- 如果我们碰到1,那么岛屿数量++。同时把与该"1"与及其相连的地方都变成0
CPP代码
class Solution {
public:
void dfs(vector<vector<char>>& grid,int r,int c)
{
int nr = grid.size();
int nc = grid[0].size();
//把当前位置改为0
grid[r][c] = '0';
//其实这里是省略了边界的情况,边界默认为是什么都不干,因为边界一定会被旁边的给影响
//把它四个方向为'1'的岛屿全部变成'0'
//这里我们一定要保证我们的数据是合法的范围
if(r-1 >= 0 && grid[r-1][c] == '1') dfs(grid,r-1,c);
if(r+1 < nr && grid[r+1][c] == '1') dfs(grid,r+1,c);
if(c-1 >= 0 && grid[r][c-1] == '1') dfs(grid,r,c-1);
if(c+1 < nc && grid[r][c+1] == '1') dfs(grid,r,c+1);
return;
}
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();//行数
if(!nr) return 0;//特判
int nc = grid[0].size();//列数
int num_islands=0;
//依次遍历所有的行和列
for(int r=0;r<nr;r++)
{
for(int c= 0;c<nc;c++)
{
if(grid[r][c]=='1') //碰到1我们就要把岛屿数量++
{
num_islands++;
dfs(grid,r,c);
}
}
}
return num_islands;
}
};
BFS
- 本题我们可以把他理解成一个图论
- 我们的每一个结点就是每一个数值
- 加了平方项以后就从当前值转移到了另一个值
- BFS常见套路
- 定义一个队列,队列中有元素就一直循环
- 初始时刻我们把起始点放入队列中,同时距离设置为0
- 每次循环取出队头,弹出队头
- 对我们取出来的队头做一定的处理
- 得到新的结果,存到队列中
class Solution {
public:
int numSquares(int n) {
queue<int> q;
vector<int> dist(n + 1,INT_MAX);//定义距离,所有点到起点的距离
q.push(0);
dist[0] = 0;//起点是0
while(q.size()){
int t = q.front();//当前点
q.pop();
if(t == n) return dist[t];//走到了终点
//否则我们枚举当前点t可以走到哪些点
for(int i = 1; i * i + t <= n; i++){
int j = t + i * i;
if(dist[j] > dist[t] + 1){//说明我们的j可以从t过来
dist[j] = dist[t] + 1;//更新我们的j值
q.push(j);
}
}
}
return 0;
}
};
双指针
167. 两数之和 II - 输入有序数组
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int i = 0, j = numbers.size() - 1;
while(i < j){
while(numbers[i] + numbers[j] > target && i < j) j--;
while(numbers[i] + numbers[j] < target && i < j) i++;
if(numbers[i] + numbers[j] == target) return {i + 1,j + 1};
}
return {-1,-1};
}
};
88. 合并两个有序数组
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int p1 = m - 1, p2 = n - 1,p3 = nums1.size() - 1;
while(p1 >= 0 && p2 >= 0){
if(nums1[p1] > nums2[p2]) nums1[p3--] = nums1[p1--];
else nums1[p3--] = nums2[p2--];
}
//结束条件是p1或者p2中有一个不符合条件
while(p2>=0) nums1[p3--] = nums2[p2--];
}
};
单调队列
滑动窗口
动态规划
64. 最小路径和
- 本题是一个比较简单的动态规划问题
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
if(m == 0 || n == 0) return 0;//特判
vector<vector<int>> f(m,vector<int>(n));//定义我们的答案
f[0][0] = grid[0][0];
//下面2行是处理一下边界条件
for(int i = 1; i < n; i++) f[0][i] = grid[0][i] + f[0][i - 1];
for(int i = 1; i < m; i++) f[i][0] = grid[i][0] + f[i - 1][0];
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++)
f[i][j] = min(f[i - 1][j],f[i][j - 1]) + grid[i][j];
}
return f[m - 1][n - 1];
}
};
62. 不同路径
- 这题跟64题,差不多
- 直接递推就可以了
- 记住要处理一下边界情况!
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> f(m,vector<int>(n));
f[0][0] = 1;
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
if(i == 0 || j == 0) f[i][j] = 1;
else f[i][j] = f[i - 1][j] + f[i][j - 1];
return f[m - 1][n - 1];
}
};
120. 三角形最小路径和
- 这是一道经典的动态规划入门题
- 思路
- 从下到上枚举,这样的话我们的答案就存在g[0]中,如果我们从上到下枚举的话,我们得额外处理边界情况
- 动态转移方程:
g[i]=min(g[i],g[i + 1]) + triangle[i][j]
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size();
vector<int> g(n);
//处理一下最后一层
for(int i = 0; i < n; i++) g[i] = triangle[n - 1][i];
for(int i = n - 2; i >= 0; i--){
for(int j = 0; j <= i; j++)
g[j] = min(g[j],g[j + 1]) + triangle[i][j];
}
return g[0];
}
};
63. 不同路径 II
- 对于有障碍的地方直接跳过
- 初始的时候vector为0,所以对于有障碍的地方它要加也是加0
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& g) {
int n = g.size(), m = g[0].size();
vector<vector<long long>> f(n,vector<long long>(m));//定义我们的状态矩阵
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(g[i][j]) continue;//如果该点是障碍我们什么都不做
if(!i && !j) f[i][j] = 1;//起点设为1
if(i) f[i][j] += f[i - 1][j];//从上面过来的
if(j) f[i][j] += f[i][j - 1];//从左边过来的
}
}
return f[n - 1][m - 1];
}
};
贪心
55. 跳跃游戏
- 这题不会做
- 看完官方的题解,只能说太妙了...
- 我们只需遍历每一个位置(注:最有小于m的地方我们才可以遍历到),判断最远可以到达的位置是否大于等于
n-1
即可
class Solution {
public:
bool canJump(vector<int>& nums) {
int n = nums.size();
int m = 0;//可到达的最远的地方
for(int i = 0; i < n; i++){
if(i <= m){//只有小于m的地方我们才有机会遍历到
m = max(m,i + nums[i]);
if(m >= n - 1)
return true;
}
}
return false;
}
};
AI大三在读