我有对象,每个对象都有一个唯一的ID,这些对象被插入到各种列表中。这些对象需要经常从相应列表的中间删除,通常为O(n)
,所以我想将它们的位置保留在dict
中,并在O(1)
中检索对象的位置每次我想删除它。
class Node(object):
def __init__(self, lst_id, unique_id):
self.lst_id = lst_id
self.unique_id = unique_id
n1 = Node('a', 1)
n2 = Node('a', 2)
n3 = Node('b', 3)
node_lsts = {}
for node in [n1,n2,n3]:
if node.lst_id in node_lsts:
node_lsts[node.lst_id].append(node)
else:
node_lsts[node.lst_id] = [node]
nodes_hash = {n1.unique_id: n1, n2.unique_id: n2, n3.unique_id: n3}
ID_TO_REMOVE = 1
在上面的示例中,如果我只是调用del nodes_hash[ID_TO_REMOVE]
,node_lsts
中的相应对象即使从字典中删除也仍然存在 - 我应该如何将其从{{1}中的相应列表中删除}}?
在C ++中,我可以保持指向列出邻居的指针作为节点成员变量(链表)并通过其内存地址查找节点,获取指向其邻居的指针,取消该节点与其邻居的链接(从而将其从' list')最后释放节点。我试图复制这种行为。
答案 0 :(得分:1)
您可以使用嵌套在dict中的dict,而不是使用嵌套在dict中的列表:
key=a value=7 (1+2+2+1+1)
假设node[node.lst_id] = {node1.unique_id: node1, node2.unique_id: node2, ... }
是ID_TO_REMOVE
,您可以将其删除:
unique_id
完整代码:
node_to_remove = nodes_hash[ID_TO_REMOVE]
del node_lsts[node_to_remove.lst_id][node_to_remove.unique_id]
<强>输出强>
class Node(object):
def __init__(self, lst_id, unique_id):
self.lst_id = lst_id
self.unique_id = unique_id
def __repr__(self):
return "[lst_id: {}, unique_id: {}]".format(self.lst_id, self.unique_id)
n1 = Node('a', 1)
n2 = Node('a', 2)
n3 = Node('b', 3)
node_lsts = {}
for node in [n1,n2,n3]:
if not node.lst_id in node_lsts:
node_lsts[node.lst_id] = {}
node_lsts[node.lst_id][node.unique_id] = node
nodes_hash = {n1.unique_id: n1, n2.unique_id: n2, n3.unique_id: n3}
ID_TO_REMOVE = 1
print("node_lsts", node_lsts)
node_to_remove = nodes_hash[ID_TO_REMOVE]
print("node_to_remove", node_to_remove)
del node_lsts[node_to_remove.lst_id][node_to_remove.unique_id]
print("node_lsts", node_lsts)
由于我们现在使用字典,所有内容都在O(1)中完成,我们避免了在尝试从列表中删除元素时出现的性能问题。
答案 1 :(得分:1)
您可以在Python中轻松创建双向链表:
class DoublyLinkedListNode(object):
def __init__(self, unique_id, lst_id, left=None, right=None):
self.unique_id, self.lst_id = unique_id, lst_id
self.left = left
self.right = right
def remove(self):
if self.left is not None:
self.left.right = self.right
if self.right is not None:
self.right.left = self.left
self.left = None
self.right = None
def append(self, node):
node.left = self
node.right = self.right
if self.right is not None:
self.right.left = node
node.right = self.right
self.right = node
def prepend(self, node):
node.left = self.left
node.right = self
if self.left is not None:
self.left.right = node
self.left = node
def iter_left(self):
current = self
while current.left is not None:
yield current.left
current = current.left
def iter_right(self):
current = self
while current.right is not None:
yield current.right
current = current.right
作为链表,对任何给定节点都有 O(1)插入和删除。如果您可以通过字典或其他更适合的数据结构保留对每个节点的引用,这将允许您平均大小写 O(1)访问和快速顺序迭代。
class ListContainer(object):
def __init__(self):
self.lists = {}
self.node_mapping = {}
def append(self, node):
if node.lst_id in self.lists:
self.lists[node.lst_id].append(node)
else:
self.lists[node.lst_id] = node
self.node_mapping[node.unique_id] = node
def remove(self, node):
...
请注意,如果您的列表中只有几百个元素,这都是浪费精力。对于如此少的元素,纯Python数据结构可能不会比Python实现的列表数据结构更快。 Big-O表示法忽略了常数项,如果你没有处理足够的元素(例如Coppersmith-Winograd algorithm),那么它可能会非常大。