704 二分查找法
首先我们做这道题,需要确定区间。
那么何为区间呢?区间就是你要遍历的范围。
常见的区间有哪些呢?
- 左闭右闭 [left,right]
- 左闭右开 [left,right)
确定区间以后,我们就可以开始做题了。
根据这道题我们常见的两个问题
第一:我们如何退出循环,是 while(left < right) ,还是 while(left <= right)
第二:我们在循环中,middle 的下标是多少呢? 是 middle - 1,还是middle。
由此我们对这道题有两种解法,分别是左闭右开,左闭右闭。
代码如下(左闭右闭):
public int search(int[] nums, int target) {
//先确定区间,左闭右闭 [0,nums.length -1]
int left = 0;
int right = nums.length - 1;
//这里我们解答第一个问题?
//为什么我这里是 left <= right
//因为我这里是左闭右闭,我的最右边的元素是可以进行遍历的
//从而也解决了第二个问题,因为我的最右边的元素是可以遍历的
//说明是遍历了,也就是判断了一遍,所以 mid - 1;可以不用再次判断一次 middle
while (left <= right) {
//确定中间下标的值
//这一步可以优化一下,后面会讲
int mid = (left + right) / 2;
int midValue = nums[mid];
if (midValue > target) {
right = mid - 1;
} else if (midValue < target) {
left = mid + 1;
} else {
//找到了目标元素,返回下标mid
return mid;
}
}
//没有找到目标元素,返回 -1
return -1;
}
代码如下(左闭右开):
public int search02(int[] nums, int target) {
//确定区间,左闭右开 [0,nums.length]
int left = 0;
int right = nums.length;
//因为我确定了区间,是左闭右开,所以我不会遍历最右边的元素
//从而解决了 第二个问题,我的最右边元素没有进行遍历,所以需要
//判断,所以 mid 并不 -1,而是 mid
while (left < right) {
//确定中间下标元素的值
int mid = (left + right) / 2;
int midValue = nums[mid];
if (midValue > target) {
right = mid;
} else if (midValue < target) {
left = mid + 1;
} else {
return mid;
}
}
//在数组中,没有找到,就返回-1
return -1;
}
优化代码如下:
1.我们可以使用位运算的方式来更好的处理这个问题
int mid = (left + right ) / 2;
可以改成 int mid = (left + right ) >> 1
小总结:
遇到这道题可以套公式:
如果你是左闭右闭,就可以是 while(left <= right),边界值已经被处理了,就不要再判断了,mid - 1,或者 mid + 1。
如果你是左闭右开,就可以是 while(left < right),边界值没有被处理,就需要判断,mid 而不是 mid -1 啦。
LeetCode 27.移除元素
其实该题直接使用暴力解法也可以的,使用双层for循环即可。第一层遍历所有元素,在找到了要移除的元素后,进行第二层的遍历,也就是移除操作。将后面的元素的值依次向前覆盖元素的值即可。
代码如下(暴力解法):
public int removeElement02(int[] arr, int val) {
//数组的大小
int size = arr.length;
//第一层循环遍历数组所有的元素
for (int i = 0; i < arr.length; i++) {
//如果找到了该元素,就移除
if (arr[i] == val) {
for (int j = i + 1; j < arr.length; j++) {
//将后面元素的值依次覆盖原来的元素值
arr[j - 1] = arr[j];
}
//因为移除了,所有size和i都--
i--;
size--;
}
}
//返回size的大小
return size;
}
但是时间复杂度是O(N2),我们还有没有更好的解法呢?
我们引出了双指针的思想来解决这道题。
使用双指针我们首先要确定双指针,这两个指针的作用分别是什么?
快指针 fastPoint :是我们用来确定新数组的元素值,即不等于目标元素的数组元素。
慢指针 slowPoint :是我们用来确定新数组的下标位置。
确定了双指针的作用,我们就将快指针指向的元素加入到慢指针指向的元素下标中即可。
代码如下(双指针):
public int removeElement(int[] arr, int val) {
//定义快慢指针
//我们要明确快慢指针的含义
//快指针:用于获取我们新数组中的元素,即在arr中不等于val的数组元素
int fastPoint = 0;
//慢指针:用于确定新数组的下标值,即在fastPoint获取到元素后应放在新数组的哪个下标下
int slowPoint = 0;
for (; fastPoint < arr.length; fastPoint++) {
if (arr[fastPoint] != val) {
arr[slowPoint] = arr[fastPoint];
slowPoint++;
//这一步可以简化成
//arr[slowPoint++] = arr[fastPoint];
}
}
return slowPoint;
}