Если вы строго ищете эффективное хранение неориентированного графа, вы можете использовать строку с несколькими соглашениями. Сначала пары имен узлов (ребра) всегда выражаются в убывающем алфавитном (или буквенно-цифровом) порядке. Во-вторых, вы не повторяете левую сторону этого спаривания, перечисляя только связанные узлы после их большего аналога.
Это даст строку, подобную этой: "B, A | C, A, B | E, D | F "
Вы можете go назад и вперед между этой строковой кодировкой и полностью расширенным словарным представлением в памяти (что будет необходимо для любых значимых манипуляций):
От строка в словарь:
sGraph = "B,A|C,A,B|E,D|F"
dGraph = { v:[] for v in sGraph.replace("|",",").split(",") }
dGraph.update({ v[0]:v[1:] for vG in sGraph.split("|") for v in[vG.split(",")]})
for v1,v2 in [(v1,v2) for v1,vN in dGraph.items() for v2 in vN]:dGraph[v2].append(v1)
Вывод:
print(dGraph)
# {'B': ['A', 'C'], 'A': ['B', 'C'], 'C': ['A', 'B'], 'E': ['D'], 'D': ['E'], 'F': []}
примечание: в зависимости от ваших потребностей обработки последний для l oop (выше) может быть опущен. Это дало бы вам полное представление графа без избыточности по краям (но было бы намного сложнее манипулировать)
# {'B': ['A'], 'A': [], 'C': ['A', 'B'], 'E': ['D'], 'D': [], 'F': []}
Форма словаря в строку (на основе полностью развернутого представления):
dGraph = {'B': ['A', 'C'], 'A': ['B', 'C'], 'C': ['A', 'B'], 'E': ['D'], 'D': ['E'], 'F': []}
sGraph = sorted(v for v,vN in dGraph.items() if any(v2<v for v2 in vN) or not vN)
sGraph = "|".join( ",".join([v1,*(v2 for v2 in dGraph[v1] if v2<v1)]) for v1 in sGraph)
вывод:
print(sGraph)
# B,A|C,A,B|E,D|F
Длина строки всегда будет меньше 2 * (E + V), где E - количество ребер, а V - количество вершин ( предполагая одну букву / символ на имя вершины).