9、二分查找
1、二分查找法
/**
* 二分查找法: O(logN)
*/
public class BinarySearch {
private BinarySearch() {
}
/**
* 二分查找法递归实现
*/
public static <E extends Comparable<E>> int searchR(E[] data, E target) {
return searchR(data, 0, data.length - 1, target);
}
private static <E extends Comparable<E>> int searchR(E[] data, int l, int r, E target) {
if (l > r) return -1;
int mid = l + (r - l) / 2;
if (data[mid].compareTo(target) == 0) return mid;
if (data[mid].compareTo(target) < 0) return searchR(data, mid + 1, r, target);
return searchR(data, l, mid - 1, target);
}
/**
* 二分查找法非递归实现
*/
public static <E extends Comparable<E>> int search(E[] data, E target) {
int l = 0;
int r = data.length - 1;
int mid;
// 在 data[l, r] 中查找 target
// 每次循环开始时: data[l]、data[r] 还没看, 因此 l == r 时, 依然要进入循环
while (l <= r) {
mid = l + (r - l) / 2;
if (data[mid].compareTo(target) == 0) return mid;
if (data[mid].compareTo(target) < 0) l = mid + 1;
else r = mid - 1;
}
return -1;
}
}
2、Ceil 上界
public class Ceil {
private Ceil() {
}
/**
* 在 data[] 中查找 > target 的最小值所在的索引
*/
private static <E extends Comparable<E>> int upper(E[] data, E target) {
int l = 0;
int r = data.length;
int mid;
// 在 data[l, r] 中查找 > target 的最左边的索引, r = data.length
// 每次循环开始时: data[l] 还没看, data[r] 可能是解, 因此当 l == r 时, r 就是解
while (l < r) {
mid = l + (r - l) / 2; // l <= mid < r, l 与 r 相邻时 mid = l
if (data[mid].compareTo(target) > 0) r = mid;
else l = mid + 1;
}
return r;
}
/**
* 存在 target 时, 返回 = target 的最右边的索引
* 没有 target 时, 返回 > target 的最左边的索引
*/
public static <E extends Comparable<E>> int ceilR(E[] data, E target) {
int upper = upper(data, target);
if (upper - 1 >= 0 && data[upper - 1].compareTo(target) == 0) return upper - 1;
return upper;
}
/**
* 存在 target 时, 返回 = target 的最左边的索引
* 没有 target 时, 返回 > target 的最左边的索引
*/
public static <E extends Comparable<E>> int ceilL(E[] data, E target) {
int l = 0;
int r = data.length;
int mid;
// 在 data[l, r] 中查找 >= target 的最左边的索引, r = data.length
// 每次循环开始时: data[l] 还没看, data[r] 可能是解, 因此当 l == r 时, r 就是解
while (l < r) {
mid = l + (r - l) / 2; // l <= mid < r, l 与 r 相邻时 mid = l
if (data[mid].compareTo(target) >= 0) r = mid;
else l = mid + 1;
}
return r;
}
}
3、Floor 下界
public class Floor {
private Floor() {
}
/**
* 查找 < target 的最大值所在的索引
*/
private static <E extends Comparable<E>> int lower(E[] data, E target) {
int l = -1;
int r = data.length - 1;
int mid;
// 在 data[l, r] 中查找 < target 的最右边的索引, l = -1
// 每次循环开始时: data[l] 可能是解, data[r] 还没看, 因此当 l == r 时, l 就是解
while (l < r) {
mid = l + (r - l + 1) / 2; // l < mid <= r, l 与 r 相邻时 mid = r
if (data[mid].compareTo(target) < 0) l = mid;
else r = mid - 1;
}
return l;
}
/**
* 存在 target 时, 返回 = target 的最左边的索引
* 没有 target 时, 返回 < target 的最右边的索引
*/
public static <E extends Comparable<E>> int floorL(E[] data, E target) {
int lower = lower(data, target);
if (lower + 1 < data.length && data[lower + 1].compareTo(target) == 0) return lower + 1;
return lower;
}
/**
* 存在 target 时, 返回 = target 的最右边的索引
* 没有 target 时, 返回 < target 的最右边的索引
*/
public static <E extends Comparable<E>> int floorR(E[] data, E target) {
int l = -1;
int r = data.length - 1;
int mid;
// 在 data[l, r] 中查找 < target 的最右边的索引, l = -1
// 每次循环开始时: data[l] 可能是解, data[r] 还没看, 因此当 l == r 时, l 就是解
while (l < r) {
mid = l + (r - l + 1) / 2; // l < mid <= r, l 与 r 相邻时 mid = r
if (data[mid].compareTo(target) <= 0) l = mid;
else r = mid - 1;
}
return l;
}
}
4、力扣问题
如果问题有明显的边界,就可以用二分查找法在这个边界去搜索问题的解
4.1、爱吃香蕉的珂珂
public class MinEatingSpeed {
/**
* piles = [3, 6, 7, 11], H = 8
* 输出: 4
*/
public static int minEatingSpeed(int[] piles, int h) {
int speedL = 1;
int speedR = Arrays.stream(piles).max().getAsInt();
int speedMid;
// arr[speedL, speedR] 代表吃香蕉的速度, time = eatingTime(speed)
// 二分搜索 time <= h 最大的 time 对应 speed
// 每次循环开始时: speedL 还没看, speedR 可能是解, 因此当 speedL == speedR 时, speedR 就是解
while (speedL < speedR) {
speedMid = speedL + (speedR - speedL) / 2;
int time = getEatingTime(piles, speedMid);
if (time <= h) speedR = speedMid;
else speedL = speedMid + 1;
}
return speedR;
}
private static int getEatingTime(int[] piles, int speed) {
int times = 0;
for (int pile : piles) {
times += pile / speed + (pile % speed == 0 ? 0 : 1);
}
return times;
}
public static void main(String[] args) {
int[] piles1 = {3, 6, 7, 11}; // 4
System.out.println(minEatingSpeed(piles1, 8));
int[] piles2 = {30, 11, 23, 4, 20}; // 30
System.out.println(minEatingSpeed(piles2, 5));
}
}
4.2、在 D 天内送达包裹的能力
public class ShipWithinDays {
public static int shipWithinDays(int[] weights, int days) {
int capacityL = Arrays.stream(weights).max().getAsInt();
int capacityR = Arrays.stream(weights).sum();
int capacityMid;
// arr[capacityL, capacityR] 代表运载能力, time = getShipTime(capacity)
// 二分搜索 time <= days 最大的 time 对应 capacity
// 每次循环开始时: capacityL 还没看, capacityR 可能是解, 因此当 capacityL == capacityR 时, capacityR 就是解
while (capacityL < capacityR) {
capacityMid = capacityL + (capacityR - capacityL) / 2;
int times = getShipTime(weights, capacityMid);
if (times <= days) capacityR = capacityMid;
else capacityL = capacityMid + 1;
}
return capacityR;
}
private static int getShipTime(int[] weights, int maxCapacity) {
int times = 0;
int hasCapacity = 0;
for (int weight : weights) {
if (hasCapacity + weight <= maxCapacity) hasCapacity += weight;
else {
times++;
hasCapacity = weight;
}
}
return times + 1;
}
}
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17304807.html