题目:
给定一个有序数组arr,代表坐落在X轴上的点。
给定一个正数K,代表绳子的长度。
返回绳子最多压中几个点?(即使绳子边缘处盖住点也算盖住)
暴力解法:
遍历一遍数组,到达的每个位置都判断这个点能压中的点。
时间复杂度:O(N)。
public static int maxPoint1(int[] arr, int len) {
int max = 0;
for (int i = 0; i < arr.length; i++) {
int pre = i - 1;
while (pre >= 0 && arr[i] - arr[pre] <= len) {
pre--;
}
max = Math.max(max, i - pre);
}
return max;
}
贪心+二分:
贪心体现在每次都拿绳子的尾部压住数组上存在的点。
当前位置的值减去绳子长度就是当前位置所能压到的最远的点。
使用二分查找寻找这个位置。
例如当前位置的值为893,绳子长度为100,当前位置能压到的最远位置为793。
使用二分查找寻找值为793的下标位置(如果不存在这个值,那么就找最小的大于793的值的位置)。
时间复杂度:O(N * log N)
public static int maxPoint2(int[] arr, int len) {
int res = 0;
for (int i = 0; i < arr.length; i++) {
int nearest = nearestIndex(arr, i, arr[i] - len);
res = Math.max(res, i - nearest + 1);
}
return res;
}
public static int nearestIndex(int[] arr, int R, int value) {
int L = 0;
int index = R;
while (L <= R) {
int mid = L + ((R - L) >> 1);
if (arr[mid] >= value) {
index = mid;
R = mid - 1;
} else {
L = mid + 1;
}
}
return index;
}
滑动窗口
判断每个位置延迟绳子的长度后,能压到的最多的点数。和方法二不同的是,窗口一直在向右边延伸,不需要每次都使用二分法去寻找。
时间复杂度:O(N)
public static int maxPoint3(int[] arr, int len) {
int max = 0;
int right = 0, N = arr.length;
// 假定窗口left 到right 判断每个点到后面最多能盖几个点,然后每个点都出个结果,比较最大的
for (int left = 0; left < N; left++) {
// 当前点根据绳子长度像后覆盖点,直到 没有数 可以延伸或者 大于绳子长度L
while (right < N && arr[right] - arr[left] <= len) {
right++;
}
// 比较旧覆盖点和新覆盖点哪个大就留哪个
max = Math.max(max, right - left);
}
// 关键是right只能向后,就像个窗口一样,而算法一需要每次都二分去查找覆盖的点
return max;
}
Go语言代码
// 暴力求解 时间复杂度O(N^2)
func maxPoint(arr []int, L int) int {
res := 0
N := len(arr)
for i := 0; i < N; i++ {
pre := i - 1
for pre >= 0 && arr[i]-arr[pre] <= L {
pre--
}
if i-pre > res {
res = i - pre
}
}
return res
}
// 贪心+二分 时间复杂度O(N * logN)
func maxPoint2(arr []int, L int) int {
res := 0
N := len(arr)
for i := 0; i < N; i++ {
nearest := nearestIndex(arr, i, arr[i]-L)
if i-nearest+1 > res {
res = i - nearest + 1
}
}
return res
}
func nearestIndex(arr []int, R, value int) int {
L := 0
index := R
for L <= R {
mid := L + ((R - L) >> 1)
if arr[mid] >= value {
index = mid
R = mid - 1
} else {
L = mid + 1
}
}
return index
}
// 滑动窗口 时间复杂度O(N)
func maxPoint3(arr []int, L int) int {
res := 0
left, right, N := 0, 0, len(arr)
for left < N {
for right < N && arr[right]-arr[left] <= L {
right++
}
if right-left > res {
res = right - left
}
left++
}
return res
}