文章目录
Day 1 第一章 数组part01
- 今日任务
- 数组理论基础,704. 二分查找,27. 移除元素
数组理论基础
文章链接:https://programmercarl.com/%E6%95%B0%E7%BB%84%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html
题目建议: 了解一下数组基础,以及数组的内存空间地址,数组也没那么简单。
1. 数组是存放在连续内存空间上的相同类型数据的集合。
数组可以方便的通过下标索引的方式获取到下标下对应的数据。
举一个字符数组的例子,如图所示:
数组的元素是不能删的,只能覆盖。
需要两点注意的是
- 数组下标都是从0开始的。
- 数组内存空间的地址是连续的
正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
2. 二维数组
- 二维数组在C++中内存的空间地址是连续的
- 像Java是没有指针的,同时也不对程序员暴露其元素的地址,寻址操作完全交给虚拟机,所以看不到每个元素的地址情况。
- Java的二维数组可能是如下排列的方式:
704. 二分查找
- 题目建议: 大家能把 704 掌握就可以,35.搜索插入位置 和 34. 在排序数组中查找元素的第一个和最后一个位置,如果有时间就去看一下,没时间可以先不看,二刷的时候在看。
- 先把 704写熟练,要熟悉 根据 左闭右开,左闭右闭 两种区间规则 写出来的二分法。
- 题目链接:https://leetcode.cn/problems/binary-search/
- 文章讲解:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html
- 视频讲解:https://www.bilibili.com/video/BV1fA4y1o715
1. 解题思路
1. nums[]是一个有序的(升序)整型数组;
2. nums[]中的数字不重复。
目标:找其中有没有target
这个数
方法:取一个数组的中间值和target进行比较,以缩小下次比较的范围
2. 容易搞混的点:
- 在while循环时 到底是
while(left < right)
还是while(left <= right)
? - 在if判断时:到底是
right = middle
呢,还是要right = middle - 1
?
3.❗重点:区间的定义
- 区间的定义就是不变量,要在二分查找的过程中,保持不变量,在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
- 二分法区间的两种写法:左闭右闭
[left, right]
or 左闭右开[left, right)
。
4. 代码
1️⃣ 左闭右闭[left, right]
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
//左闭右闭[left, right]
while(left <= right){ //⚠️
//也可以(right+left)/2,但下面这样可以防止溢出
int mid = (right-left)/2+left;
if(nums[mid] < target) left = mid+1;//⚠️
else if(nums[mid] == target) return mid;
else right = mid-1;//⚠️
}
return -1;
}
}
2️⃣ 左闭右开[left, right)
❗定义right时,right = nums.length
,不用再-1
了
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length;
//左闭右开[left, right)
while(left < right){ //⚠️
int mid = (right-left)/2+left;
if(nums[mid] < target) left = mid+1;
else if(nums[mid] == target) return mid;
else 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
- 视频讲解:https://www.bilibili.com/video/BV12A4y1Z7LP
0. 前提
- 数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
- 所以删除数组内的一个元素就是将其后面的所有数向前移一位做覆盖,在内存中数组所占内存没变,但会默认
nums.length-1
。
一开始没有想到所有元素向前移,看力扣题目只想到把前面相等的数和后面不相等的数交换一下位置,但这样数组中只有一个元素就无法交换了
1. 暴力解法
这个题目暴力的解法就是两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
class Solution {
public int removeElement(int[] nums, int val) {
int size = nums.length;
for(int i=0; i<nums.length; i++){
if(nums[i] == val){
for(int x=i+1; x<nums.length; x++){
nums[x-1] = nums[x];//⚠️x最大是nums.length-1,因为不能出现nums[x+1],所以x=i+1
}
i--; //因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; //此时数组的大小-1
}
}
return size;
}
}
2. 双指针法
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。 把快指针找到的新数组需要的元素赋给慢指针(做新数组下标)
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
删除过程如下:
class Solution {
public int removeElement(int[] nums, int val) {
int fast, slow = 0;
for(fast=0; fast<nums.length; fast++){
if(nums[fast] != val){
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
}
❗双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。
3. 相向双指针法
其他知识点
Day 1
1. 获取数组长度
- 一维数组
int array[] = new int[3];
数组的长度:array.length - 二维数组
二维数组 int array[][] = new int[3][3];
行长度:array.length ;列长度:array[i].length
2. 两个int型相加可能会越界
3. Markdown如何缩进&居中
- 利用
shift+space
切换成全角输入,再用空格就是一个全空格了 - 添加
html
语句
缩进比例如下:
lalal
lalala 半角的不断行的空白格(
)
lalala 半角的空格( 
)
lalala 全角的空格( 
)
<center>这一行需要居中</center>