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

问题描述 投票:2回答:2

我有对象,每个对象都有一个唯一的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中的相应对象仍然保留,即使它已从字典中删除 - 我应该如何从O(1)中的相应列表中删除它?

在C ++中,我可以保留指向列出邻居的指针作为节点成员变量(链表)并通过其内存地址查找节点,获取指向其邻居的指针,取消该节点与其邻居的链接(从而将其从“列表”中删除)最后释放节点。我试图复制这种行为。

python list pointers dictionary memory
2个回答
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),它可能会非常大。


1
投票

您可以使用嵌套在dict中的dict,而不是使用嵌套在dict中的列表:

node[node.lst_id] = {node1.unique_id: node1, node2.unique_id: node2, ... }

假设ID_TO_REMOVEunique_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)

OUTPUT

node_lsts {'a': {1: [lst_id: a, unique_id: 1], 2: [lst_id: a, unique_id: 2]}, 'b': {3: [lst_id: b, unique_id: 3]}}
node_to_remove [lst_id: a, unique_id: 1]
node_lsts {'a': {2: [lst_id: a, unique_id: 2]}, 'b': {3: [lst_id: b, unique_id: 3]}}

由于我们现在使用字典,所以一切都在O(1)中完成,我们避免了当我们尝试从列表中删除元素时出现的性能问题。

© www.soinside.com 2019 - 2024. All rights reserved.