0%

图简介

图简介

图其实我们在生活中用到的很多,比如我们要从北京西站到望京,打开高德地图,开始进行导航,会有多种方式可供选择。这就是图

出行路线

要找出换乘最少的路线。这种问题被称为最短路径问题,解决最短路径问题的算法被称为广度优先算法。

什么是图

图是由节点和边组成的,一个节点可能与众多节点直接相连,这些节点被称为邻居。

有向图

顶点关系,左边起点,右边终点,所有边都有方向

无向图

没有方向

完全图

若一个无向图具有n个顶点,而每个顶点与其他n-1个顶点之间都有边,则称为无向完全图,也就是含有n个顶点的无向完全图有n(n-1)/2条边。类似的,有向图是每两个顶点之间有两条方向相反的边,也就是含有n个顶点的有向完全图有n(n-1)条边

图的存储结构

邻接矩阵

顶点之间的关系,适用稠密图存(边多)

  • 无向图的是对称的,第i行(列)就是顶点的度 2e非零个数
  • 有向图是不对称的,行是出度,列是入度 e个非零个数
无向图的邻接矩阵

无向图的邻接矩阵

  • 无向图的邻接矩阵是对称的,且主对角线元素全为0(因为自己到自己没有边)。
  • 顶点i的度=第i行(列)中1的个数。
  • 完全图的邻接矩阵中,主对角元素为0,其余全为1。
有向图的邻接矩阵

有向图的邻接矩阵

  • 有向图的邻接矩阵可能不是对称的。
  • 顶点的出度=第i行元素之和;
    顶点的入度=第i列元素之和;
    顶点的=第i行元素之和+第i列元素之和

邻接链表

  • 顶点: 按编号顺序将顶点数据存储在一维数组中。
  • 关联同一顶点的边: 用线性链表存储。
无向图的邻接表

无向图邻接表

  • 邻接表不唯一
  • 若无向图中有n个顶点、e条边,则其邻接表需要n个头结点和2e个表结点。适宜存储稀疏图。
  • 无向图中顶点v${i}$的度为第i个单链表中的结点数
有向图的邻接表

有向图的邻接表

  • 顶点 v$i$的出度为第i个单链表中的结点个数。
  • 顶点v$i$的入度为整个单链表中邻接点域值是i-1的结点个数

图的遍历

深度优先DFS

使用的是回溯的算法,对于邻接矩阵的时间复杂度是O($n^2$),对于邻接表的复杂度为O(n+e)

广度优先BFS

对于邻接矩阵的时间复杂度是O($n^2$),对于邻接表的复杂度为O(n+e)

广度优先搜索

广度优先搜索是一种用于图的查找算法,可用来解决两类问题

  • 从节点A出发,是否有前往节点B的路径?
  • 从节点A出发到节点B的哪条路径最短?

广度优先搜索的执行过程中,搜索范围从起点开始逐渐向外延伸。

举个在关系网中找某人的例子

比如,朋友是一度关系,朋友的朋友是二度关系。一度关系胜过二度关系,二度关系胜过三度关系。所以先在一度关系中搜索,确定其中没有老师后,才在二度关系中搜索。所以这里我们使用队列来解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
* 找学校老师的关系
* @author zh
* @date 2023/7/18 09:58
*/
public class GraphSearch {
public static void main(String[] args) {
// 张玲要找一个老师,先在自己的关系网中找,如果没有找到,则去找自己朋友的朋友
// 假设 王燚 是老师 ,所以就变成了找王燚
Map<String, List<String>> relationship = new HashMap<>();
init(relationship);
searchTeacher(relationship,"张思");




}

public static void searchTeacher(Map<String, List<String>> relationship,String me){
List<String> relations = relationship.get(me);
if(relations == null || relations.isEmpty()){
return;
}
// 有些朋友是共有的,排除掉
Set<String> tried = new HashSet<>();
// 查找次数
int count = 0;
// 查找队列
// 先把自己的朋友入队
Queue<String> searchQueue = new ArrayDeque<>(relations);
while (!searchQueue.isEmpty()){
// 取出一人
String p = searchQueue.poll();
tried.add(p);
count++;
// 如果这个人是 王燚
if("王燚".equals(p)){
System.out.println("找到 老师 了,找了"+count+"次");
return;
}else { // 否则将这个人的朋友也添加到查找队列中
List<String> pRelations = relationship.get(p);
if(pRelations != null && pRelations.size() > 0){
for(String pp : pRelations){
if(!tried.contains(pp)){
searchQueue.add(pp);
}
}

}
}
}
// 队列为空,说明关系网中没有老师
System.out.println("朋友关系网中没有老师");


}

/**
* 初始化关系
* @param relationship
*/
public static void init(Map<String, List<String>> relationship){
relationship.put("张玲",initP1Relation());
relationship.put("张毅",initP2Relation());
relationship.put("张二",initP3Relation());
relationship.put("张三",initP4Relation());
relationship.put("张思",initP5Relation());

}

public static List<String> initP1Relation(){
List<String> p1List = new ArrayList<>();
p1List.add("张毅");
p1List.add("张二");
p1List.add("张三");
p1List.add("张思");
p1List.add("张武");
p1List.add("张柳");
return p1List;
}

public static List<String> initP2Relation(){
List<String> p2List = new ArrayList<>();
p2List.add("张玲");
p2List.add("王燚");
p2List.add("张二");
p2List.add("王三");
p2List.add("王思");
p2List.add("王五");
return p2List;
}

public static List<String> initP3Relation(){
List<String> p3List = new ArrayList<>();
p3List.add("赵毅");
p3List.add("张玲");
p3List.add("张三");
p3List.add("张二");
p3List.add("赵武");
p3List.add("赵六");
return p3List;
}

public static List<String> initP4Relation(){
List<String> p4List = new ArrayList<>();
p4List.add("孙怡");
p4List.add("张玲");
p4List.add("张二");
p4List.add("张思");
p4List.add("孙武");
p4List.add("孙刘");
return p4List;
}

public static List<String> initP5Relation(){
List<String> p5List = new ArrayList<>();

p5List.add("张玲");
p5List.add("张三");

return p5List;
}
}

欢迎关注我的其它发布渠道