也许是找到 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 位完成所有基准测试。