数据结构——图
图是由点与边组成的,通常表示为G(V,E),其中G表示图,V为点的集合,E为边的集合。图差不多是树的升级版,树只能父母节点连接子女节点,但是图的话允许所有的节点都可以互连。如下是一种常见的图(社交关系网络图)
图的应用
图可以用来存储关系复杂的数据,例如典型的社交网络的数据。可以通过图来存储社交软件中各个联系人间的关系信息。还有就是平时手机上用的地图软件,地图中的路线信息估计也是由图来存储的,用有权重的图来存储之后可以通过一些特定的算法,如直接通过暴力枚举,来获取一个最短导航的路径。
图的分类
有向图
无向图
图的表示
邻接矩阵
首先可以利用矩阵来表示图,例如我们有下面一张图。
图中三个点,构造一个三阶矩阵, A 33 A_{33} A33,一开始时让所有元素都为0。当1点连上了2点时,就让 A 12 = 1 A_{12}=1 A12=1表示,后面的以此类推。最后能得到下面的邻接矩阵。
[ 0 1 0 0 0 0 1 1 0 ] \left[\begin{matrix} 0 & 1 & 0 \\ 0 & 0 & 0 \\ 1& 1&0 \end{matrix}\right] ⎣⎡001101000⎦⎤
邻接矩阵适合用来表示稠密图(点少边多)
C++代码
/*
邻接矩阵建图
适合稠密图(点少边多)
*/
const int maxn = 1e5+5; // 图中点的最大数量
int g[maxn][maxn]; // 邻接矩阵 g[u][v]表示u->v的边的数量
int main(){
int n,m; // 点数n 边数m
memset(g,0,sizeof(g)); // 清空矩阵
cout<<"请输入图的点数和边数"<<endl;
cin>>n>>m;
cout<<"请输入每一条边(示例:1 2表示1到2有一条边)"<<endl;
for(int i=0,v,u;i<m;++i){
cin>>v>>u;
++g[v][u];
// g[u][v]=1; // 若建无向图则要建立双向边
}
}
Python代码
#此函数用于清空矩阵
def memset(g,value,maxn):
for i in range(maxn):
g.append([])
for j in range(maxn):
g[i].append(0)
#邻接矩阵建图
maxn = int(3) #图中点的最大数量
g = [] #邻接矩阵 g[u][v]表示u->v的边的数量
memset(g,0,maxn)
#输入点数与边数,后面可以利用边的数量来限制输入的关系数量
n,m = map(int,input("请输入图的点数和边数:").split())
for i in range(1,m+1):
v,u = map(int, input("请输入每一条边(示例:1 2表示1到2有一条边)").split())
g[v-1][u-1] = 1
print(g)
如下为运行结果
邻接表表示
邻接表的思路就是通过建立一个表来记录每个节点的信息,这些信息包括这个节点连接的节点的序号与权重等等。
邻接表建图适合稀疏图(点多边少)
C++代码
/*
* 使用vector实现邻接表
* 邻接表建图适合稀疏图(点多边少)
*/
class Edge{
public:
int from; // 始点
int to; // 终点
Edge(){}
Edge(int _from,int _to):from(_from),to(_to) {}
};
class Graph{
int point_nums; // 点的数量
int edge_nums; // 边的数量
vector<vector<Edge> > G;
public:
Graph(int n,int m):point_nums(n), edge_nums(m),G(point_nums) {}
int add_edge(int from,int to){ // 增加一条从from点到to点的单向边
G[from].push_back(Edge(from,to)); // 尾插法在点from的出边表插入边
}
};
int main(){
int n,m; // 点数n 边数m
cout<<"请输入图的点数和边数"<<endl;
cin>>n>>m;
Graph g(n,m);
cout<<"请输入每一条边(示例:1 2表示1到2有一条边)"<<endl;
for(int i=0,v,u;i<m;++i){
cin>>v>>u;
g.add_edge(v,u);
// g.add_edge(u,v); // 若建无向图则要建立双向边
Python代码
# 节点类,此类用来存储每个点的数据 包括这个点连接了哪条边权重多少等等
class Vertex:
def __init__(self, id):
# 点的ID
self.id = id
# 点的连接信息
self.connect_to = {}
# 添加连接,一开始默认权重是0的,所以连接时要设置一下权重
def add_neighbor(self, nbr_id, weight=0):
self.connect_to[nbr_id] = weight
# 获取这个点所连接的点的所有数据
def get_connections(self):
return self.connect_to.keys()
# 放回此点的id号
def get_id(self):
return self.id
# 获取连接某个点的权重
def get_weight(self, nbr_id):
return self.connect_to[nbr_id]
def __str__(self):
return f"点{self.id}"
# 图类,上面的类只是用来存储一个点的数据的,那么下面这个类则是存储图中所有点的数据的
class Graph:
def __init__(self):
# 存储的主体,用字典来存储
self.vert_list = {}
# 用于表示图中有多少个点
self.num_vertices = 0
# 添加点
def add_vertex(self, id):
# 图中点的数量加一
self.num_vertices += 1
# 新建一个点类
new_vert = Vertex(id)
# 将点类添加到图中
self.vert_list[id] = new_vert
return new_vert
# 获取某个点的数据
def get_vertex(self, id):
# 设置一个判断语句,若输入的Id确实存在再返回,否则返回none
if id in self.vert_list:
return self.vert_list[id]
else:
return None
# 添加边,此函数的from_代表连接的点类,to表示被连接的点类,weight表示这条边的权重
def add_edge(self, from_, to, weight=0):
# 首先需要判断from_与to两个点是否存在,若不存在则创建
if from_ not in self.vert_list:
new_vertex = self.add_vertex(from_)
if to not in self.vert_list:
new_vertex = self.add_vertex(to)
# 调用点类的add_neighbor方法来进行添加
self.vert_list[from_].add_neighbor(self.vert_list[to], weight)
def get_all_vertices(self):
# 返回所有节点的信息
return self.vert_list.keys()
def __contains__(self, id):
return id in self.vert_list
# 此函数是用来迭代的,若不设置的话图对象就无法直接迭代
def __iter__(self):
return iter(self.vert_list.values())
def __str__(self):
return "图类"
if __name__ == "__main__":
#创建图类
g = Graph()
#打印图类 这里就用到了 __str__函数
print(g)
#迭代添加节点
for i in range(6):
g.add_vertex(i)
#查看是否添加成功
print(g.get_all_vertices())
#添加边
g.add_edge(0, 1, 1)
g.add_edge(0, 2, 1)
g.add_edge(1, 3, 1)
#查看0节点连接的对象 对象是类所以返回地址
print(g.vert_list[0].get_connections())
#遍历图里面的点数据
for v in g:
print('v=', v)
for w in v.get_connections():
# print('w=', w)
print("({},{})".format(v.get_id(), w.get_id()))
#以下为运行结果
'''
图类
dict_keys([0, 1, 2, 3, 4, 5])
dict_keys([<__main__.Vertex object at 0x00000110A5DDBC70>, <__main__.Vertex object at 0x00000110A5DE4BB0>])
v= 点0
(0,1)
(0,2)
v= 点1
(1,3)
v= 点2
v= 点3
v= 点4
v= 点5
'''