问题描述:
给定一个有序数组arr,从左到右依次表示X轴上从左往右点的位置,给定一个正整数K,返回如果有一根长度为K的绳子,最多能盖住几个点,绳子的边缘点碰到X轴上的点,也算盖住。
思路一:
使用暴力解法。从数组的开头开始遍历,查找数组中遍历下标的前部分下标,知道不符合条件时停止遍历。
public static int test(int[] arr,int L){
int max = 0;
for (int i = 0;i<arr.length;i++){
int pre = i-1;
while (pre >=0 &&arr[i] - arr[pre] <= L){
pre--;
}
max = Math.max(max,i-pre);
}
return max;
}
思路二:
定义一个nearestIndex(int[] arr,int R,int value)方法,它的主要作用是从数组的0下表到R下表之间查找出最接近value值的数字,并返回其下标。从数组的开头开始遍历,通过nearestIndex方法找出数组中符合要求的下标,计算出两者之间的差值,全部遍历结束后,就可以查找出最大的差值,即为该题的解。(即把暴力解法中遍历下标前部分的方法使用二分法进行,时间复杂度从O(n)降到了O(logn))
public static int maxPoint1(int[] arr,int L){
int res = 1;
for (int i = 0;i <arr.length;i++){
int nearest = nearestIndex(arr,i,arr[i]-L);
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;
}
思路三:
滑动窗口,规定L,R判断此时R -> L是否大于给定的长度K ,一直增大R,直到找到此时以L开头时的最大长度记录此时的值。然后向右移动L继续上面的步骤。(只需要遍历数组一次即可完成)
public static int maxPoint2(int[] arr,int L){
int left = 0;
int right = 0;
int N = arr.length;
int max = 0;
while (left<N){
while (right<N && arr[right] - arr[left] <=L){
right++;
}
max = Math.max(max,right-(left++));
}
return max;
}
使用对数器进行验证
public static int[] generateArray(int len,int max){
int[] ans = new int[(int)(Math.random()*len)+1];
for (int i =0;i<ans.length;i++){
ans[i] = (int) (Math.random()*max);
}
Arrays.sort(ans);
return ans;
}
public static void main(String[] args) {
int len = 100;
int max = 1000;
int testTime = 100000;
System.out.println("测试开始!");
for (int i =0;i<testTime;i++){
int L = (int)(Math.random()*max);
int[] arr = generateArray(len,max);
int ans1 = maxPoint1(arr,L);
int ans2 = maxPoint2(arr,L);
int ans3 = test(arr,L);
if (ans1 != ans2 || ans2 != ans3){
System.out.println("Oops!");
break;
}
}
System.out.println("测试结束!");
}