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

今日任务 :数组理论基础,704. 二分查找,27. 移除元素

碎碎念

在正式打卡之前,想简单介绍一下自己的情况,同时也是记录自己此刻的一种状态吧。本人基础很差,本科毕业于民办三本,二战考研也没有上岸心仪的学校,调剂到了一所双非院校。科研三年做的是医工交叉的方向,和找工作毫无关联。整体的状态属于除了学习的一切,我都感觉我做的还不错,唯独学习,我感觉自己差劲到极点。

一路走来,都属于摆烂状态,可能只有考研期间真正为了自己的努力拼搏过。现在我已经研三,秋招也结束了。整个秋招之路,收获了一家上市公司的嵌软岗位,一家驱动工程师和数不清的高校计算机专任教师offer,但是我都没有签。没有签约的原因并不是说自己觉得自己了不起想要更好的,而是处于种种原因感觉都不适合自己,我也很焦虑。秋招之前抱着雄心壮志说要好好刷力扣,好好搞项目,幻想着找一份不错的C++开发,但是事实总是很残酷,时间过分飞快,我自己也越来越摆烂,别人劝我考公务员,说我适合去高校,说我不适合写代码,说女生去了私企面临结婚生子,也是分分钟会被辞退。一年以前,我会反驳,我会不服气,想要证明自己,但是科研压力、个人惰性、导师的高标准要求,让我只想抽空摆烂,毫无斗志可言,2023年11月找工作两个多月过去,当别人再说我不适合写代码,我会说对,确实,我承认。12月当我真正找到教师工作的时候,我发现我还是不甘心,并不是说教师不好,我也羡慕一年三个月的假期,但是可能我太心气傲了吧,我还是想春招在拼一下。

一直磨磨唧唧没有勇气开始我的春招准备,可能现在也确实晚了,但是我还是想试一下。想证明自己也可以,也想给那些和我一样的,出身不好但也不愿安于现状的朋友们一些鼓励吧。希望我两个月时间努力刷题,看八股,巩固项目,可以有好的收获!不求进什么大厂,只希望能找到c++开发的中厂的工作!!希望抓住校招这宝贵的机会!

704 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

前言

● 这道题也是我准备秋招刷的第一道题(当时只刷了55道题,没有坚持下去,现在后悔万分)。
● 本题可以暴力直接遍历数组直接查找target,这样的话时间复杂度为O(n)
● 采用二分查找可以提高效率,时间复杂度O(log n),但是要有前提条件,

  1. 有序数组;
  2. 数组中最好无重复元素,如果有重复元素要思考怎么解决;

代码

● 这道题我解题花费4分钟,直接出于固有印象以及熟悉写了一下代码,虽然也运行通过了,但看了卡哥的讲解,发现自己没有考虑周全。

  1. 我直接采用了左右指针左闭右闭,没有考虑边界问题。也可以尝试左闭右开;
  2. 在判断中间指针时,没有考虑溢出的可能,应该写为int mid = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2 更好;
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;    
        while(left<=right){
        	int mid = (left+right)/2;
            if(nums[mid]<target){
                left = mid+1;
            }
            else if(nums[mid]>target){
                right = mid-1;
            }
            else{
                return mid;
            }
        }
        return -1;
    }
};

左闭右开的写法:

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

相关题目练习1:35. 搜索插入位置

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

相关题目练习2:34.在排序数组中查找元素的第一个和最后一个位置(仍需二刷)

这道题要在排序数组中查找,所以第一时间想到了二分查找,但是由于target不止一个,所以之前的传统二分查找不在适用。对于返回下标不为1个的情况下,具体怎么做呢
本题由于时有序数组,所以重复元素一定是挨在一起的,本题就是查找 target 在 nums 中的左右边界。

  • 若target在数组中,查找左右边界(关键);
  • 若target在数组包含范围内,但不在数组中,返回[-1,-1];
  • 若targer<nums[0]或target>nums[n-1],返回[-1,-1]

之前做过但是忘记了,对于边界的查找,我自己没有想出来,太菜了!!看卡哥的讲解

// - 若target在数组包含范围内,但不在数组中,返回[-1,-1];
// - 若target在数组中,查找左右边界(**关键:不要找target,转而找target的两边**); 
// - 若targer<nums[0]或target>nums[n-1],返回[-1,-1]
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int leftBorder = getleftBorder(nums, target);
        int rightBorder = getrightBorder(nums, target);
        if(leftBorder == -2 || rightBorder == -2)  return {-1,-1};
        if (rightBorder-leftBorder >1)
            return {leftBorder+1,rightBorder-1};      
        return {-1,-1};
    }
private:
    int getrightBorder(vector<int>& nums, int target){
        int left = 0;
        int right = nums.size()-1;
        int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
         while (left <= right) {
            int mid = left + ((right - left) / 2);
            if (nums[mid] > target) {
                right = mid - 1;
            } else { // 寻找右边界,nums[middle] == target的时候更新left
                left = mid + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }

        int getleftBorder(vector<int>& nums, int target){
        int left = 0;
        int right = nums.size()-1;
        int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
         while (left <= right) {
            int mid = left + ((right - left) / 2);
            if (nums[mid] >= target) {
                right = mid - 1;
                leftBorder = right;
            } else { 
                left = mid + 1;           
            }
        }
        return leftBorder;
    }
};

对于左右边界的选取,说直白一些就是不要找target,而是找target的两边。在普通二分查找中,当 nums[mid] == target 时,直接返回 mid,而在本题中,则是要继续向左或者向右查找,看是否还有和 target 相等的数组元素。所以在边界赋值的条件里总是包含 nums[mid]==target

27. 移除元素

解法一:暴力求解

对于本题,第一反应就是挨个遍历,找到重复的就将数组从后往前覆盖,然后继续向后遍历,直到遍历结束。注意在遍历过程中,覆盖完以后要将size-- && i--

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

解法二:双指针,快慢指针

本题详细讲解可以看卡哥讲解

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        //快慢指针
        int fast=0,slow=0;
        while(fast<nums.size())
        {
            if(nums[fast]!=val)
            {
                nums[slow]=nums[fast];
                fast++;
                slow++;
            }
            else
            {
                fast++;
            }
        }
        return slow;
    }
};

拓展题随后有机会再联系。今日力扣学习三个小时,明天继续加油!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值