图的遍历
一、DFS(Depth First Search)
概念:
从初始访问点出发,访问其第一个邻接节点;然后把这个邻接节点作为初始节点,继续访问它的第一个邻接节点。
即每次都在访问完当前节点过后,访问它的第一个邻接节点。
算法:
- 访问初始节点v(下标),并标记v已被访问;
- 查找节点v的第一个邻接节点w(下标);
- 如果w存在,则继续
4
步骤;如果w不存在,则返回1
步骤; - 如果w未被访问过,对w进行dfs递归操作(重复
1
、2
、3
操作); - 查找节点v的w邻接节点的下一个邻接节点,返回
3
步骤
源代码如下:
import java.util.ArrayList;
import java.util.Arrays;
public class graph {
private ArrayList<String> vertexList;// 定义顶点列表
private int[][] edges;// 存储图对应的邻接矩阵
private int numOfEdges;// 存储边
private boolean isVisited[];// 标记顶点是否已被访问
public static void main(String[] args) {
// TODO Auto-generated method stub
// 测试图
int n = 5;// 定义顶点个数
String vertexs[] = { "A", "B", "C", "D", "E" };
// 创建图对象
graph graph = new graph(n);
// 循环添加顶点
for (String vertex : vertexs) {// 每次循环从vertexValue取出来一个值(定义为value)
graph.insertVertex(vertex);// 把顶点加进去
}
// 添加边
// A-B A-C B-C B-D B-E
// 权值默认为是1
graph.insertEdges(0, 1, 1);// A-B
graph.insertEdges(0, 2, 1);// A-C
graph.insertEdges(1, 2, 1);// B-C
graph.insertEdges(1, 3, 1);// B-D
graph.insertEdges(1, 4, 1);// B-E
// 显示邻接矩阵
graph.show();
// DFS
graph.dfs();
}
// ————————————--------------------------------------------------------
// 首先定义图的属性
// 1.构造器
public graph(int n) {// 首先要传入顶点的数目
// 初始化矩阵和vertexList
edges = new int[n][n];
vertexList = new ArrayList<String>();// 初始化顶点列表
numOfEdges = 0;// 可省略,因为默认为0
isVisited = new boolean[5];
}
// 2.插入顶点
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
// 3.设置边
/**
*
* @param v1 第一个顶点的下标
* @param v2 第二个顶点的下标
* @param weight 权值
*/
public void insertEdges(int v1, int v2, int weight) {
// 这里是定义无向图,所以两个方向都要定义
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
// 4.返回顶点个数
public int getnumOfVertexs() {
return vertexList.size();
}
// 5.返回边个数
public int getnumOfEdges() {
return numOfEdges;
}
// 6.返回顶点i(顶点的下标)的对应的数据
/**
*
* @param i返回数据下标
* @return 返回数据 例:0->A 1->B 2->C 3->D 4->E
*/
public String getValueByIndex(int i) {
return vertexList.get(i);
}
// 7.返回v1,v2两顶点之间的权值
public int getWeight(int v1, int v2) {
return edges[v1][v2];
}
// 8.打印邻接矩阵(本质:遍历图)
public void show() {
for (int[] link : edges) {
System.out.println(Arrays.toString(link));
}
}
//————————————--------------------------------------------------------
// DFS算法步骤
// 1.得到第一个邻接节点下标w
/**
*
* @param index
* @return 如果存在就返回它的下标,没有就返回-1
*/
public int getFirstNerghborIndex(int index) {// 传入当前节点的下标index
for (int w = 0; w < vertexList.size(); w++) {
if (edges[index][w] > 0) {// 如果下一个邻接节点存在
return w;// 返回它的下标
}
}
return -1;// 没有返回就返回-1
}
// 2.根据前一个邻接节点的下标来获取下一个邻接节点
/**
*
* @param v1
* @param v2
* @return
*/
public int getNextNeighbor(int v1, int v2) {
for (int w = v2 + 1; w < vertexList.size(); w++) {
if (edges[v1][w] > 0) {
return w;
}
}
return -1;
}
// 3.开始深搜遍历
public void dfs(boolean[] isVisited, int i) {// 判断是否遍历过的标记,定义节点下标
// 首先访问当前节点,输出
System.out.print(getValueByIndex(i) + "->");
// 访问过后,要标记节点已访问
isVisited[i] = true;
// 查找节点i的第一个邻接节点
int w = getFirstNerghborIndex(i);
while (w != -1) {// 存在
if (!isVisited[w]) {// 如果没有被标记过
dfs(isVisited, w);// 以w为初始节点重新开始,递归操作dfs
}
// 如果已经被访问过
w = getNextNeighbor(i, w);
}
}
// 对dfs进行重载,遍历所有节点
public void dfs() {
for (int i = 0; i < getnumOfVertexs(); i++) {
while (!isVisited[i]) {// 如果没被访问过
dfs(isVisited, i);
}
}
}
}
二、BFS(Broad First Search)
.addLast() | 获取最后一个节点 |
---|
概念:
类似一个分层搜索过程,BFS需要一个队列以保持访问过节点的顺序,以便按照这个顺序来访问他们的邻接节点。
算法:
- 访问初始节点v,并将其标记已经访问;
- 把节点v加入队列;
- 当队列非空时,继续执行,否则算法结束;
- 出队列,取得队头结点u;
- 查找初始节点也是队头结点u第一个邻接节点w;
- 若节点u的第一个邻接节点w不存在,执行步骤
3
,否则循环执行下述三个操作:
(1)若节点w未被访问,访问它并标记已经访问;
(2)节点w入队列;
(3)查找节点u的继w邻接节点后的下一个邻接节点,转到步骤6
。
源代码如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
public class graph {
private ArrayList<String> vertexList;// 定义顶点列表
private int[][] edges;// 存储图对应的邻接矩阵
private int numOfEdges;// 存储边
private boolean isVisited[];// 标记顶点是否已被访问
public static void main(String[] args) {
// TODO Auto-generated method stub
// 测试图
int n = 5;// 定义顶点个数
String vertexs[] = { "A", "B", "C", "D", "E" };
// 创建图对象
graph graph = new graph(n);
// 循环添加顶点
for (String vertex : vertexs) {// 每次循环从vertexValue取出来一个值(定义为value)
graph.insertVertex(vertex);// 把顶点加进去
}
// 添加边
// A-B A-C B-C B-D B-E
// 权值默认为是1
graph.insertEdges(0, 1, 1);// A-B
graph.insertEdges(0, 2, 1);// A-C
graph.insertEdges(1, 2, 1);// B-C
graph.insertEdges(1, 3, 1);// B-D
graph.insertEdges(1, 4, 1);// B-E
// 显示邻接矩阵
graph.show();
/*// DFS
System.out.println();
System.out.println("DFS:");
graph.dfs();
*/
System.out.println();
System.out.println();
System.out.println("BFS:");
graph.bfs();
}
// ————————————--------------------------------------------------------
// 首先定义图的属性
// 1.构造器
public graph(int n) {// 首先要传入顶点的数目
// 初始化矩阵和vertexList
edges = new int[n][n];
vertexList = new ArrayList<String>();// 初始化顶点列表
numOfEdges = 0;// 可省略,因为默认为0
isVisited = new boolean[vertexList.size() + 1];
}
// 2.插入顶点
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
// 3.设置边
/**
*
* @param v1 第一个顶点的下标
* @param v2 第二个顶点的下标
* @param weight 权值
*/
public void insertEdges(int v1, int v2, int weight) {
// 这里是定义无向图,所以两个方向都要定义
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
// 4.返回顶点个数
public int getnumOfVertexs() {
return vertexList.size();
}
// 5.返回边个数
public int getnumOfEdges() {
return numOfEdges;
}
// 6.返回顶点i(顶点的下标)的对应的数据
/**
*
* @param i返回数据下标
* @return 返回数据 例:0->A 1->B 2->C 3->D 4->E
*/
public String getValueByIndex(int i) {
return vertexList.get(i);
}
// 7.返回v1,v2两顶点之间的权值
public int getWeight(int v1, int v2) {
return edges[v1][v2];
}
// 8.打印邻接矩阵(本质:遍历图)
public void show() {
for (int[] link : edges) {
System.out.println(Arrays.toString(link));
}
}
//————————————--------------------------------------------------------
// DFS算法步骤
// 1.得到第一个邻接节点下标w
/**
*
* @param index
* @return 如果存在就返回它的下标,没有就返回-1
*/
public int getFirstNerghborIndex(int index) {// 传入当前节点的下标index
for (int w = 0; w < vertexList.size(); w++) {
if (edges[index][w] > 0) {// 如果下一个邻接节点存在
return w;// 返回它的下标
}
}
return -1;// 没有返回就返回-1
}
// 2.根据前一个邻接节点的下标来获取下一个邻接节点
/**
*
* @param v1为前驱点
* @param v2为前驱点的当前邻接节点
* @return
*/
public int getNextNeighbor(int v1, int v2) {
for (int w = v2 + 1; w < vertexList.size(); w++) {
if (edges[v1][w] > 0) {
return w;
}
}
return -1;
}
// 3.开始深搜遍历
private void dfs(boolean[] isVisited, int i) {// 判断是否遍历过的标记,定义节点下标
// 首先访问当前节点,输出
System.out.print(getValueByIndex(i) + "->");
// 访问过后,要标记节点已访问
isVisited[i] = true;
// 查找节点i的第一个邻接节点
int w = getFirstNerghborIndex(i);
while (w != -1) {// 存在
if (!isVisited[w]) {// 如果没有被标记过
dfs(isVisited, w);// 以w为初始节点重新开始,递归操作dfs
}
// 如果已经被访问过
// 以u为前驱点,找w的下一个邻接节点
// w为辅助变量,每次进行此操作的时候更新值
w = getNextNeighbor(i, w);
}
}
// 对dfs进行重载,遍历所有节点
public void dfs() {
for (int i = 0; i < getnumOfVertexs(); i++) {
while (!isVisited[i]) {// 如果没被访问过
dfs(isVisited, i);
}
}
}
// ————————————--------------------------------------------------------
// BFS算法步骤
private void bfs(boolean[] isVisited, int i) {
int u;// 初始节点,队头结点
int w;// 邻接节点
// 使用队列记录访问节点的顺序
LinkedList queue = new LinkedList();
// 访问节点(输出节点信息)
System.out.print(getValueByIndex(i) + "->");
标记为已访问
isVisited[i] = true;
// 若已访问,添加节点到队列
queue.addLast(i);
// 重复操作
while (!queue.isEmpty()) {// 如果队列不为空
// 取出队列的头节点下标
u = (Integer) queue.removeFirst();
// 得到第一个邻接节点的下标w
w = getFirstNerghborIndex(u);
while (w != -1) {// 找到邻接节点
// 判断是否访问过
if (!isVisited[w]) {// 如果没有访问过
System.out.print(getValueByIndex(w) + "->");// 访问
isVisited[w] = true;// 标记为已访问
// 邻接节点入队列
queue.addLast(w);
}
// 以u为前驱点,访问w的下一个邻接节点
w = getNextNeighbor(u, w);
}
}
}
// 对bfs进行重载,遍历所有节点
public void bfs() {
for (int i = 0; i < getnumOfVertexs(); i++) {
while (!isVisited[i]) {// 如果没被访问过
bfs(isVisited, i);
}
}
}
}
如果要同时显示DFS和BFS遍历结果,需将构造器中的初始化部分摘出来,分别放到DFS和BFS的重载方法里,每次调用时重新分配,就不会出现执行完一种遍历无法执行第二种遍历的情况。
代码如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
public class graph {
private ArrayList<String> vertexList;// 定义顶点列表
private int[][] edges;// 存储图对应的邻接矩阵
private int numOfEdges;// 存储边
private boolean isVisited[];// 标记顶点是否已被访问
public static void main(String[] args) {
// TODO Auto-generated method stub
// 测试图
int n = 5;// 定义顶点个数
String vertexs[] = { "A", "B", "C", "D", "E" };
// 创建图对象
graph graph = new graph(n);
// 循环添加顶点
for (String vertex : vertexs) {// 每次循环从vertexValue取出来一个值(定义为value)
graph.insertVertex(vertex);// 把顶点加进去
}
// 添加边
// A-B A-C B-C B-D B-E
// 权值默认为是1
graph.insertEdges(0, 1, 1);// A-B
graph.insertEdges(0, 2, 1);// A-C
graph.insertEdges(1, 2, 1);// B-C
graph.insertEdges(1, 3, 1);// B-D
graph.insertEdges(1, 4, 1);// B-E
// 显示邻接矩阵
graph.show();
// DFS
System.out.println();
System.out.println("DFS:");
graph.dfs();
System.out.println();
System.out.println();
System.out.println("BFS:");
graph.bfs();
}
// ————————————--------------------------------------------------------
// 首先定义图的属性
// 1.构造器
public graph(int n) {// 首先要传入顶点的数目
// 初始化矩阵和vertexList
edges = new int[n][n];
vertexList = new ArrayList<String>();// 初始化顶点列表
numOfEdges = 0;// 可省略,因为默认为0
}
// 2.插入顶点
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
// 3.设置边
/**
*
* @param v1 第一个顶点的下标
* @param v2 第二个顶点的下标
* @param weight 权值
*/
public void insertEdges(int v1, int v2, int weight) {
// 这里是定义无向图,所以两个方向都要定义
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
// 4.返回顶点个数
public int getnumOfVertexs() {
return vertexList.size();
}
// 5.返回边个数
public int getnumOfEdges() {
return numOfEdges;
}
// 6.返回顶点i(顶点的下标)的对应的数据
/**
*
* @param i返回数据下标
* @return 返回数据 例:0->A 1->B 2->C 3->D 4->E
*/
public String getValueByIndex(int i) {
return vertexList.get(i);
}
// 7.返回v1,v2两顶点之间的权值
public int getWeight(int v1, int v2) {
return edges[v1][v2];
}
// 8.打印邻接矩阵(本质:遍历图)
public void show() {
for (int[] link : edges) {
System.out.println(Arrays.toString(link));
}
}
//————————————--------------------------------------------------------
// DFS算法步骤
// 1.得到第一个邻接节点下标w
/**
*
* @param index
* @return 如果存在就返回它的下标,没有就返回-1
*/
public int getFirstNerghborIndex(int index) {// 传入当前节点的下标index
for (int w = 0; w < vertexList.size(); w++) {
if (edges[index][w] > 0) {// 如果下一个邻接节点存在
return w;// 返回它的下标
}
}
return -1;// 没有返回就返回-1
}
// 2.根据前一个邻接节点的下标来获取下一个邻接节点
/**
*
* @param v1
* @param v2
* @return
*/
public int getNextNeighbor(int v1, int v2) {
for (int w = v2 + 1; w < vertexList.size(); w++) {
if (edges[v1][w] > 0) {
return w;
}
}
return -1;
}
// 3.开始深搜遍历
private void dfs(boolean[] isVisited, int i) {// 判断是否遍历过的标记,定义节点下标
// 首先访问当前节点,输出
System.out.print(getValueByIndex(i) + "->");
// 访问过后,要标记节点已访问
isVisited[i] = true;
// 查找节点i的第一个邻接节点
int w = getFirstNerghborIndex(i);
while (w != -1) {// 存在
if (!isVisited[w]) {// 如果没有被标记过
dfs(isVisited, w);// 以w为初始节点重新开始,递归操作dfs
}
// 如果已经被访问过
w = getNextNeighbor(i, w);
}
}
// 对dfs进行重载,遍历所有节点
public void dfs() {
isVisited = new boolean[5];
for (int i = 0; i < getnumOfVertexs(); i++) {
while (!isVisited[i]) {// 如果没被访问过
dfs(isVisited, i);
}
}
}
// ————————————--------------------------------------------------------
// BFS算法步骤
private void bfs(boolean[] isVisited, int i) {
int u;// 初始节点,队头结点
int w;// 邻接节点
// 使用队列记录访问节点的顺序
LinkedList queue = new LinkedList();
// 访问节点(输出节点信息)
System.out.print(getValueByIndex(i) + "->");
标记为已访问
isVisited[i] = true;
// 若已访问,添加节点到队列
queue.addLast(i);
// 重复操作
while (!queue.isEmpty()) {// 如果队列不为空
// 取出队列的头节点下标
u = (Integer) queue.removeFirst();
// 得到第一个邻接节点的下标w
w = getFirstNerghborIndex(u);
while (w != -1) {// 找到邻接节点
// 判断是否访问过
if (!isVisited[w]) {// 如果没有访问过
System.out.print(getValueByIndex(w) + "->");// 访问
isVisited[w] = true;// 标记为已访问
// 邻接节点入队列
queue.addLast(w);
}
// 以u为前驱点,访问w的下一个邻接节点
w = getNextNeighbor(u, w);
}
}
}
// 对bfs进行重载,遍历所有节点
public void bfs() {
isVisited = new boolean[5];
for (int i = 0; i < getnumOfVertexs(); i++) {
while (!isVisited[i]) {// 如果没被访问过
bfs(isVisited, i);
}
}
}
}