二分查找
正常实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public int binarySearch(int[] nums, int key) {
int l = 0, h = nums.length - 1;
while (l <= h) {
int m = l + (h - l) / 2;
if (nums[m] == key) {
return m;
} else if (nums[m] > key) {
h = m - 1;
} else {
l = m + 1;
}
}
return -1;
}
|
二分查找变种
二分查找可以有很多变种,变种实现要注意边界值的判断。例如在一个有重复元素的数组中查找 key 的最左位置的实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
int leftBound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,锁定左侧边界
right = mid - 1;
}
}
// 最后要检查 left 越界的情况
if (left >= nums.length || nums[left] != target)
return -1;
return left;
}
|
寻找右边界
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
int right_bound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,锁定右侧边界
left = mid + 1;
}
}
// 最后要检查 right 越界的情况
if (right < 0 || nums[right] != target)
return -1;
return right;
}
|
上述在 == 分支上进行一个判断,就可以减少搜索。
1
2
3
4
5
|
if (mid == 0 || array[mid - 1] != n) {
return mid;
} else {
high = mid - 1;
}
|
四种二分查找变形
运用二分查找
算法框架,使用一个单调函数包装我们要比较的部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// 函数 f 是关于自变量 x 的单调递增函数
int f(int x, int[] nums) {
return nums[x];
}
int leftBound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,锁定左侧边界
right = mid - 1;
}
}
// 最后要检查 left 越界的情况
if (left >= nums.length || nums[left] != target)
return -1;
return left;
}
|
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
|
class Solution {
public int minEatingSpeed(int[] piles, int h) {
int sum = 0;
int left = 1, right = (int)Math.pow(10, 9) + 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (isValid(mid, piles) <= h) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
public int isValid(int k, int[] piles) {
int h = 0;
for (int i = 0; i < piles.length; i++) {
h += (int)Math.ceil(piles[i] / (k * 1.0));
// if (piles[i] % k == 0)
// h -= piles[i] / k;
// else
// h -= (piles[i] / k + 1);
}
return h;
}
}
|
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
34
35
|
class Solution {
public int shipWithinDays(int[] weights, int days) {
if (weights == null || weights.length == 0)
return -1;
int left = weights[0], sum = 0;
for (int num : weights) {
left = Math.max(left, num);
sum += num;
}
int right = sum;
while (left <= right) {
int mid = left + (right - left) / 2;
if (f(mid, weights) <= days) {
right = mid - 1;
} else
left = mid + 1;
}
return left;
}
public int f(int k, int[] weights) {
int i = 0, sum = 0, ret = 0;
while (i < weights.length) {
sum += weights[i];
if (sum > k) {
sum = weights[i];
ret++;
}
i++;
}
if (sum != 0)
ret++;
return ret;
}
}
|