【问题标题】:Split sorted list into two lists将排序列表拆分为两个列表
【发布时间】:2022-08-09 11:01:06
【问题描述】:

我试图将排序的整数列表拆分为两个列表。第一个列表将包含n 下的所有整数,第二个列表将包含n 上的所有整数。请注意,n 不必在原始列表中。

我可以很容易地做到这一点:

under = []
over  = []
for x in sorted_list:
    if x < n:
        under.append(x)
    else
        over.append(x)

但似乎应该可以以更优雅的方式执行此操作,因为知道列表已排序。来自itertoolstakewhiledropwhile 听起来像是解决方案,但我会重复列表两次。

从功能上讲,我能做的最好的就是:

i = 0
while sorted_list[i] < n:
    i += 1

under = sorted_list[:i]
over = sorted_list[i:]

但我什至不确定它是否真的比只遍历列表两次更好,而且它绝对不是更优雅。

我想我正在寻找一种方法来获取takewhile 返回的列表和剩余的列表,也许是成对的。

  • 更好地使用二进制搜索来查找索引

标签: python list split sortedlist


【解决方案1】:

这里正确的解决方案是the bisect module。使用bisect.bisect 找到n 右侧的索引(或者如果它丢失则插入的索引),然后围绕该点进行切片:

 import bisect # At top of file

 split_idx = bisect.bisect(sorted_list, n)
 under = sorted_list[:split_idx]
 over = sorted_list[split_idx:]

虽然任何解决方案都将是O(n)(毕竟您必须复制元素),但比较通常比简单的指针复制(以及相关的引用计数更新)更昂贵,并且bisect 减少了比较工作将list 排序为O(log n),因此这通常(在较大的输入上)会简单地逐个元素地迭代和复制,直到找到分割点。

如果您希望nover 而不是under 结尾,请使用bisect.bisect_left(查找n 的最左侧索引)而不是bisect.bisect(等效于bisect.bisect_right)。

【讨论】:

  • 关于速度,我想说这不是真正的“对象比较与指针副本”,而是“对象比较与引用计数器增量”。也许它可以在 O(s) 中完成,其中 s 是第二部分中的元素数。使用over = sorted_list[split_idx:]del sorted_list[split_idx:]under = sorted_list(不确定del 的实际复杂性)。
  • @KellyBundy:当然,引用计数增量在碎片内存上运行,因此它们可能比指针副本贵一点。无论如何,您都必须支付它们(指针副本也是如此),并且它们在比较成本旁边都是微不足道的。是的,如果您不需要保留原始sorted_list,您可以将其用作underdel 复制到over 的部分(dellist 的末尾实现尽可能高效;最坏的情况是,它重新分配到可用空间,并且必须 O(n) 复制幸存的指针,而不触及 refcnts)。
  • 指针副本也一样吗?我不这么认为。它们在内存中是连续的,并且是按顺序处理的。见this。复制列表比重复/反向/前置/取消前置(类似于纯指针复制)慢约 30 倍。
  • @KellyBundy:对不起,我并不是说你的优化没有通过避免指针复制来保存任何东西(我最后的括号承认你的优化重用sorted_list确实避免了一半list的refcnt操作工作,我做到了在评论的开头说 refcnt 操作比原始指针副本更昂贵)。我的意思是,在我的代码和 OP 提出的解决方案中(所有这些都保留 sorted_list 完整),您必须支付指针复制和 refcnt 操作成本(而且它们比比较便宜很多)。
  • this,复制排序列表比复制未排序列表慢约 5 倍。据我了解,这种巨大差异完全是由于增加了引用计数(并且在排序列表中对缓存不友好)。
【解决方案2】:

我将使用以下方法,在其中找到索引并使用切片创建underover

sorted_list = [1,2,4,5,6,7,8]
n=6

idx = sorted_list.index(n)
under = sorted_list[:idx]
over = sorted_list[idx:]

print(under)
print(over)

输出(与您的代码相同):

[1, 2, 4, 5]
[6, 7, 8]

编辑:据我了解,这里错误的问题是找到最近索引的适应解决方案:

import numpy as np

sorted_list = [1,2,4,5,6,7,8]
n=3

idx = np.searchsorted(sorted_list, n)
under = sorted_list[:idx]
over = sorted_list[idx:]

print(under)
print(over)

输出:

[1, 2]
[4, 5, 6, 7, 8]

【讨论】:

  • 坦克你,但我应该更详细地描述这个问题。我实际上并不知道列表中有哪些整数,因此可能没有n 来查找索引。
  • 如果您不知道在哪里,您如何确定拆分点?或者如果nNone,是否应该有一个默认的分割点?
  • 考虑sorted_list = [1,3]n = 2。在这种情况下under = [1]over = [3]。然而,2 不在 sorted_list 中。
  • 好,我懂了。我添加了另一个解决方案,它也为缺失值提供了正确的索引。