【问题标题】:Get index of array element faster than O(n)比 O(n) 更快地获取数组元素的索引
【发布时间】:2011-09-08 16:43:14
【问题描述】:

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

经过几次尝试后,我发现 缓存 通过使用 (value, index) 字段而不是值本身存储结构来在元素内部建立索引,从而在性能上迈出了一大步(20 倍的胜利)。

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

【问题讨论】:

    标签: ruby arrays performance indexing


    【解决方案1】:

    为什么不使用 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

    【讨论】:

    • 这正是 OP 所说的他们不想要的,因为他们的数组很大。 Array#index 是 O(n) 并且多次这样做会影响性能。哈希查找是 O(1)。
    • @tim,嗯,我不记得在我回答的时候这是 same 问题,也许 OP 稍后修改了这个问题,这会使这个问题无效回答。
    • 那不是说是在特定的时间编辑过的吗?
    • 呵呵,没错。好吧,我和另外 30 个人当时正在阅读它。我猜:/
    【解决方案2】:

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

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

    【讨论】:

    • 如果数组很长,最快
    • 根据您的用例,如果存在重复值,这可能会出现问题。上面描述的方法将返回等价或#rindex(值的最后出现)要获得#index等价结果,这意味着哈希返回值的第一个索引,您需要在创建之前按照反转数组的方式做一些事情哈希然后从初始数组的总长度中减去返回的索引值 - 1. # (array.length - 1 ) - hash['b']
    • 转换成哈希不是需要 O(n) 时间吗?我想如果它会被多次使用,那么哈希转换的性能会更高。但是对于单次使用,和遍历数组没有区别吗?
    • 是的,如果它真的很重要,那么单次使用可能会更糟,因为哈希计算不会像比较那样快速短路。
    • 如果有重复元素,@hololeap 的回答会更完整。
    【解决方案3】:

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

    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] }
    

    【讨论】:

    • 这是一个比@sawa 更完整的答案。
    【解决方案4】:

    是否有充分的理由不使用哈希?数组的查找是 O(1)O(n)

    【讨论】:

    • 重点是——我在哈希上调用#keys,它返回一个我正在使用的数组。不过,我可能也会考虑我的架构......
    【解决方案5】:

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

    使用二分搜索。

    二分搜索有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
    

    【讨论】:

      【解决方案6】:

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

      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
      

      【讨论】:

      • 它实际上并不难阅读。第一部分,如果下限大于上限(递归已归档),则返回。第二部分通过将中点 m 与该点的值与 e 进行比较来检查我们是否需要左侧或右侧。如果我们没有我们想要的答案,我们会递归。
      • 我认为它对人们投反对票而不是编辑的自我感觉更好。
      【解决方案7】:

      结合@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
      

      【讨论】:

        【解决方案8】:

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

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

        【讨论】:

          猜你喜欢
          • 2018-01-13
          • 2019-05-25
          • 2014-08-25
          • 2011-02-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-12-15
          相关资源
          最近更新 更多