是否可以交换有序字典的两个键?

Posted

技术标签:

【中文标题】是否可以交换有序字典的两个键?【英文标题】:Is it possible to swap two keys of an ordered dictionary? 【发布时间】:2021-06-14 02:47:33 【问题描述】:

注意:我使用的是 python 3.8.2,所以字典被认为是有序的

我正在创建一棵二叉树,并使用字典对树建模。 例如: 1:[2, 3], 2:[4, 5], 3:[], 4:[], 5:[]

在示例中,上面的树如下所示:

         1
        / \
       2   3
      / \
     4   5

我试图模拟一些节点的“上升”,并保持字典的顺序。 我知道使用 myDict[key1], myDict[key2] = myDict[key2], myDict[key1] 是行不通的,因为 values 的位置发生了变化,而不是键。

我也在考虑使用.popitem() 删除最后一个值,直到我在 key1 或 key2 处,然后继续直到我到达另一个键,但这似乎有点困难。有没有其他方法可以做到这一点?

【问题讨论】:

默认字典是根据插入排序的。一个昂贵的选择是创建一个新字典,您首先在其中添加正确的键顺序,然后使用旧值更新新字典。因此,您的新值将被反转,但其余值将在插入时排序。类似的想法,将dict转换为元组或列表,执行交换,然后重新创建字典。 【参考方案1】:

由于您只想交换两个密钥,我们可以从以下代码开始:

>>> changedDict = 
>>> for key, value in myDict.items():
...     if key not in (key1, key2):
...         changedDict[key] = value

然后继续如下:

...     elif key == key1:
...         changedDict[key] = myDict[key2]
...     else:
...         changedDict[key] = myDict[key1]

【讨论】:

@wim 但是你能想到别的办法吗? 这并不是那么糟糕 - 正如我在回答中提到的那样,考虑到 dict 排序的限制,这是一种简单而可靠的方法。【参考方案2】:

字典虽然现在保持插入顺序,但不能随意排序。如果您真的想使用字典顺序信息来构建树,我认为唯一可靠的方法是为每个交换操作构建一个新字典,复制原始字典的内容。

如果您想要一个任意排序的字典,更合理的方法是从 collections.abc.MutableMapping 继承并使用字典和其他一些数据结构(例如列表)跟踪该对象内的数据。

这听起来可能很复杂,但它可能比你想象的要简单:

from collections.abc import MutableMapping

class SuperOrdered(MutableMapping):
    def __init__(self):
        self.data = 
        self.order = []
    def __setitem__(self, key, value):
        if key not in self.data:
            self.order.append(key)
        self.data[key] = value
    def __getitem__(self, key):
        return self.data[key]
    def __delitem__(self, key):
        del self.data[key]
        self.order.remove(key)
    def __len__(self):
        return len(self.data)
    def __iter__(self):
        yield from iter(self.order)
    def replace_key(self, oldkey, newkey, value):
        if newkey in self.data:
            del self[newkey]
        position = self.order.index(oldkey)
        self.order[position] = newkey
        self.data[newkey] = value
    def __repr__(self):
        return f"self.__class__.__name__(', '.join(repr(key) + ':' + repr(self.data[key]) for key in self))"


瞧——映射+“replace_key”方法应该足以让您按照您的想法构建树。

这是上面交互式提示中的类:

In [18]: aa = SuperOrdered()                                                                                             

In [19]: aa["a"] = 1;aa["b"] = 2;aa["c"] = 3                                                                             

In [20]: aa                                                                                                              
Out[20]: SuperOrdered('a':1, 'b':2, 'c':3)

In [21]: aa.replace_key("a", "d", 4)                                                                                     

In [22]: aa                                                                                                              
Out[22]: SuperOrdered('d':4, 'b':2, 'c':3)

除了这个答案,而且超出主题:如果你想检查一个我希望“生产就绪”的树实现,我已经发布了一个作为我的extradict 包的一部分(pip 可安装)。

更新:也可以从collections.OrderedDict 继承并在那里添加replace_key 方法。该代码必须处理 OrderedDict 内部,但这并不难。

外部链接: Github modification

【讨论】:

__delitem__ 方法是 O[n] - 如果您希望它是 O[1​​],您可以使用相同的类,但将 self.data 中的每个值保留为一个元组,其中第一个元素是它的顺序。在所有点中,您检索值,仅返回原始值,并使用 `__delitem__. Even doing this, keep the self.order` 列表中的顺序信息,以便您可以按所需顺序进行迭代。 在这里,我对 O(1) 实现做了一个要点:gist.github.com/jsbueno/9c215d44c2e6f0cc925385889046d6a9(如果及时显示有用,我可能会将它包含在“extradict”包中) 如果我改用collections.OrderedDict 会怎样? collections.OrderedDict 与当前字典一样缺乏功能。它现在只是为了向后兼容。问题是:它们遵守严格的键插入顺序,并且无法以任何其他方式控制键顺序。 那么你认为collections.OrderedDict 有一天可能会从python 中删除吗?

以上是关于是否可以交换有序字典的两个键?的主要内容,如果未能解决你的问题,请参考以下文章

python3 有序字典

python列表,元组,字典,集合的比较总结

任意一个序列,每次可交换序列中任意两个数,最少需要多少次交换可以使序列有序?

发现一个秘密:既python3.6之后字典竟然变成了有序集合,我再次验证了一下!

排序算法的分类

Python强化训练笔记——让字典保持有序性