【问题标题】:Finding first and last index of some value in a list in Python在 Python 的列表中查找某个值的第一个和最后一个索引
【发布时间】:2010-10-06 01:28:25
【问题描述】:

是否有任何内置方法属于列表的一部分,可以为我提供某个值的第一个和最后一个索引,例如:

verts.IndexOf(12.345)
verts.LastIndexOf(12.345)

【问题讨论】:

  • 我也在阅读它们,但对 python 一无所知会使事情变得更加困难。我觉得它的语法很神秘。
  • 如果您的列表已排序,那么您可能需要查看 bisect 模块 docs.python.org/3/library/bisect.html>。
  • 在这里使用内置函数枚举和反转查看 Dikei 的精彩回答:stackoverflow.com/questions/9836425/…

标签: python list search


【解决方案1】:

使用max + enumerate 获取最后一个元素的出现

rindex = max(i for i, v in enumerate(your_list) if v == your_value)

【讨论】:

    【解决方案2】:

    也许是找到 last 索引的两种最有效的方法:

    def rindex(lst, value):
        lst.reverse()
        i = lst.index(value)
        lst.reverse()
        return len(lst) - i - 1
    
    def rindex(lst, value):
        return len(lst) - operator.indexOf(reversed(lst), value) - 1
    

    两者都只占用 O(1) 额外空间,并且第一个解决方案的 两个 就地反转比创建反向副本要快得多。让我们将其与之前发布的其他解决方案进行比较:

    def rindex(lst, value):
        return len(lst) - lst[::-1].index(value) - 1
    
    def rindex(lst, value):
        return len(lst) - next(i for i, val in enumerate(reversed(lst)) if val == value) - 1
    

    基准测试结果,我的解决方案是红色和绿色的:

    这是为了在一百万个数字的列表中搜索一个数字。 x 轴表示搜索元素的位置:0% 表示它位于列表的开头,100% 表示它位于列表的末尾。所有解决方案在位置 100% 处最快,两个 reversed 解决方案几乎没有时间,双反向解决方案需要一点时间,而反向复制需要很多时间。

    仔细看看右端:

    在位置 100% 处,反向复制解决方案和双反向解决方案将所有时间都花在了反转上(index() 是即时的),因此我们看到两个原地反转的速度大约是原来的 7 倍就像创建反向副本一样。

    上面是lst = list(range(1_000_000, 2_000_001)),它几乎在内存中按顺序创建了 int 对象,这对缓存非常友好。让我们在用random.shuffle(lst) 洗牌之后再做一次(可能不太现实,但很有趣):

    正如预期的那样,一切都变慢了。反向复制解决方案遭受的损失最大,在 100% 时,它现在需要的时间大约是双反向解决方案的 32 倍 (!)。而enumerate-解决方案现在是第二快的,仅次于位置 98%。

    总体而言,我最喜欢operator.indexOf 解决方案,因为它是所有位置的后半部分或四分之一中最快的解决方案,如果您实际上正在为某事做rindex,这可能是更有趣的位置。而且它只比早期位置的双反转解决方案慢一点。

    在 Windows 10 Pro 1903 64 位上使用 CPython 3.9.0 64 位完成所有基准测试。

    【讨论】:

    • 请注意reverse; index; reverse 不是线程安全的。
    【解决方案3】:

    Python 列表具有index() 方法,您可以使用该方法查找列表中项目第一次出现 的位置。请注意,list.index() 在列表中不存在该项目时引发ValueError,因此您可能需要将其包装在try/except

    try:
        idx = lst.index(value)
    except ValueError:
        idx = None
    

    要有效地查找列表中项目最后次出现的位置(即不创建反向中间列表),您可以使用此函数:

    def rindex(lst, value):
        for i, v in enumerate(reversed(lst)):
            if v == value:
                return len(lst) - i - 1  # return the index in the original list
        return None    
    
    print(rindex([1, 2, 3], 3))     # 2
    print(rindex([3, 2, 1, 3], 3))  # 3
    print(rindex([3, 2, 1, 3], 4))  # None
    

    【讨论】:

      【解决方案4】:
      s.index(x[, i[, j]])
      

      在 s 中第一次出现 x 的索引(在索引 i 处或之后,索引 j 之前)

      【讨论】:

        【解决方案5】:

        如果您正在搜索myvaluemylist 中最后一次出现的索引:

        len(mylist) - mylist[::-1].index(myvalue) - 1
        

        【讨论】:

        • 如果myvalue 不存在于mylist 中,这将抛出ValueError
        【解决方案6】:

        这个方法可以比上面更优化

        def rindex(iterable, value):
            try:
                return len(iterable) - next(i for i, val in enumerate(reversed(iterable)) if val == value) - 1
            except StopIteration:
                raise ValueError
        

        【讨论】:

        • “优化”意味着更节省空间,但速度会慢 50%。
        【解决方案7】:

        序列有一个方法index(value),它返回第一次出现的索引——在你的情况下,这将是verts.index(value)

        您可以在verts[::-1] 上运行它以找出最后一个索引。这里是len(verts) - 1 - verts[::-1].index(value)

        【讨论】:

        • 谢谢,list[::-1] 是怎么执行的?顶点[::-1]?
        • 顺便说一句 verts[::-1] 只是反转列表,对吗?所以我必须补偿索引,对吧?
        • 当然,它会是:len(verts) - 1 - verts[::-1].index(value)
        • 这可以简化为 guild[::-1][0]
        【解决方案8】:

        作为一个小辅助函数:

        def rindex(mylist, myvalue):
            return len(mylist) - mylist[::-1].index(myvalue) - 1
        

        【讨论】:

          猜你喜欢
          • 2019-12-09
          • 1970-01-01
          • 2021-04-14
          • 2013-09-15
          • 2021-04-20
          • 2018-05-20
          • 2019-08-12
          • 1970-01-01
          • 2014-12-21
          相关资源
          最近更新 更多