第一章 数组part01
题目:704. 二分查找,27. 移除元素
参考链接:代码随想录
704.二分查找
关键点:边界点写法容易把握不好 ,到底是小于还是小于等于?等于middle还是 等于middle-1?
一般常见的写法有:左闭右闭和左闭右开
思路:使用left,right指针分别指向数组开始和结尾,然后每次循环计算middle,将mid和target比较,直到不满足left<right,返回-1。注意下标的开闭区间不同写法。
class Solution {//(版本一)左闭右闭区间
public int search(int[] nums, int target) {
//避免当target小于nums[0],num[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 middle = (left + right)/2;
//mid = left + ((right - left) >> 1); 移位操作?
if(nums[middle] == target)
return middle;
else if(nums[middle] > target)
right = middle -1;
else if(nums[middle] < target)
left = middle +1;
}
return -1;
}
}
class Solution {//(版本二)左闭右开区间
public int search(int[] nums, int target) {
int left = 0,right = nums.length;
while(left < right){
int middle = left + ((right - left) >> 1);
if(nums[middle] == target )
return middle;
else if(nums[middle] > target)
right = middle;
else if(nums[middle] < target)
left = middle + 1;
}
return -1;
}
}
补充说明:
①避免当target小于nums[0],nums[nums.length - 1]时多次运算,这部分开始没想到,思路不全面,标记一下。
if(target < nums[0] || target > nums[nums.length - 1]){ return -1;
②mid = left + ((right - left) >> 1); 移位操作?
这里采用了 >> 来获得区间的中点下标,学习一下该写法。
>>:右移,m>>n 代表 m除2的n次方(常用m >> 1,即m/2)
<<:左移,m<<n 代表 m 乘2的n次方
27.移除元素
思路:有的同学可能说了,多余的元素,删掉不就得了。
要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
暴力解法:
这个题目暴力的解法就是两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。 很明显暴力解法的时间复杂度是O(n^2),这道题目暴力解法在leetcode上是可以过的。
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
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也向前移动一位
size--;//此时 数组的大小-1
}
}
return size;
}
}
双指针法:
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
很多同学这道题目做的很懵,就是不理解 快慢指针究竟都是什么含义,所以一定要明确含义,后面的思路就更容易理解了。
快慢双指针法 :
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;
}
}
相向双指针法 :
//相向双指针法
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length - 1;
while(right >= 0 && nums[right] == val) right--; //将right移到从右数第一个值不为val的位置
while(left <= right) {
if(nums[left] == val) { //left位置的元素需要移除
//将right位置的元素移到left(覆盖),right位置移除
nums[left] = nums[right];
right--;
}
left++;
while(right >= 0 && nums[right] == val) right--;
}
return left;
}
}
相向双指针法 (版本二):
// 相向双指针法(版本二)
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
if(nums[left] == val){
nums[left] = nums[right];
right--;
}else {
// 这里兼容了right指针指向的值与val相等的情况
left++;
}
}
return left;
}
}
总结
- 二分查找注意边界问题,通过最开始定义的搜索区间来确定判断条件和更新区间。
- 数组中删除元素实际为覆盖元素,使用双指针解题时要明确两个指针指向的意义和移动过程。