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

时间:2014-05-05 20:56:54

标签: 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]

方法bsearch_index不存在:只有bsearch,它返回匹配元素而不是匹配元素的索引。有没有内置的方法来实现这个目标?

6 个答案:

答案 0 :(得分: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)

修改

我使用长度为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> 

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

答案 1 :(得分: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}>

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

得到以下结果:

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)

答案 2 :(得分:3)

&#34;方法bsearch_index不存在&#34;:Ruby 2.3引入bsearch_index。 (感谢在方法名称存在之前获取方法名称)。

答案 3 :(得分:2)

试试这个

var animal = {};
var one = 'sound';
var two = 'age';

animal['cat'] = {};
animal['cat'][one] = 'meow';
animal['cat'][two] = 9;

console.log(animal);

这使用(0...a.size).bsearch { |n| a[n] > new_element } 上定义的bsearch来搜索数组,从而返回索引。

性能优于Range,它会实现each_with_index临时数组元组,从而阻塞垃圾回收。

答案 4 :(得分:1)

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)

答案 5 :(得分:-2)

index方法接受一个块,并返回块为真的第一个索引

a = [1,2,4,5,6] 
new_elem = 3
insert_at = a.index{|b| b > new_elem}
#=> 2
a.insert(insert_at, new_elm) 
#=>[1,2,3,4,5,6]