代码随想录算法训练营第八天 |LeetCode344、LeetCOde541、剑指 Offer05、LeetCode151、剑指 Offer=58-II

代码随想录算法训练营第八天 |LeetCode344.反转字符串、541.反转字符串 II、剑指 Offer05.替换空格、LeetCode151.反转字符串中的单词、剑指 Offer=58-II.左旋转字符串

2023年2月25日 补博客的同时再做一遍

344. 反转字符串

题目链接:344. 反转字符串

思路:

  • 双指针遍历交换

时间复杂度:O(n),空间复杂度O(1)

代码:

class Solution {
public:
    void reverseString(vector<char>& s) {
        int left = 0;
        int right = s.size()-1;
        while(left < right)
        {
            std::swap(s[left],s[right]);
            ++left;
            --right;
        }
    }
};

总结:

  • 比较简单
  • 做题体验:哈哈哈哈 > 嘿嘿 > 哎哎 > 嘤嘤
  • 时间:未计
  • 一刷

541. 反转字符串 II

题目链接:541. 反转字符串 II

思路:

  • 每次反转的起始位置是2k * n(n = 0,1,2,3……)
  • 每次反转的结束位置是起始位置之后的k-i位,或者是字符串末尾
  • 有这些信息,剩余的工作就和反转字符串一样了

时间复杂度:O(n),空间复杂度O(1)

代码:

class Solution {
public:
    string reverseStr(string s, int k) {
        for(int i = 0;i<s.size();i += 2*k)
        {
            int length = s.size();
            int begin = i;
            auto end = min(i+ k -1,length-1);
            while(begin < end)
            {
                std::swap(s[begin],s[end]);
                ++begin;
                --end;
            }
        }
        return s;
    }
};

总结:

  • 一开始一直没搞清结构,做得不太理想,关键在于for(int i = 0;i<s.size();i += 2*k)这一步,想通了就豁然开朗了
  • 做题体验:哎哎哈哈 > 嘿嘿 > 哎哎 > 嘤嘤
  • 时间:未计
  • 一刷

剑指 Offer 05. 替换空格

题目链接:剑指 Offer 05. 替换空格

思路:

  • 简单的做法是直接遍历字符串,不是空格的话插入新字符串,是空格的话填入所需字符串
  • carl哥提供了一种空间复杂度更低的方式

简单方式:

时间复杂度:O(n),空间复杂度O(n)

代码:

class Solution {
public:
    string replaceSpace(string s) {
        string res;
        for(auto c:s)
        {
            if(c==' ')
            {
                res+="%20";
            }
            else
            {
                res.push_back(c);
            }
        }
        return res;
    }
};

性能更优方式:

时间复杂度:O(n),空间复杂度O(1)

代码:

// 原地做法:
class Solution
{
public:
    string replaceSpace(string s)
    {
        // 原始大小
        int oldSize = s.size();
        int count = 0;
        for (auto a : s)
        {
            if (a == ' ')
                ++count;
        }
        int newSize = oldSize + 2 * count;
        s.resize(newSize); // 扩容字符串
        for (int j = oldSize - 1, i = newSize - 1; j < i; --j, --i) // 从字符串末尾开始更新
        {
            if (s[j] == ' ')
            {
                s[i] = '0';
                s[--i] = '2';
                s[--i] = '%';
            }
            else
            {
                s[i] = s[j];
            }
        }
        return s;
    }
};


总结:

  • 本来以为很简单的题目,看了下carl的文章,还有意外的收获
  • 做题体验:嘿嘿哈哈 > 嘿嘿 > 哎哎 > 嘤嘤
  • 时间:未计
  • 二刷

151. 反转字符串中的单词

题目链接:151. 反转字符串中的单词

思路:

  • 这题有许多注意的点,一个是反转单词的方式,另一个是去多余空格
  • 反转单词:如果想在原地直接移动几个单词的位置比较困难,可以考虑先反转整个字符串,再反转每一个单词
  • 去除多余空格,对于前中后的空格都要去除,同时要注意时间复杂度,可以考虑类似27.移除元素的思路,用双指针进行覆盖操作

时间复杂度:O(n),空间复杂度O(1)

代码:

class Solution
{
public:
    string reverseWords(string s)
    {

        // 先对数组进行逆序操作
        std::reverse(s.begin(), s.end());

        // 按要求去除多余空格
        int left = 0;
        int right = 0;
        // 先去掉前面空格
        while (right < s.size() && s[right] == ' ') // 注意检查越界
            ++right;
        // 再去掉中间空格
        for (; right < s.size(); ++right)
        {
            // 思路和代码随想录相反
            if (s[right] != ' ' || (right > 0 && s[right] == ' ' && s[right - 1] != ' '))
            {
                s[left] = s[right];
                ++left;
            }
        }
        // 去掉最后空格
        if (left > 0 && s[left - 1] == ' ') // 注意检查越界
            --left;
        s.resize(left);

        // 反转每一个单词
        auto begin = s.begin();
        auto end = s.begin();
        for (auto it = s.begin();; ++it)
        {
            if (*it == ' ')
            {
                end = it;
                std::reverse(begin, end);
                begin = ++end;
            }
            else if (it == s.end())
            {
                end = it;
                std::reverse(begin, end);
                break;
            }
        }
        return s;
    }
};

总结:

  • 这题就败在了去除多余空格上,而去除空格使用的27.移除元素的方式,当时没有自己看carl哥的方法,执迷用自己的思路
  • 以后就算思路不同,也得多看看carl哥提供的,哈哈
  • 做题体验:哎哎哈哈 > 嘿嘿 > 哎哎 > 嘤嘤
  • 时间:未计
  • 一刷

剑指 Offer 58 - II. 左旋转字符串

题目链接:剑指 Offer 58 - II. 左旋转字符串

思路:

  • 这边我写三个解法,第一个最好,时间复杂度是O(1),后面两个是O(n),单纯记录下
  • 解1:翻转前n个字符,翻转剩余字符,翻转全部字符
  • 解2:用模的思想用一个循环,循环内一行代码操作
  • 解3:简单地累加两段字符串

解1

时间复杂度:O(n),空间复杂度O(1)

代码:

class Solution
{
public:
    string reverseLeftWords(string s, int n)
    {
        std::reverse(s.begin(),std::next(s.begin(),n));
        std::reverse(std::next(s.begin(),n),s.end());
        std::reverse(s.begin(),s.end());
        return s;
    }
};

解2

时间复杂度:O(n),空间复杂度O(n)

代码:

class Solution
{
public:
    string reverseLeftWords(string s, int n)
    {
        string str;
        for (int i = n; (i % s.size() != n) || i == n; ++i)
            str += s[i % s.size()];
        return str;
    }
};

解3

时间复杂度:O(n),空间复杂度O(n)

代码:

class Solution
{
public:
    string reverseLeftWords(string s, int n)
    {
        std::string str(std::next(s.begin(), n), s.end());
        str += std::string(s.begin(), std::next(s.begin(), n));
        return str;
    }
};

总结:

  • 这里用到了一个std库中的方法std::next(),代码中的std::next(s.begin(),n)s.begin()+n有啥区别?来看看小江给我的回答:

xxx.begin()会返回一个迭代器

但迭代器类型有6种

让一个迭代器+n必须满足random_access_iterator_tag

但std::next会对所有迭代器进行特化(特殊处理)

比如vector就是连续型迭代器所以+n等同于std::next(xxx,n)

因为vector底层数据就是连续排布的

但是如果是list双向迭代器,它底层是不连续的

list.begin()+n就是违法操作

但std::next(list.begin(),n)就是合法的

它会让list.begin()连续++ n次

  • 做题体验:嘿嘿哈哈 > 嘿嘿 > 哎哎 > 嘤嘤
  • 时间:未计
  • 一刷
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值