前言:主要是记录一些算法题的解题思路与技巧,纯当笔记用。
图片与部分代码来源:leetcode
图片与部分代码来源:leetcode
图片与部分代码来源:leetcode
力扣链接:力扣链接
这题可以用动态规划做,定义 dp 数组,dp[i] 的含义表示为字符串从 0 到 i 的解码方法有多少种。遍历字符串 s 同时更新 dp 数组的值。如果 s[i] 的值不为 0,则 s[i] 可以单独解码为一个字母,dp[i]+=dp[i-1] ; 如果他和 s[i-1] 合并解码是一个符合条件的值, dp[i]+=dp[i-2] 。仔细思考会发现这题本质上与青蛙跳台阶那题是一样的。
class Solution {
public:
int numDecodings(string s) {
int n = s.size();
vector<int> f(n + 1);
f[0] = 1;
for (int i = 1; i <= n; ++i) {
if (s[i - 1] != '0')
f[i] += f[i - 1]; //单独解码
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26))
f[i] += f[i - 2]; //合并解码
}
return f[n];
}
};
力扣链接:有效的括号字符串
关于字符串匹配问题,使用栈来解决是一种很好的思路,这题也是一样。可以创建两个栈,栈一用来存放 ( ,栈二用来存放 * 。遍历字符串 s 。当遇到 ( 时,存入栈一,当遇到 * 时,存入栈二。当遇到 ) 时,可以查看栈一中是否有元素,如果有,则代表存在 ( 可以与 ) 匹配,如果栈一不存在元素而栈二存在元素则 ) 也能被匹配,因为 * 可以当成 ( 使用。
如果遍历完字符串 s 后栈一中还存在元素,则说明还有未被匹配的 ( ,可以查看栈二中是否存在元素,使用 * 来匹配还未被匹配的 ( 。但要注意的一点是如果你的栈中存放的是字符的话,那么你没办法判断 * 与 ( 的相对位置。也就是说会出现 * 在 ( 后面的情况,显然这种情况是不符合条件的。所以我们的栈中不存字符,而是存放字符在字符串中的相对位置。这样就能判断出来了。
class Solution {
public:
bool checkValidString(string s) {
stack<int> sck1;
stack<int> sck2;
for(int i=0;i<s.size();++i){
if(s[i]=='(') //遇到 (
sck1.push(i);
else if(s[i]=='*') //遇到 *
sck2.push(i);
else if(s[i]==')'){ //遇到 )
if(sck1.size())
sck1.pop();
else if(sck2.size())
sck2.pop();
else
return false;
}
}
while(sck1.size() && sck2.size()){ //判断未必匹配的 ( 是否能被匹配
if(sck2.top()<sck1.top())
return false;
sck1.pop();
sck2.pop();
}
return sck1.empty();
}
};
力扣链接:数组中重复的数据
如果没有时间复杂度的要求,那么使用哈希表应该是很自然也很容易的方法。但不能使用额外的空间,而且这题显然是要使用哈希表的。对于这样的问题,应该想到使用原数组作为哈希表来进行标记,这算是这类题型常用的套路,一个小技巧。
我们将每个数放在它数值对应的下标处,如 5 应该放在下标为 4 的地方,3 应该放在下标为 2 的地方,如果数字出现两次,那么就还有一个相等的数字不在数值对应的下标处,对于这种数,我们把它加入结果集。
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
int n=nums.size();
vector<int> ret;
for(int i=0;i<n;++i){
while(nums[i]!=nums[nums[i]-1]) //把数字放到它数值对应的下标处,循环处理是为了把交换过来的数字也放到它数值对应的下标处,否则会漏掉一些数
swap(nums[i],nums[nums[i]-1]);
}
for(int i=0;i<n;++i){
if(nums[i]!=i+1) //找到出现了两次的数字,加入结果集
ret.push_back(nums[i]);
}
return ret;
}
};
力扣链接:旋转数组的最小数字
还是惯常的思路,遇到有序数组,找某个数,首先应该想到的就是二分法。这题虽然把有序数组进行了旋转,但还是可以用二分法。
class Solution {
public:
int minArray(vector<int>& numbers) {
int left = 0,right=numbers.size()-1;
while (left < right) {
int mid = left + (right - left) / 2;
if (numbers[mid] < numbers[right]) //未被翻转的部分,最小的数在 mid 前面
right = mid;
else if (numbers[mid] > numbers[right]) //最小的数在 mid 与 right 之间
left = mid + 1;
else
right -= 1;
}
return numbers[left];
}
};
力扣链接:旋转图像
要旋转 90 度,可以先水平翻转,在沿主对角线翻转就行了。
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
//水平翻转
int n=matrix.size();
for(int i=0;i<n/2;++i){
for(int j=0;j<n;++j)
swap(matrix[i][j],matrix[n-i-1][j]);
}
//对角线翻转
for(int i=0;i<n;++i){
for(int j=0;j<i;++j)
swap(matrix[i][j],matrix[j][i]);
}
}
};