704. 二分查找
文档讲解:代码随想录
视频讲解:B站视频链接
状态:初次刷题,使用左开右开的方法做出来了,但是由于这种方法对区间把握不当,修改6次后才AC
对于一个已排序且知道排序方向的数组,同时数组内无重复元素,可以通过二分法可以在O(logn)时间内得到答案。这道题的重点在于对搜索区间的把握,主流的方法有左闭右闭法[left, right]和左闭右开法[left, right),即选择二者的其中一种方法时,该如何设定搜索区间、且正确判断区间已收缩到0是关键。其中如何设定搜索空间代码体现就是right = middle + ?
,判断搜索区间收缩到0代码体现就是while循环的退出条件。下面将用分别讨论这两种方法:
-
左闭右闭法:
对于这一种方法,左右两端要求都包含在搜索区间内,因此判断搜索区间收缩到0的条件应该是left > right
,即right == left
时,搜索区间只包含一个数组元素。由于闭区间左右两端的值都会搜索到,因此,在进行区间收缩时应该不包括中值,代码如下:
class Solution { public int search(int[] nums, int target) { int left = 0; int right = nums.length - 1; int middle = (right - left) / 2 + left; while (left <= right) { middle = (right - left) / 2 + left; if(target == nums[middle]) return middle; else if(target > nums[middle]) left = middle + 1; else if(target < nums[middle]) right = middle - 1; } return -1 ; } }
-
左闭右开法:
与第一种方法不同,右侧采用开区间,即右端right
参与中值索引的计算,但是不包括在搜索区间内,因此搜索区间收缩到0的条件应该是left == right
,此时搜索区间不包括任何数组元素。另外,由于
right
不在搜索区间内,因此设定右端索引时,应该使right = middle
,这样正好不包括已进行过搜搜的中值。而左端如上所述,应该保持不变。代码实现如下:class Solution { public int search(int[] nums, int target) { int left = 0; int right = nums.length - 1; int middle = (right - left) / 2 + left; while (left < right) { middle = (right - left) / 2 + left; if(target == nums[middle]) return middle; else if(target > nums[middle]) left = middle + 1; else if(target < nums[middle]) right = middle; } return -1 ; } }
27. 移除元素
文档讲解:代码随想录
视频讲解:B站视频
状态:使用遍历法并搬移数组元素实现,之后查看文档中的GIF动图,实现双指针方法。
关于双指针方法,使用两个指针遍历一次元素,示意图如下:
快指针负责遍历元素并对当前遍历的元素进行判断,是否为目标元素,若是目标元素,将慢指针暂停遍历,直到快指针遍历到下一个非目标元素,覆盖慢指针的值,慢指针开始递增。
慢指针负责存储有效数组长度,并停驻在可能要被覆盖的元素处,等待快指针进行覆盖。只有当快指针指向非目标元素并覆盖慢指针的值时,慢指针才递增。
换句话说,快指针负责遍历,它的递增不需要条件,
慢指针负责指向需要覆盖的元素处,只有当快指针覆盖了慢指针的值后才递增,实现代码如下:
class Solution {
public int removeElement(int[] nums, int val) {
int fast;
int slow = 0;
for (fast = 0; fast < nums.length; fast++) {
if(nums[fast] == val) {
continue;
}
if(fast != slow) nums[slow] = nums[fast];
slow++;
}
return slow;
}
}
在这个过程中要特别注意指针越界问题。我在第一次解题时,在for循环中对fast指针进行两次递增操作,而不是暂停慢指针使得两个指针速度错开,这使得调试代码时发生数组越界问题。