首页 > 编程语言 >代码随想录算法训练营第一天

代码随想录算法训练营第一天

时间:2023-09-06 21:34:06浏览次数:51  
标签:right target nums int 训练营 随想录 middle 算法 left

代码随想录算法训练营第一天 | LeetCode 704(二分查找) LeetCode 35(搜索插入位置) LeetCode 34(在排序数组中查找元素的第一个和最后一个位置 ) LeetCode 27(移除元素)

数组理论基础

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

  • 数组下标都是从0开始的
  • 数组内存空间的地址是连续的 所以在增删操作的时候,就要去移动其他元素的地址
  • 数组元素是不能删除的,只能覆盖
  • 二维数组在内存的空间地址可能是连续的也可能是不连续的,这取决于编程语言的内存管理;C++中是连续的,Java中是不连续的

704:二分查找

LeetCode 704(二分查找)
思路:

数组有序 查找唯一值下标 时间复杂度O(log n)
类似双指针 设置left&right两个指针开始指向数组开头和结束位置,因为数组有序,且元素唯一,那么我们就可以取left&right的中间值所对应的数组元素和target值比较,若大于target,则更新right;若小于target,则更新left。
那么问题来了!
1.是 while(left < right) 还是 while(left <= right)
2.是 right = middle呢,还是要right = middle - 1
解决方法: 边界条件更新时遵循循环不变量原则
解题代码:

方法一:左闭右开 [left,right)

class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        int middle;
        //左闭右开 [left,right) left == right时无意义
        while(left < right){
            middle = left + ((right - left) >> 1);
            if(nums[middle] == target){
                return middle;
            }else if(nums[middle] > target){
                //此时 target值 在[left,middle)中 所以将right赋值为middle
                right = middle;
            }else {
                //此时 target值 在[middle + 1,right)中 所以将left赋值为middle + 1
                left = middle + 1;
            }
        }
        return -1;
    }
}

方法二: 左闭右闭

class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int middle;
        //左闭右闭 [left,right] left == right时仍有意义
        while(left <= right){
            middle = left + ((right - left) >> 1);
            if(nums[middle] == target){
                return middle;
            }else if(nums[middle] > target){
                //此时 target值 在[left,middle-1]中 所以将right赋值为middle - 1 
                right = middle -1;
            }else {
                //此时 target值 在[middle + 1,right]中 所以将left赋值为middle +1
                left = middle + 1;
            }
        }
        return -1;
    }
}

总结

关注nums[middle]在下一个区间会不会和target值比较

35:搜索插入位置

LeetCode 35(搜索插入位置)
思路:

这道题相当于在数组中插入目标值,分下述四种情况
1.目标值在数组所有元素之前
2.目标值等于数组中某个元素
3.目标值在数组某两个元素之间
4.目标值在数组元素之后

暴力法

class Solution {
    public int searchInsert(int[] nums, int target) {
        for(int i = 0; i < nums.length; ++i){
            if(nums[i] >= target){
                return i;
            }
        }
        return nums.length;
    }
}

二分法 左闭右开

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        int middle = 0;
        while(left < right) {
            middle = left + ((right - left) >> 1);
            if(target < nums[middle]) {
                right = middle;
            }else if(target > nums[middle]) {
                left = middle + 1;
            }else{
                return middle;
            }
        }
        // 目标值在数组所有元素之前
        // 目标值插入数组中的位置 [left, right) ,return right 即可
        // 目标值在数组所有元素之后的情况 [left, right),因为是右开区间,所以 return right
        return right; 
    }
}

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

LeetCode 34(在排序数组中查找元素的第一个和最后一个位置)
思路:

方法一:
先用二分找到任意一个目标值 找不到则返回[-1,-1]
找到了 则以该目标值为起点向前后探查边界

方法二:
使用两次二分法 查找左右边界

方法一:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        int middle;
        int begin = -1;
        int end = -1;
        while(left < right){
            middle = left + ((right - left) >> 1);
            if(nums[middle] > target){
                right = middle;
            }else if(nums[middle] < target){
                left = middle + 1;
            }else {
                //找到target值 向两侧遍历查找目标值
                begin = middle;
                end = middle;
                while(begin - 1 >= 0 &&nums[begin-1] == target){
                    begin--;
                }
                while(end + 1 < nums.length && nums[end + 1 ] == target){
                    end++;
                }
                return new int[]{begin,end};
            }
        }
        return new int[]{begin,end};
    }
}

方法二

class Solution {
    int[] searchRange(int[] nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // 情况一 target 在数组范围的右边或者左边
        if (leftBorder == -2 || rightBorder == -2) return new int[]{-1, -1};
        // 情况三 target 在数组范围中,且数组中存在target
        if (rightBorder - leftBorder > 1) return new int[]{leftBorder + 1, rightBorder - 1};
        // 情况二 target 在数组范围中,且数组中不存在target
        return new int[]{-1, -1};
    }

    int getRightBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        // 记录一下rightBorder没有被赋值的情况
        int rightBorder = -2;
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            } else {
                // 寻找右边界,nums[middle] == target的时候更新left
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }

    int getLeftBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        // 记录一下leftBorder没有被赋值的情况
        int leftBorder = -2; 
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] >= target) {
                // 寻找左边界,nums[middle] == target的时候更新right
                right = middle - 1;
                leftBorder = right;
            } else {
                left = middle + 1;
            }
        }
        return leftBorder;
    }
}

27:移除元素

LeetCode 27(移除元素)
要点:数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
思路:

方法一:直接暴力 双重循环 一个for遍历数组 一个for更新数组
方法二:

快慢指针
相向指针

方法一

class Solution {
    public int removeElement(int[] nums, int val) {
        int size = nums.length;
        for (int i = 0; i < size; i++) {
            // 发现需要移除的元素,就将数组集体向前移动一位
            if (nums[i] == val) { 
                for (int j = i + 1; j < size; j++) {
                    nums[j - 1] = nums[j];
                }
                //因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                i--;
                //此时数组的大小-1
                size--; 
            }
        }
        return size;
    }
};

方法二

//快慢双指针
class Solution {
    public int removeElement(int[] nums, int val) {
        //定义慢指针 指向更新 新数组下标的位置
        int slow = 0;
        //快指针 寻找新数组的元素 ,新数组就是不含有目标元素的数组
        for (int fast = 0; fast < nums.length; fast++){
            if(nums[fast] != val){
                nums[slow++] = nums[fast];
            }
        }
        return slow;
    }
}
//相向双指针
class Solution {
    public int removeElement(int[] nums, int val) {
        //定义双向指针
        int left = 0;
        int right = nums.length - 1;
        //找到右侧第一个不为val值的索引
        while(right >= 0 && nums[right] == val){
            right--;
        }
        while(left <= right){
            //判断nums[left]是否为val
            if(nums[left] == val){
                nums[left] = nums[right];
                right--;
            }
            left++;
            //再去寻找右侧第一个不为val值的索引
            while(right >= 0 && nums[right] == val){
                right--;
            }
        }
        return left;
    }
}

标签:right,target,nums,int,训练营,随想录,middle,算法,left
From: https://www.cnblogs.com/Q316/p/17683411.html

相关文章

  • 分治算法学习
    思路分析:先找根(最大值)分为左右子树,转化为构建最大的左右子树,很明显,这里需要用到递归算法实现#include<bits/stdc++.h>usingnamespacestd;intnums[1001];voidconstructMaxTree(intarr[],intl,intr){ if(l>=r){ cout<<arr[l]<<""; return; } //找到最......
  • C++ 算法竞赛、02 周赛篇 | AcWing 第2场周赛
    AcWing第2场周赛竞赛-AcWing3626三元一次方程AcWing3626.三元一次方程-AcWing两层循环#include<iostream>usingnamespacestd;voidfind(intn){for(intx=0;x<=1000/3;x++){for(inty=0;y<=1000/5;y++){int......
  • 算法刷题:一步步优化系列01.最长连续序列
    题目链接:最长连续序列目录暴力解法(超时)优化内层查找(On->O1但超时)问题:重复的边界会重新迭代优化重复迭代:在值域而非定义域迭代,去重(超时)问题:值域大且元素离散度大时,会大量迭代到不存在的元素,空迭代优化空迭代:HashSet去重,每次迭代的元素都存在(26ms)从左边界重......
  • 安防监控/视频汇聚/云存储/AI视频智能算法引擎:遛狗AI检测算法详解
    根据最新修订发布的《中华人民共和国动物防疫法》规定:遛狗不栓绳,养狗不办证、未定期接种疫苗等行为都是违法行为。作为一个合格的“铲屎官"出门遛狗一定要牵好狗绳,保护他人和爱犬的安全。但就算法律明文规定,还是有很多人无视法律法规,在外遛狗不牵绳,任其自由活动。在日常管理中,遛狗......
  • 方案:TSINGSEE青犀视频AI智能算法平台电动车入梯检测解决方案
    一、方案背景随着大众的出行要求逐渐提升,交通拥堵现象也随处可见,电动车出行,就成了大家的首选。随着电动车数量的激增,众多用户为了个人方便,大多在室内停放或充电,有的甚至停放在走道、楼梯间等公共区域,由于电瓶车车体大部分为易燃可燃材料,一旦起火,燃烧速度快,并产生大量有毒烟气,人员逃......
  • 视频云存储/安防监控/AI分析/视频AI智能分析网关:垃圾满溢算法
    随着我国科技的发展和城市化进程加快,大家对于生活环境以及空气质量更加重视,要求越来越严格。城市街道垃圾以及生活区垃圾满溢已经成为城市之痛。乱扔垃圾,垃圾不入桶这些行为已经严重影响到了城市的美化问题。特别是炎热的夏日和雨水季节,大量垃圾堆放会释放有毒有害气体,暴雨过后,漂浮......
  • 安防监控/视频汇聚/云存储/AI视频智能算法引擎系统:遛狗检测算法详解
    根据最新修订发布的《中华人民共和国动物防疫法》规定:遛狗不栓绳,养狗不办证、未定期接种疫苗等行为都是违法行为。作为一个合格的“铲屎官"出门遛狗一定要牵好狗绳,保护他人和爱犬的安全。但就算法律明文规定,还是有很多人无视法律法规,在外遛狗不牵绳,任其自由活动。在日常管理中,......
  • AIRIOT大学计划暑期训练营圆满结束,产教融合培养物联网产业人才
    ​ 为促进物联网产业的纵深发展和创新,推进教育链、产业链与创新链的有机衔接,提高学生理论、实践和创新能力,7月3日-7月28日,由航天科技控股集团股份有限公司(简称“航天科技”)开展AIRIOT大学计划第三期暑假训练营圆满收官。来自北京工业大学、浙江工业大学、安徽建筑大学、澳门城......
  • Bresenham算法画椭圆
    目录椭圆特性Bresenham算法画椭圆区域1区域2算法步骤算法程序椭圆特性椭圆定义椭圆:平面内到定点F1、F2的距离之和等于常数2a(2a>|F1F2|)的动点P的轨迹。椭圆数学表达式:\[\tag{1}|PF1|+|PF2|=2a\]F1、F2称为椭圆的2个焦点,两焦点之间距离2c(|F1F2|=2c)称为焦距。椭圆与两焦点......
  • 【校招VIP】前端算法考察之链表算法
    考点介绍:链表是一种物理存储结构上非连续的数据结构,数据的逻辑顺序是通过链表中的指针链接次序实现相互勾连。链表相对数组而言有很多不同之处,在特定场景下能发挥独特的优势。例如链表的插入和删除操作比数组效率高,数组需要改变其他元素的位置,而链表只需要改变指针的指向。......