代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素

状态:之前学过二分法,但只是了解概念,属于有思路但一写就废,也并不知道其中的条件判断的细节。

解题思路:

1.使用二分法查找,根据target的值与左右指针的比较,缩小比较的区间,而这个区间的边界确定是比较重点的部分

2.切入二分法的条件:升序,无重复元素

代码实现:
1.左闭右闭写法
class Solution {
public:
    int search(vector<int>& nums, int target) {
             int left =0;
             int right = nums.size()-1;    //从这可以看出是左闭右闭
             while(left<=right)    //因为是左闭右闭,因此可以左右相等是有意义的
             {
                int mid = left + ((right-left)>>1);   //防止超过int最大值
                if(nums[mid]>target)       
                    right = mid -1;    //更新右区间,因为可以取到,而mid已经参与比较了,因此-1
                else if(nums[mid]<target)
                    left = mid +1 ;    //同理+1
                else return mid;     //当找到就返回下标
             } 
             return -1;   //未找到就返回-1
    }
};
2.左闭右开写法
class Solution {
public:
    int search(vector<int>& nums, int target) {
             int left =0;
             int right = nums.size();    //右区间不能取到
             while(left<right)      //左右相等无意义
             {
                int mid = left + ((right-left)>>1);
                if(nums[mid]>target)
                    right = mid;      //更新右区间时,因为右区间取不到,不需要-1
                else if(nums[mid]<target)
                    left = mid +1 ;
                else return mid;
             }
             return -1;
    }
};

时间复杂度:O(log n)

空间复杂度:O(1)

总结:

1.循环不变量

这里的不变量是定义的区间,是左闭右闭,还是左闭右开,这是不变的,在后续改成区间值的时候,也不变

2.判断条件的确定

这里有两个需要确定的地方,一个是while中对于left和right的判断

当左右都闭时,例如[2,2],左右区间相等时,这是有意义的,因此为left<=right

当左闭右开时,例如[2,2),左右区间相等是没有意义的,因此判断条件为left<right

其次是对于边界的确定:

当左闭右闭时,因为在if中已经对于mid下标所在值进行了比较了,因此后续这个下标并不需要再参与比较,而因为左和右都是闭的,是可以取到的,因此左边界修改要mid加1,右边界修改要mid-1

当左闭右开时,因为右边是取不到的,因此右边界修改要变成mid,左边界为mid+1

3.本质(个人理解)

在左闭右闭的情况下,将条件改为nums[mid]>=target时,是向左搜索趋势,记录最后一个<=target的数,先right=mid-1,再可以使用leftBoard来记录right(左趋势),这里注意如果nums里有target,那么leftBoard+1才是target的下标,如果没有,那么leftBoard是最后一个<target的数的下标

同理:条件为nums[mid]<=target时,是向右搜索趋势,记录第一个>=target的数,先left = mid +1,再使用rightBoard来记录left(右趋势),nums有target,那么rightBoard-1是target的下标,如果没有,rightBoard是第一个>target的数的下标

 27.移除元素

题目链接:. - 力扣(LeetCode)

文章讲解:代码随想录

视频讲解:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili

状态:平时都是直接用库函数,而并不知道原理,因此一写就废

需要了解的知识:数组在内存空间是连续的,因此对于移除某个元素,实际上是覆盖操作,在C++中二维数组内存地址也是连续的

解题思路:

1.暴力解法

代码实现:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
                int count = nums.size();
                for(int i=0;i<count;i++)
                {
                    if(nums[i] == val)    //当查询到目标值时,将目标值后面的所有值往前覆盖一个
                    {
                        for(int j= i+1; j<count;j++)
                        {
                            nums[j-1] = nums[j];
                        } 
                        i--;         //注意i一定要减一,再次判断这个下标
                        count--;    //数组长度-1
                    }
                }
                return count;
    }
};

需要注意的是:i--是必须的,否则遇到连续目标值的出现,会发生错误

时间复杂度:O(n^2)

空间复杂度:O(1)

2.双指针法

代码实现:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
                int slow = 0;    //定义一个慢指针
                for(int fast =0;fast<nums.size();fast++)   //外层循环是快指针
                {
                    if(nums[fast]!=val)   //当快指针不等于目标值时,填入慢指针的位置
                    {
                        nums[slow++] = nums[fast];
                    }
                }
                return slow;    //这里的slow是已经++过的,因此其在数值上等于数组的长度
    }
};

时间复杂度:O(n)

空间复杂度:O(1)

总结:

1.暴力解法

该思路中,实现较为简单,但需要注意的是这个i--,再删除完一个元素后,这个元素的下标值要重新判断以便,以防重置目标值的出现

2.双指针的思想

快慢两个指针,可以在一个for循环中,完成两个for循环所做的事

快指针:用于寻找新数组的元素,我的理解是:找符合条件的萝卜,去前方探路

慢指针:用于记录新数组的下标,我的理解是:这是一个一个坑,由快指针找到的萝卜来填入,后方更进。

收获就是觉得每天有事干了,很充分,这种知识进入大脑的感觉很喜欢┭┮﹏┭┮,后面会持续坚持的,ヾ(◍°∇°◍)ノ゙加油

  • 29
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值