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

一、数组理论基础

  • 数组是存放在连续内存空间上的相同类型数据的集合。

  • 数组下标都是从0开始的。

  • 数组内存空间的地址是连续的。

  • 数组的元素是不能删的,只能覆盖。

二、LeetCode704 二分查找

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

看到题目的第一眼,虽然知道二分法,但是不熟悉,第一眼想法就是暴力解法,用for循环将target跟数组中的每个值比较,显然效率是比较低的。

看完代码随想录之后,知道了二分法的原则之后,思路会更清晰,也不用死记硬背了

主要有一个疑问点,就是用mid = (left + right)/ 2时,为什么有时候会越界?且左闭右闭版本使用mid = left + ((right - left) / 2),左闭右开版本使用mid = left + ((right - left) >> 1)?

解答:(后续再研究)

题眼:

  • 有序数组
  • 无重复元素

见到这两点可以想二分法。

要点:

  • 区间的定义:不变量
  • 循环不变量法则:也就是说一旦确定了区间定义,在while循环中要坚持这个定义的不变量

要点解释:

如果一开始定义区间为[left,right],那么while循环的条件要使用 left <= right,因为在left == right时,在这个区间是有意义的;

如果一开始定义区间为[left,right),那么while循环的条件要使用 left < right,因为在left == right时,左边说该区间包含这个值,右边又说该区间是不包含这个值,故显然是没有意义的;

写二分法,区间定义一般为两种:左闭右闭、左闭右开

#include<iostream>
#include<vector>

using namespace std;
class Solution {
public:
    //暴力解法
    int search(vector<int>& nums, int target) {
        for (int i = 0; i < nums.size(); i++)
        {
            if (nums.at(i) == target)
                return i;
        }
        return -1;
    }
    //二分法,左闭右闭
    int search1(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;      
        while (left <= right)
        {
            int mid = (left + right) / 2;
            if (nums[mid] > target)
            {
                right = mid - 1;
            }
            else if (nums[mid] < target)
            {
                left = mid + 1;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
    //二分法,左闭右开
    int search2(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size();    //右开----[left,right)
        while (left < right)
        {
            int mid = left + ((right - left) >> 1);     //??
            if (nums[mid] > target)
            {
                right = mid;
            }
            else if (nums[mid] < target)
            {
                left = mid + 1;
            }
            else
            {
                return mid;
            }            
        }
        return -1;
    }
    //自己尝试,二分法,左开右闭-----还不知道是否正确
    int search3(vector<int>& nums, int target) {
        int left = -1;          //左开
        int right = nums.size() - 1;
        /*当left == right时
        左边说该区间包含这个值,右边又说该区间是不包含这个值
        显然是没有意义的,故不取等*/
        while (left < right)    
        {
            int mid = (left + right) / 2;
            if (nums[mid] > target) //target在左区间,更新right的值
            {
                right = mid - 1;    //右闭
            }
            else if (nums[mid] < target)
            {
                left = mid;         //左开
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
};

int main()
{
    Solution solution;

    vector<int> array = { -1,0,3,5,9,12 };

    int result = solution.search(array,12);
    int result1 = solution.search1(array, 12);
    int result2 = solution.search2(array, 12);
    int result3 = solution.search2(array, 12);


    cout << result << endl;
    cout << result1 << endl;
    cout << result2 << endl;
    cout << result3 << endl;

}

有关二分法,感觉理解还是不是很透彻,因为实在是时间过于紧迫,后续还会再研究。

三、LeetCode 27移除元素

暴力解法

刚开始暴力解法都没有思路,看了代码随想录的动图才有思路写完

写完之后去力扣运行发现超时,代码如图:

在这里插入图片描述

//正解
// 时间复杂度:O(n^2)
// 空间复杂度:O(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; j < nums.size() - 1; j++)
                {
                    nums[j] = nums[j + 1];
                }
                i--;        //所有元素都向前移动了一位,如果i不向前移动一位的话,这一轮结束后i++,会漏掉该元素
                size--;     //数组大小-1
            }
            
        }
        return size;
    }
};

双指针法

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

刚开始看完全没有思路,看了文字版的也不是很理解,看完视频才慢慢理解这种思想

核心要点就是要理解快指针慢指针用处、含义

  • 快指针:寻找新数组中的元素,新数组就是不含要移除的元素的数组,快指针会遍历整个数组
  • 慢指针:指向更新 新数组下标的位置,即指向新数组所需要元素
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];
                nums[slowIndex] = nums[fastIndex];		//此时快指针指向的元素是新数组需要的,于是赋值给慢指针所指的下标
                slowIndex++;	//随后慢指针指向新数组的下一个元素
            }
            //如果此时快指针指向的元素是要移除的元素,则不赋值给慢指针指向的元素,故不进入if判断里面的内容
        }
        //最后快指针遍历完整个数组,便将要移除的元素覆盖了,直接返回慢指针的值即可
        return slowIndex;
    }
}

用例:

-103591238239

移除元素3

自己可以对该例子,多思考几次,走几次流程。

四、额外知识—库函数erase

在这里插入图片描述

  • 底层原理即是双指针法

仅了解,后续学语法时再研究。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值