首先来看一下图的存储结构
在这里,明确一下图和网的区别:图节点的关系为否联通 网节点之间的连通路径具有长度
在图邻接矩阵的表示中,非联通节点的值为0,连通节点的值为1
如果对于网而言,一般节点自身值为0,连通节点之间为距离,不连通节点的距离设置为不可能出现的最大值
下面的邻接矩阵表示的是网,因为后面学习最小生成树算法和最短路径算法中用的到
class AdjMatrix
{
int[][] adjMatrix= {{0,9,4,Integer.MAX_VALUE,6},
{9,0,3,3,Integer.MAX_VALUE},
{4,3,0,5,Integer.MAX_VALUE},
{Integer.MAX_VALUE,3,5,0,1},
{6,3,Integer.MAX_VALUE,1,0}};//邻接矩阵
int nodeNum=5;//图中节点个数
int[] data= {0,1,2,3,4};//表示邻接矩阵各位置的值 可以为任意类型
public AdjMatrix(){}//默认构造函数 使用默认值
//分配内存,期待后期赋值
public AdjMatrix(int nodeNum)
{
this.nodeNum=nodeNum;
adjMatrix=new int[nodeNum][nodeNum];
data=new int[nodeNum];
}
}
关于用邻接矩阵表示的图有以下基本操作:
邻接矩阵的遍历
深度优先遍历
递归实现
/******************
* 深度优先遍历用邻接矩阵表示的图
* @param matrix 图结构
*/
public static void DFSTraversal(AdjMatrix graph)
{
//非空判断
if(graph==null||graph.data.length==0)
{
return;
}
int nodeNum=graph.data.length;//节点个数
//建立boolean数组表示节点是否访问
boolean[] visited=new boolean[nodeNum];
for(int i=0;i<nodeNum;i++)
{
if(!visited[i])//检测节点是否访问过
{
DFS(graph,i,visited);
}
}
}
/**********************
* 访问图中的一个节点
* @param matrix 用来表示图的邻接矩阵
* @param index 访问节点的位置
* @param visited
*/
private static void DFS(AdjMatrix graph,int index,boolean[] visited)
{
//访问i节点 并设置访问标识
System.out.println(graph.data[index]);
visited[index]=true;
for(int i=0;i<graph.data.length;i++)
{
if(graph.adjMatrix[index][i]!=0&&graph.adjMatrix[index][i]!=Integer.MAX_VALUE&&!visited[i])
{
DFS(graph,i,visited);
}
}
}
/*********************
广度优先遍历
借用队列实现
/************************
* 广度优先遍历图
* @param graph 需要进行遍历的图结构
*/
public static void BFSTraversal(AdjMatrix graph)
{
//非空判断
if(graph==null||graph.data.length==0)
{
return;
}
//记录节点个数
int nodeNum=graph.data.length;
//使用数组模拟队列
int[] queue=new int[nodeNum];
int queueHead=0;//队列头节点位置
int queueNext=0;//队列尾节点位置
//记录节点是否被读取过
boolean[] visited=new boolean[nodeNum];
//保证所有的节点都被访问过 此循环用于处理非联通图的情况
for(int i=0;i<nodeNum;i++)
{
if(!visited[i])
{
visited[i]=true;
//访问该节点位置
System.out.println(graph.data[i]);
queue[queueNext++]=i;//将节点加入队列中
//当队列不为空时 此循环可以将与0位置联通的节点全部遍历
while(queueHead!=queueNext)
{
int tempIndex=queue[queueHead++];//弹出一个位置
//查看该节点可达的节点
for(int j=0;j<nodeNum;j++)
{
if(graph.adjMatrix[i][j]!=0&&graph.adjMatrix[i][j]!=Integer.MAX_VALUE&&!visited[j])
{
visited[j]=true;//访问该位置
System.out.println(graph.data[j]);
queue[queueNext++]=j;//节点入队
}
}
}
}
}
}
最小生成树求解
prim算法
* 普里姆算法的思路:划定节点集和边集,
* 节点集和边集由一个数组lowcost来表示,下标表示节点的位置,
* 值为零表示此节点在节点集中,不为零则表示节点集中的节点到此节点的最短距离
* 初始化时将开始节点加入节点集中,并将边集置为节点集中的节点到非节点集中节点的权值
* 每次从边集中选出一条最短的边,则该边一个节点在节点集中,另一个节点不在,将不在节点集中的节点加入节点集中
* 并设置节点集中的各节点之间的距离为0
* 另设置adjvex数组,其下标表示节点在图中的位置,值表示lowest数组相应位置的路径长度值是由哪个节点指向此节点的
* prim算法本质上是一种动态规划算法**
/* 根据图的邻接矩阵获取最小生成树
* @param matrix 图的邻接矩阵
*/
public static void getMinCreateTreePrim(int[][] matrix)
{
if(matrix==null||matrix.length==0||matrix[0].length==0)
{
return ;
}
//图中的节点数量
int nodeNum=matrix.length;
int[] adjvex=new int[nodeNum];
int[] lowcost=new int[nodeNum];//从节点集中的节点到其他节点的最短距离 为零表示节点处于节点集中
//假设从0节点开始访问
lowcost[0]=0;//0位置节点加入节点集中
//初始化 path中已经都初始化为0
for(int i=0;i<nodeNum;i++)
{
//值为零表示此节点在节点集中,不为零则表示节点集中的节点到此节点的最短距离
lowcost[i]=matrix[0][i];
adjvex[i]=0;
}
//n个节点的最小生成树具有n-1条边 每次循环确定一条边
for(int i=1;i<nodeNum;i++)
{
int minPath=Integer.MAX_VALUE;//本次循环最短边的长度
int minPathIndex=0;//本次循环最短边是由哪条边发出的
//遍历lowest数组 查找最短边
for(int j=0;j<nodeNum;j++)
{
if((lowcost[j]!=0)&&(lowcost[j]<minPath))
{
minPath=lowcost[j];//最短长度
minPathIndex=j;
}
}
//将minPathIndex节点加入节点集中
// System.out.println(adjvex[minPathIndex]+" "+minPathIndex);
lowcost[minPathIndex]=0;
//更新lowest数组值
for(int j=0;j<nodeNum;j++)
{
//如果新加入的节点集的节点到这个节点的距离小于之前节点集中节点到此节点的最小距离
if(lowcost[j]>matrix[minPathIndex][j])
{
//修改从节点集到此节点的最小距离
lowcost[j]=matrix[minPathIndex][j];
adjvex[j]=minPathIndex;//修改指向此节点的节点位置
}
}
}
}
最短路径求解
Dijkstra算法
比较迪杰斯特拉算法和普里姆算法,你回发现有很多相似的地方
如果将起点视为终点,考虑其他节点到起点的距离,算法可能会更好理解。
循环中,首先找到了距离起点最近的一个点,然后更新除起点到起点和距离最近的点到起点的距离之外的其他距离,如果其他节点经过最近的点中转后到起点的距离小于直接到起点的距离,那么更新此值,并记录经过哪个节点中转。
代码中,只返回了最短路径的长度,如果你想获得最短路径,可以使用preVex数组,它的值记录着每个节点的前驱节点,这个路径即为end–>preVex[end]–>preVex[preVex[end]]–>…–>start。
/***********************
* 迪杰斯特拉算法求解最短路径
* @param matrix 邻接矩阵
* @param start 开始节点位置
* @param end 结束节点的位置
* @return 从start到end的节点最短路径长度
*/
public static int getMinPathDijkstra(int[][] matrix,int start,int end)
{
if(matrix==null||matrix.length==0||matrix[0].length==0)
{
return -1;
}
//起点和终点位于同一位置
if(start==end)
{
return 0;
}
//记录图节点个数
int nodeNum=matrix.length;
//前驱节点的下标
int[] preVex=new int[nodeNum];
//从开始节点到其它节点的最短距离
int[] shortPath=new int[nodeNum];
//已经确定过的节点 原点到该节点的最短路径已经确定
boolean[] sureNode=new boolean[nodeNum];
//初始化阶段
sureNode[start]=true;//开始节点已经访问过
for(int i=0;i<nodeNum;i++)
{
//当前节点到其它节点的距离为
shortPath[i]=matrix[start][i];
//其它节点的前驱节点都为start
preVex[i]=start;
}
//开始循环确定节点 每次确定一个节点
for(int i=1;i<nodeNum;i++)
{
int minPath=Integer.MAX_VALUE;//当前节点到其它节点的最短距离
int minPathIndex=0;//所谓其它节点的位置
//此处的循环为了查找当前节点集中 两节点之间的最小值 用于确定一个节点
for(int j=0;j<nodeNum;j++)
{
if(!sureNode[j]&&shortPath[j]<minPath)
{
minPath=shortPath[j];
minPathIndex=j;
}
}
//确定一个节点
sureNode[minPathIndex]=true;
//更新其它节点到开始节点的最短距离
for(int j=0;j<nodeNum;j++)
{
if(!sureNode[j]&&matrix[minPathIndex][i]!=Integer.MAX_VALUE&&shortPath[j]>minPath+matrix[minPathIndex][j])
{
shortPath[j]=minPath+matrix[minPathIndex][j];
preVex[j]=minPathIndex;
}
}
}
return shortPath[end];
}
}