首页 > 编程语言 >代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素、977.有序数组的平方

代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素、977.有序数组的平方

时间:2025-01-22 22:58:28浏览次数:1  
标签:977 right nums int 随想录 middle 移除 left size

LeetCode704

2025-01-22 18:30:38 星期三

代码随想录视频内容简记

梳理一下三个比较重要的部分

  1. 首先是对于整个代码的循环条件,这个很重要

  2. 判断middle位置在我看来初学也是比较重要一步

    注意:所有的middle位置判断都是if语句实现的,固定的大于和小于。这个不用纠结一不一样

  3. 更新left和right位置

左闭右闭

大致代码内容

  1. while(left <= right)

  2. if(nums[middle] > target)

  3. 则判断middle位置,位于左区间的右边界,则更新right

    如果是if(nums[middle] < target),则更新右区间的左边界,也就是更新left。因为nums[middle]始终是在区间正中的

  4. right = middle - 1,关于此处做了着重的讲解。因为第二步中的判断,nums[middle]已经大于了target值,所以此时如果赋right = middle会错误引入一个边界之外的值,引入到哪里?就是引入到下一次while循环,这样就出问题了,所以只能是right = middle - 1

左闭右开

  1. 首先是需要注意这个的right = number赋值的时候有些区别,和上面的左闭右闭不一样,后者是right = number - 1

  2. 其次时需要注意在循环体分别更新left和right的方式不一样

大致代码内容

  1. while(left < right)

    这里因为右边界取不到,所以是<

  2. 依然是条件if(nums[middle] > target)

    此时此时更新左区间的右边界,也就是rightright = middle,因为下一次循环不包含右边界,所以可以等于middle,加上等于号。

    如果是if(nums[middle] < target),则更新右区间的左边界,也就是更新left。这个需要区分开,这个是left = middle + 1,因为下一次循环包含左边界,所以既然nums[middle]不一样,就不能把他再引入下一次循环,所以是middle + 1

LeetCode测试

写出代码均是一遍通过,好用得很,没有什么不明白的地方。

左闭右闭

点击查看代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while(left <= right){
            int middle = (left + right)/2;
            if(nums[middle] < target){
                left = middle + 1 ;
            }
            else if(nums[middle] > target){
                right = middle - 1;
            }
            else{
                return middle;
            }
        }
        return -1;
    }
};

左闭右开

点击查看代码
class Solution {
    public:
        int search (vector<int>& nums, int target) {
            int left = 0, right = nums.size();
            while (left < right) {
                int middle = (left + right) / 2;
                if (nums[middle] < target) {
                    left = middle + 1;
                }
                else if (nums[middle] > target) {
                    right = middle;
                }
                else return middle;
            }
            return -1;
        }
};

LeetCode27

代码随想录视频内容简记

首先是数组在内存中的实际大小是5,当我们删除了一个元素3之后,虽然返回的数组大小应该是4,但是其实3只是被覆盖了,其实际的数组大小仍然是5没有变

暴力解法

个人感觉暴力解法还是有一些坑在的,因为卡哥的视频里没有详细讲解,

下面先梳理一下

  1. 首先是两层循环嵌套用来解决数组的移动问题

  2. for循环的条件判断,这个很重要

  3. 针对于特殊的测试用例[0,1,2,2,...]的i--

大致代码内容

  1. 因为每当找到一次val值之后,从他开始整体向前移动,所以循环的条件判断不能再仅仅写i < nums.size(),而需要在一开始进行赋值一个size = nums.size(),然后对size在每一次找到之后进行size--操作

  2. 因为上面说的这个[0,1,2,2]这个特殊用例,我们可以设想,如果是两层for循环,外层循环在找到2之后进入循环内部,此时的第二个2会往前移动一位。此时的数组比如是[0,1,2,3]。但是内层循环结束之后,外层循环又会进行i++操作,导致i指针会跳过第二个2,直接判断下一位3。这样出现的问题就是还有一个存在。

所以每次内层循环结束之后还要进行的操作就是i--,让i往前移动一位重新进行判断

双指针法

梳理

  1. 核心是一个for循环,for (int fast = 0; fast < nums.size(); fast++)是一个时间复杂度为\(O(n)\)的算法。

  2. 首先是一个快指针fast,还有一个慢指针slow

  3. 快指针用来获取新数组的元素,而慢指针用来更新新数组的位置

大致代码内容

  1. if (nums[fast] != val)这里就是快指针要获取新数组元素的特性,所以写为nums[fast]

  2. 另外需要注意,是!=才需要更新slow指针

LeetCode测试

暴力解法

点击查看代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for (int i = 0; i < size; i++){
            if (val == nums[i]){
                for (int j = i + 1; j < size; j++){
                    nums[j - 1] = nums[j];
                }
                i--;
                size--;
            }
        }
        return size;
    }
};

这个时间复杂度是\(O(n^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];
                slow++;
            }
        }
        return slow;
    }
};

LeetCode977

代码随想录视频内容简记

题目中给出一个非递减顺序的数组,要求其每一位数平方之后按从小到大排列,并返回该数组

梳理

  1. 左右向中间依次递减

[-5,1,2,3]这样的一个数组,其平方之后得到[25,1,4,9],注意观察后发现是左右大,中间小

  1. 从最大获取数组元素

对两边同时进行比较,得到最大的元素。所以,双指针思想的应用在此,有一点特殊,需要额外引入一个指针j来和i同时进行比较出最大的。由此,我们才是完成了双指针中中获取新数组元素的一步

  1. 从最后更新数组位置

得到想要的新数组元素之后,需要对其进行更新。按照题目要求,需要从最后更新,来保持从小到大的排列

大致代码内容

  1. k = nums.size() - 1这个待会用来更新数组位置用

  2. for (int i = 0, int j = nums.size() - 1; i <= j;)注意这个for循环有点特殊,首先是没有操作语句的,而且在初始化语句中有两个分别是ij的初始化,还有就是这个条件判断是ij作比较,感觉我没见过这种的

    这里的条件判断就是为了让循环向中间合拢,所以这么写

  3. 下面的if判断,应该是分为三种,首先是nums[i] * nums[i] < nums[j] * nums[j],则我们更新nums[k] = nums[j],这里就用上前面的k了。然后剩下分别是nums[i] * nums[i] > nums[j] * nums[j]nums[i] * nums[i] = nums[j] * nums[j]。但是这里也有一点特殊,就是当nums[i] * nums[i] = nums[j] * nums[j]时,随便怎么写成nums[k] = nums[j]或者nums[k] = nums[i]都ok的

LeetCode测试

库函数解法

需要用到一个库函数sort()

点击查看代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for (int i = 0; i < nums.size(); i++){
            nums[i] = nums[i] * nums[i];
        }
        sort(nums.begin(), nums.end());
        return nums;
    }
};

双指针法

这个有坑,一测试的时候发现了

不能像27题一样,想当然用nums[k] = nums[j] * nums[j]这样的操作。因为这个题不是顺序遍历的,而且涉及到3个索引的值,分别是i,j,k,所以会有互相覆盖的情况产生。

举例:

  1. 刚开始是[-4,-1,0,3,10],-4和10比较之后应该赋k为100,第一遍循环得到[-4,-1,0,3,100],正常。

  2. 第二遍循环,-4和3比较之后赋k为16,此时得到的是[-4,-1,0,16,100]会把3覆盖掉,影响到了正常下一次的-1和3的比较,所以要新开辟一个vector result来单独存放k更新的结果。

修改之前的代码

点击查看代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int k = nums.size() - 1;
        for (int i = 0, j = nums.size() - 1; i <= j;){
            if (nums[i] * nums[i] < nums[j] * nums[j]){
                nums[k] = nums[j] * nums[j];
                j--;
                k--;
            }
            else{
                nums[k] = nums[i] * nums[i];
                i++;
                k--;
            }
        }
        return nums;
    }
};

修改后的代码

点击查看代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int k = nums.size() - 1;
        vector<int> result(nums.size(), 0);
        for (int i = 0, j = nums.size() - 1; i <= j;){
            if (nums[i] * nums[i] < nums[j] * nums[j]){
                result[k] = nums[j] * nums[j];
                j--;
                k--;
            }
            else{
                result[k] = nums[i] * nums[i];
                i++;
                k--;
            }
        }
        return result;
    }
};

标签:977,right,nums,int,随想录,middle,移除,left,size
From: https://www.cnblogs.com/bnbncch/p/18684626

相关文章

  • 代码随想录:复原IP地址
    这道题倒是不难,但是字符串的一些操作很麻烦。字符串的erase操作,如果单个参数传入的是索引,就会删除对应位置直到结尾的所有字符;如果单个参数传入的是迭代器,就会删除那个对应位置的单个字符。classSolution{public://切割次数,只能切三次inttime=0;stringtarget;......
  • 代码随想录:分割回文窜
    本所谓切割,就是找切割位置,就是组合classSolution{public:vector<string>target;vector<vector<string>>res;vector<vector<string>>partition(strings){rb(s,0);returnres;}voidrb(strings,intst......
  • 代码随想录:组合总和二
    这题说实话有点晕晕乎乎的,最后直接把代码随想录的代码复制过来了。要解决的问题是,尽管用了不同位置的相同元素,但是会产生相同的结果。解决方法是排序后,跳过相同元素。代码随想录那个used数组我属实没看懂,这个方法倒是看懂了。classSolution{private:vector<vector<int......
  • 代码随想录:组合总和
    回溯的本质就是多层for循环嵌套,用于解决不知道有多少层for循环的情况,适当剪枝其实也是for循环里增加限制条件classSolution{public:vector<int>sum;vector<vector<int>>res;vector<vector<int>>combinationSum(vector<int>&candidates,inttarget){......
  • 代码随想录——动态规划31打家劫舍III(树状DP)
    这道题目是打家劫舍III(HouseRobberIII),是打家劫舍系列问题的变种。问题描述如下:小偷发现了一个新的区域,这个区域的所有房屋排列类似于一棵二叉树。如果两个直接相连的房屋在同一晚被打劫,房屋会自动报警。给定这棵二叉树的根节点root,求在不触发警报的情况下,小偷能够盗取的最......
  • 代码随想录:二叉搜索时的插入
    /***Definitionforabinarytreenode.*structTreeNode{*intval;*TreeNode*left;*TreeNode*right;*TreeNode():val(0),left(nullptr),right(nullptr){}*TreeNode(intx):val(x),left(nullptr),right(nullptr){}*......
  • 代码随想录:将有序数组转化为二叉搜索树
    /***Definitionforabinarytreenode.*structTreeNode{*intval;*TreeNode*left;*TreeNode*right;*TreeNode():val(0),left(nullptr),right(nullptr){}*TreeNode(intx):val(x),left(nullptr),right(nullptr){}*......
  • 代码随想录:修剪二叉搜索树
    /***Definitionforabinarytreenode.*structTreeNode{*intval;*TreeNode*left;*TreeNode*right;*TreeNode():val(0),left(nullptr),right(nullptr){}*TreeNode(intx):val(x),left(nullptr),right(nullptr){}*......
  • 代码随想录:删除二叉搜索树中的节点
    由于涉及到树的结构变化,用递归写比较简单,竟然一次跑通了/***Definitionforabinarytreenode.*structTreeNode{*intval;*TreeNode*left;*TreeNode*right;*TreeNode():val(0),left(nullptr),right(nullptr){}*TreeNode(int......
  • 代码随想录:把二叉搜索树转化为累加树
    相当于将数组从右到左遍历,下一个数加上一个数,二叉搜索树中序遍历(左中右)为顺序,右中左则为倒叙/***Definitionforabinarytreenode.*structTreeNode{*intval;*TreeNode*left;*TreeNode*right;*TreeNode():val(0),left(nullptr),rig......