特点:
图形结构中,每个结点的前驱结点数和后续结点数可以任意多个,能表示多对多的关系。
存储方式
邻接矩阵:一个int[n][n]的二维数组,其中n为图的节点数,图节点关系稀疏时浪费空间很严重
邻接表:每个节点通过链表存储与之相连的节点信息
使用邻接矩阵实现一个图,并实现深度优先搜索和广度优先搜索
深度优先遍历思路:
从某一个顶点出发,访问与它邻接的一个顶点,然后再从该顶点出发,访问一个与它邻接并且未访问过的节点,如果存在这样的顶点则访问,不存在则返回上一个顶点寻找另一个与它邻接并且未访问的顶点。如果回到第一个顶点,则从存储的下一个未访问的节点出发。
广度优先遍历思路:
从某一个顶点出发,访问与它邻接的所有顶点,并且存储在队列中,然后从队头开始,访问队列中的每一个顶点的邻接顶点(未访问的)并存在队列中,直到队列为空,再从存储的下一个未访问顶点出发。
先定义类和常用方法:
public class Graph { //无向图
private ArrayList<String> vertexList; //存储顶点集合
private int[][] edges; //存储图对应的邻接矩阵
private int edgeCount; //边的条数
public Graph(int n) { //构造器,传入顶点个数
vertexList = new ArrayList<String>(n);
edges = new int[n][n];
}
//插入节点
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
//添加边
public void addEdge(int v1,int v2,int weight) {
edges[v1][v2] = weight;
edges[v2][v1] = weight;
edgeCount++ ;
}
//获取顶点个数
public int getNumOfVertex() {
return vertexList.size();
}
//获取边的条数
public int vertexList() {
return edgeCount;
}
//通过顶点索引获取顶点值
public String getValueByIndex(int i) {
return vertexList.get(i);
}
//返回边的权值
public int getEdgebyIndex(int v1,int v2) {
return edges[v1][v2];
}
//显示邻接矩阵
public void showEdges() {
for(int[] arr:edges) {
System.out.println(Arrays.toString(arr));
}
}
}
深度优先搜索
//从下标start开始查找index节点的邻接点
private int getBorder(int index,int start) {
for(int j=start;j<vertexList.size();j++) {
if( edges[index][j] > 0) {
return j;
}
}
return -1;
}
//访问某个顶点,并递归访问它的未访问邻接顶点
private void dfs(boolean[] isVisited,int i) {
System.out.print(getValueByIndex(i)+ "->");//访问该节点
isVisited[i] = true;//把该节点设置为已访问
int border = getBorder(i,0);//查找节点i的第一个邻接点
while(border != -1) {
if(!isVisited[border]) {
dfs(isVisited,border);
}
border = getBorder(i, border + 1);//访问i的邻接点中border的下一个邻接点
}
}
public void dfs(){
boolean[] isVisited = new boolean[vertexList.size()];//记录顶点是否被访问过
for(int i=0;i<vertexList.size();i++) {
if(!isVisited[i]) {
dfs(isVisited,i);
}
}
}
广度优先搜索
public void bfs(){
boolean[] isVisited = new boolean[vertexList.size()];//记录顶点是否被访问过
//存储未访问邻接点的队列
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(vertexList.size());
for(int i=0;i<vertexList.size();i++) {
if(!isVisited[i]) {
System.out.print(getValueByIndex(i)+ "->");//访问该节点
isVisited[i] = true;//把该节点设置为已访问
bfs(isVisited,queue,i);
}
}
}
private void bfs(boolean[] isVisited,ArrayBlockingQueue<Integer> queue,int i) {
for(int j=i;j<vertexList.size();j++) {
if( edges[i][j] > 0 && isVisited[j] == false) {
queue.add(j); //把未访问的邻接点加入队列
System.out.print(getValueByIndex(j)+ "->");//把该节点设置为已访问
isVisited[j] = true;
}
}
while(!queue.isEmpty()) {
int index = queue.poll();
bfs(isVisited,queue,index);
}
}
使用该类创建一个图,如下图的关系
public static void main(String[] args) {
String[] vertexs = {"A","B","C","D","E","F"};
int n = vertexs.length;
Graph graph = new Graph(n);
for(String vertex:vertexs) {
graph.insertVertex(vertex);
}
graph.addEdge(0, 1, 1);
graph.addEdge(0, 2, 2);
graph.addEdge(0, 4, 4);
graph.addEdge(1, 3, 3);
graph.addEdge(2, 3, 2);
graph.addEdge(2, 4, 3);
graph.addEdge(2, 5, 5);
graph.addEdge(3, 5, 4);
System.out.println("邻接矩阵:");
graph.showEdges();
System.out.println("深度遍历:");
graph.dfs();
System.out.println("\n广度遍历:");
graph.bfs();
}
运行结果: