代码随想录算法训练营31期第一天| 704. 二分查找 27. 移除元素
二分查找
题目要求在一个数组中寻找元素并返回他的下标 不难想到用二分查找法 但是初次写对于边界的控制条件是一个困难 看完karl的讲解后豁然开朗 总结如下:
如果采用左闭右闭的写法,首先我们知道此时left是可以等于right的,这在区间不冲突,当nums[middle]<target时,我们知道应该移动左边界了,那左边界应该是移动成middle?还是middle+1? 实际上应该是middle+1,这是因为我们已经知道nums[middle]一定不会是target了,而我们又是一个左闭右闭的写法,因此新的左边界不应该等于middle了,而应该是middle+1。同理,当nums[middle]>target时,右边界应该变成middle-1
如果我们采用左闭右开的写法,首先我们知道此时left不能与right相等,这不符合区间的定义。当nums[middle]<target时,与上一种是一样的,left变成middle-1,但当nums[middle]>target时,right此时应该等于middle了,为什么呢?因为这是右开区间,right是取不到的
下面实现一下两种写法:
左闭右闭
class Solution {
public int search(int[] nums, int target) {
int leftBorder = 0;
int rightBorder = nums.length-1;
int middle;
while(leftBorder <= rightBorder){//因为是左闭右闭 二者可以相等
middle = (leftBorder + rightBorder)/2;
if(nums[middle] == target){//找到了直接返回 因为题里明确了没有重复
return middle;
}
else {
if(nums[middle]>target){
rightBorder = middle-1;
}
else{
leftBorder = middle+1;
}
}
}
return -1; //找不到默认返回-1
}
}
左闭右开
class Solution {
public int search(int[] nums, int target) {
int leftBorder = 0;
int rightBorder = nums.length-1;
int middle;
while(leftBorder < rightBorder){ //左闭右开 二者不能相等
middle = (leftBorder + rightBorder)/2;
if(nums[middle] == target){
return middle;
}
else {
if(nums[middle]>target){
rightBorder = middle; //注意这里是middle
}
else{
leftBorder = middle+1;
}
}
}
return -1;
}
}
总结
要注意一下使用二分法的前提:
- 数组为有序数组(这样才能比较大小)
- 数组中无重复元素
同时二分查找的时间复杂度为O(log2n) 空间复杂度为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++){
//建议以j从i+1开始 后面用j-1 否则的话判断边界还挺容易蒙的
//不要用for(int j =i;j<size-1;j++)这种
nums[j-1] = nums[j];
}
size--;
i--; //因为后面的挪过来变成此时的nums[i]了 我们需要考虑多个val的情况
}
}
return size;
}
}
很显然这里的时间复杂度为O(n2)
双指针法
双指针是我们在处理数组和链表都很常用的一种方法 我们关键需要了解快慢指针指的都是什么
在这里快指针指的是新数组中的元素 慢指针指的是新数组元素处的位置
这里放一下代码随想录网站上的动图:
class Solution {
public int removeElement(int[] nums, int val) {
int fastIndex = 0;
int slowIndex = 0;
for(fastIndex=0;fastIndex<nums.length;fastIndex++){
if(nums[fastIndex]!=val){
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
}
这里只需要一个if就能解决了 这是因为我们需要再明确一下快慢指针的含义 当快指针对应的元素不是val 就证明这是新数组的元素 那自然而然要把他放到新数组对应的位置 也就是慢指针 而当我们快指针指到了val 这不是我们想要的元素 那么快指针肯定是需要继续往下移动的 那慢指针保持不动是为什么呢 这是因为慢指针指的是新数组下标的位置 他是一步步来的 只有快指针指向正确的位置 他才移动
时间复杂度O(n) 空间复杂度O(1)