前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【代码随想录】二刷-双指针法

【代码随想录】二刷-双指针法

作者头像
半生瓜的blog
发布2023-05-13 14:26:48
1300
发布2023-05-13 14:26:48
举报
文章被收录于专栏:半生瓜のblog半生瓜のblog

双指针

27. 移除元素

代码语言:javascript
复制
// 时间复杂度 O(n)
// 空间复杂度 O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow = 0;
        int fast = 0;
        int n = nums.size();
        while(fast < n){
            if(nums[fast] != val){
                swap(nums[slow++],nums[fast]);
            }
            fast++;
        }
        return slow;
    }
};

344. 反转字符串

代码语言:javascript
复制
// 时间复杂度 O(n)
// 空间复杂度 O(1)
class Solution {
public:
    void reverseString(vector<char>& s) {
        for(int i =0,j=s.size()-1;i < j;i++,j--){
            swap(s[i],s[j]);
        }
    }
};

剑指 Offer 05. 替换空格

代码语言:javascript
复制
// 时间复杂度O(n)
// 空间复杂度O(n)
class Solution {
public:
    string replaceSpace(string s) {
        int count = 0;
        int oldSize = s.size();
        for(char& c:s){
            if(c == ' ')count++;
        }
        s.resize(oldSize+count*2);
        int newSize = s.size();
        // "We are happy    ."
        //             i    j
        // %20
        // i越界之前,j肯定可以追上,从而终止循环。
        for(int i = oldSize-1,j=newSize-1;i < j;i--,j--){
            if(s[i] != ' '){
                s[j] = s[i];
            }else{
              s[j]='0';
              s[j-1]='2';
              s[j-2]='%';
              j -= 2;
            }
        }
        return s;
    }
};

151. 反转字符串中的单词

  • 去掉多余空格
  • 整体反转
  • 反转每个单词
代码语言:javascript
复制
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
    // 左闭右闭
    void myReverse(string& s,int begin,int end){
        for(int i =begin,j=end;i<j;i++,j--){
            swap(s[i],s[j]);
        }
    }
    void delExtraSpace(string& s){
        // 删除空格,重新添加符合要求的空格
        int slow = 0;
        for(int i = 0;i < s.size();i++){
            if(s[i] != ' '){
                if(slow != 0)s[slow++] = ' ';// 单词前添加空格,除去第一个
                while(i < s.size() && s[i] != ' '){
                    s[slow++] = s[i++];
                }
            }
        }
        s.resize(slow);
    }
    string reverseWords(string s) {
        delExtraSpace(s);   
        myReverse(s,0,s.size()-1);
        int slow = 0;
        for(int i = 0;i <= s.size();i++){
            if(i == s.size() || s[i] == ' '){
                myReverse(s,slow,i-1);// 到空格了,其那面是单词
                slow = i+1;
            }
        }
        return s;
    }
};

206. 反转链表

代码语言:javascript
复制
// 时间复杂度 O(n)
// 空间复杂度 O(1)
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* tmp;
        ListNode* pre = nullptr;// 想下可以理解,调转过来,第一个结点的next为nullpter
        ListNode* cur = head;
        while(cur){
            tmp = cur->next;// 保存下一个结点
            
            cur->next = pre;
            pre = cur;// 当前结点为下一个的next
            cur = tmp;// 当前结点变为next
        }
        return pre;
    }
};

  • 递归
代码语言:javascript
复制
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
    ListNode* dfs(ListNode* cur,ListNode* pre){
        if(cur == nullptr)return pre;
        ListNode* tmp = cur->next;
        cur->next = pre;
        // pre = cur; 直接在下面传cur即可
        return dfs(tmp,cur);
    }

    ListNode* reverseList(ListNode* head) {
        return dfs(head,nullptr);
    }
};

19. 删除链表的倒数第 N 个结点

  • 至于为什么要让fast先走n+1步,感觉是这图独有的技巧性,还是主要理解虚拟头结点的使用。——统一处理所有结点。
代码语言:javascript
复制
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* tmpHead = new ListNode(0);
        tmpHead->next = head;
        ListNode* fast = tmpHead;
        ListNode* slow = tmpHead;
        n++;// 因为要删除结点,所以要停在目标结点前。fast先走n+1步
        while(fast && n--){
            fast = fast->next;
        }
        while(fast){
            fast = fast->next;
            slow = slow->next;
        }
        ListNode* tmp = slow->next;
        slow->next = tmp->next;
        delete tmp;
        return tmpHead->next;
    }
};

面试题 02.07. 链表相交

  • 让我想起了【设计链表那道题】,有关虚拟头结点的使用,一是可以统一管理所有结点,二来说也是为了删除结点,给出删除第几个,第几个指的是索引,从0开始,while(n–)会停在对应结点的前一个结点。
    • 联想出,删除第几个,如果这个第几个不是索引,就是字面意思的第几个,从1开始,那么我们可以将n-1,然后开始while(n–),最终停在指定结点的前一个。
代码语言:javascript
复制
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int lenA = 0;
        int lenB = 0;
        ListNode* curA = headA;
        ListNode* curB = headB;
        while(curA){
            lenA++;
            curA = curA->next;
        }
        while(curB){
            lenB++;
            curB = curB->next;
        }
        // 默认A为更长的
        if(lenB > lenA){
            swap(headA,headB);
            swap(lenA,lenB);
        }

        int dis = lenA - lenB;
        curA = headA;
        curB = headB;
        while(dis-- && curA){
            curA = curA->next;
        }
        // 同一个起跑线
        while(curA){
            if(curA == curB)return curA;
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

142. 环形链表 II

  • 感觉上面这几个链表的双指针偏具体题的特殊性一些。
代码语言:javascript
复制
// 时间复杂度 O(n)
// 空间复杂度 O(1)
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast && fast->next){
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast){
                ListNode* tmp = head;
                while(tmp != fast){
                    tmp = tmp->next;
                    fast = fast->next;
                }
                return fast;
            }
        }
        return NULL;
    }
};

15. 三数之和

代码语言:javascript
复制
// 时间复杂度 O(n²)
// 空间复杂度 O(n)
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>>ret;
        sort(nums.begin(),nums.end());
        int n = nums.size();
        for(int i = 0;i < n;i++){// a=nums[i]
            if(nums[i] > 0)break;// 去掉无意义的计算
            if(i > 0 && nums[i] == nums[i-1]){// a去重
                continue;
            }
            int left = i+1;
            int right = n-1;
            while(left < right){
                int tmpSum = nums[i]+nums[left]+nums[right];
                if(tmpSum > 0)right--;
                else if(tmpSum < 0)left++;
                else{
                    // 收集结果
                    ret.push_back({nums[i],nums[left],nums[right]});
                    
                    // 去重
                    // left ->left                 right<-right
                    while(left < right && nums[left] == nums[left+1])left++;
                    while(left < right && nums[right] == nums[right-1])right--;
                    // 经过上述两个循环后,left right分别指向最后一个与上面收集结果时候相等的元素,经过下面再次++后,指向第一个与上述结果不相等我两个元素。
                    // 收缩-调整位置
                    left++;
                    right--;
                }
            }
        }
        return ret;
    }
};

  • 哈希表去重
代码语言:javascript
复制
// 时间复杂度O(n²)
// 空间复杂度O(n)
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>>ret;
        sort(nums.begin(),nums.end());
        int n = nums.size();
        for(int i = 0;i < n;i++){// a=nums[i]
            if(nums[i] > 0)break;// 去掉无意义的计算
            if(i > 0 && nums[i] == nums[i-1]){// a去重
                continue;
            }
            unordered_set<int>set;
            for(int j = i+1;j < n;j++){// b=nums[j]
                if(j > i + 2 && 
                    nums[j] == nums[j-1] &&
                    nums[j-1] == nums[j-2]){// b去重
                    continue;
                }
                int c = 0 - nums[i] - nums[j];//  c
                if(set.find(c) != set.end()){// 找到了
                    ret.push_back({nums[i],nums[j],c});
                    set.erase(c);// c去重
                }else{// 没找到
                    set.emplace(nums[j]);
                }
            }
        }
        return ret;
    }
};

18. 四数之和

  • 即在三数之和的基础上加一层for循环。
代码语言:javascript
复制
// 时间复杂度O(n³)
// 空间复杂度O(n)
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>>ret;
        sort(nums.begin(),nums.end());
        int n = nums.size();
        for(int i = 0;i < n;i++){// a = nums[i]
            if(nums[i] > target && nums[i] >= 0)break;// 剪枝,去掉无意义计算

            if(i > 0 && nums[i] ==  nums[i-1])continue;// 去重

            for(int j = i+1; j < n;j++){// b = nums[j]
                if(nums[i] + nums[j] > target && 
                    nums[i]+ nums[j] >= 0){// 剪枝,去掉无意义计算
                       continue;
                }
                if( j > i + 1 && nums[j] == nums[j-1])continue;// b去重
                int left = j + 1;
                int right = n-1;
                while(left < right){
                    // 防止溢出,转long
                    long tmpSum = (long)nums[i] + nums[j] + nums[left] + nums[right];
                    if(tmpSum > target)right--;
                    else if(tmpSum < target)left++;
                    else{// 收集结果
                        ret.push_back({nums[i],nums[j],nums[left],nums[right]});
                        while(left < right && nums[left] == nums[left+1])left++;
                        while(left < right && nums[right] == nums[right-1])right--;

                        right--;
                        left++;
                    }
                }

            }
        }
        return ret;
    }
};

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-11-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 双指针
    • 27. 移除元素
      • 344. 反转字符串
        • 剑指 Offer 05. 替换空格
          • 151. 反转字符串中的单词
            • 206. 反转链表
              • 19. 删除链表的倒数第 N 个结点
                • 面试题 02.07. 链表相交
                  • 142. 环形链表 II
                  • 15. 三数之和
                    • 18. 四数之和
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档