2019独角兽企业重金招聘Python工程师标准>>>
图遍历的定义:
从图中某个顶点出发,访问图中其余顶点,并且是图中每个顶点仅被访问一次。包括2中,深度优先遍历(DFS)和广度优先遍历(BFS)。
准备工作,我们需要对之前的java代码进行一些补充,来满足遍历算法的实现。
public class Graph {
public List
public int[][] arcs;
public static void main(String[] args){
Graph graph = new Graph();
String nodeNames[] = {"aaa", "bbb", "ccc", "ddd"};
for (String s : nodeNames){
graph.nodes.add(new Node(s));
}
graph.arcs = new int[nodeNames.length][nodeNames.length];
for (int i = 0; i < graph.nodes.size(); i ++){
for (int j = 0; j < graph.nodes.size(); j ++){
graph.arcs[i][j] = i == j ? 0 : 1;
}
}
}
}
public class Node {
private String nodeName;
public Node(String name){
this.nodeName = name;
}
private boolean visitFlag = false;
public void visit(){
if (visitFlag){
throw new RuntimeException("This node has been visited!");
}else{
System.out.println(nodeName);
visitFlag = true;
}
}
public boolean isVisited(){
return visitFlag;
}
}
连通图的深度优先遍历
先说算法,访问V0,找到它的第一个未访问邻接节点V1,访问V1,找到它的第一个未访问邻接节点V2,访问V2,以此类推,如果某个结点的所有邻接节点都已访问,找到最后一个访问节点,继续找它的第一个未访问邻接节点,直到所有节点访问完毕。
代码的解释,首先定义一个当前序号curIndex = 0,然后定义一个已访问node的index栈,还有已访问结点个数。循环当中,首先访问当前节点,之后,找它的第一个未访问邻接节点,如果找到的话,将当前index进栈,找到的这个节点作为当前节点,继续循环,如果没找到,需要从已访问节点栈当中拿最顶端的出来,继续找它的第一个未访问邻接节点。如果已访问节点数量==总结点数量,意味着遍历结束。还有一个问题,我们这个算法只适用于连通图,如果是非连通图,会报错“The graph is not all connected!”。
public void dfs() {
int curIndex = 0;
Stack
int visitedCnt = 0;
while (true) {
Node curNode = nodes.get(curIndex);
if (!curNode.isVisited()) {
curNode.visit();
visitedCnt ++;
if (visitedCnt == nodes.size()){
break;
}
}
LoopForFound: while(true){
for (int i = 0; i < arcs[curIndex].length; i++) {
if (arcs[curIndex][i] == 1 && !nodes.get(i).isVisited()) {
preindexes.push(curIndex);
curIndex = i;
break LoopForFound;
}
}
if (preindexes.isEmpty()){
throw new RuntimeException("The graph is not all connected!");
}
curIndex = preindexes.pop();
}
}
}
连通图的广度优先遍历
先说算法,访问V,找到它的所有未访问邻接节点,全部访问一遍,然后拿到它的第一个邻接节点V1,找到V1的所有未访问邻接节点,全部访问一遍,在拿到V0的第二个邻接节点V2,以此类推,直到所有节点访问完毕。
代码的解释,curIndex 表示当前访问节点序号,然后是已访问节点队列visitedQueue ,已访问节点个数,循环的开始,有一个特殊的判断,如果是第一次进来,直接把curIndex 设为0,访问,并进入队列,判断已访问个数;如果不是第一次,直接从队列当中取一个出来作为当前序号;然后查找并访问当前节点的所有未访问邻接节点, 并进入队列,然后判断已访问个数。还有一个问题,我们这个算法只适用于连通图,如果是非连通图,会报错“The graph is not all connected!”。
public void bfs() {
int curIndex = -1;
Queue
int visitedCnt = 0;
BFSTopLoop: while(true){
if (curIndex == -1){
curIndex = 0;
nodes.get(curIndex).visit();
visitedQueue.offer(curIndex);
visitedCnt ++;
if (visitedCnt == nodes.size()){
break BFSTopLoop;
}
}else{
if (visitedQueue.isEmpty()){
throw new RuntimeException("The graph is not all connected!");
}
curIndex = visitedQueue.poll();
}
for (int i = 0; i < arcs[curIndex].length; i ++){
if (arcs[curIndex][i] == 1){
Node tmp = nodes.get(i);
if (!tmp.isVisited()){
tmp.visit();
visitedQueue.offer(i);
visitedCnt ++;
if (visitedCnt == nodes.size()){
break BFSTopLoop;
}
}
}
}
}
}
上面2个遍历算法,都是针对连通图的实现,如果要适应非连通图,需要做下面几点修改
1、把while(true)的外面加上对所有节点的循环
2、抛出异常的部分改成continue最外层循环
看一下代码,首先是非连通图的深度优先遍历,红色部分是针对之前的算法修改的部分。
public void dfsForNoAllConnected() {
int visitedCnt = 0;
TOPLOOP:
for (int curIndex = 0; curIndex < nodes.size(); curIndex++) {
if (nodes.get(curIndex).isVisited()){
continue;
}
Stack
while (true) {
Node curNode = nodes.get(curIndex);
if (!curNode.isVisited()) {
curNode.visit();
visitedCnt++;
if (visitedCnt == nodes.size()) {
break TOPLOOP;
}
}
LoopForFound:
while (true) {
for (int i = 0; i < arcs[curIndex].length; i++) {
if (arcs[curIndex][i] == 1 && !nodes.get(i).isVisited()) {
preindexes.push(curIndex);
curIndex = i;
break LoopForFound;
}
}
if (preindexes.isEmpty()) {
continue TOPLOOP;
}
curIndex = preindexes.pop();
}
}
}
}
然后是非连通图的广度度优先遍历
public void bfsForNoAllConnected() {
int visitedCnt = 0;
TOPLOOP:
for (int curIndex = 0; curIndex < nodes.size(); curIndex++) {
if (nodes.get(curIndex).isVisited()){
continue;
}
Queue
boolean firstLoop = true;
BFSTopLoop:
while (true) {
if (firstLoop) {
nodes.get(curIndex).visit();
visitedQueue.offer(curIndex);
visitedCnt++;
if (visitedCnt == nodes.size()) {
break TOPLOOP;
}
firstLoop = false;
} else {
if (visitedQueue.isEmpty()) {
continue TOPLOOP;
}
curIndex = visitedQueue.poll();
}
for (int i = 0; i < arcs[curIndex].length; i++) {
if (arcs[curIndex][i] == 1) {
Node tmp = nodes.get(i);
if (!tmp.isVisited()) {
tmp.visit();
visitedQueue.offer(i);
visitedCnt++;
if (visitedCnt == nodes.size()) {
break TOPLOOP;
}
}
}
}
}
}
}