代码随想录打卡——day1——【数组】——1.二分与双指针

 以前学过二分与双指针,所以这算是复习


1.二分

1.1 一般二分

题目链接: 704. 二分查找

我的做法:二分

class Solution {
public:
    int search(vector<int>& nums, int target) 
    {
        int l = 0;
        int r = nums.size() - 1;
        // 我的目标 找大于等于target 的左边界
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(nums[mid] >= target)r = mid ;
            else l = mid + 1;
        }
        if(nums[l] == target)return l;
        return -1;
    }
};
  1. 板子两个划线部分背。这里用的是左闭右开的。
  2. 理解二分用途:找一个区间,比如>= target、 >target、<= target 、<target的区间。 
  3.  分清楚check()里面 ,一般都是 check(mid) ---> nums[mid]是不是左端点或者右端点?
  4. 对于mid=l+r(+1) 判断的标准看在if-else段中, l 还是 r =mid,不是看 if()l/r 是l还是r !!!


1.2 二分拓展

35. 搜索插入位置

// 我的版本:
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) 
    {
       int l = 0;
       int r = nums.size() - 1;
       // 目标:找到第一个 >= target的下标
       while(l < r)
       {
           int mid = (l + r) >> 1;
           if(nums[mid] >= target) r = mid;
           else l = mid + 1;
       }
       if(nums[l] == target)return l;
       else if(nums[l] < target) return l+1;
       else return l;
    }
};

/*
[1,3,5,6]
0 0  0
2 0  1
4 1  2
7 3  3
*/

变形题目时,在最后return时候我需要写出来几种特殊情况,每种情况分析再找规律。

应该  1.写出3种情况  2.针对每种情况写代码

SXL的思路:

我二刷时候的思路:比较清晰了:

34. 在排序数组中查找元素的第一个和最后一个位置

两个二分的思路: 

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        vector<int> out;
        // 容易想到用两次二分 第一次>= target 的临界下标 + 第二次 <= target的临界下标
        //  【1】 第一次 >= target 的临界下标
        int l = 0;
        int r = nums.size() - 1;
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(nums[mid] >= target) r = mid;
            else l  = mid + 1;
        }
        out.push_back(-1);
        out.push_back(-1);
        int l1 = l;
        if(nums.size() == 0 || nums[l] != target)return out;
        //  【2】 第一次 <= target 的临界下标
        l = 0;
        r = nums.size() - 1;
        while(l < r)
        {
            int mid = (l + r + 1) >> 1;
            if(nums[mid] <= target) l = mid;
            else r  = mid - 1;
        }
        out.pop_back();
        out.pop_back();
        out.push_back(l1);
        out.push_back(l);
        return out;
    }
};



2.双指针

2.1普通双指针 ---》27. 移除元素

 暴力做法:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        //暴力解法  多开一个vector<int> 没有原地
        vector<int> tmp;
        for(int i = 0; i < nums.size();i++)
            if(nums[i] != val) tmp.push_back(nums[i]);
        nums.clear();
        for(int i = 0; i < tmp.size();i++)
            nums.push_back(tmp[i]);
        return nums.size();
    }
};

 内存占用太多啦!


 我的双向双指针做法:【好吧--内存还是占8.5MB??】

class Solution {
public:
    int removeElement(vector<int>& nums, int val) 
    {
        // 解法2 ---- 双指针 把val全都移到后面
        int l = 0;
        int r = nums.size() - 1;
        while( l < r)
        {
            if(nums[l] == val && nums[r] != val)
            {
                int tmp = nums[l];
                nums[l] = nums[r];
                nums[r] = tmp;
                l++;
                r--;
            }
            else if(nums[l] == val && nums[r] == val)
                r--;
            else if(nums[l] != val && nums[r] == val)
            {
                l++;
                r--;
            }
            else if(nums[l] != val && nums[r] != val)
                l++;
        }
        for(int i = nums.size() - 1; i >= 0;i--)
            if(nums[i] == val)nums.pop_back();
        return nums.size();
    }
};

 看了答案,carl的做法是单向双指针,快指针是找新数组的指针,满指针是构造新数组的指针。更简单,但感觉上面我的比较好理解。

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

 2023-7-8 二刷

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        //思路:单向单双指针
        /*
         slow 从当前位置走到右侧最近val处
         fast 从当前位置走到右侧最近非val处
         交换slow和fast的数据
         !注意slow和fast的边界
        */
        int slow = 0;
        int fast = 0;
        while(fast < nums.size())
        {
            while(slow < nums.size() && nums[slow] != val)slow++;
            fast = slow;
            if(fast < nums.size())
            {
                while(fast < nums.size() && nums[fast] == val)fast++;
                if(fast < nums.size()) swap(nums[slow],nums[fast]);
            }
        }
        return nums.size() - (fast-slow);
    }
};

 总结了一个规律:当外层while有边界判断,中间的++的每一个while循环判断中也要加上边界判断防止越界!

2024-4-11 三刷

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow = 0;  // 每次找到接下来一个==val的idx
        int fast = 0;  // 每次找到接下来第一个非val的idx

        while(slow < nums.size() && fast < nums.size())
        {
            cout << slow << fast << endl;
            while(slow < nums.size() && nums[slow] != val)slow++;
            fast = slow + 1;
            while(fast < nums.size() && nums[fast] == val)fast++;
            if(slow < nums.size() && fast < nums.size())
            {
                int tmp = nums[slow];
                nums[slow] = nums[fast];
                nums[fast] = tmp;
            }
        }
        return slow;
    }
};

2.2 双指针拓展

26. 删除有序数组中的重复项 【这题要用上面的同向快慢双指针、双向的双指针哒咩!】

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        // 同向 快慢双指针做法
        int slow = 0;  // slow为构造新数组的指针
        int old = -0xffff;
        for(int fast = 0; fast < nums.size();fast++)  //fast为寻找新数组的指针
        {
            if(nums[fast] != old)
            {
                old = nums[fast];
                nums[slow++] = nums[fast];
            }
        }
        return slow;
    }
};

283. 移动零  比较简单 还是同向快慢双指针

class Solution {
public:
    void moveZeroes(vector<int>& nums) 
    {
        //  本题还是同向双指针
        int slow = 0;
        for(int fast = 0; fast < nums.size(); fast++)
            if(nums[fast] != 0)nums[slow++] = nums[fast];
        for(;slow < nums.size();slow++)
            nums[slow] = 0;
    }
};

844. 比较含退格的字符串

还是同向双指针,我一开始没想到栈,没考虑,下面两类特殊情况,后面错的了才加flag打补丁,发现是栈,下面第一种情况需要分类讨论,靠一点分类逻辑:

              

// 我的版本
class Solution {
public:
    bool backspaceCompare(string s, string t) 
    {
        //  为了达到用 O(n) 的时间复杂度和 O(1) 的空间复杂度 还是要用同向双指针
        int slow_s = 0;
        int flag = 0;  //当前要回退的flag=当前可以回退的个数  ===  其实这里用栈比较好写
        for(int fast_s = 0; fast_s < s.size();fast_s++)
        {
            if(s[fast_s] == '#' && flag)  // 回退
            {
                slow_s--;
                flag--;
            }else 
            {
                if(s[fast_s] == '#' && slow_s == 0)continue;
                s[slow_s++] = s[fast_s];
                flag++;
            }
        }
        s.erase(slow_s,s.size() - slow_s);

        flag = 0;  //当前要回退的flag
        int slow_t = 0;
        for(int fast_t = 0; fast_t < t.size();fast_t++)
        {
            if(t[fast_t] == '#' && flag)
            {
                slow_t--;
                flag--;
            }
            else 
            {
                if(t[fast_t] == '#' && slow_t == 0)continue;
                t[slow_t++] = t[fast_t];
                flag++;
            }
        }
        t.erase(slow_t,t.size() - slow_t);
        cout << s << " " << t << endl;
        if(s == t)return 1;
        else return 0;
    }
};

换一个写法——模拟栈

class Solution {
public:
    bool backspaceCompare(string s, string t) 
    {
        string a,b;
        for(int i = 0; i < s.size();i++)
        {
            if(s[i] != '#')a.push_back(s[i]);
            else if(s[i] == '#')
                if(a.size() == 0)continue;
                else a.pop_back();
        }
        for(int i = 0; i < t.size();i++)
        {
            if(t[i] != '#')b.push_back(t[i]);
            else if(t[i] == '#')
                if(b.size() == 0)continue;
                else b.pop_back();
        }

        if(a == b)return 1;
        else return 0;
    }
};

感受

1. 复习熟练了二分和双指针

2. 写这些还是有趣的hh,靠一点推理( 但不多,可能简单题吧

3. 这几天忙编译code, SXL落下太多了,周六还在写周三day1的题目,累计用时大概3-4h( 断断续续写了几天


todo

1. 二分还剩下

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值