0%

LeetCode 0034 一般二分搜索的重点

题解仓库,更新中 GitHub LeetCode-Java

题目大意

给定一个排好序的数组,内里元素可能有重复,给定一个目标值,找出目标值最左端和最右端的index。传送门

典型的二分搜索

二分搜索并不容易写,因为实际上的二分搜索也可以被分成很多类,但是当人们提起时,往往混为一谈,我觉得,基本的,典型的二分搜索有这两类:

  1. 寻找一个特定值
  2. 寻找满足某个条件的连续区间的边界

第一种是最经典的二分搜索,也是初学者最有可能接触到的二分搜索。

1
2
3
4
5
6
7
8
9
10
11
while (left < right) {
int mid = (right - left) / 2 + left;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
return -1;
}

而第二种则是本题目中的要求,而且本题将上下界都涵盖了,非常经典。

二分搜索的重点

那么,二分搜索的重点有哪些呢?其实只有两点:

  1. 保证每次循环后,如果我们的解存在,那么一定还在$[left, right]$区间里。
  2. 每次循环,区间$[left, right]$一定会缩小

我们来看看本题的解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private int leftEdge(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0, right = nums.length - 1;
while (left < right) {
int mid = (right - left) / 2 + left;
if (nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
if (nums[left] == target) {
return left;
}
return -1;
}

private int rightEdge(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0, right = nums.length - 1;
while (left < right) {
int mid = (right - left) / 2 + left + 1;
if (nums[mid] <= target) {
left = mid;
} else {
right = mid - 1;
}
}
if (nums[left] == target) {
return left;
}
return -1;
}

笔者是先完成了 leftEdge ,后完成 rightEdge 的。从其中的改变可以看出二分搜索三个重点。

改变一

1
2
3
4
5
if (nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}

变为

1
2
3
4
5
if (nums[mid] <= target) {
left = mid;
} else {
right = mid - 1;
}

为什么?因为只有这样改变,才能保证重点一的成立。

leftEdge 中,right = mid 而不是 right = mid - 1 是因为如果 mid 已经是所求解,即区间左端点,那么 mid - 1 会将解排除在外。
同理,rightEdge 中,left = mid 而不是 left = mid + 1

改变二

1
int mid = (right - left) / 2 + left;

变为

1
int mid = (right - left) / 2 + left + 1;

这一改变其实是与改变一联动,其目的是保证重点二的成立。为什么?

因为如果在 rightEdge 中还使用经典的 int mid = (right - left) / 2 + left;,由于我们使用了 left = mid 来缩减区间,试想如果 right = 2, left = 1nums[mid] <= target 成立, 那么经过计算 mid 永远等于 left,也即区间其实不会缩小,程序进入死循环。

由于两个方法都是为了寻找区间端点,终归相似之处较多,也基本属于经典范畴。

但是 LeetCode 上还有另外一题 0033 Search-in-Rotated-Sorted-Array 是一道较为新颖的二分搜索题,乍一看由于没有了经典二分中的序关系,不像二分,但想懂后还是很舒畅的。