1. 定义
图: 由顶点(vertex) 和边(edge) 组成, 通常表示为G = (V, E)
G 表示是一个图, V 是顶点集合, E 是边的集合
顶点集合V 有穷且非空
任意两个顶点之间都可以用边来表示他们之间的关系, 边集E 可以是空的
例如:
图其他如: 社交网络, 之间的关系, 地图导航, 两地之间的路, 游戏开发等
图的应用2. 有向图(Directed Graph)
有向图的边是有明确方向的
有向图有向无环图(Directed Acyclic Graph), DAG
如果一个有向图, 从任一顶点触发无法经过若干条边回到该顶点, 那么它就是一个有向无环图
有向无环图3. 出度, 入度
适用于有向图
出度(Out-degree)
一个顶点的出度x, 是指有x 条边以该顶点为起点
入度(In-degree)
一个顶点的入度为x, 是指有x 条边以该顶点为终点
4. 无向图(Undirected Graph)
无向图的边是无方向的
无向图效果类似于有向图
有向图5. 混合图(Mixed Graph)
混合图的边可能是无向的, 可能是有向的
混合图6. 简单图, 多重图
平行边
在无向图中, 关联一对顶点的无向边如果多于一条, 则称这些边为平行边
有向图中, 关联一对顶点的有向边如果多于一条, 并且他们的方向相同, 则称这些边为平行边
多重图(Multigraph)
有平行边或者有自环的图
简单图(Simple Graph)
既没有平行边也没有自环的图
多重简单图7. 无向完全图(Undirected Complete Graph)
无向完全图的任意两个顶点之间都存在边
n 个顶点的无向完全图有n(n - 1)/2 条边
无向完全图8. 有向完全图(Directed Complete Graph)
有向完全图的任意两个顶点之间都存在方向相反的两条边
n 个顶点的有向完全图有n(n - 1) 条边
有向完全图稠密图(Dense Graph): 边数接近于或等于完全图
稀疏图(Sparse Graph): 边数远远少于完全图
9. 有权图(Weighted Graph)
有权图的边可以有权值(Weight)
有权图10. 连通图(Connected Graph)
如果顶点x 和y 之间存在可相互抵达的路径(直接或间接的路径), 则称x 和y 是联通的
如果无向图G 中任意两个顶点都是联通的, 则称G 为连通图
连通图11. 连通分量(Connected Component)
连通分量: 无向图的极大连通子图
连通图只有一个连通分量, 即其自身. 非连通的无向图有多个连通分量
连通分量12. 强连通图(Strongly Connected Graph)
如果有向图G 中任意两个顶点都是连通的, 则称G 为强连通图
强连通图13. 强连通分量(Strongly Connected Component Graph)
强连通分量: 有向图的极大强连通子图
强连通图只有一个强连通分量, 即其自身. 非强连通的有向图有多个强连通分量
强连通分量12. 图的实现方式:
邻接矩阵(Adjacency Matrix)
邻接表(Adjacency List)
邻接矩阵
存储方式
- 一维数组存放顶点信息
- 二维数组存放边的信息
邻接矩阵比较适合稠密图
邻接矩阵1 邻接矩阵2邻接表(Adjacency List)
邻接表1 邻接表213. 图的遍历搜索
图的接口定义
public abstract int edgesSize();
public abstract int verticesSize();
public abstract void addVertex(V v);
public abstract void addEdge(V from, V to);
public abstract void addEdge(V from, V to, E weight);
public abstract void removeVertex(V v);
public abstract void removeEdge(V from, V to);
public abstract void bfs(V begin, VertexVisitor<V> visitor);
public abstract void dfs(V begin, VertexVisitor<V> visitor);
public interface VertexVisitor<V> {
boolean visit(V v);
}
图的遍历
从图中某一顶点访问其他顶点, 且每个顶点尽被访问一次
- 广度优先搜索(Breadth First Search) BFS, 又称宽度优先搜索, 横向优先搜索
- 深度优先搜索(Depth First Search), DFS
广度优先搜索
二叉树的层序遍历也是一种广度优先搜索
广度优先搜索1 广度优先搜索2思路
广度优先搜索思路 @Override
public void bfs(V begin, VertexVisitor<V> visitor) {
Vertex<V, E> beginVertex = vertices.get(begin);
if (beginVertex == null) return;
// 已经遍历过的顶点
Set<Vertex<V, E>> visitedVertices = new HashSet<>();
// 使用队列, 存储即将访问的顶点
Queue<Vertex<V, E>> queue = new LinkedList<>();
// 出队
queue.offer(beginVertex);
visitedVertices.add(beginVertex);
while (!queue.isEmpty()) {
Vertex<V, E> vertex = queue.poll();
// System.out.println(vertex.value);
// 遍历停止
if (visitor.visit(vertex.value)) return;
for (Edge<V, E> edge : vertex.outEdges) {
// 已经访问过, 直接跳过
if (visitedVertices.contains(edge.to)) continue;
queue.offer(edge.to);
visitedVertices.add(edge.to);
}
}
}
深度优先搜索
二叉树的前序遍历也是一种深度优先搜索
广度优先搜索 // 递归实现深度优先搜索
public void dfs(V begin) {
Vertex<V, E> beginVertex = vertices.get(begin);
if (beginVertex == null) return;
dfs(beginVertex, new HashSet<>());
}
// 迭代实现深度优先遍历
@Override
public void dfs(V begin, VertexVisitor<V> visitor) {
if (visitor == null) return;
Vertex<V, E> beginVertex = vertices.get(begin);
if (beginVertex == null) return;
Set<Vertex<V, E>> visitedVertices = new HashSet<>();
Stack<Vertex<V, E>> stack = new Stack<>();
// 先访问起点
stack.push(beginVertex);
visitedVertices.add(beginVertex);
// System.out.println(beginVertex.value);
if (visitor.visit(begin)) return;
while (!stack.isEmpty()) {
Vertex<V, E> vertex = stack.pop();
for (Edge<V, E> edge : vertex.outEdges) {
if (visitedVertices.contains(edge.to)) continue;
stack.push(edge.from);
stack.push(edge.to);
visitedVertices.add(edge.to);
// System.out.println(edge.to.value);
if (visitor.visit(edge.to.value)) return;
break;
}
}
}
网友评论