图论基础
什么是图
在计算机科学中,一个图就是一些顶点的集合,这些顶点通过一系列边结对(连接)。顶点用圆圈表示,边就是这些圆圈之间的连线。顶点之间通过边连接。
如图 这是一个无向图,其中A、B、C、D、E、F称之为顶点
图的基本概念
图右很多种大概分为 无向图 、有向图。在此基础上还可以添加权重,称之为带权的有向图和无向图。 如果图中的任意两个顶点都是相互连通的则称之为连通图。如下图
其中图有一个概念就是度,对于无向图 顶点边数叫做度, 对于有向图顶点的边数叫做出度和入度。
如图2 中每个顶点的度都是3
图1中 顶点A的入度是2,出度是0
图3中 顶点A的入度是2,出度是1
图的表达形式
从上面的各种图之间的关系,可以看出顶点与顶点之间相互关系很复杂,计算机领域我们通常两种存储结构表示图,邻接矩阵和邻接表。
邻接矩阵
邻接矩阵使用两个数组来表示图,一个是一维数组用来存储顶点信息,另一个是用一个二维数组表示顶点与顶点之间的关系。
- 无向图用邻接矩阵表示
- 有向图用邻接矩阵表示
- 带权有向图邻接矩阵表示
邻接表
玲姐表表示图的结构类似于HashMap 的数组+链表的表示方式。这种方式主要为了解决,邻接矩阵占用空间大的问题, 但是其访问方式相比矩阵要复杂并且分析问题难度也较大,所以90%都用矩阵的方式表示图。
深度优先与广度优先
深度优先
深度优先遍历也叫做深度优先搜索,它的遍历规则就是不断的沿着顶点的深度方向搜索。如上图的迷宫,每次我们走到一个岔口,按照一定的顺序,发现一个可以走的点,就沿着这个方向一直走,直到无路可走,就返回到上一次的岔口位置换一个方向继续这样走。 这就叫深度优先。
还是用这个图说明。
从A开始搜索,发现C 可以走。
从C开始,发现B 可以走,
在从B开始,发现A可以走,回到了起点说明不通
退回到C,发现D可以走
从D开始,发现A可以走,由回到起点,说明不通。
上面的就是 深度优先。
/**
* 深度优先(很象二叉树的前序)
*/
public void dfs() {
for (int i = 0; i < verticesSize; i++) {
if (!isVisited[i]) {
System.out.println("viested vertice " + i);
dfs(i);
}
}
}
public void dfs(int i) {
isVisited[i] = true;
//获取到第一个 可以走的点
int v = getFirstNeightBor(i);
while (v != -1) {
//如果没有被走过
if (!isVisited[v]) {
System.out.println("visted vertice " + v);
// 沿着这个点继续往前走
dfs(v);
}
v = getNextNeightBor(i, v);
}
}
广度优先
还是上面图的例子,当我们走到一个岔口,我们先遍历这个岔口所有可以走的点 (广度的意思)
还是深度优先的这个图说明。
/**
* 广度优先
*/
public void bfs(){
for (int i = 0; i < verticesSize; i++) {
isVisited[i]=false;
}
for (int i = 0; i < verticesSize; i++) {
if(!isVisited[i]){
isVisited[i]=true;
System.out.println("visited vertice:"+ i);
bfs(i);
}
}
}
public void bfs(int i) {
LinkedList<Integer> queue = new LinkedList<>();
//找第一个邻接点
int fn = getFirstNeightBor(i);
if (fn == -1) {
return;
}
if (!isVisited[fn]) {
isVisited[fn] = true;
System.out.println("visted vertice:" + fn);
queue.offer(fn);
}
//开始把后面的邻接点都入队
int next = getNextNeightBor(i, fn);
while (next != -1) {
if (!isVisited[next]) {
isVisited[next] = true;
System.out.println("visted vertice:" + next);
queue.offer(next);
}
next = getNextNeightBor(i, next);
}
//从队列中取出来一个,重复之前的操作
while(!queue.isEmpty()){
int point=queue.poll();//v1 v2
bfs(point);
}
}
图相关的属性和方法
public class Graph {
public int[] vertices;//顶点集
public int[][] matrix;//图的边的信息
public int verticesSize;
public static final int MAX_WEIGHT = Integer.MAX_VALUE;
public boolean[] isVisited;
public Graph(int verticesSize) {
this.verticesSize = verticesSize;
vertices = new int[verticesSize];
matrix = new int[verticesSize][verticesSize];
isVisited = new boolean[verticesSize];
for (int i = 0; i < verticesSize; i++) {
vertices[i] = i;
}
}
/**
* 计算v1到v2的权重(路径长度)
*/
public int getWeight(int v1, int v2) {
int weight = matrix[v1][v2];
return weight == 0 ? 0 : (weight == MAX_WEIGHT ? -1 : weight);
}
/**
* 获取顶点
*/
public int[] getVertices() {
return vertices;
}
/**
* 获取出度
*/
public int getOutDegree(int v) {
int count = 0;
for (int i = 0; i < verticesSize; i++) {
if (matrix[v][i] != 0 && matrix[v][i] != MAX_WEIGHT) {
count++;
}
}
return count;
}
/**
* 获取入度
*/
public int getInDegree(int v) {
int count = 0;
for (int i = 0; i < verticesSize; i++) {
if (matrix[i][v] != 0 && matrix[i][v] != MAX_WEIGHT) {
count++;
}
}
return count;
}
}