Studying-代码随想录算法训练营day1| 数组理论基础,704二分查找,27.移除元素

第一天💪💪💪,编码语言:c++

数据理论基础:

文档讲解:代码随想录数组理论基础

 一句话总结:数组是存放在连续空间上的相同类型数据的集合。

特点:· 由于在内存上是连续存放的,地址存在连续性,因此可以通过下标直接读取每个元素。

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

注意:

  1. 数组在进行中间元素插入和删除操作的时候,需要注意移动后面所有的元素。 且插入元素实际上是通过把后面元素后移,再将插入元素覆盖插入下标原有元素,如果数组大小不够,则无法进行插入删除元素是将后面的元素前移覆盖,数组大小不变,因此最后一个元素实际上和前一个元素相同,如果是自己设计的类,可以通过一个int类型数据size,记录当前数组内有效元素个数。
  2. c++中的vector类底层实现是数组,但严格讲vector是个容器,它的一系列实现是通过数组来实现的。因此vector的尾部元素插入(push_back())、删除(pop_back())的时间复杂度为O(1),但可以进行中间元素的插入(insert())、删除(erase())的时间复杂度为O(n).

  

二维数组:增加了一个维度的一维数组,包含两个索引下标。

注意:在c++中二位数组在内存的空间地址上也是连续的。地址从行开始连续,即a[0][0]->a[0][1].

704.二分查找:

文档讲解:代码随想录二分查找

视频讲解:手撕二分查找

题目:注意无重复元素,否则二分法得到的答案可能不唯一

初看:由于是有序序列,可以通过从第一个元素逐个往后比较查找的方式,找到目标元素。也可以通过从中间元素往左右查找的方式,找到目标元素。同时需要处理目标元素不存在的情况。

代码:

//时间复杂度O(logn)
//空间复杂度O(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)/2; //与直接(left+right)/2相比可防止数值过大越界;
            if (nums[mid] == target) return mid;
            else if (nums[mid] < target) left = mid + 1; //中间值小于目标值,目标值可能区间在中间值右边且不包含中间值;
            else right = mid - 1;//中间值大于目标值,目标值可能区间在中间值左边且不包含中间值;
        }
        return -1; //当right < left时以为这此时目标值不存在,它可能在当前nums[right]和nums[left]之间
    }
};

困难:最初写的时候对于while内循环是写"<"还是写"<=“存在疑问,看完卡哥讲解视频后,理解了不同符号的区间含义。

收获:

  1. 二分法的比较区间一般有两种分别是左闭右闭[left,right],左闭右开[left,right)。(左不能开,因为左边开的话初始值为-1,可能会存在mid为负数下标的情况,例如left=-1,right=0)
  2. 确定哪个比较符号符合哪个区间就看最后一次比较。[left,right]区间中,在left=right的情况下,区间仍然成立,因此此时应该再次进入循环,直到right<left才退出循环,因此此时while的比较判断应该是(left <= right);[left,right)区间中,在left = right的情况下,区间已经不成立,此时就需要退出循环,所以while的比较判断应该是(left < right),且由于right不取,因此在第三个判断中应该right = mid。
  3. 判断循环的条件,其实就是看不变量,在这道题目中区间的定义就是不变量,循环中坚持根据区间的定义来做边界的处理。

 27.移除元素:

文档讲解:代码随想录移除元素

视频讲解:手撕移除元素

 题目:

初看:两种想法1.遍历数组,找到val值则让后面的元素都往前覆盖(即删除该元素),时间复杂度为O(n^2);2.快慢指针,慢指针保存正确数组元素,快指针遍历数组,当快指针指向val值时跳过,不为val值时则赋给慢指针。

代码:

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            //当nums[fastIndex]不等于val时进行赋值
            if (val != nums[fastIndex]) nums[slowIndex++] = nums[fastIndex];
        }
        return slowIndex;
    }
};

另一种方式:左右指针,减少多余赋值操作,时间复杂度,空间复杂度不变

时间复杂度O(n)
空间复杂度O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int n = nums.size();
        int left = 0;
        int right = n - 1;
        while (left <= right) {
            //找到从左边开始第一个等于val的值
            while (left <= right && nums[left] != val) {
                left++;
            }
            //找到右边开始一个不等于val的值
            while (left <= right && nums[right] == val) {
                right--;
            }
            //把左边的val值删掉,覆盖为右边的不等于val的值
            if (left < right) {
                nums[left++] = nums[right--];
            }
        }
        return left;
    }
};

 收获:

  1. 学习到了快慢指针的使用方式,重点在于快指针是寻找新数组需要的元素,在此题目中,要寻找的元素就是不等于val的元素;慢指针是更新新数组的下标,使其成为新的数组。
  2. 再次了解,数组的删除不是直接把元素删掉,而是元素的覆盖,数组的大小本身是不变的,此题目中,用慢指针来指代新数组的大小。

总结

第一天总结:今天的题目是此前刷过的,入门的时候还是很难的,现在看来依旧有所收获,再接再厉💪💪 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值