第一天| 第一章 数组part01 数组理论基础、704. 二分查找、27. 移除元素
数组理论基础
数组的特点:
- 数组本身是
引用数据类型
,而数组中的元素可以是任何数据类型
,包括基本数据类型和引用数据类型。 - 创建数组对象会在内存中开辟一整块
连续的空间
。占据的空间的大小,取决于数组的长度和数组中元素的类型。 - 数组中的元素在内存中是依次紧密排列的,有序的。
- 数组,一旦初始化完成,其长度就是确定的。数组的
长度一旦确定,就不能修改
。 - 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
- 数组名中引用的是这块连续空间的首地址。
704. 二分查找
-
题目链接:https://leetcode.cn/problems/binary-search/
-
文章讲解:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html
-
题目介绍:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
-
使用二分查找的前提是:(1)数组为有序数组;(2)数组中无重复元素。
-
二分查找根据边界范围的划分分为两种解法:
-
第一种:左闭右闭;
-
左闭右闭(left <= right)意味着:
- (1)循环条件为while(left <= right)
- (2)初次定义的right必须是数组的索引最大值,即int right = nums.length
- (3)target不管是大于还是小于nums[middle],对应的区间边界的值都需要变化。具体地,如果target < nums[middle],那么right = middle - 1;如果target > nums[middle],那么left = middle + 1。
-
在循环中写判断条件的时候,先判断nums[middle] == target是否成立,千万不要写成这样:
-
while (left <= right) { int middle = left + (right - left)/2; if (target > nums[middle]) { left = middle + 1; } else if (target < nums[middle]) { right = middle - 1; } else { return middle; } }
-
上面代码问题出在:如果循环条件为left == right时,上面两个判断条件都不符合,所以执行else,返回了middle。这样是错误的。
-
因为,有可能target不在nums数组最小值和最大值的范围内,所以return middle需要指定条件,必须是target == nums[middle]。
-
-
正确的做法是:
-
class Solution { public int search(int[] nums, int target) { // 避免当 target 小于nums[0] 或 大于nums[nums.length - 1]时多次循环运算 // 这部分可有可无 if (target < nums[0] || target > nums[nums.length - 1]) { return -1; } // 以下是关键代码 int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + ((right - left) >> 1); if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else if (nums[mid] > target) right = mid - 1; } return -1; } }
- 这个代码还有一个需要注意的地方:
- int mid = left + ((right - left) >> 1);
- 没有采用(left + right) / 2的原因是:防止溢出
- 这个代码还有一个需要注意的地方:
-
-
-
第二种:左闭右开。
-
左闭右开意味着:
- (1)循环条件为while(left < right)
- (1)初次定义的right等于nums.length
- (2)如果target < nums[middle],那么right = middle - 1;如果target > nums[middle],那么left = middle。
-
正确的做法是:
-
class Solution { public int search(int[] nums, int target) { int left = 0, right = nums.length; while (left < right) { int mid = left + ((right - left) >> 1); if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else if (nums[mid] > target) right = mid; } return -1; } }
-
-
-
-
27. 移除元素
-
题目链接:https://leetcode.cn/problems/remove-element/
-
文章讲解:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html
-
题目介绍:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
-
注意:
- 数组占用了内存地址中的连续空间,数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改。我们不能对其中的元素进行删除,只能覆盖。
-
提供两种解法:
-
第一、暴力求解:
-
提供两层循环,外层循环遍历数组,一旦数组中的值与目标值相同,执行内层循环,将该位置之后的元素赋给当前位置。
-
由于数组的length一旦初始化之后就无法更改,所以想要修改它,必须把它赋给一个新值
-
代码为:
-
class Solution { public int removeElement(int[] nums, int val) { int length = nums.length; for (int i = 0; i < length; i++) { if (nums[i] == val) { for(int j = i + 1; j < length; j++) { nums[j - 1] = nums[j]; } i--; length--; } } return length; } }
-
-
-
第二:快慢指针:
-
快指针去寻找新数组中需要的值;
-
慢指针确定新数组的索引值。
-
解释一下:就是快指针不会停下来,它一直扫描这个数组,如果快指针扫描到的元素不是目标值,那么就把值赋给慢指针对应的元素,并且跟着快指针一起++;一旦快指针扫描到的元素是目标值,慢指针就停下来(不++了),快指针继续++,找目标值。
-
图解详见:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html#%E6%80%9D%E8%B7%AF
-
class Solution { public int removeElement(int[] nums, int val) { int slowindex = 0; for (int fastindex = 0; fastindex < nums.length; fastindex++) { if (nums[fastindex] != val) { nums[slowindex] = nums[fastindex]; slowindex++; } } return slowindex; } }
-
-
-