【问题标题】:Binary search - why ceil?二进制搜索 - 为什么选择 ceil?
【发布时间】:2017-09-25 00:30:25
【问题描述】:

我正在研究二分搜索算法,我见过很多次这样写的算法(这是C++,但这里的语言不是那么重要):

    int start = 0;
    int end = vec.size() - 1;       
    do {
        int mid = (lo + hi) / 2;
        if (target < vec[mid])
          start = mid + 1;
        else if (target > vec[mid])
          end = mid - 1;
        else
          // found
    } while (start <= end);

不过我也见过这样的实现:

    int start = 0;
    int end = vec.size() - 1;       
    do {
        int mid = (int)ceil((lo + hi) / 2.0);
        if (target < vec[mid])
          start = mid + 1;
        else if (target > vec[mid])
          end = mid - 1;
        else
          // found
    } while (start <= end);

两者似乎都有效。我应该得到ceil 并执行第二种情况的浮点运算而不是使用第一个版本,是否有任何正确性或性能原因?

【问题讨论】:

  • 注意(lo + hi + 1) / 2可以避免浮点数。
  • 我想这基本上在这里得到了回答:stackoverflow.com/questions/27655955/…(因此很想标记为重复)。
  • 请注意,您的中点公式错误,因为left + right 有溢出的风险。更好的做法是:(int)(((unsigned)lo + (unsigned)hi) / 2),它为您提供了所需的二进制补码环绕行为。 (另请参阅this answer,尽管它是在 C# 的上下文中。有关此的大量信息可通过 Google 在线获取。)
  • @CodyGray:实际上,制作索引 unsigned(或者更好的是,size_t从一开始就是合乎逻辑的事情做...
  • Kaidul 的回答是正确的,但见stackoverflow.com/questions/39416560/…

标签: c++ algorithm binary-search


【解决方案1】:

int mid = (lo + hi) / 2:

当 [left, right] 之间的数组大小为奇数时,您正在通过取两个潜在中间元素的左侧元素来决定 mid 元素,即对于数组 [4, 5],您的中间值为 4。所以没有floor 中的任何 ceil,该部门的工作方式与 floor 非常相似。

(int)ceil((lo + hi) / 2.0);:

当 [left, right] 之间的数组大小为奇数时,即 [4, 5] 您的中间值为 5 时,您通过取两个潜在中间元素中的右侧元素来决定 mid 元素。

所以这两种选择都会起作用,因为您根据某些有效条件(target &lt; vec[mid]target &gt; vec[mid])放弃/参与其中,分区点在这里并不重要。

另一件事是,在像int mid = (lo + hi) / 2 这样的操作过程中,如果总和超过整数范围,则在添加lohi 时可能会遇到溢出。像mid = lo + (hi - lo) / 2 这样写是安全的,这将产生相同的输出。

希望对你有帮助!

编辑

所以两者都起作用只是因为我从新的中丢弃了中间元素 重新开始搜索时的搜索范围,对吧?

是的。如果您不丢弃 mid 元素,它将陷入无限循环,即 [4, 5],4 将始终被选为 mid 并且对于像 left = mid 这样的调用,它将创建一个无限循环。

【讨论】:

  • 所以两者都起作用只是因为我在重新开始搜索时丢弃来自新搜索范围的中间元素,对吗? (否则可能会死循环)
猜你喜欢
  • 1970-01-01
  • 2015-12-29
  • 2019-03-02
  • 1970-01-01
  • 1970-01-01
  • 2012-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多