使用bsearch查找用于将新元素插入到排序数组中的索引

Jon*_*nah 10 ruby bsearch

我有一个排序的唯一数组,并希望有效地插入一个不在数组中的元素,如下所示:

a = [1,2,4,5,6]
new_elm = 3
insert_at = a.bsearch_index {|x| x > new_elm } # => 2
a.insert(insert_at, new_elm) # now a = [1,2,3,4,5,6]
Run Code Online (Sandbox Code Playgroud)

该方法bsearch_index不存在:only bsearch,返回匹配元素而不是匹配元素的索引.有没有内置的方法来实现这一目标?

wva*_*aal 10

您可以使用Enumerator返回的对象返回each_with_index嵌套的[value, index]对数组,然后在该数组上执行二进制搜索:

a = [1,2,4,5,6]
new_elm = 3

index = [*a.each_with_index].bsearch{|x, _| x > new_elm}.last
=> 2

a.insert(index, new_elm)
Run Code Online (Sandbox Code Playgroud)

编辑:

我用一个长度数组运行一些简单的基准测试来回答你的问题1e6 - 1:

require 'benchmark'

def binary_insert(a,e)
  index = [*a.each_with_index].bsearch{|x, _| x > e}.last
  a.insert(index, e)
end

a = *1..1e6
b = a.delete_at(1e5)
=> 100001

Benchmark.measure{binary_insert(a,b)}
=> #<Benchmark::Tms:0x007fd3883133d8 @label="", @real=0.37332, @cstime=0.0, @cutime=0.0, @stime=0.029999999999999805, @utime=0.240000000000002, @total=0.2700000000000018> 
Run Code Online (Sandbox Code Playgroud)

考虑到这一点,您可以考虑尝试使用堆或trie而不是数组来存储您的值.特别是堆具有恒定的插入和移除时间复杂性,使其成为大型存储应用的理想选择.在这里查看这篇文章:Ruby算法:排序,trie和堆


Uri*_*ssi 9

怎么用SortedSet?:

require 'set'

a = SortedSet.new [1,2,4,5,6]
new_elm = 3
a << new_elm # now a = #<SortedSet: {1, 2, 3, 4, 5, 6}>
Run Code Online (Sandbox Code Playgroud)

SortedSet使用rbtree.实现.我做了以下基准测试:

def test_sorted(max_idx)
  arr_1 = (0..max_idx).to_a
  new_elm = arr_1.delete(arr_1.sample)
  arr_2 = arr_1.dup
  set_1 = SortedSet.new(arr_1)
  Benchmark.bm do |x|
    x.report { arr_1.insert(arr_1.index { |x| x > new_elm }) }
    x.report { arr_2.insert([*arr_2.each_with_index].bsearch{|x, _| x > new_elm}.last) }
    x.report { set_1 << new_elm }
  end
end
Run Code Online (Sandbox Code Playgroud)

结果如下:

test_sorted 10_000
# =>       user     system      total        real
# =>   0.000000   0.000000   0.000000 (  0.000900)
# =>   0.010000   0.000000   0.010000 (  0.001868)
# =>   0.000000   0.000000   0.000000 (  0.000007)

test_sorted 100_000
# =>       user     system      total        real
# =>   0.000000   0.000000   0.000000 (  0.001150)
# =>   0.000000   0.010000   0.010000 (  0.048040)
# =>   0.000000   0.000000   0.000000 (  0.000013)

test_sorted 1_000_000
# =>       user     system      total        real
# =>   0.040000   0.000000   0.040000 (  0.062719)
# =>   0.280000   0.000000   0.280000 (  0.356032)
# =>   0.000000   0.000000   0.000000 (  0.000012)
Run Code Online (Sandbox Code Playgroud)


eXa*_*eXa 6

Ruby 2.3.1 引入了bsearch_index因此问题现在可以这样解决:

a = [1,2,4,5,6]
new_elm = 3

index = a.bsearch_index{|x, _| x > new_elm}
=> 2

a.insert(index, new_elm)
Run Code Online (Sandbox Code Playgroud)


ste*_*lag 5

“该方法bsearch_index不存在”:Ruby 2.3引入了bsearch_index。(在方法名称存在之前就获得它的名称的荣誉)。