【问题标题】:My basic binary search method does not work for one specific value in an int array in Java我的基本二进制搜索方法不适用于 Java 中 int 数组中的一个特定值
【发布时间】:2017-06-14 11:19:50
【问题描述】:

我是一个初学者 java 学习者,这里有一个小程序,它输出 int 数组中搜索到的 int 值的索引。这是代码

public static void main(String[] args) {

    int rank =findNumber(7, new int[]{2, 4, 7, 10, 11, 45, 50, 59, 60, 66, 69, 70, 79});
    System.out.println("Your number's index is " + rank);

}

public static int findNumber(int key, int... numbers) {
    int low = 0;
    int high = numbers.length - 1;
    int mid = (low + high) / 2;
    int rank = 0;
    for (int i = low; i < high; i++) {
        if (key < numbers[mid]) {
            high = mid;
            mid = (low + high) / 2;
        } else if (key > numbers[mid]) {
            low = mid;
            mid = (low + high) / 2;
        } else {
            rank = mid;
            return rank;
        }
    }
    return rank;
}

问题是它适用于数组中的所有数字,除了 7。我试图通过调试找到它,但一无所获。

谁能告诉我这里有什么问题?

【问题讨论】:

    标签: java arrays search binary int


    【解决方案1】:

    问题在于,即使您更改了lowhigh,循环仍依赖于变量i,该变量仅通过在循环的每次迭代结束时递增而改变。但是,二分查找的终止条件应该是当low &gt;= high。您现在拥有的循环条件i &lt; highlowhigh 之间的关系无关,这可能导致循环过早结束或在二分查找已经结束时继续循环。

    我建议做的是在 for 循环中初始化 low,将终止条件更改为 low &lt;= high,并制作更新序列 mid = (low + high)/2,因为您在每次迭代 for 循环后都这样做,除非您找到号码。然而,此时循环将终止返回找到的数字的索引。这是它的样子,

    public static int findNumber(int key, int... numbers) {
        int high = numbers.length - 1;
        int mid = (0 + high) / 2; //replaced low with 0 here
        int rank = 0;
        for (int low = 0; low <= high; mid = (low + high) / 2) {
            if (key < numbers[mid]) {
                high = mid - 1;
            } else if (key > numbers[mid]) {
                low = mid + 1;
            } else {
                rank = mid;
                return rank;
            }
        }
        return rank;
    }
    

    编辑:所以经过一些测试,我意识到另一个错误是您在方法中更新lowhigh 的方式。之前更新这些变量时,您只是在执行 high = midlow = mid ,但这样做是有风险的,因为这样做,您基本上仍将 numbers[mid] 保持在将在循环的下一次迭代中评估的范围内.这个数字 numbers[mid] 应该从范围中省略,因为键将小于、大于或等于 numbers[mid]。如果该键小于或大于numbers[mid],那么将它保持到下一个要通过二分搜索查看的数字范围是没有意义的。因此,解决方案是通过low = mid + 1high = mid - 1 更新低和高。

    【讨论】:

    • 我花了一段时间才弄明白,但我相信我修好了!
    • 说实话,在我看到你的解释之前,我完全不知道i 的行为。这是未检测到 7 号索引的主要原因。i 只是超过了它的high 限制,并且循环过早终止,正如您所描述的。您的最终值解决方案也适用于high&gt;=low。这正是程序捕获任何值所需要的迭代。非常感谢您的精彩解释和时间
    【解决方案2】:

    正如其他人所说,停止条件已关闭。以不同的方式考虑停止条件可能更容易。更改上限和下限,以便搜索数组的“一半”。您何时知道要查找的项目在数组中不存在?那是指数交叉的时候。低大于高。 (while low &lt;= high) { ... } 在这种情况下,您可以返回一个您知道数组中不应该存在的虚拟值。例如,如果您只存储正值,则返回 -1。否则你可以返回一个整数(非原始类型)来表示一个索引,如果找不到一个项目,特别是null

    【讨论】:

    • 如果密钥不存在怎么办?无限循环
    • 是的,主要问题是边界设置不正确,因为我一直在努力解决这个问题,所以我什至看不到“关键不存在”案例没有任何虚拟值。谢谢!
    【解决方案3】:

    我认为您的 for 循环有点错误。而不是

    for (int i = low; i < high; i++)
    

    应该是:

    for (int i = low; i <= high; i++)
    

    【讨论】:

      【解决方案4】:

      问题在于for 循环。您将循环传递的数量限制为high,但high 会从循环内更改。你的意思是像for (int i = low; i &lt; numbers.length - 1; i++) { ... } 这样的东西吗?

      【讨论】:

      • 不,循环边界打算在每次迭代时都会改变,因为它的目的是使键值成为两个上下边界的中间
      • 恐怕你误解了二分搜索的工作原理。你甚至没有在循环中使用i。作为练习,尝试将您的for 替换为我建议的内容并亲自查看。 :)
      • 他比你更了解它......你的循环将不必要地检查密钥是否不存在 his fixed code vs your code
      • @Selvin 当前算法并不能真正处理查找值不存在时的情况。它返回 0,这表明该元素作为数组的第一个元素存在。事实上,该算法适用于值“2”的事实是巧合。算法(错误地)没有找到值,所以它返回 0,这恰好是正确的答案。要查看这一点,只需将最后一个 return rank; 更改为 -1,然后测试 findNumber(2, new int[]{2, 4, 7, 10, 11, 45, 50, 59, 60, 66, 69, 70, 79});。它会返回-1。见here
      • 好点...另一方面,更改为 i &lt; numbers.length - 1 将找不到 79 ideone.com/frBSCg
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 2015-03-21
      • 2011-08-21
      • 1970-01-01
      • 2013-12-19
      • 2020-11-28
      相关资源
      最近更新 更多