二分查找

一些 Ruby 方法支持在集合中进行二分查找

Array#bsearch

返回通过给定的代码块确定的二分查找所选择的元素。

Array#bsearch_index

返回通过给定的代码块确定的二分查找所选择的元素的索引。

Range#bsearch

返回通过给定的代码块确定的二分查找所选择的元素。

如果没有给定代码块,这些方法都返回一个枚举器。

给定一个代码块,这些方法中的每一个都会从 self 返回一个元素(或元素索引),该元素由二分查找确定。该搜索会在 O(log n) 次操作中找到 self 中满足给定条件的元素,其中 n 是元素的计数。 self 应该被排序,但是不会进行检查。

有两种搜索模式

查找最小值模式

方法 bsearch 返回代码块返回 true 的第一个元素;代码块必须返回 truefalse

查找任意模式

方法 bsearch 返回某个代码块返回零的元素(如果有);代码块必须返回一个数值。

代码块不应该混合模式,有时返回 truefalse,有时返回数值,但是不会进行检查。

查找最小值模式

在查找最小值模式下,代码块必须返回 truefalse。进一步的要求(尽管没有检查)是,不存在索引 ij 使得

不那么正式地说:代码块是这样的,所有评估为 false 的元素都先于所有评估为 true 的元素。

在查找最小值模式下,方法 bsearch 返回代码块返回 true 的第一个元素。

示例

a = [0, 4, 7, 10, 12]
a.bsearch {|x| x >= 4 } # => 4
a.bsearch {|x| x >= 6 } # => 7
a.bsearch {|x| x >= -1 } # => 0
a.bsearch {|x| x >= 100 } # => nil

r = (0...a.size)
r.bsearch {|i| a[i] >= 4 } #=> 1
r.bsearch {|i| a[i] >= 6 } #=> 2
r.bsearch {|i| a[i] >= 8 } #=> 3
r.bsearch {|i| a[i] >= 100 } #=> nil
r = (0.0...Float::INFINITY)
r.bsearch {|x| Math.log(x) >= 0 } #=> 1.0

这些代码块在查找最小值模式下是有意义的

a = [0, 4, 7, 10, 12]
a.map {|x| x >= 4 } # => [false, true, true, true, true]
a.map {|x| x >= 6 } # => [false, false, true, true, true]
a.map {|x| x >= -1 } # => [true, true, true, true, true]
a.map {|x| x >= 100 } # => [false, false, false, false, false]

这将没有意义

a.map {|x| x == 7 } # => [false, false, true, false, false]

查找任意模式

在查找任意模式下,代码块必须返回一个数值。进一步的要求(尽管没有检查)是,不存在索引 ij 使得

不那么正式地说:代码块是这样的

在查找任意模式下,方法 bsearch 返回代码块返回零的某个元素,如果找不到这样的元素则返回 nil

示例

a = [0, 4, 7, 10, 12]
a.bsearch {|element| 7 <=> element } # => 7
a.bsearch {|element| -1 <=> element } # => nil
a.bsearch {|element| 5 <=> element } # => nil
a.bsearch {|element| 15 <=> element } # => nil

a = [0, 100, 100, 100, 200]
r = (0..4)
r.bsearch {|i| 100 - a[i] } #=> 1, 2 or 3
r.bsearch {|i| 300 - a[i] } #=> nil
r.bsearch {|i|  50 - a[i] } #=> nil

这些代码块在查找任意模式下是有意义的

a = [0, 4, 7, 10, 12]
a.map {|element| 7 <=> element } # => [1, 1, 0, -1, -1]
a.map {|element| -1 <=> element } # => [-1, -1, -1, -1, -1]
a.map {|element| 5 <=> element } # => [1, 1, -1, -1, -1]
a.map {|element| 15 <=> element } # => [1, 1, 1, 1, 1]

这将没有意义

a.map {|element| element <=> 7 } # => [-1, -1, 0, 1, 1]