在数学中,图是描述于一组对象的结构,其中某些对象对在某种意义上是“相关的”。这些对象对应于称为顶点的数学抽象(也称为节点或点),并且每个相关的顶点对都称为边(也称为链接或线)。通常,图形以图解形式描绘为顶点的一组点或环,并通过边的线或曲线连接。--百度百科

networkx是一个python包,用于创建、操作和研究复杂网络的结构、动态和功能。

图是一种比较复杂的数据结构,包括了一整套数学逻辑,实现起来过于复杂,还是直接用现成的networkx就好。

NetworkX提供:研究社会、生物和基础设施网络结构和动态的工具;一种适用于多种应用的标准编程接口和图形实现;为协作性、多学科项目提供快速发展环境;与现有的数值算法和C、C++和FORTRAN代码的接口;能够轻松处理大型非标准数据集。使用NetworkX,您可以以标准和非标准数据格式加载和存储网络,生成多种类型的随机和经典网络,分析网络结构,构建网络模型,设计新的网络算法,绘制网络,等等

要实现的图的边和节点示意如下,不过在实现的过程中均以无向图为主。

python数据结构之图_最短路径

代码主体结构如下:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# _ooOoo_
# o8888888o
# 88" . "88
# ( | - _ - | )
# O\ = /O
# ____/`---'\____
# .' \\| |// `.
# / \\|||:|||// \
# / _|||||-:- |||||- \
# | | \\\ - /// | |
# | \_| ''\---/'' | _/ |
# \ .-\__ `-` ___/-. /
# ___`. .' /--.--\ `. . __
# ."" '< `.___\_<|>_/___.' >'"".
# | | : `- \`.;`\ _ /`;.`/ - ` : | |
# \ \ `-. \_ __\ /__ _/ .-` / /
# ==`-.____`-.___\_____/___.-`____.-'==
# `=---='
'''
@Project :pythonalgorithms
@File :graphdatastructure.py
@Author :不胜人生一场醉
@Date :2021/7/16 22:18
'''

import networkx as nx
import matplotlib.pyplot as plt


if __name__ == "__main__":
testSimpleGraph()
testGraphaddfrom()
testGraphlabelpic()
testGraphpospic()
testGraphfunc()
testGraphAlgorithms()

1、实现一个简单的无向图,并绘制出来

def testSimpleGraph():
g = nx.Graph() # 创建空的无向图
# g = nx.DiGraph() # 创建空的有向图
g.add_node(1)
g.add_node(2)
g.add_node(3)
g.add_node(4)
g.add_edge(1, 2)
g.add_edge(1, 3)
g.add_edge(2, 3)
g.add_edge(3, 4)
nx.draw_networkx(g)
plt.show()

结果如下:

python数据结构之图_有向图_02

2、通过列表实现无向图,并绘制出来:

def testGraphaddfrom():
g = nx.Graph() # 创建空的无向图
g.add_nodes_from([1, 2, 3, 4, 5, 6, 7])
g.add_edges_from([(1, 2), (1, 3), (1, 4), (1, 5), (1, 6),
(2, 3), (2, 4), (2, 6),
(3, 6),
(4, 5),
(5, 6),
(6, 7)])
nx.draw_networkx(g)
plt.show()

结果如下:

python数据结构之图_最短路径_03

3、在图可视化中追加节点标签和边的标签

def testGraphlabelpic():
# 数组,7个节点,13条边,有向图
# a b c d e f g
# a 4 7 4 7 8
# b 4 5 6
# c ` 3
# d 2
# e 5
# f 8
# g
g = nx.Graph()
g.add_nodes_from([
(1, {"name": "a"}),
(2, {"name": "b"}),
(3, {"name": "c"}),
(4, {"name": "d"}),
(5, {"name": "e"}),
(6, {"name": "f"}),
(7, {"name": "g"}),
])
g.add_edges_from([(1, 2, {'weight': 4}),
(1, 3, {'weight': 7}),
(1, 4, {'weight': 4}),
(1, 5, {'weight': 7}),
(1, 6, {'weight': 8}),
(2, 3, {'weight': 4}),
(2, 4, {'weight': 5}),
(2, 6, {'weight': 6}),
(3, 6, {'weight': 3}),
(4, 5, {'weight': 2}),
(5, 6, {'weight': 5}),
(6, 7, {'weight': 8})
])

pos = nx.spring_layout(g)
# 调用draw(G, pos)将基础的点边拓扑先画出来
nx.draw(g, pos)
node_labels = nx.get_node_attributes(g, 'name')
# {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g'}
# 调用draw_networkx_labels画节点标签
nx.draw_networkx_labels(g, pos, labels=node_labels)
edge_labels = nx.get_edge_attributes(g, 'weight')
# {(1, 2): 4, (1, 3): 7, (1, 4): 4, (1, 5): 7, (1, 6): 8, (2, 3): 4, (2, 4): 5, (2, 6): 6, (3, 6): 3, (4, 5): 2, (5, 6): 5, (6, 7): 8}
# 调用draw_networkx_edge_labels画和边的标签。
nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels)
plt.show()

结果如下:

python数据结构之图_有向图_04

4、在图可视化中继续追加节点的位置和边的权重

def testGraphpospic():
# 数组,7个节点,13条边,有向图
# a b c d e f g
# a 4 7 4 7 8
# b 4 5 6
# c ` 3
# d 2
# e 5
# f 8
# g
g = nx.Graph()
g.add_nodes_from([
(1, {"name": "a", "pos": (3, 17), "degree": 5, "out_degree": 5, "in_degree": 0}),
(2, {"name": "b", "pos": (1, 13), "degree": 4, "out_degree": 3, "in_degree": 1}),
(3, {"name": "c", "pos": (2, 8), "degree": 2, "out_degree": 1, "in_degree": 1}),
(4, {"name": "d", "pos": (5, 13), "degree": 3, "out_degree": 1, "in_degree": 2}),
(5, {"name": "e", "pos": (7, 13), "degree": 3, "out_degree": 1, "in_degree": 2}),
(6, {"name": "f", "pos": (5, 8), "degree": 5, "out_degree": 1, "in_degree": 4}),
(7, {"name": "g", "pos": (7, 1), "degree": 1, "out_degree": 0, "in_degree": 1})
])

g.add_edges_from([(1, 2, {'weight': 4}),
(1, 3, {'weight': 7}),
(1, 4, {'weight': 4}),
(1, 5, {'weight': 7}),
(1, 6, {'weight': 8}),
(2, 3, {'weight': 4}),
(2, 4, {'weight': 5}),
(2, 6, {'weight': 6}),
(3, 6, {'weight': 3}),
(4, 5, {'weight': 2}),
(5, 6, {'weight': 5}),
(6, 7, {'weight': 8})
])

# 获取节点位置
pos = nx.get_node_attributes(g, 'pos')
# {1: (3, 17), 2: (1, 13), 3: (2, 8), 4: (5, 13), 5: (7, 13), 6: (5, 8), 7: (7, 1)}
# 获取节点大小值=出入度*100
nodesize = list(nx.get_node_attributes(g, 'degree').values())
nodesize = [i * 100 for i in nodesize]
# 调用draw(G, pos)将基础的点边拓扑先画出来
nx.draw(g, pos, node_size=nodesize)
# 获取节点标签名称
node_labels = nx.get_node_attributes(g, 'name')
# {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g'}
# 调用draw_networkx_labels画节点标签
nx.draw_networkx_labels(g, pos, labels=node_labels)
# 获取边的标签名称
edge_labels = nx.get_edge_attributes(g, 'weight')
# {(1, 2): 4, (1, 3): 7, (1, 4): 4, (1, 5): 7, (1, 6): 8, (2, 3): 4, (2, 4): 5, (2, 6): 6, (3, 6): 3, (4, 5): 2, (5, 6): 5, (6, 7): 8}
# 调用draw_networkx_edge_labels画和边的标签。
nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels)
# 获取边的权重,即对长度进行转换
edgewidth = list(nx.get_edge_attributes(g, 'weight').values())
nx.draw_networkx_edges(g, pos, width=edgewidth)
plt.show()

python数据结构之图_结点_05

6、测试networkx中关于日常操作的基本函数

def testGraphfunc():
# 数组,7个节点,13条边,有向图
# a b c d e f g
# a 4 7 4 7 8
# b 4 5 6
# c ` 3
# d 2
# e 5
# f 8
# g
# 判断两节点间是否有边 Graph.has_edge(u, v)
# 网络中结点的个数 G.number_of_nodes()
# 网络中边的个数 G.number_of_edges()
# 获取结点i的邻居节点 G.neighbors(i) 或 G[i](在有向图中输出的是出度的邻居,G[i]会输出权重)
# 获取所有节点的度 G.degree()
# 获取节点i的属性 G.nodes[i]["wight"]
# 查看图中所有点的信息 G.nodes.data()
g = nx.Graph()
g.add_nodes_from([
(1, {"name": "a", "pos": (3, 17), "degree": 5, "out_degree": 5, "in_degree": 0, "wight": 5}),
(2, {"name": "b", "pos": (1, 13), "degree": 4, "out_degree": 3, "in_degree": 1}),
(3, {"name": "c", "pos": (2, 8), "degree": 2, "out_degree": 1, "in_degree": 1}),
(4, {"name": "d", "pos": (5, 13), "degree": 3, "out_degree": 1, "in_degree": 2}),
(5, {"name": "e", "pos": (7, 13), "degree": 3, "out_degree": 1, "in_degree": 2}),
(6, {"name": "f", "pos": (5, 8), "degree": 5, "out_degree": 1, "in_degree": 4}),
(7, {"name": "g", "pos": (7, 1), "degree": 1, "out_degree": 0, "in_degree": 1})
])

g.add_edges_from([(1, 2, {'weight': 4}),
(1, 3, {'weight': 7}),
(1, 4, {'weight': 4}),
(1, 5, {'weight': 7}),
(1, 6, {'weight': 8}),
(2, 3, {'weight': 4}),
(2, 4, {'weight': 5}),
(2, 6, {'weight': 6}),
(3, 6, {'weight': 3}),
(4, 5, {'weight': 2}),
(5, 6, {'weight': 5}),
(6, 7, {'weight': 8})
])
# 判断两节点间是否有边 Graph.has_edge(u, v)
print('节点2和节点4是否有边=', g.has_edge(2, 4)) # True
print('节点3和节点4是否有边=', g.has_edge(3, 4)) # False
# 网络中结点的个数 G.number_of_nodes()
print('网络中结点的个数=', g.number_of_nodes()) # 7
# 网络中边的个数 G.number_of_edges()
print('网络中边的个数=', g.number_of_edges()) # 12
# 获取结点i的邻居节点 G.neighbors(i)
print('获取结点1的邻居节点=', g.neighbors(1))
# 获取结点1的邻居节点= <dict_keyiterator object at 0x00000261558B2D10>
print('获取结点1的邻居节点=', list(g.neighbors(1)))
# 获取结点1的邻居节点= [2, 3, 4, 5, 6]
# 获取所有节点的度 G.degree()
print('获取所有节点的度=', g.degree())
# 获取所有节点的度= [(1, 5), (2, 4), (3, 3), (4, 3), (5, 3), (6, 5), (7, 1)]
# 获取节点i G.nodes[i]
print('获取节点i=', g.nodes[1])
# 获取节点i= {'name': 'a', 'pos': (3, 17), 'degree': 5, 'out_degree': 5, 'in_degree': 0, 'wight': 5}
# 获取节点i的属性名 G.nodes[i]
print('获取节点i的属性名=', list(g.nodes[1]))
# 获取节点i= ['name', 'pos', 'degree', 'out_degree', 'in_degree', 'wight']
# 获取节点i的属性值 = 5
# 获取节点i的属性值 G.nodes[i]["wight"]
print('获取节点i的属性值=', g.nodes[1]["wight"])
# 获取节点i的属性 = 5
# 查看图中所有点的信息 G.nodes.data()
print('查看图中所有点的信息=', g.nodes.data())

# 查看图中所有点的信息= [
# (1, {'name': 'a', 'pos': (3, 17), 'degree': 5, 'out_degree': 5, 'in_degree': 0, 'wight': 5}),
# (2, {'name': 'b', 'pos': (1, 13), 'degree': 4, 'out_degree': 3, 'in_degree': 1}),
# (3, {'name': 'c', 'pos': (2, 8), 'degree': 2, 'out_degree': 1, 'in_degree': 1}),
# (4, {'name': 'd', 'pos': (5, 13), 'degree': 3, 'out_degree': 1, 'in_degree': 2}),
# (5, {'name': 'e', 'pos': (7, 13), 'degree': 3, 'out_degree': 1, 'in_degree': 2}),
# (6, {'name': 'f', 'pos': (5, 8), 'degree': 5, 'out_degree': 1, 'in_degree': 4}),
# (7, {'name': 'g', 'pos': (7, 1), 'degree': 1, 'out_degree': 0, 'in_degree': 1})]

结果如下:

节点2和节点4是否有边= True
节点3和节点4是否有边= False
网络中结点的个数= 7
网络中边的个数= 12
获取结点1的邻居节点= <dict_keyiterator object at 0x0000026FA0EC27C0>
获取结点1的邻居节点= [2, 3, 4, 5, 6]
获取所有节点的度= [(1, 5), (2, 4), (3, 3), (4, 3), (5, 3), (6, 5), (7, 1)]
获取节点i= {'name': 'a', 'pos': (3, 17), 'degree': 5, 'out_degree': 5, 'in_degree': 0, 'wight': 5}
获取节点i的属性名= ['name', 'pos', 'degree', 'out_degree', 'in_degree', 'wight']
获取节点i的属性值= 5
查看图中所有点的信息= [(1, {'name': 'a', 'pos': (3, 17), 'degree': 5, 'out_degree': 5, 'in_degree': 0, 'wight': 5}), (2, {'name': 'b', 'pos': (1, 13), 'degree': 4, 'out_degree': 3, 'in_degree': 1}), (3, {'name': 'c', 'pos': (2, 8), 'degree': 2, 'out_degree': 1, 'in_degree': 1}), (4, {'name': 'd', 'pos': (5, 13), 'degree': 3, 'out_degree': 1, 'in_degree': 2}), (5, {'name': 'e', 'pos': (7, 13), 'degree': 3, 'out_degree': 1, 'in_degree': 2}), (6, {'name': 'f', 'pos': (5, 8), 'degree': 5, 'out_degree': 1, 'in_degree': 4}), (7, {'name': 'g', 'pos': (7, 1), 'degree': 1, 'out_degree': 0, 'in_degree': 1})]
[(1, 2, {'weight': 4}), (1, 4, {'weight': 4}), (2, 3, {'weight': 4}), (3, 6, {'weight': 3}), (4, 5, {'weight': 2}), (6, 7, {'weight': 8})]
节点1-节点7最短路径= [1, 6, 7]


7、测试networkx中关于最短路径、连通性、各种遍历等算法功能:

def testGraphAlgorithms():
# 数组,7个节点,13条边,有向图
# a b c d e f g
# a 4 7 4 7 8
# b 4 5 6
# c ` 3
# d 2
# e 5
# f 8
# g
g = nx.Graph()
g.add_nodes_from([
(1, {"name": "a", "pos": (3, 17), "degree": 5, "out_degree": 5, "in_degree": 0, "wight": 5}),
(2, {"name": "b", "pos": (1, 13), "degree": 4, "out_degree": 3, "in_degree": 1}),
(3, {"name": "c", "pos": (2, 8), "degree": 2, "out_degree": 1, "in_degree": 1}),
(4, {"name": "d", "pos": (5, 13), "degree": 3, "out_degree": 1, "in_degree": 2}),
(5, {"name": "e", "pos": (7, 13), "degree": 3, "out_degree": 1, "in_degree": 2}),
(6, {"name": "f", "pos": (5, 8), "degree": 5, "out_degree": 1, "in_degree": 4}),
(7, {"name": "g", "pos": (7, 1), "degree": 1, "out_degree": 0, "in_degree": 1})
])

g.add_edges_from([(1, 2, {'weight': 4}),
(1, 3, {'weight': 7}),
(1, 4, {'weight': 4}),
(1, 5, {'weight': 7}),
(1, 6, {'weight': 8}),
(2, 3, {'weight': 4}),
(2, 4, {'weight': 5}),
(2, 6, {'weight': 6}),
(3, 6, {'weight': 3}),
(4, 5, {'weight': 2}),
(5, 6, {'weight': 5}),
(6, 7, {'weight': 8})
])

# 最小生成树
tree = nx.minimum_spanning_tree(g, algorithm='prim')
print(tree.edges(data=True))
# [(1, 2, {'weight': 4}),
# (1, 4, {'weight': 4}),
# (2, 3, {'weight': 4}),
# (3, 6, {'weight': 3}),
# (4, 5, {'weight': 2}),
# (6, 7, {'weight': 8})]

# 最短路径
print("节点1-节点7最短路径=",nx.dijkstra_path(g, 1, 7))
# [1, 6, 7]
# 所有节点之间的最短路径
gen = nx.all_pairs_shortest_path(g)
print("所有节点之间的最短路径=",dict(gen))
# {
# 1: {1: [1], 2: [1, 2], 3: [1, 3], 4: [1, 4], 5: [1, 5], 6: [1, 6], 7: [1, 6, 7]},
# 2: {2: [2], 1: [2, 1], 3: [2, 3], 4: [2, 4], 6: [2, 6], 5: [2, 1, 5], 7: [2, 6, 7]},
# 3: {3: [3], 1: [3, 1], 2: [3, 2], 6: [3, 6], 4: [3, 1, 4], 5: [3, 1, 5], 7: [3, 6, 7]},
# 4: {4: [4], 1: [4, 1], 2: [4, 2], 5: [4, 5], 3: [4, 1, 3], 6: [4, 1, 6], 7: [4, 1, 6, 7]},
# 5: {5: [5], 1: [5, 1], 4: [5, 4], 6: [5, 6], 2: [5, 1, 2], 3: [5, 1, 3], 7: [5, 6, 7]},
# 6: {6: [6], 1: [6, 1], 2: [6, 2], 3: [6, 3], 5: [6, 5], 7: [6, 7], 4: [6, 1, 4]},
# 7: {7: [7], 6: [7, 6], 1: [7, 6, 1], 2: [7, 6, 2], 3: [7, 6, 3], 5: [7, 6, 5], 4: [7, 6, 1, 4]}}

print("各点之间可达性=",nx.communicability(g))
# 各点之间可达性= {
# 1: {1: 11.66037697759954, 2: 9.730688193140374, 3: 8.180239781697592, 4: 7.645194148638486, 5: 7.720951055876168, 6: 10.188202427433492, 7: 2.4556767248680185},
# 2: {1: 9.730688193140374, 2: 8.966431076518731, 3: 7.2767859598006615, 4: 6.497877866101427, 5: 6.132652147982442, 6: 8.800266284965774, 7: 2.136327166832645},
# 3: {1: 8.180239781697592, 2: 7.2767859598006615, 3: 6.750798019391801, 4: 4.922928096425332, 5: 4.985335865445396, 6: 7.643419657110174, 7: 1.9072819535912968},
# 4: {1: 7.645194148638486, 2: 6.497877866101427, 3: 4.922928096425332, 4: 5.962359580374006, 5: 5.4295610915115375, 6: 6.2992895922037295, 7: 1.3858918410969554},
# 5: {1: 7.720951055876168, 2: 6.132652147982442, 3: 4.985335865445396, 4: 5.4295610915115375, 5: 6.053240117357615, 6: 6.892984331374484, 7: 1.7072857005030746},
# 6: {1: 10.188202427433492, 2: 8.800266284965774, 3: 7.643419657110174, 4: 6.2992895922037295, 5: 6.892984331374484, 6: 10.11197286752748, 7: 3.0514157654095175},
# 7: {1: 2.4556767248680185, 2: 2.136327166832645, 3: 1.9072819535912968, 4: 1.3858918410969554, 5: 1.7072857005030746, 6: 3.0514157654095175, 7: 1.9054013217324557}}


print("获得图中非连通点的列表=",list(nx.isolates(g)))
# 获得图中非连通点的列表 = []
g.add_node(8)
print("获得图中非连通点的列表=", list(nx.isolates(g)))
# 获得图中非连通点的列表= [8]

# 遍历
d_gen = nx.dfs_edges(g, 1) # 按边深度搜索, 1为起点
b_gen = nx.bfs_edges(g, 1)
print("深度搜索:",list(d_gen))
# [(1, 2), (2, 3), (3, 6), (6, 5), (5, 4), (6, 7)]
print("广度搜索:",list(b_gen))
# [(1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (6, 7)]
print(nx.dfs_tree(g, 1).nodes()) # 按点深搜g
# [1, 2, 3, 6, 5, 4, 7]
print("先序遍历:", list(nx.dfs_preorder_nodes(g, 1)))
# 先序遍历: [1, 2, 3, 6, 5, 4, 7]
print("后序遍历:", list(nx.dfs_postorder_nodes(g, 1)))
# 后序遍历: [4, 5, 7, 6, 3, 2, 1]
print("节点1和节点2的共同邻居:", list(nx.common_neighbors(g, 1, 2)))
# 节点0和节点33的共同邻居: [3, 4, 6]
print("节点1和节点7的共同邻居:", list(nx.common_neighbors(g, 1, 7)))
# 节点0和节点33的共同邻居: [6]

结果如下:

所有节点之间的最短路径= {1: {1: [1], 2: [1, 2], 3: [1, 3], 4: [1, 4], 5: [1, 5], 6: [1, 6], 7: [1, 6, 7]}, 2: {2: [2], 1: [2, 1], 3: [2, 3], 4: [2, 4], 6: [2, 6], 5: [2, 1, 5], 7: [2, 6, 7]}, 3: {3: [3], 1: [3, 1], 2: [3, 2], 6: [3, 6], 4: [3, 1, 4], 5: [3, 1, 5], 7: [3, 6, 7]}, 4: {4: [4], 1: [4, 1], 2: [4, 2], 5: [4, 5], 3: [4, 1, 3], 6: [4, 1, 6], 7: [4, 1, 6, 7]}, 5: {5: [5], 1: [5, 1], 4: [5, 4], 6: [5, 6], 2: [5, 1, 2], 3: [5, 1, 3], 7: [5, 6, 7]}, 6: {6: [6], 1: [6, 1], 2: [6, 2], 3: [6, 3], 5: [6, 5], 7: [6, 7], 4: [6, 1, 4]}, 7: {7: [7], 6: [7, 6], 1: [7, 6, 1], 2: [7, 6, 2], 3: [7, 6, 3], 5: [7, 6, 5], 4: [7, 6, 1, 4]}}
各点之间可达性= {1: {1: 11.66037697759954, 2: 9.730688193140374, 3: 8.180239781697592, 4: 7.645194148638486, 5: 7.720951055876168, 6: 10.188202427433492, 7: 2.4556767248680185}, 2: {1: 9.730688193140374, 2: 8.966431076518731, 3: 7.2767859598006615, 4: 6.497877866101427, 5: 6.132652147982442, 6: 8.800266284965774, 7: 2.136327166832645}, 3: {1: 8.180239781697592, 2: 7.2767859598006615, 3: 6.750798019391801, 4: 4.922928096425332, 5: 4.985335865445396, 6: 7.643419657110174, 7: 1.9072819535912968}, 4: {1: 7.645194148638486, 2: 6.497877866101427, 3: 4.922928096425332, 4: 5.962359580374006, 5: 5.4295610915115375, 6: 6.2992895922037295, 7: 1.3858918410969554}, 5: {1: 7.720951055876168, 2: 6.132652147982442, 3: 4.985335865445396, 4: 5.4295610915115375, 5: 6.053240117357615, 6: 6.892984331374484, 7: 1.7072857005030746}, 6: {1: 10.188202427433492, 2: 8.800266284965774, 3: 7.643419657110174, 4: 6.2992895922037295, 5: 6.892984331374484, 6: 10.11197286752748, 7: 3.0514157654095175}, 7: {1: 2.4556767248680185, 2: 2.136327166832645, 3: 1.9072819535912968, 4: 1.3858918410969554, 5: 1.7072857005030746, 6: 3.0514157654095175, 7: 1.9054013217324557}}
获得图中非连通点的列表= []
获得图中非连通点的列表= [8]
深度搜索: [(1, 2), (2, 3), (3, 6), (6, 5), (5, 4), (6, 7)]
广度搜索: [(1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (6, 7)]
[1, 2, 3, 6, 5, 4, 7]
先序遍历: [1, 2, 3, 6, 5, 4, 7]
后序遍历: [4, 5, 7, 6, 3, 2, 1]
节点1和节点2的共同邻居: [3, 4, 6]
节点1和节点7的共同邻居: [6]