图的表示
图的表示方法有俩种,邻接矩阵和邻接表。俩种方法各有特色。
邻接矩阵:边的关系使用二维数组保存。邻接表:边的关系使用链表
本文以邻接矩阵来演示图的基本操作。
图顶点类
- isVisited代表该节点是否访问过
public class Vertex {
//显示A,B,C,D
public char label;
public boolean isVisited=false;
public Vertex(char lable){
this.label=lable;
}
}
图类
-
成员变量需要有保存顶点的数组
-
保存边的二维数组
-
顶点的最大数目
-
当前顶点数
-
栈和队列是dfs和bfs使用的,我调用的是我自己写的api
初始化
public Graph(){
verticesList=new Vertex[maxSize];
adjMat=new int[maxSize][maxSize];
//初始化邻接矩阵,为0代表没有链接
for (int i = 0; i < maxSize; i++) {
for (int j = 0; j < maxSize; j++) {
adjMat[i][j]=0;
}
}
nVertext=0;
stack=new MyStack(maxSize);
queue=new MyQueue(maxSize);
}
深度优先搜索
public void dfs(){
//首先访问0号顶点
verticesList[0].isVisited=true;
//显示该顶点
displayVertex(0);
//压入栈
stack.push(0);
while (!stack.empty()){
int v=getUnvisitedVertex(stack.peek());
if(v==-1){
//找不到
stack.pop();
} else {
verticesList[v].isVisited=true;
displayVertex(v);
stack.push(v);
}
}
//搜索完成以后复原
for (int i = 0; i < nVertext; i++) {
verticesList[i].isVisited=false;
}
}
广度优先搜索
public void bfs(){
//首先访问0号顶点
verticesList[0].isVisited=true;
//显示该顶点
displayVertex(0);
//添加到队列
queue.insert(0);
while (!queue.empty()){
int v=getUnvisitedVertex(queue.peek());
//添加未被访问的节点
while (v!=-1){
verticesList[v].isVisited=true;
displayVertex(v);
queue.insert(v);
v=getUnvisitedVertex(queue.peek());
}
queue.remove();
}
//搜索完成以后复原
for (int i = 0; i < nVertext; i++) {
verticesList[i].isVisited=false;
}
}
图的最小生成树
最小生成树就是走最少的路线遍历完整个图,实现和dfs几乎一模一样
我只是做了简单的修改,打印出最小生成树路径
public void mst(){
//首先访问0号顶点
verticesList[0].isVisited=true;
//压入栈
stack.push(0);
while (!stack.empty()){
int cur=stack.peek();
int v=getUnvisitedVertex(stack.peek());
if(v==-1){
//找不到
stack.pop();
} else {
verticesList[v].isVisited=true;
stack.push(v);
//打印路径
displayVertex(cur);
System.out.print("-");
displayVertex(v);
System.out.print(" ");
}
}
//搜索完成以后复原
for (int i = 0; i < nVertext; i++) {
verticesList[i].isVisited=false;
}
}
总体实现
public class Graph {
//顶点数组
private Vertex[] verticesList;
//邻接矩阵adjacent matrix
private int[][] adjMat;
//顶点的最大数目
private int maxSize=10;
//已经添加的顶点
private int nVertext;
//栈,dfs
private MyStack stack;
//队列
private MyQueue queue;
public Graph(){
verticesList=new Vertex[maxSize];
adjMat=new int[maxSize][maxSize];
//初始化邻接矩阵,为0代表没有链接
for (int i = 0; i < maxSize; i++) {
for (int j = 0; j < maxSize; j++) {
adjMat[i][j]=0;
}
}
nVertext=0;
stack=new MyStack(maxSize);
queue=new MyQueue(maxSize);
}
/**
* 添加顶点
*/
public void addVertex(char lable){
verticesList[nVertext++]=new Vertex(lable);
}
/**
* 添加边
*/
public void addEdge(int start,int end){
adjMat[start][end]=1;
adjMat[end][start]=1;
}
/**
* 获得邻接的未访问过的节点,-1表示找不到
*/
public int getUnvisitedVertex(int v){
for(int i=0;i<nVertext;i++){
if(adjMat[v][i]==1&&verticesList[i].isVisited==false)
return i;
}
return -1;
}
public void dfs(){
//首先访问0号顶点
verticesList[0].isVisited=true;
//显示该顶点
displayVertex(0);
//压入栈
stack.push(0);
while (!stack.empty()){
int v=getUnvisitedVertex(stack.peek());
if(v==-1){
//找不到
stack.pop();
} else {
verticesList[v].isVisited=true;
displayVertex(v);
stack.push(v);
}
}
//搜索完成以后复原
for (int i = 0; i < nVertext; i++) {
verticesList[i].isVisited=false;
}
}
public void bfs(){
//首先访问0号顶点
verticesList[0].isVisited=true;
//显示该顶点
displayVertex(0);
//添加到队列
queue.insert(0);
while (!queue.empty()){
int v=getUnvisitedVertex(queue.peek());
//添加未被访问的节点
while (v!=-1){
verticesList[v].isVisited=true;
displayVertex(v);
queue.insert(v);
v=getUnvisitedVertex(queue.peek());
}
queue.remove();
}
//搜索完成以后复原
for (int i = 0; i < nVertext; i++) {
verticesList[i].isVisited=false;
}
}
//最小生成树,链接每个顶点最少的连线,定点数-1
public void mst(){
//首先访问0号顶点
verticesList[0].isVisited=true;
//压入栈
stack.push(0);
while (!stack.empty()){
int cur=stack.peek();
int v=getUnvisitedVertex(stack.peek());
if(v==-1){
//找不到
stack.pop();
} else {
verticesList[v].isVisited=true;
stack.push(v);
//打印路径
displayVertex(cur);
System.out.print("-");
displayVertex(v);
System.out.print(" ");
}
}
//搜索完成以后复原
for (int i = 0; i < nVertext; i++) {
verticesList[i].isVisited=false;
}
}
public void displayVertex(int v){
System.out.print(verticesList[v].label+"");
}
}
测试类
结点构成了互相连接的五角星,测试dfs,bfs和图的最小生成树
public class GraphTest {
public static void main(String[] args) {
Graph graph=new Graph();
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addVertex('D');
graph.addVertex('E');
graph.addEdge(0,1);
graph.addEdge(0,2);
graph.addEdge(0,3);
graph.addEdge(0,4);
graph.addEdge(1,2);
graph.addEdge(1,3);
graph.addEdge(1,4);
graph.addEdge(2,3);
graph.addEdge(2,4);
graph.addEdge(3,4);
graph.dfs();
System.out.println();
graph.bfs();
System.out.println();
graph.mst();
}
}
总结
- 图的顶点最重要的是要有一个访问属性
- 图的主要操作是增加节点,增加边,遍历,求最小生成树
反思
- 从测试中就看出代码写的比较繁琐,预想中构造一个图直接传入边就好了,比如
new Graph('A','B')
- 节点类型可以拓展成泛型
- dfs和bfs可以使用递归实现,比较懒就不写了
- 有时间再修改完善