【问题标题】:Finding cycles in a dictionary在字典中查找循环
【发布时间】:2019-06-19 04:19:58
【问题描述】:

我有一本字典,其值为:

m = {1: 2, 7: 3, 2: 1, 4: 4, 5: 3, 6: 9}

所需的输出应该是循环值,例如 1:2 -> 2:1 = cycle,它们都存在于字典中。 4:4也是一个循环。

我的输出应该是

[(1, 2), [4]]

代码

m = {1: 2, 7: 3, 2: 1, 4: 4, 5: 3, 6: 9}
k = list(m.items())
print(k)

p = [t[::-1] for t in k]
print(p)

my_list = [(a, b) for (a, b) in p for (c, d) in k  if ((a ==c) and (b == d))]

for i in k:
     if i in my_list:
             print("cycles : ", i)

输出:

我得到的输出是

cycles : (1, 2)
cycles : (2, 1)
cycles : (4, 4)

有人可以帮我解决这个问题吗?

【问题讨论】:

  • 为什么 (2,1) 是一个元组,而 [4] 是一个列表?
  • 另外,字典可以包含 None 值吗?
  • 循环可以超过两个元素吗?
  • @MarkMeyer。我已将其添加到我的答案中

标签: python


【解决方案1】:

让我们把它分成两个步骤。首先我们会找到循环。然后我们将根据您的需要格式化输出。

查找循环很容易,因为您有一个字典:值变成键,查找速度很快。我们可以将循环按排序顺序存储在一个集合中以避免重复:

cycles = set()
for k, v in m.items():
    if m.get(v) == k:
        cycles.add(tuple(sorted((k, v))))

不是我推荐它,但上面可以写成难以辨认的单行:

cycles = set(tuple(sorted(item)) for item in m.items() if m.get(item[1]) == item[0])

现在来格式化数据。您想要一个列表输出,以及格式为列表的重复条目:

output = [[k] if k == v else (k, v) for k, v in cycles]

如果你不喜欢干净的代码,你可以想象如何将整个操作变成一个单行代码:)

更新

值得考虑循环长于一两个条目的情况。您似乎希望每个周期只存储一个元素,所以让我们这样做。我们可以跟踪字典中每个元素的链。如果链的任何部分形成一个循环,我们可以找到要报告的循环的最小元素,并删除所有访问过的元素,因为它们都不再被考虑:

def find_cycles(m):
    n = m.copy()  # don't mutilate the original
    cycles = []
    while n:
        visited = {}
        count = 0
        k, v = n.popitem()
        while v is not None:
            visited[k] = (count, v)
            count += 1
            k = v
            v = n.pop(k, None)

        if k in visited:
            cycle_start = visited[k][0]
            item = min((k, v) for k, (c, v) in visited.items() if c >= cycle_start)
            cycles.append(item)

    return [[k] if k == v else (k, v) for k, v in cycles]

例如:

>>> find_cycles({1:2, 2:3, 3:4, 4:5, 5:1, 6:1, 7:1, 8:8})
[(1, 2), [8]]

更好的概括可能是返回一个包含整个循环的元组,从最小的键开始。大多数情况下,if k in visited: 下的语句需要更改才能做到这一点:

    visited[k] = count
    ...
    if k in visited:
        if len(visited) == 1:
            cycle = list(visited.keys())
        else:
            cycle_start = visited[k]
            cycle = sorted((c, k) for k, c in visited.items() if c >= cycle_start)
            cycle = tuple(k for c, k in cycle)
            k = min(range(len(cycle)), key=lambda x: cycle[x])
            cycle = cycle[k:] + cycle[:k]
       cycles.append(cycle)

return cycles

这个版本信息量更大:

>>> find_cycles({1:2, 2:3, 3:4, 4:5, 5:1, 6:1, 7:1, 8:8})
[(1, 2, 3, 4, 5), [8]]

如果您有兴趣,这里是我的 IDEOne 暂存空间:https://ideone.com/6kpRrW

【讨论】:

  • 正确的感谢方式是选择适合你的答案,有声望的时候点赞
【解决方案2】:

已编辑 嘿,一旦找到,您只需将其替换为列表中的任何其他符号即可。然后它会给你想要的结果。

map = {1:2, 7:3, 2:1, 4:4, 5:3, 6:9}

k = list(map.items())

print(k)

result = []

for i in k:
    if i != -1 and i[::-1] in k:
        result.append((set(i)))
        k[k.index(i[::-1])] = -1

print(result)
#if you want tuples as output
[print(tuple(x),end = " ") for x in result]

【讨论】:

  • 在迭代时删除会产生不可预知的结果
  • 或者说是可预测的,但不是有意的
  • 是的,现在我编辑了解决方案。替换而不是删除。
猜你喜欢
  • 1970-01-01
  • 2021-08-30
  • 2019-05-13
  • 2021-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多