【问题标题】:Inserting in order to a nested list按顺序插入嵌套列表
【发布时间】:2013-03-23 04:55:27
【问题描述】:

假设我有一个这样的嵌套列表:

nested_list=[[123,'Aaron','CA'],[124,'Bob','WY'],[125,'John','TX']]
insert_me=[122,'George','AL']

列表当前按每个子列表的中间值排序(按字母顺序),我想将值 insert_me 添加到嵌套列表中的正确位置。为了保持字母顺序,需要在其中包含“Bob”和“John”的列表之间添加它。我知道 bisect 通常会用于这样的任务,但不明白如何将 bisect 用于这样的嵌套列表。

【问题讨论】:

  • 最终,如果您要执行一大堆插入操作,树可能是更好的数据结构。

标签: python nested-lists


【解决方案1】:

请参阅 Python 文档中bisect 的示例:

与 sorted() 函数不同,它对 bisect() 没有意义 具有关键或反向参数的函数,因为这会导致 低效的设计(连续调用 bisect 函数不会 “记住”之前的所有键查找)。

相反,最好搜索预先计算的键列表以找到 相关记录的索引:

>>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
>>> data.sort(key=lambda r: r[1])
>>> keys = [r[1] for r in data]         # precomputed list of keys
>>> data[bisect_left(keys, 0)]
('black', 0)
>>> data[bisect_left(keys, 1)]
('blue', 1)
>>> data[bisect_left(keys, 5)]
('red', 5)
>>> data[bisect_left(keys, 8)]
('yellow', 8)

所以在你的情况下:

nested_list = [[123,'Aaron','CA'],[124,'Bob','WY'],[125,'John','TX']]
insert_me = [122,'George','AL']                                
keys = [r[1] for r in nested_list]
nested_list.insert(bisect.bisect_left(keys,insert_me[1]),insert_me)
[[123, 'Aaron', 'CA'],
 [124, 'Bob', 'WY'],
 [122, 'George', 'AL'],
 [125, 'John', 'TX']]

为避免每次都重新构建keys,请将新值也插入keys

keys.insert(bisect_left(keys,insert_me[1]),insert_me[1])

更新:

对插入/对分、追加/排序和 heapq 解决方案进行了一些性能比较:

# elements  heapq   insert/bisect  append/sorted
10,000      0.01s   0.08s           2.43s         
20,000      0.03s   0.28s          10.06s
30,000      0.04s   0.60s          22.81s

【讨论】:

  • 这样做的问题是,每次您 insert 一些会破坏您的 O(logn) 效率的东西时,您都需要重新构建您的密钥。 (当然,insert 已经是 O(n) 所以......这已经比你想要的更糟糕了......)
  • 但是为了保持 O(nlogn) 效率,不是每次都重新构建的键列表不能被缓存吗?
  • 您随后也可以使用 bisect_left 插入键中......所以 2O(n)。但我同意 mgilson 的观点——如果要进行许多插入,树结构可能更适合。
  • @isedev - 关于更新键列表的好点。出于某种原因,我没有想到这一点。如果最终列表真的很大,我仍然认为列表不是正确的数据结构。某种形式的 AVL 树可能更好。
【解决方案2】:

我会为您的问题使用heap 的专业化。从this answer 获取堆类,您的代码将是:

import heapq

class MyHeap(object):
    def __init__(self, initial=None, key=lambda x:x):
        self.key = key
        if initial:
            self._data = [(key(item), item) for item in initial]
            heapq.heapify(self._data)
        else:
            self._data = []

    def push(self, item):
        heapq.heappush(self._data, (self.key(item), item))

    def pop(self):
        return heapq.heappop(self._data)[1]

h = MyHeap([[123,'Aaron','CA'],[124,'Bob','WY'],[125,'John','TX']], key=lambda x:x[1])
h.push([122,'George','AL'])
for _ in xrange(4):
    print h.pop()

您使用push 添加的每个列表都将相对于第二个元素(我们在构造函数中使用key=lambda x:x[1] 参数控制)按顺序排列。通过调用pop,您可以按顺序获取元素。

【讨论】:

    【解决方案3】:

    您可以使用sorted() 按字母顺序排列列表。

    nested_list=[[123,'Aaron','CA'],[124,'Bob','WY'],[125,'John','TX']]
    insert_me=[122,'George','AL']
    
    nested_list.append(insert_me)
    nested_list=sorted(nested_list, key=lambda x:x[1])
    

    Sorted()

    【讨论】:

    • 这将非常低效 - 您在每次插入后对列表进行排序......另外,使用 operator.getitem(1) 而不是 lambda 更清洁(IMO)。
    • 没错,我确实考虑过这一点。然而,这样做的目的是在嵌套列表中重复插入新的子列表,并且每次插入后都必须对列表进行排序,这会极大地影响效率。
    • 是的,它会有点麻烦。如果仅在需要查看列表内容时这样做会更好。
    猜你喜欢
    • 1970-01-01
    • 2020-12-06
    • 2012-10-21
    • 1970-01-01
    • 2017-03-14
    • 2012-08-23
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    相关资源
    最近更新 更多