【代码随想录37期】第二周总结

26 篇文章 0 订阅
25 篇文章 0 订阅

【代码随想录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不到最后一个字母,如:
在这里插入图片描述

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值