Leetcode刷题:双指针

文章目录

    • 27. 移除元素
    • 26. 删除有序数组中的重复项
    • 283. 移动零
    • 344. 反转字符串
    • 844. 比较含退格的字符串
    • 977. 有序数组的平方
    • 80. 删除有序数组中的重复项 II
    • 5. 最长回文子串
    • 557. 反转字符串中的单词 III

27. 移除元素

27. 移除元素

数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
暴力求解的时间复杂度为 O ( n 2 ) O(n^2) O(n2),双指针可以变为 O ( n ) O(n) O(n)

Leetcode刷题:双指针_第1张图片

class Solution {
public:
    int removeElement(vector& nums, int val) {
        int flag = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] != val) {
                nums[flag] = nums[i];
                flag++;
            } 
        }
        return flag;
    }
};

26. 删除有序数组中的重复项

26. 删除有序数组中的重复项
Leetcode刷题:双指针_第2张图片

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slowIndex = 0;
        for (int fastIndex = 1; fastIndex < nums.size(); fastIndex++) {
            if (nums[slowIndex] != nums[fastIndex]) {
                slowIndex++;
                nums[slowIndex] = nums[fastIndex];
            }
        }
        return ++slowIndex;

    }
};

通用解法

为了让解法更具有一般性,我们将原问题的「最多保留 1 位」修改为「最多保留 k 位」。

对于此类问题,我们应该进行如下考虑:

由于是保留 k 个相同数字,对于前 k 个数字,我们可以直接保留。
对于后面的任意数字,能够保留的前提是:与当前写入的位置前面的第 k 个元素进行比较,不相同则保留。
举个,我们令 k=1,假设有样例:[3,3,3,3,4,4,4,5,5,5]

  1. 设定变量 idx,指向待插入位置。idx 初始值为 0,目标数组为 []
  2. 首先我们先让第 1 位直接保留(性质 1)。idx 变为 1,目标数组为 [3]
  3. 继续往后遍历,能够保留的前提是与 idx 的前面 1 位元素不同(性质 2),因此我们会跳过剩余的 3,将第一个 4 追加进去。idx 变为 2,目标数组为 [3,4]
  4. 继续这个过程,跳过剩余的 4,将第一个 5 追加进去。idx 变为 3,目标数组为 [3,4,5]
  5. 当整个数组被扫描完,最终我们得到了目标数组 [3,4,5] 和 答案 idx 为 3。
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        return process(nums,1);
    }
    int process(vector<int>& nums,int k){
        int idx = 0;
        for(auto x : nums){
            if(idx < k or nums[idx - k] != x){
                nums[idx++] = x;
            }
        }
        return idx;  
    }
};

283. 移动零

283. 移动零
Leetcode刷题:双指针_第3张图片

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (nums[fastIndex] != 0) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        while (slowIndex < nums.size()) {
            nums[slowIndex++] = 0;
        }

    }
};

减少操作次数:左指针寻找第一个0值,右指针寻找第一个非0值,交换。

344. 反转字符串

Leetcode刷题:双指针_第4张图片

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

swap这种简单的函数可以直接用

844. 比较含退格的字符串

Leetcode刷题:双指针_第5张图片
1. 重构字符串

复杂度分析

时间复杂度:O(N+M),其中 N 和 M 分别为字符串 S 和 T 的长度。我们需要遍历两字符串各一次。

空间复杂度:O(N+M),其中 N 和 M 分别为字符串 S 和 T 的长度。主要为还原出的字符串的开销。

class Solution {
public:
    bool backspaceCompare(string S, string T) {
        return build(S) == build(T);
    }

    string build(string str) {
        string ret;
        for (char ch : str) {
            if (ch != '#') {
                ret.push_back(ch);
            } else if (!ret.empty()) {
                ret.pop_back();
            }
        }
        return ret;
    }
};

2. 双指针

class Solution {
public:
    bool backspaceCompare(string S, string T) {
        int i = S.length() - 1, j = T.length() - 1;
        int skipS = 0, skipT = 0;

        while (i >= 0 || j >= 0) {
            while (i >= 0) {
                if (S[i] == '#') {
                    skipS++, i--;
                } else if (skipS > 0) {
                    skipS--, i--;
                } else {
                    break;
                }
            }
            while (j >= 0) {
                if (T[j] == '#') {
                    skipT++, j--;
                } else if (skipT > 0) {
                    skipT--, j--;
                } else {
                    break;
                }
            }
            if (i >= 0 && j >= 0) {
                if (S[i] != T[j]) {
                    return false;
                }
            } else {
                if (i >= 0 || j >= 0) {
                    return false;
                }
            }
            i--, j--;
        }
        return true;
    }
};

977. 有序数组的平方

Leetcode刷题:双指针_第6张图片

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int flag = -1;
        vector<int> ans;
    for (int i = 0; i < nums.size(); i++) {
        if (nums[i] <= 0) {
            flag = i;
        } else {
            break;
        }
    }
        
    int left = flag, right = flag + 1;
    while (left >= 0 || right < nums.size()) {
        if (left < 0) {
            ans.push_back(nums[right] * nums[right]);
            right++;
        } else if (right == nums.size()) {
            ans.push_back(nums[left] * nums[left]);
            left--;
        } else if (nums[left] * nums[left] < nums[right] * nums[right]){
            ans.push_back(nums[left] * nums[left]);
            left--;
        } else {
            ans.push_back(nums[right] * nums[right]);
            right++;
        }
    }
        return ans;
    }
};
  • 先找到正负数交接的位置,然后向两边扩展(并比较大小)

80. 删除有序数组中的重复项 II

Leetcode刷题:双指针_第7张图片

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slowIndex = 0, k = 2;
        for (auto x: nums){
            if(slowIndex < k or nums[slowIndex - k] != x) {
                nums[slowIndex++] = x;
            }
        }
        return slowIndex;
    }
};

5. 最长回文子串

Leetcode刷题:双指针_第8张图片

class Solution {
public:
    string palindrome(string s, int left, int right) {
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
            left--;
            right++;
        }
        return s.substr(left + 1, right - left - 1);
    }
    string longestPalindrome(string s) {
        int res = 0;
        string ans;
        for (int i = 0; i < s.size(); i++) {
            string s1 = palindrome(s, i, i);
            string s2 = palindrome(s, i, i + 1);
            int cnt1 = s1.size();
            int cnt2 = s2.size();
            if (cnt1 > res) {
                res = cnt1;
                ans = s1;
            }
            if (cnt2 > res) {
                res = cnt2;
                ans = s2;
            }
        }
        return ans;
    }
};
  • 从中间向两边找

557. 反转字符串中的单词 III

Leetcode刷题:双指针_第9张图片

1. 没用双指针

class Solution {
public:
    string reverseWords(string s) {
        int cnt = 0;
        string s1 = "", ans = "";
       
        for (int i = 0; i <= s.size(); i++) {
            if (i == s.size() or s[i] == ' ') {
                ans += s1;
                s1 = "";
                if (i != s.size()) {
                    ans += ' ';
                }
            } else {
                s1 = s[i] + s1;
                
            }
        }
        return ans;
    }
};
  • 时间复杂度O(N),其中 NN 为字符串的长度。原字符串中的每个字符都会在 O(1) 的时间内放入新字符串中。

  • 空间复杂度O(N)。我们开辟了与原字符串等大的空间。

2. 双指针

class Solution {
public: 
    string reverseWords(string s) {
        int length = s.length();
        int i = 0;
        while (i < length) {
            int start = i;
            while (i < length && s[i] != ' ') {
                i++;
            }

            int left = start, right = i - 1;
            while (left < right) {
                swap(s[left], s[right]);
                left++;
                right--;
            }
            while (i < length && s[i] == ' ') {
                i++;
            }
        }
        return s;
    }
};

  • 时间复杂度O(N)。字符串中的每个字符要么在 O(1) 的时间内被交换到相应的位置,要么因为是空格而保持不动。

  • 空间复杂度O(1)。因为不需要开辟额外的数组。

你可能感兴趣的:(c++,leetcode,算法,c++)