我的目标是输入一个键和一个数组,然后使用二进制搜索输出该数组中小于或等于该键的值的数量。
这是我的代码:
import java.util.*;
import java.io.*;
public class search {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int key = scan.nextInt();
int size = scan.nextInt();
int [] array = new int [size];
for (int i = 0;i < size ;i++ ) {
array[i] = scan.nextInt();
}
Arrays.sort(array);
System.out.println(binary(key, array));
}
public static int binary (int key, int [] array){
int lo = 0;
int hi = array.length - 1;
while (lo < hi){
int mid = (lo + hi) / 2;
if (array[mid] <= key){
lo = mid;
}
else {
hi = mid - 1;
}
}
return lo + 1;
}
}
使用数据键= 5,数组= {2,4,6,7},程序运行正常。但是当三个值小于或等于密钥时,它就会变得混乱。例如,key = 5,array = {2,4,5,6}产生无限循环。我找到了原因,但我不知道如何绕过它。
基本上mid
值一直计算为相同的值。我该怎么做才能解决这个问题?如果代码本质上是错误的,那么这意味着the set solution for a USACO problem是错误的。
答案 0 :(得分:0)
您的算法似乎很好。但我发现为mid分配值存在问题。
int mid = (hi+lo)/2
想想你的情况
hi=4
mid = 3
现在mid的新值将是(3 + 4)/ 2 = 3(整数除法)
所以循环将继续运行而不会中断。
在这种情况下,你可以做的是通过检查这个条件来增加mid的值。
但更有效的是它似乎更好地检查mid的值重复,然后打破循环。 最后检查array [hi]的值是否与数组[mid]
相同希望这会有所帮助..
祝你有愉快的一天.. :)
修改强>
更好的做法是
将您的while循环更改为
while(mid<hi+1){
}
然后检查循环后值的相等性。
OR 只需设置
mid = (mid+hi+1)/2
答案 1 :(得分:0)
样品溶液看起来很好。您的代码存在问题,mid = (lo + hi) / 2
向lo
方向转,这是一个问题,因为更新案例为lo = mid
和hi = mid - 1
。 hi == lo + 1
时,在第一种情况下,没有进展。你应该像样本一样向上舍入:mid = (lo + hi + 1) / 2
(警告:可能在长数组上溢出;检查你的约束)。
该示例还会检查第一个值是否小于键。
答案 2 :(得分:0)
在你的循环中,你必须使你的间隔变小,你还必须排除你刚才看到的元素。你离开时会这样做,但是当你走右时却不这样做。
您还会错过长度为1的间隔,因为您使用包含的下限和上限。您的循环条件应为lo <= hi
。
最后,你返回太多:当键小于第一个元素时,结果应为0,当它大于最后一个元素时,它应该是数组长度。
所以:
static int binary(int key, int[] array)
{
int lo = 0;
int hi = array.length - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (array[mid] <= key) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return lo;
}
在我看来,最好使用独占上限,就像Java通常那样。 (例如,长度为n
的数组在indoces 0到n - 1
中有元素,上限n
在有效范围之外。)如果没有其他内容,则它与其他Java一致码。所以:
static int binary(int key, int[] array)
{
int lo = 0;
int hi = array.length;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (array[mid] <= key) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}