算法思路
- 节点有多个后继节点时先遍历边权重小的
- 指定一个起始节点
- 把当前节点所有后继节点按路径权值升序排序
- 取出第一个后继节点,以此节点为起始节点重复第二步
- 若当前节点没有后继节点,则回溯到仍有未访问的后继节点的节点处重复第三步
算法实现
图的实现
- 此实现方法没有节点类
- 用枚举类型
UNDISCOVERD
表示未被发现,VISITED
表示已被访问 - 采用邻接矩阵和顶点索引
- 邻接矩阵
int[][] matrix
(邻接矩阵无需设置为沿对角线对称)matrix[i][j]
表示从索引i
的节点指向索引j
的节点的权值- 权值为0表示两点不连接或者自身与自身不连接
public class Graph<T> {
private int N; // 节点个数
public int[][] matrix; // 邻接矩阵
private Status[] statuses; // 保存每个节点的状态
private T[] datas; // 保存每个节点的数据
}
enum Status { // 节点对象的状态
// 未被发现, 已被遍历
UNDISCOVERD, VISITED
}
重点
- 找出所有后继节点
index
是当前节点索引List<Integer> toVisit
保存当前节点可能遍历的后继节点的索引- 循环遍历每个节点,查询邻接矩阵,如果
matrix[index][j] > 0
则说明当前节点有一条边指向索引j
节点,找到一个后继节点,加到toVisit
集合
List<Integer> toVisit = new ArrayList<>();
for (int j = 0; j < N; j++) {
if (matrix[index][j] > 0 && statuses[j] != Status.VISITED) {
toVisit.add(j); // 添加到待遍历列表
}
}
- 把当前节点所有后继节点按路径权值升序排序
- 集合
toVisit
调用sort()
方法需要重写compare()
方法(默认升序) - o1 和o2 是两个对象也即两节点索引,
return matrix[index][o1] - matrix[index][o2]
的作用是,集合调用sort()方法进行排序时,按前当前节点指向索引o1
节点的边权重减去当前节点指向索引o2
节点的边权重,小于0(前一条边的权重小)则两条边的位置不变,大于0则交换位置(大概意思是这样)
- 集合
// 找出当前节点到所有待遍历的节点中权值最小的一个
toVisit.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return matrix[index][o1] - matrix[index][o2];
}
});
- 方法参数是节点索引
- 取出最小权重的后继节点,用这个后继节点索引递归
- 按权重顺序,并且判断该节点状态是否为
VISITED
,不是才递归
// 对当前节点所有后继节点遍历
for (int i : toVisit) {
if (statuses[i] != Status.VISITED) {
depthFirstTravel(i);
}
}
具体步骤
depthFirstTravel(int index)
- 将传进来索引对应节点状态设置为已访问
VISITED
- 输出节点保存数据
datas[index]
- 声明
List<Integer> toVisit
保存某个节点可能遍历的后继节点的索引 - 循环遍历找出后继节点
- 后继节点按权重排序
- 按顺序递归
/**
* 深度优先遍历
*
* @param index 从这个索引的节点开始遍历
* @return void
*/
public void depthFirstTravel(int index) {
// 设置该节点状态为已被遍历
statuses[index] = Status.VISITED;
System.out.println(datas[index]);
List<Integer> toVisit = new ArrayList<>(); // 保存某个节点可能遍历的后继节点的索引
// 保存当前节点所有后继节点的索引
for (int j = 0; j < N; j++) {
if (matrix[index][j] > 0 && statuses[j] != Status.VISITED) {
toVisit.add(j); // 添加到待遍历列表
}
}
// 找出当前节点到所有待遍历的节点中权值最小的一个
toVisit.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return matrix[index][o1] - matrix[index][o2];
}
});
// 对当前节点所有后继节点遍历
for (int i : toVisit) {
if (statuses[i] != Status.VISITED) {
depthFirstTravel(i);
}
}
}
测试
- 6个节点,对应保存数据为字母ABCDEF
int[][] set
是为了初始化邻接矩阵graph.setMatrix(set[i][0], set[i][1], set[i][2])
- 执行深度优先遍历
public static void main(String[] args) {
Graph<String> graph = new Graph<>(6);
graph.setDatas(new String[]{"A", "B", "C", "D", "E", "F"});
int[][] set = {{0, 1, 1},
{0, 2, 3},
{1, 3, 2},
{1, 5, 2},
{2, 3, 4},
{2, 5, 7},
{3, 4, 1},
{4, 5, 8}};
for (int i = 0; i < set.length; i++) {
graph.setMatrix(set[i][0], set[i][1], set[i][2]);
}
graph.depthFirstTravel(0);
}
输出结果:A B D E F C
完整代码
public class Graph<T> {
private int N; // N个节点
public int[][] matrix; // 邻接矩阵
private Status[] statuses; // 保存每个节点的状态
private T[] datas; // 保存每个节点的数据
public Graph(int N) {
this.N = N;
matrix = new int[N][N];
statuses = new Status[N];
datas = (T[]) new Object[N]; // 泛型数组实例化
initStatuses();
}
public void setDatas(T[] datas) {
this.datas = datas;
}
/**
* 初始化状态数组
*
* @return void
*/
private void initStatuses() {
for (int i = 0; i < N; i++) {
statuses[i] = Status.UNDISCOVERD;
}
}
/**
* 邻接矩阵保存的信息是从一个节点指向另一个节点的信息
*
* @param from 从这个节点
* @param to 指向这个节点
* @param weight 路径权重
* @return void
*/
public void setMatrix(int from, int to, int weight) {
matrix[from][to] = weight;
}
/**
* 深度优先遍历
*
* @param index 从这个索引的节点开始遍历
* @return void
*/
public void depthFirstTravel(int index) {
// 设置该节点状态为已被遍历
statuses[index] = Status.VISITED;
System.out.println(datas[index]);
List<Integer> toVisit = new ArrayList<>(); // 保存某个节点可能遍历的后继节点的索引
// 保存当前节点所有后继节点的索引
for (int j = 0; j < N; j++) {
if (matrix[index][j] > 0 && statuses[j] != Status.VISITED) {
toVisit.add(j); // 添加到待遍历列表
}
}
// 找出当前节点到所有待遍历的节点中权值最小的一个
toVisit.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return matrix[index][o1] - matrix[index][o2];
}
});
// 对当前节点所有后继节点遍历
for (int i : toVisit) {
if (statuses[i] != Status.VISITED) {
depthFirstTravel(i);
}
}
}
public static void main(String[] args) {
Graph<String> graph = new Graph<>(6);
graph.setDatas(new String[]{"A", "B", "C", "D", "E", "F"});
int[][] set = {{0, 1, 1},
{0, 2, 3},
{1, 3, 2},
{1, 5, 2},
{2, 3, 4},
{2, 5, 7},
{3, 4, 1},
{4, 5, 8}};
for (int i = 0; i < set.length; i++) {
graph.setMatrix(set[i][0], set[i][1], set[i][2]);
}
graph.depthFirstTravel(0);
}
}
enum Status { // 节点对象的状态
// 未被发现, 已被遍历
UNDISCOVERD, VISITED
}
来源:CSDN
作者:只能刻苦驴
链接:https://blog.csdn.net/qq_43269370/article/details/103246265