图是表示一种多对多关系的数据结构
它包括无向图,有向图,带权图
无向图:就是顶点之间的连线(边)没有方向箭头
有向图:就是顶点之间的连线(边)有方向箭头
带权图:就是顶点之间的连线(边)表明了大小如上海到北京的距离,北京到天津的距离类似的导航地图就是带权图
下图就是一个无向图:
字母所表示的是顶点,顶点间的连线叫做边
按照A,B,C,D,E,F,G的顺序添加节点,并添加对应的边构成下图
深度优先遍历:
从初始访问结点出发,我们知道初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点。总结起来可以这样说:每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。
具体算法表述如下:
1.访问初始结点v,并标记结点v为已访问。
2.查找结点v的第一个邻接结点w。
3.若w存在,则继续执行4,否则算法结束。
4.若w未被访问,对w进行深度优先遍历递归(即把w当做另一个v,然后进行步骤123)。
5.查找结点v的w邻接结点的下一个邻接结点,转到步骤3。
按照上图便利的顺序应该是A->B->C->G->D->E->F->
广度优先遍历:
类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点。
具体算法表述如下:
1.访问初始结点v并标记结点v为已访问。
2.结点v入队列
3.当队列非空时,继续执行,否则算法结束。
4.出队列,取得队头结点u。
5.查找结点u的第一个邻接结点w。
6.若结点u的邻接结点w不存在,则转到步骤3;否则循环执行以下三个步骤:
1). 若结点w尚未被访问,则访问结点w并标记为已访问。
2). 结点w入队列
3). 查找结点u的继w邻接结点后的下一个邻接结点w,转到步骤6。
按照上图便利的顺序应该是A->B->C->D->E->G->F->
java代码实现:
package com.yg.graph;/*
@date 2020/3/15 9:58
*/
import java.time.chrono.IsoChronology;
import java.util.*;
public class Graph {
//存放顶点
private List<String> vertexList;
//表示顶点之间是否相连 0不连通,1连通
private int[][] edges;
//表示边的数量
private int numOfEdges = 0;
//表示遍历时是否已经访问过
private boolean[] isVisited;
public static void main(String[] args) {
//构建图
//添加顶点
String[] vertexs = {"A", "B", "C", "D", "E","F","G"};
Graph graph = new Graph(vertexs.length);
for (String vertex : vertexs) {
graph.addVertex(vertex);
}
//添加边
graph.addEdge(0, 1, 1);
graph.addEdge(0, 2, 1);
graph.addEdge(1, 2, 1);
graph.addEdge(1, 3, 1);
graph.addEdge(1, 4, 1);
graph.addEdge(3, 4, 1);
graph.addEdge(4, 5, 1);
graph.addEdge(2, 6, 1);
graph.showGraph();
//深度优先遍历
// graph.dfs(0);
//广度优先遍历
graph.bfs(0);
}
public Graph(int n) {
vertexList = new ArrayList<>(n);
edges = new int[n][n];
numOfEdges = 0;
isVisited = new boolean[n];
}
//深度优先遍历,index当前遍历的顶点
private void dfs(int index) {
//打印当前顶点
System.out.print(vertexList.get(index)+"->");
isVisited[index]=true;
//得到第一个邻接顶点
int w=getFristNeighbor(index);
//邻接顶点存在
while (w != -1) {
if (!isVisited[w]) {
dfs(w);
}
w=getNextNeighbor(index,w);
}
}
//重载dfs
public void dfs() {
//这是为了预防有向图中部分节点到不了的情况
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]) {
dfs(i);
}
}
}
//广度优先遍历
private void bfs(int index) {
//记录节点访问顺序
Queue queue=new LinkedList();
System.out.print(vertexList.get(index)+"->");
isVisited[index]=true;
queue.add(index);
while (!queue.isEmpty()) {
//队列头节点下表
int vertexIndex= (int) queue.poll();
//得到第一个邻接顶点的下标
int fristNeighbor = getFristNeighbor(vertexIndex);
//是否还有邻近顶点
while (fristNeighbor != -1) {
//是否访问过
if (!isVisited[fristNeighbor]) {
System.out.print(vertexList.get(fristNeighbor)+"->");
isVisited[fristNeighbor]=true;
//入队
queue.add(fristNeighbor);
}
fristNeighbor=getNextNeighbor(vertexIndex,fristNeighbor);
}
}
}
//重载bfs
public void bfs() {
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]) {
bfs(i);
}
}
}
//得到当前顶点的第一个邻接顶点,index当前顶点的下标
public int getFristNeighbor(int index) {
for (int i = 0; i < vertexList.size(); i++) {
if (edges[index][i] > 0) {
return i;
}
}
return -1;
}
//得到当前顶点邻接顶点的下一个顶点
public int getNextNeighbor(int index,int neighborIndex) {
for (int i =neighborIndex+1; i <vertexList.size() ; i++) {
if (edges[index][i]>0) {
return i;
}
}
return -1;
}
//添加顶点
public void addVertex(String vertex) {
vertexList.add(vertex);
}
//添加边
/*
* @param val1 顶点的下标,第几个添加的顶点,小标从0开始
* @param val2
* @param weight 权重,为1表明有边,0表示没边
* @return : void
* @date : 2020/3/15 10:05
*/
public void addEdge(int val1, int val2, int weight) {
edges[val1][val2] = weight;
edges[val2][val1] = weight;
numOfEdges++;
}
//得到顶点数量
public int getNumOfVertex() {
return vertexList.size();
}
//得到边的数量
public int getNumOfEdges() {
return numOfEdges;
}
//打印图
public void showGraph() {
for (int[] vertex : edges) {
System.out.println(Arrays.toString(vertex));
}
}
}