代码随想录算法训练营第一天 | 704. 二分查找、35.搜索插入位置 、 34. 在排序数组中查找元素的第一个和最后一个位置、 27. 移除元素

代码随想录算法训练营第一天 | 704. 二分查找、35.搜索插入位置 、 34. 在排序数组中查找元素的第一个和最后一个位置、 27. 移除元素


今天用了三个半小时,包括写代码和学习使用博客(第一次写),收获满满,主要是对二分法有了更深刻的理解,双指针的应用有待提高。

704、二分查找

题目链接:704、二分查找
文章链接
状态:最后返回-1的情况没弄好。

思路:首先审题是升序有序数组,要用二分查找。二分查找主要分为四种:左闭右闭[用的最多]、左闭右开[其次]、左开右闭和左开右开用的都很少。因此主要写前两种的代码。

第一种左闭右闭

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

第二种 左闭右开

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

27.移除元素

题目链接:27.移除元素
文章链接
状态:暴力解法的i–和size–放错了括号,双指针解法没有问题。

思路:看到题目的第一想法就是用两个循环暴力解出来,但是当测试数据特别多的时候,时间复杂度会爆表,在工作时会很浪费算力,但是在考试上机时实在想不出来暴力算法还是可以拿分的。双指针解法是跟卡哥学的,很好用。就是有时候想不起来用双指针,还是要多加练习,这次的双指针代码自己写出来了。

1.暴力解法

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for (int i  = 0; i < size; i++)
        {
            if (nums[i] == val)
            {
                for (int j = i + 1; j < size; j++)
                {
                    nums[j-1] = nums[j];
                }
                i--;//我的理解是可能存在两个连续的目标值;
                size--;
            }
        }
        return size;
    }
};

2.双指针解法

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

35.搜索插入位置

题目链接
文章链接没有,就看我做的吧。
状态:知道是用二分法但是想不出来该怎么写。

思路:一直在想二分法但是没想出来,看题解看懂了,但是中间有段卡住了,主要讲一下为什么卡住。
我在纸上举了一个例子[1,3,5,6,7,8,9] 令target = 2。一开始l = 0,r = 6;所以mid = 3,此时num[mid] = 6 > 2,此时r = 2.所以mid = 1。此时num[mid] = 3 > 2,
所以r = 0。我一直以为到这里就结束了,返回值 l 是0,也不会是该插入的下标啊,在这里我思考了很久,我又看了一遍题解,想到了while的循环还没有完,还需要再进行一次,此时l = 0, r = 0,mid = 0,所以num[0]= 1 < 2,所以l = mid + 1 = 1,此时l > r 不满足循环 退出 返回 l,此时 l = 1,满足题意,我豁然开朗。我参照的题解在下面。

题解

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

添加上一位老哥的解释:最后直接返回left就可以了,根据if的判断条件,left左边的值一直保持小于target,right右边的值一直保持大于等于target,而且left最终一定等于right+1,这么一来,循环结束后,在left和right之间画一条竖线,恰好可以把数组分为两部分:left左边的部分和right右边的部分,而且left左边的部分全部小于target,并以right结尾;right右边的部分全部大于等于target,并以left为首。所以最终答案一定在left的位置。

还有一种开区间的写法,我理解深刻的。

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

这里是背的y总的模板,在数组最后加入元素时r = size,此时循环判断里是r = mid的情况,如果是l = mid 只能是r = size - 1。

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

题目链接:34.在排序数组中查找元素的第一个和最后一个的位置
状态:大体的代码可以写出来,但是运行不出来。

思路:用两次二分法,分别找到大于等于的边界和小于等于的边界。

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

这是自己写的代码,但是运行不出来,自己看不出来什么问题,先这样,晚上ac出来再改。

问题解决了没有考虑nums数组为空的时候,加了一条判断完美解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值