代码随想录算法训练营第一天 | LeetCode 704.二分查找、LeetCode 27.移除元素

简介: 代码随想录算法训练营第一天 | LeetCode 704.二分查找、LeetCode 27.移除元素

LeetCode 27.移除元素

文章讲解:代码随想录二分查找代码随想录移除元素

视频讲解:二分搜索法移除元素

1. 数组理论基础

文章讲解:数组理论基础

1.1 什么是数组

数组:可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。

  • 数组中存放的元素其类型相同
  • 数组的空间是连在一起的
  • 每个空间有自己的编号,起始位置的编号为0,即数组的下标。

1.2 数组的创建及初始化

数组的初始化主要分为动态初始化以及静态初始化。

1.2.1 动态初始化:在创建数组时,直接指定数组中元素的个数
int[] array = new int[10];

1.2.2 静态初始化:在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定

int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};

1.3 数组的使用

1.3.1 数组中元素访问

数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过下标访问其任意位置的元素。比如:

int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);
// 也可以通过[]对数组中的元素进行修改
array[0] = 100;
System.out.println(array[0]);
[注意事项]:
  1. 数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素
  2. 下标从0开始,介于[0, N)之间不包含NN为元素个数,不能越界,否则会报出下标越界异常
1.3.2 遍历数组

所谓 "遍历" 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作,比如:打印。

int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);

上述代码可以起到对数组中元素遍历的目的,但问题是:

  1. 如果数组中增加了一个元素,就需要增加一条打印语句
  2. 如果输入中有100个元素,就需要写100个打印语句
  3. 如果现在要把打印修改为给数组中每个元素加1,修改起来非常麻烦。

通过观察代码可以发现,对数组中每个元素的操作都是相同的,则可以使用循环来进行打印。

注意:在数组中可以通过 数组对象.length 来获取数组的长度

int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < array.length; i++){
System.out.println(array[i]);
}

1.4 数组是引用类型

基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的

引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址

1.5 二位数组

二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组.

1.5.1 基本语法
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
1.5.2 代码实例
int[][] arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int row = 0; row < arr.length; row++) {
for (int col = 0; col < arr[row].length; col++) {
System.out.printf("%d\t", arr[row][col]);
}
System.out.println("");
}
// 执行结果
1 2 3 4
5 6 7 8
9 10 11 12

2. LeetCode 704. 二分查找

2.1 自己的思路

  1. 因为自己前几天做了类似的题,也是二分法的,只不过要插入一个元素
  2. 当时自己对于二分法其实就是左右指针,一个0一个数组长度-1这样子,自己值得思考的问题就是以上的易错点,当时自己觉得这个left是小于还是小于等于right呢?也是思考了很久,因为以前听过,就凭借点记忆力直接小于等于然后下面的right=mid-1这样子,我自己是觉得理所应当的,但并不明白其中的逻辑。于是在看了carl哥的视频和文章才豁然开朗

2.2 易错点

  1. while()里是写left<=right还是left<right
  2. if(nums[mid]>target){}里是写right=mid-1还是right=mid

2.3 思路

2.3.1 左闭右闭写法:
  1. right=arr.length-1,因为是右边闭的,所以这么写能包含整个数组的值
  2. [1,1]合法区间,可以写成while(left<=right),此时left<=right在区间里是有意义的
  3. 往下写if(nums[mid]>target){}里是right=mid-1还是right=mid呢?因为是右是闭区间,因为mid位置的值已经是大于target了,这个已经不是要找的值了,区间里还包括它就没有意义了。
  4. 举个极端的例子:如果边界可以取到mid的话,比如到了最后left=0,right=1,mid=0,如果target在1,left=middle还是会被设为0,middle永远取不到1,就死循环了
2.3.2 代码
class Solution {
    public int search(int[] nums, int target) {
      int left=0;
      int right=nums.length-1;//此时是左闭右闭区间,[left,right],由于右区间有效,所以要-1
      while(left<=right){//因为是左闭右闭区间,left==right是有意义的
        int mid=(left+right)/2;
        if(nums[mid]==target){//找到target
          return mid;
        }else if(nums[mid]>target){//target在左区间
          right=mid-1;// [left,mid-1]
        }else{//target在右区间
          left=mid+1;// [mid+1,right]
        }
      }
      return -1;
    }
}
2.3.3 左闭右开写法:
  1. right=arr.length,因为是右边开的,所以这么写能包含整个数组的值,写成arr.length-1的话就不能包含数组最右边的值
  2. [1,1)不合法区间,可以写成while(left<right),此时若还是left<=right在区间里是没有意义的
  3. 往下写if(nums[mid]>target){}里是right=mid-1还是right=mid呢?因为是右是开区间,因为mid位置的值已经是大于target了,这个已经不是要找的值了,但又因为右区间是开的,所以包括进来是可以的
2.3.4 代码
class Solution {
    public int search(int[] nums, int target) {
      int left=0;
      int right=nums.length;//此时是左闭右开区间,[left,right),由于右区间无效,所以不要-1,-1后就少了最右边的值了
      while(left<right){//因为是左闭右开区间,left==right是无意义的
        int mid=(left+right)/2;
        if(nums[mid]==target){//找到target
          return mid;
        }else if(nums[mid]>target){//target在左区间
          right=mid;// [left,mid-1)
        }else{//target在右区间
          left=mid+1;// [mid+1,right)
        }
      }
      return -1;
    }
}

3. LeetCode 27. 移除元素

3.1 自己的思路&易错点

  1. 自己做这题时,想着用一个len记录数组原始长度
  2. 然后通过遍历找到目标值直接删除,然后len--直到所有的目标值删除
  3. 想着直接把数组上的值删除就行了

3.2 思路

3.2.1 暴力
  • 这个题目暴力的解法就是两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
3.2.2 代码
class Solution {
    public int removeElement(int[] nums, int val) {
        int len = nums.length;
        for (int i = 0; i < len; i++) {
            if (nums[i] == val) { // 找到需要移除的元素,就将数组集体向前移动一位
                for (int j = i + 1; j < len; j++) {
                    nums[j - 1] = nums[j];
                }
                i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                len--; // 此时数组的大小-1
            }
        }
        return len;
    }
}
3.2.3 双指针
  1. 快指针是用来获取新数组中的元素(需要的元素,要删掉的不要),新数组就是不含有目标元素的数组(if ( nums[fast]!=val ) 不需要这个目标值,我们要删除掉)
  2. 慢指针是获取我们新数组中需要更新的位置(当 nums[fast]==val 时这是新数组不需要的那个目标值,此时快指针继续后移,慢指针停住)
  3. 但都是在一个数组中操作
3.2.4 代码
class Solution {
    public int removeElement(int[] nums, int val) {
        int slow=0;//慢指针是获取我们新数组中需要更新的位置
        //快指针是用来获取新数组中的元素
        for(int fast=0;fast<nums.length;fast++){
            if(nums[fast]!=val){//只有fast上不是要移除的元素,才将fast上的值赋给slow,并且slow后移
                nums[slow++]=nums[fast];
            }
        }
        return slow;
    }
}
3.2.5 双指针应用场景

双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。

4. 总结

4.1 时长

       说来惭愧,这两题没那么难,但因为本人算法基础太差第二题没做出来,而且也是第一次写博客的原因,总耗时差不多2h17m。

4.2 心得体会

       今天刷的题,带我自己复习了一下数组的内容,以及巩固了二分法的注意事项,还有双指针的一些应用场景,怎么使用等等,以及对于第二题暴力求解的思路也有很大感悟!希望自己能坚持下去,如果博客写的不好欢迎在评论区指正,你的指点是让我进步的最大帮助!

相关文章
|
2月前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
39 1
|
2月前
【LeetCode 27】347.前k个高频元素
【LeetCode 27】347.前k个高频元素
41 0
|
2月前
【LeetCode 06】203.移除链表元素
【LeetCode 06】203.移除链表元素
33 0
|
2月前
【LeetCode 01】二分查找总结
【LeetCode 01】二分查找总结
17 0
|
2月前
【LeetCode-每日一题】移除元素
【LeetCode-每日一题】移除元素
34 0
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
63 6
|
4月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
128 2
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
49 1
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口