如何解决使用 BinarySearch 的变体在双调数组中查找最大值
这是我的代码,其中 a
是要查找最大值的数组(每个元素都是不同的)。从我的角度来看,如果 a[mid]
小于 a[mid-1]
,那么最大值的位置应该在 [lo,mid-1]
的范围内。然而,当我实现我的想法时,程序是无止境的。解决方案是使用 [lo,mid]
作为下一次迭代。
我的问题是:为什么我不应该在第 9 行中使用 mid-1
而不是 mid
?
第一次编辑:有人问我为什么不直接排序并选择第一个元素? 因为问题是在双调数组中找到一个键(元素先升后降)。我的解决办法是找到数组的最大值,将其分成两部分,分别使用BinarySearch来查找key。如果我对数组进行排序,我将破坏原来的顺序。
第二次编辑:添加更多详细代码和预期输出。
public static int getMaxIndex(int[] a) {
int hi = a.length - 1;
int lo = 0;
while (lo < hi && a[lo] <= a[lo+1]) {
int mid = lo + (hi - lo) / 2;
if (a[mid] > a[mid-1]) {
lo = mid;
} else {
hi = mid - 1; // Should be mid,not mid-1,why?
}
}
return lo;
}
public static void main(String[] args) {
int[] a = {1,2,3,5,7,9,8,6,4}; // Bitonic array
System.out.println(getMaxIndex(a)); // Should be 5
}
解决方法
双音阵列
数字严格递增,索引后数字严格递减
例如:[15,20,26,5,1]
如果数组只包含唯一元素,则可以使用以下条件找到最大值
- 迭代
left index < right index
- 计算
middle index
(在没有单个中间索引的情况下向左计算) - 如果中间的值小于下一个元素,则向右移动
- else 向左向左移动
arr[mid] < arr[mid + 1]
永远不会抛出越界异常,因为 left < right
在循环中保持不变。因此 mid
(index) 将始终小于 right
(index) 并且因此在 mid
int left = 0,right = arr.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] < arr[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return arr[left]; // or arr[right]
15 20 26 5 1
l=0,r=4 l m r
m=2
a[m] > a[m+1] l r
so,r=m
l=0,r=2 l m r
m=1
a[m] < a[r+1] l,r
m=l+1
Now exit while loop l == r and return value at l or r
OP 代码分析
- 一个简单的可重现示例是 [1,2] - 这将导致
lo
停留在相同的索引上并导致无限循环。 - 任何减少到
lo + 1 == hi
和arr[lo] < arr[hi]
的数据集都会将mid
设置为lo
(lo + (hi - lo) / 2
将是lo
),因此分配lo = mid
导致无限循环
您遇到的问题是您并不总是更改 mid
。在您的示例中,在某一时刻 lo
是 4 并指向数组中的 7
而 hi
是 5 并指向 9
。在 lo
和 hi
仅相距 1 的情况下,mid
设置为 lo
。在这里,您正在比较 a[mid] > a[mid-1]
。问题 1,mid-1
超出范围并且在极少数情况下给出 ArrayIndexOutOfBoundsException
。其次,如果条件为 true
,则将 lo
设置为 mid
,这是它已有的值。你无处可去。您处于无限循环中。
我试过你的方法
System.out.println(getMaxIndex(new int[] { 3,8 })); // Expecting 1
我得到了 java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 2
解决方案在 Horse 的回答中。
,这里,bitonic
这个词的意思是,数组按升序排列,经过某个时间点(可能是)它开始减少。
同样,您必须找到数组开始减少的枢轴点(如果存在),如果您能够找到该点,that's it
,这个枢轴点就是您的答案。
所以我附上一些输入/输出序列以使其更清晰:
{1,2,3,7,9,8,6,4}; --> should return 5 (index at which element is maximum)
{15,1} --> should return 2 (index at which element is maximum)
{0,1,3} --> should return 3 (index at which element is maximum)
现在你必须修改你的代码,如下所示:
public static int getMaxIndex(int[] arr) {
int left = 0,right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if(mid == 0 || mid == arr.length-1) {
// if no such pivotal point exist in sequence/array
return mid;
}else if(arr[mid] > arr[mid+1] && arr[mid] > arr[mid-1]){
// it's the pivotal point,where element at left side smaller and element at right side too
return mid;
}
if (arr[mid] < arr[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return -1;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。