【代码随想录37期】Day06 有效的字母异位词、两个数组的交集、快乐数、两数之和
【代码随想录37期】Day07四数相加Ⅱ、赎金信、三数之和、四数之和
【代码随想录37期】Day08 反转字符串、反转字符串Ⅱ、替换数字、反转字符串里面的单词、右旋转字符串
【代码随想录37期】Day09 实现strStr、重复的子字符串
【代码随想录37期】Day10 用栈实现队列、用队列实现栈
【代码随想录37期】Day11 有效的括号、删除字符串中的所有相邻重复项、逆波兰表达式求值
三数之和
尝试转化为两数之和:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
for (int i = 0; i < nums.size(); i++)
{
unordered_map<int, int> umap;
int target = 0 - nums[i];//转化为2数之和
for (int j = 0; j < nums.size(); j++)
{
if (j == i)
continue;
if (umap.find(target - nums[j]) != umap.end())
{
vector<int> v = { nums[i], nums[j], umap[target - nums[j]] };
ret.emplace_back(v);
}
else
{
umap[nums[j]] = nums[j];
}
}
}
return ret;
}
转化成功,但是并不能过,因为两数之和那道题不考虑重复,只有唯一解,但是三数之和不是
这份代码考虑了nums[i]的重复,但是没有考虑left和right的重复,错误用例看后图
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++)
{
if (nums[i] > 0)
return ret;
if (i > 0 && nums[i - 1] == nums[i])//不用nums[i+1],因为是跟走过循环的元素比,所以是i-1
continue;
int left = i + 1;
int right = nums.size() - 1;
while (left < right)
{
if (nums[i] + nums[left] + nums[right] == 0)
{
ret.emplace_back(vector<int>{nums[i], nums[left], nums[right]});
right--;
left++;
}
else if (nums[i] + nums[left] + nums[right] < 0)
{
left++;
}
else {
right--;
}
}
}
return ret;
}
AC
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++)
{
if (nums[i] > 0)
return ret;
if (i > 0 && nums[i - 1] == nums[i])//不用nums[i+1],因为是跟走过循环的元素比,所以是i-1
continue;
int left = i + 1;
int right = nums.size() - 1;
while (left < right)
{
if (nums[i] + nums[left] + nums[right] == 0)
{
ret.emplace_back(vector<int>{nums[i], nums[left], nums[right]});
right--;
left++;
while (left < nums.size() && nums[left] == nums[left - 1])
left++;
while (right > i && nums[right] == nums[right + 1])
right--;
}
else if (nums[i] + nums[left] + nums[right] < 0)
{
left++;
}
else {
right--;
}
}
}
return ret;
}
四数之和
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ret;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++)
{
if (nums[i] > target && nums[i] >= 0)//剪枝
return ret;
if (i > 0 && nums[i] == nums[i - 1])//去重
continue;
for (int j = i + 1; j < nums.size(); j++)
{
if (nums[i] + nums[j] > target && nums[i] + nums[j]>= 0)//剪枝
break;
if (j > i + 1 && nums[j] == nums[j - 1])//去重
continue;
int left = j + 1;
int right = nums.size() - 1;
while (left < right)
{
if ((long)nums[i] + nums[j] + nums[left] + nums[right] == target)
{
ret.emplace_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});
left++;
right--;
while (left < right && nums[left] == nums[left - 1])
left++;
while (right > left && nums[right] == nums[right + 1])
right--;
}
else if ((long)nums[i] + nums[j] + nums[left] + nums[right] > target)
{
right--;
}
else {
left++;
}
}
}
}
return ret;
}
关键点
这题真挺难的,细节很多,非常容易错,适合多写几遍
怎么剪枝?
按照三数之和的思路,首先元素会用sort改成非递减顺序,所以nums[i] <=nums[j]<=left<=right,所以nums[i]的剪枝就是当它大于等于零时,剩下的三个数一定不必它小,所以这时如果已经比targt大就不用比了,但是nums[j]的剪枝有个细节就是不能return,而是break,因为此时虽然nums[i] + nums[j] > target,但下一个i+1的循环里,最小的j的值不一定就大于现在的j的值,所以我们剪枝要考虑外层循环,不能直接return
为什么要有long?
因为-109 <= nums[i] <= 109,上周末的总结有提到10^9 约为2^30(因为1024约等于1000),四个nums[i]加起来有可能超过了int的范围,所以要改成long
在64位操作系统中,int和long都占4字节,但long的范围更大,为-263~263-1
j去重时为什么要j > i + 1?
我的理解是,i,j,left,right互不相等,因为j是从i+1开始的,所以j-1要>=i+1,所以j就要大于i+1
反转字符串中的单词
这题真是easy?细节也太多了
string reverseMessage(string message) {
reverse(message.begin(), message.end());
int slow = 0, fast = 0;
for (int i = 0; fast < message.size(); fast++)
{
if (message[fast] != ' ')
{
if (slow != 0)
{
message[slow++] = ' ';
}
while (fast < message.size() && message[fast] != ' ')
{
message[slow++] = message[fast++];
}
}
}
message.resize(slow);
int start = 0;
for (int j = 0; j <= message.size(); j++)
{
if (message[j] == ' ' || j == message.size())
{
reverse(message.begin() + start, message.begin() + j);
start = j + 1;
}
}
return message;
}
关键点
首先就得处理空格
主要是要移除多余的空格,去判断空格有没有连续比较麻烦,一个比较好的思路是全部移除空格再手动加上去,if+while的做法很值得学习
为什么要resize,为什么是resize(slow)?
resize是为了把后面冗余的部分去掉
至于新的大小为什么是slow,看slow的循环即可,slow最后多一个slow++,数值上正好是size的大小(大小为slow的下标是0 ~ slow-1)
为什么后面的for循环是小于等于而不是小于?
因为reverse是左闭右开的,不等于的话j到不了message.size(),reverse不到最后一个字母,如: