Day01 |数组part01: 704. 二分查找、27. 移除元素

Day01 |数组part01: 704. 二分查找、27. 移除元素

姑且算是二刷吧?之前跟着卡哥的视频刷了大概50题,后来因为准备期末考就放下了,趁暑假报了班鞭策一下自己,为了跟进度还从第一题刷起。

704 二分查找

题目链接:https://leetcode.cn/problems/binary-search/

视频链接:【手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找】https://www.bilibili.com/video/BV1fA4y1o715?vd_source=82cd289daf33ddd26b7489e3bbe8cd72

二分查找是我本科学数据结构的时候就学过的,所以看视频的时候思路很好理解,主要注意的有两点:

  • 只有有序的数组才能使用二分查找;
  • 注意数组边界的设置。

最后两遍才AC,第一遍是因为编译错误,写成了python的语法。。。第二遍编译通过。我的代码如下:

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 + right; //防止溢出
            if(nums[mid] < target){
                left = mid + 1;
            }else if (nums[mid] > target){//不是elif!!!
                right = mid - 1;
            }else{
                return mid;
            }
        }
        return -1;
    }
};

这里注意使用int mid = (left - right) / 2 + right

防止两个大数相加溢出,这里的数比较小当然不会出现。

然后是题解:

// 版本一
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

我写的是解法一,也符合我学数学时的思维。

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

题目链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

这是一个拓展题目,首先拿到就有思路,设置左右指针分别从两边向中间逼近, 其中每个指针的值等于目标值后停止移动,如果左指针跑到右指针后面还没找到, 说明没有返回-1。

但这道题提交了好几遍才AC,以上是我的AC代码:

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

        if(left > right){//没找到
            left = -1;
            right = -1;
        }
        res[0] = left;
        res[1] = right;
        return res;
    }
};

出的问题:

  • nums.size()分别为0和1时,程序会崩溃, 我这里是作为特殊情况处理了,但我认为是我边界定的不对,即使AC了可能边界定的好,可以使代码更简洁;
  • 对于测试时vector数组的赋初值问题,可采用下面的方法,从数组赋值:
    int arr[] = {5,7,7,8,8,10};
    vector<int> nums(arr,arr+6);

查看了题解过后,发现大家还是用二分解法比较多,首先先找到一个target,然后从target左右循环找边界, 最惊艳我的还是这个两次二分的算法,分别找左边界和右边界:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left=0, right=nums.size()-1, middle, ans_left=-1, ans_right=-1;
        // 查找左边界
        while(left<=right){
            middle = left + (right - left) / 2;
            if(nums[middle]==target){
                ans_left = middle;
                right = middle - 1;//因为是左边界,所以要看看左边是不是还有目标值存在
            }
            else if(nums[middle]<target)
                left = middle + 1;
            else
                right = middle - 1;
        }
        // 查找右边界
        left=0;
        right=nums.size()-1;
        while(left<=right){
            middle = left + (right - left) / 2;
            if(nums[middle]==target){
                ans_right = middle;
                left = middle + 1;//因为是右边界,所以要看看右边是不是还有目标值存在
            }
            else if(nums[middle]<target)
                left = middle + 1;
            else
                right = middle - 1;
        }
        return {ans_left, ans_right};
    }
};

这里学到了数值对的赋值方法,不要再傻傻的赋值了,使用{a,b}进行返回。

27. 移除元素

题目链接:https://leetcode.cn/problems/remove-element/

视频链接:【数组中移除元素并不容易! | LeetCode:27. 移除元素】https://www.bilibili.com/video/BV12A4y1Z7LP?vd_source=82cd289daf33ddd26b7489e3bbe8cd72

这题也是二刷过了一下就有思路了,设置快慢指针,快指针遍历数组,慢指针存放数据,最后修改数组的长度:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        if(nums.size() == 0){
            return 0;
        }
        int fast = 0,slow = 0;
        while(fast <= nums.size() - 1){
            if(nums[fast] != val){
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
};
  • code过程中产生错误,原因一般是下标溢出,需要对特殊情况进行讨论
运行失败:
	Line 1034: Char 9: runtime error: reference binding to null pointer of type 'int' (stl_vector.h)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_vector.h:1043:9

  • 以后一定要优先考虑数组大小为0或1的情况。

总结

今天的内容还算简单,基本二刷看到就有思路了,最重要的是把CLion的环境搭好了,之后可以愉快的用IDE进行debug了。可以看出二刷还是有效果的,明天也要加油⛽️。

你可能感兴趣的:(c++)