图的概念
图的定义
由顶点和边组成的一种结构,顶点的集合V,边的集合E,所以图记为G=(V,E)
无向图
顶点之间的边没有方向则这个边叫作无向边,用无序偶(vi,v2)表示对如果图中任意两个顶点之间都是无向边那么这个图就是无向图,无向图中两个顶点之间都存在边,则图称为无向完全图
有向图
顶点之间的边有方向这条边就叫有向边也称为弧用<v1,v2>表示v1称为弧尾(Tail),v2称为弧头(Head),如果任意顶点之间的边都是有向边,则该图称为有向图
图的权
有些图的边或弧具有与他相关的数字,这个数字就叫权
连通图
一个图中任意两个顶点v1,v2都是连通的.这个图就叫连通图
度
无向图顶点的边数叫度,有向图顶点的边数叫出度和入度
图的数据存储结构(邻接矩阵)
图的遍历
深度优先
- 第一步:访问A之后,有CDF三条路,任意选一个A的邻接点比如C
- 第二步:C之后任选一个邻接点往下走,走到B没有路了也就回溯
- 第三步:回到C在将没有走到的D走完
- 最后再回到A,把他的邻接点走完
访问路径: A -> C -> B -> D -> F -> G -> E
广度优先遍历
- 访问A
- 访问上一步A的邻接点(C,D,F)
- 访问上一步(C,D,F)的邻接点(B,G)
- 访问上一步(B,G)的邻接点有E
- 访问上一步E的邻接点没有了结束
A -> C -> D -> F -> B -> G -> E
代码实现
定义图的结构:
package 图;
/**
* @author peiqi
* @Title:
* @Package
* @Description: 定义图的结构
* @date 2021/10/815:56
*/
public class Graph {
//节点数目
protected int size;
//定义数组,保存顶点信息
protected String[] nodes;
/**
* 定义矩阵保存顶点信息
*/
protected int[][] edges;
/**
* A B C D E F G
* A 0 0 1 1 0 1 0
* B 0 0 1 0 0 0 0
* C 1 1 0 1 0 0 0
* D 1 0 1 0 0 0 0
* E 0 0 0 0 0 0 1
* F 1 0 0 0 0 0 1
* G 0 0 0 0 1 1 0
*/
public Graph(){
//初始化顶点
nodes = new String[]{"A","B","C","D","E","F","G"};
size = nodes.length;
//初始化边---- 为了直观,做一个常量定义
final int A=0,B=1,C=2,D=3,E=4,F=5,G=6;
edges = new int[size][size];
//1代表有路径
edges[A][C] = 1;
edges[A][D] = 1;
edges[A][F] = 1;
edges[B][C] = 1;
edges[C][A] = 1;
edges[C][D] = 1;
edges[C][B] = 1;
edges[D][A] = 1;
edges[D][C] = 1;
edges[E][G] = 1;
edges[F][A] = 1;
edges[F][G] = 1;
edges[G][F] = 1;
edges[G][E] = 1;
}
}
遍历操作:
package 图;
/**
* @author peiqi
* @Title:
* @Package
* @Description: 图的遍历
* @date 2021/10/815:56
*/
public class GraphCover extends Graph{
//遍历标志防止死环遍历
private int[] visit = new int[size];
/**
* 深度优先遍历
* 一条路走到黑,不撞南墙不回头
* 对每一个可能的分支路径深入到不能再深入为止
* @param start 从哪个节点开始遍历
*/
public void DeepFirst(int start){
//标记1表示该顶点已经被处理过
visit[start] = 1;
//输出节点数据
System.out.println(this.nodes[start]);
//获取邻接点
for (int i = 0; i < this.size; i++) {
//该节点没有访问过,且有路径可走
if (this.edges[start][i]==1&&visit[i]==0){
//递归继续向下走,直到不能走
DeepFirst(i);
}
}
}
/**
* 广度优先遍历
* 广度优先搜索遍历图的过程中以v 为起始点,由近至远,
* 依次访问和v 有路径相通且路径长度为1,2,…的顶点
*
* 对数组进行掐头截尾得到元素
*/
private int[] queue = new int[size];
public void BreadthFirst(int front,int tail){
//记录尾部
int last = tail;
for (int i = front; i <=tail ; i++) {
int node = queue[i];
//输出节点数据
System.out.println(this.nodes[node]);
//找出所有的邻接点
for (int j = 0; j < this.size; j++) {
if (this.edges[node][j]==1&&visit[i]==0){
//标记
visit[j] = 1;
//tail往下走
queue[++last] = j;
}
}
}
//遍历下一批节点
if (last>tail){
BreadthFirst(tail+1,last);
}
}
//初始化
public void BreadthFirst(int start){
queue[0] = start;
visit[start] = 1;
BreadthFirst(0,0);
}
}
Dijkstra算法
求图的最短路径的算法
- 首先扫描开始的中节点的邻接点,记录邻接点的权重值
- 找出邻接点里最小的那个值,把它也作为中心节点
- 重复以上操作
代码实现
package 图;
/**
* @author peiqi
* @Description:
* @date 2021/10/910:48
*/
public class Dijkstra {
//节点数目
protected int size;
//定义数组,保存顶点信息
protected String[] nodes;
//定义矩阵保存顶点的权重值
protected int[][] edges;
//节点确认--中心标识
private int[] isMarked;
//源到节点的路径信息
private String[] path;
//源到节点的距离
private int[] distances;
/**
* 初始化
*/
public void init(){
//初始化顶点
nodes = new String[]{"AA","A","B","C","D","E","F","G","H","M","K","N"};
//节点编号-常量
final int AA=0,A=1,B=2,C=3,D=4,E=5,F=6,G=7,H=8,M=9,K=10,N=11;
size=nodes.length;
edges = new int[size][size];
edges[AA][A] = 3;
edges[AA][B] = 2;
edges[AA][C] = 5;
edges[A][AA] = 3;
edges[A][D] = 4;
edges[B][AA] = 2;
edges[B][C] = 2;
edges[B][G] = 2;
edges[B][E] = 3;
edges[C][AA] = 5;
edges[C][E] = 2;
edges[C][B] = 2;
edges[C][F] = 3;
edges[D][A] = 4;
edges[D][G] = 1;
edges[E][B] = 3;
edges[E][C] = 2;
edges[E][F] = 2;
edges[E][K] = 1;
edges[E][H] = 3;
edges[E][M] = 1;
edges[F][C] = 3;
edges[F][E] = 2;
edges[F][K] = 4;
edges[G][B] = 2;
edges[G][D] = 1;
edges[G][H] = 2;
edges[H][G] = 2;
edges[H][E] = 3;
edges[K][E] = 1;
edges[K][F] = 4;
edges[K][N] = 2;
edges[M][E] = 1;
edges[M][N] = 3;
edges[N][K] = 2;
edges[N][M] = 3;
}
public Dijkstra(){
init();
isMarked = new int[size];
path = new String[size];
distances = new int[size];
for (int i=0;i<size;i++){
path[i] = "";
distances[i] = Integer.MAX_VALUE;
}
}
/**
* 扫描输入的节点的邻接点,记录邻接点的权重值
* @param node
*/
private void flushLast(int node){
//标记为中心节点
isMarked[node] = 1;
//输出路径
System.out.println(path[node]);
//扫描邻接点
for (int i = 0; i < size; i++) {
if (this.edges[node][i]>0){
//计算初始节点到i节点的权重值
int distant = distances[node]+this.edges[node][i];
//找到距离最小的路径
if (distant<distances[i]){
distances[i] = distant;
//记录路径
path[i] = path[node]+"-->"+nodes[i];
}
}
}
}
/**
* 找到最短路径
* @return
*/
public int getShort(){
int last = -1;
int min = Integer.MAX_VALUE;
for (int i = 0; i < size; i++) {
//如果被标记了就跳过
if (isMarked[i]==1){
continue;
}
if (distances[i]<min){
min = distances[i];
//记录节点
last = i;
}
}
return last;
}
/**
* 查找最短路径
* @param node
*/
public void search(int node){
path[node] = nodes[node];
distances[node] = 0;
do {
flushLast(node);
node = getShort();
}while (node!=-1);
}
public static void main(String[] args) {
Dijkstra dijkstra = new Dijkstra();
dijkstra.search(0);
}
}