获取数组元素的索引比O(n)更快


104

给定我有一个巨大的数组,并从中得到一个值。我想获取数组中值的索引。还有其他方法,而不是致电Array#index获取吗?问题来自需要保持非常大的数组并调用Array#index大量的时间。

经过几次尝试,我发现通过存储具有字段的结构而不是值本身来缓存元素内部的索引(value, index)在性能方面迈出了一大步(获胜20倍)。

我仍然想知道是否有一种更方便的方法来查找en元素的索引而不进行缓存(或者有一种很好的缓存技术可以提高性能)。

Answers:


118

将数组转换为哈希。然后寻找钥匙。

array = ['a', 'b', 'c']
hash = Hash[array.map.with_index.to_a]    # => {"a"=>0, "b"=>1, "c"=>2}
hash['b'] # => 1

2
如果阵列很长,则最快
Kevin

17
如果存在重复的值,则取决于您的用例,这可能会出现问题。上面描述的方法将返回等效值或#rindex(值的最后一次出现)以获取#index等效结果,这意味着散列返回值的第一个索引,您需要在创建数组之前对数组进行反转哈希,然后从初始数组的总长度中减去返回的索引值-1。#(array.length-1)
-hash

2
转换为哈希表是否需要O(n)时间?我想如果要使用不止一次,那么哈希转换将更加高效。但是对于单次使用,遍历数组没有什么不同吗?
ahnbizcad

是的,如果这真的很重要,那么对于单次使用可能会更糟,因为哈希计算不会像比较表那样迅速短路。
彼得·德威斯

199

为什么不使用index或rindex?

array = %w( a b c d e)
# get FIRST index of element searched
puts array.index('a')
# get LAST index of element searched
puts array.rindex('a')

索引:http//www.ruby-doc.org/core-1.9.3/Array.html#method-i-index

rindex:http ://www.ruby-doc.org/core-1.9.3/Array.html#method-i-rindex


13
这正是OP所不希望的,因为它们的数组很大。Array#index为O(n),多次执行将导致性能下降。哈希查找为O(1)。
蒂姆(Tim)

4
@tim,好吧,我在回答该问题时不记得这是同一问题,也许OP稍后会修改该问题,这会使该答案无效。
罗杰

3
难道不是说当时是在特定时间编辑过的吗?
蒂姆(Tim)

嘿,是的。好吧,我和另外30个人正在阅读。我猜:/
罗杰(Roger)

9

其他答案未考虑数组中多次列出条目的可能性。这将返回一个哈希值,其中每个键是数组中的唯一对象,每个值是对应于对象所在位置的索引数组:

a = [1, 2, 3, 1, 2, 3, 4]
=> [1, 2, 3, 1, 2, 3, 4]

indices = a.each_with_index.inject(Hash.new { Array.new }) do |hash, (obj, i)| 
    hash[obj] += [i]
    hash
end
=> { 1 => [0, 3], 2 => [1, 4], 3 => [2, 5], 4 => [6] }

这样可以快速搜索重复的条目:

indices.select { |k, v| v.size > 1 }
=> { 1 => [0, 3], 2 => [1, 4], 3 => [2, 5] }

6

是否有充分的理由不使用哈希?查找是O(1)O(n)用于该阵列。


关键是-我正在调用#keys哈希,它将返回我正在使用的数组。尽管如此,我还是可能会考虑一下我的体系结构……
gmile

3

如果是排序数组,则可以使用二进制搜索算法(O(log n))。例如,使用以下功能扩展Array-class:

class Array
  def b_search(e, l = 0, u = length - 1)
    return if lower_index > upper_index

    midpoint_index = (lower_index + upper_index) / 2
    return midpoint_index if self[midpoint_index] == value

    if value < self[midpoint_index]
      b_search(value, lower_index, upper_index - 1)
    else
      b_search(value, lower_index + 1, upper_index)
    end
  end
end

3
实际上,它并不难读。第一部分,如果下界大于上限(递归已提交),则返回。第二部分通过将中点m与该点的值与e进行比较来检查是否需要左侧或右侧。如果我们没有想要的答案,我们会递归。
ioquatix 2014年

我认为这样做对人们的自我批评比编辑更好。
安德烈·菲格瑞多

2

综合使用@sawa的答案和此处列出的注释,您可以在数组类上实现“快速”索引和rindex。

class Array
  def quick_index el
    hash = Hash[self.map.with_index.to_a]
    hash[el]
  end

  def quick_rindex el
    hash = Hash[self.reverse.map.with_index.to_a]
    array.length - 1 - hash[el]
  end
end

2

如果您的数组具有自然顺序,请使用二进制搜索。

使用二进制搜索。

二进制搜索具有O(log n)访问时间。

以下是有关如何使用二进制搜索的步骤,

  • 数组的顺序是什么?例如,按名称排序吗?
  • 使用bsearch找到的元素或指数

代码示例

# assume array is sorted by name!

array.bsearch { |each| "Jamie" <=> each.name } # returns element
(0..array.size).bsearch { |n| "Jamie" <=> array[n].name } # returns index

0

我仍然想知道是否有一种更方便的方法来查找en元素的索引而不进行缓存(或者有一种很好的缓存技术可以提高性能)。

您可以使用二进制搜索(如果数组是有序的,并且存储在数组中的值在某种程度上是可比的)。为此,您需要能够告诉二进制搜索是在当前元素的“左侧”还是“右侧”查找。但是我相信index,如果要从同一数组中获取元素,则在插入时存储at然后再使用它并没有错。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.