从内存位置的哈希查找中删除列表中的项目?

时间:2017-12-17 00:27:55

标签: python list pointers dictionary memory

我有对象,每个对象都有一个唯一的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')最后释放节点。我试图复制这种行为。

2 个答案:

答案 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),那么它可能会非常大。