60天刷题记录Day1| 二分法(Leetcode704, 33, 81, 35, 34, 69, 367)、双指针(lc27, 26,283,844)

363 阅读2分钟

今天是2月1号,参加代码随想录训练营第一天,做了好几道题,希望能一直坚持下去!
今天的任务是数组理论基础。主要学习的知识点是二分法双指针。例题是704. 二分查找27. 移除元素

二分法

Leetcode 704. Binary Search

这是一道二分法的模版题。对于解二分法的题,我比较习惯背Acwing里y总的算法模版。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1;
        int mid;
        while(l < r){
            mid = l + r >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        if(nums[r] != target) return -1;
        return r;
    }
};

其中if(nums[mid] >= target)中的语句为check函数。
这个算法有两种更新方式:

  1. mid = l + r + 1 >> 1
    那么当check的结果为true时,区间应更新为[mid, r],更新方式为l = mid。 当check的结果为true时,区间应更新为[l, mid - 1],更新方式为r = mid - 1
  2. mid = l + r >> 1
    那么当check的结果为true时,区间应更新为[l, mid],更新方式为r = mid。 当check的结果为true时,区间应更新为[mid + 1, r],更新方式为l = mid + 1

如何选择呢?
先写check函数,想如何更新。如果更新方式是l = mid, r = mid - 1要补上+1。

又复习了几道去年暑假做的二分法的题,已经忘了咋做了,真的要经常复习。

Leetcode 33. Search in Rotated Sorted Array

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1;
        while(l < r){
            int mid = l + r + 1 >> 1;
            if(nums[mid] >= nums[0]) l = mid;
            else r = mid - 1;
        }

        if(target >= nums[0]) l = 0;
        else l = r + 1, r = nums.size() - 1;

        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }

        if(nums[r] != target) return -1;
        return r;
    }
};

这道题先通过二分法找到pivot,再判断出target所在的区间继续二分。

  • Leetcode 81. Search in Rotated Sorted Array II
class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1;
        
        while(nums[0] == nums[r] && r > 0) r --;
        if(r <= 0) return nums[0] == target;
        
        while(l < r){
            int mid = l + r + 1 >> 1;
            if(nums[mid] >= nums[0]) l = mid;
            else r = mid - 1;
        }

        if(target >= nums[0]) l = 0;
        else l = r + 1, r = nums.size() - 1;

        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }

        if(nums[r] != target) return false;
        return true;
    }
};

这道题与上一题的区别是数组中有重复元素。如果nums[0] == nums[r],不满足使用二分法所需的二段性,要先去掉重复元素。

Leetcode 35. Search Insert Position

直接套用二分法模版就可以。

Leetcode 34. Find First and Last Position of Element in Sorted Array

用两次二分法,check函数分别为nums[mid] >= targetnums[mid] <= target

Leetcode 69. Sqrt(x)

class Solution {
public:
    int mySqrt(int x) {
        int l = 0, r = x;
        while(l < r){
            int mid = l + r + 1ll >> 1;
            if(mid <= x / mid) l = mid;
            else r = mid - 1;
        }
        return r;
    }
};

check函数如果为mid * mid <= x会出现数据太大而Runtime Error。
这里用这种更新方式是因为要找的不是第一个i的平方大于目标数x的i,而是要最后一个i的平方不大于目标数x的i。有一个小技巧是把+1换成+1ll,将mid转成long long型。

Leetcode 367. Valid Perfect Square

上一题的简单版

双指针

Leetcode 27. Remove Element

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

用k当作慢指针,i当作快指针。i不停往前走。当i遇到等于val的数k不动;当遇到不等于val的数,就把这个数复制到k所在位置,然后k继续往前走。

Leetcode 26. Remove Duplicates from Sorted Array

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

nums[i] != nums[i - 1]时,把nums[i]复制到k所在位置,然后k往前走。

Leetcode 283. Move Zeroes

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

nums[i] != 0时,把nums[i]复制到k所在位置,然后k往前走。

Leetcode 844. Backspace String Compare

双指针做法:

class Solution {
public:
    bool backspaceCompare(string s, string t) {
        return get(s) == get(t);
    }
    string get(string s){
        int slow = 0;
        for(int fast = 0; fast < s.size(); fast ++)
        {
            if(s[fast] != '#')
            s[slow ++] = s[fast];
            else if(slow != 0)
                slow --;
        }
        s.resize(slow);
        return s;
    }
};

感觉用stack会简单点:

class Solution {
public:
    bool backspaceCompare(string s, string t) {
        return get(s) == get(t);
    }
    string get(string s){
        string res;
        for(auto c : s){
            if(c == '#') {
                if(res.size()) res.pop_back();
            }
            else res += c;
        }
        return res;
    }
};

Leetcode 977. Squares of a Sorted Array

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n);
        
        for(int i = 0, j = n - 1, k = n - 1; i <= j;){
            if(nums[i] * nums[i] >= nums[j] * nums[j]){
                res[k --] = nums[i] * nums[i];
                i ++;
            }
            else{
                res[k --] = nums[j] * nums[j];
                j --;
            } 
        }
        return res;
    }
};

nums[i]作为数组头,nums[j]作为数组尾,谁的平方大就把谁放入res里,然后向中间移动。