一:深度优先遍历介绍.
1. 深度优先遍历的定义:
假设图中的所有的顶点都没有访问过,我们是任选一个顶点作为起始出发点(一般选择使用节点集的第一个元素作为起始顶点).
深度核心思想就是从起始顶点出发,然后一致沿着可以到达的节点走,一条路一直走,直到不可达是,返回到上一个节点,接着走,直到这条路也走不通了,再次返回上一个节点,就这样,一致递归,直到图中的所有的节点都访问结束.
2. 深度优先遍历的特点:
遍历图的时候尽可能深的遍历,一条路走到结束.尽可能去最远的地方深度优先遍历结束后会得到一个深度优先队列,如果没有指定起始顶点,那么DFS序列就不唯一,不同的起始顶点得到的DFS序列就不唯一.
消耗内存小,可以找到是否有解,难以寻找最优解.
3. 深度优先遍历的实现方式:
3.1 递归实现.
3.2 非递归实现.(使用栈).
4. 深度优先遍历的可以解决的问题模型:
给定初始状态和目标状态,从初始状态判断到目标状态是否有解.
用于二叉树的前中后序遍历.递归版/非递归版,在自己秋季校招面试时也手写过.
二叉树的先序遍历(根-左-右).
public List<TreeNode> preIterator(){
return preIterator(root);
}
private List<TreeNode> preIterator(TreeNode node){
List<TreeNode> list=new ArrayList<TreeNode>();
// 处理根节点
list.add(node);
// 递归处理左子树
if(node.left!=null){
list.addAll(preIterator(node.left));
}
// 递归处理右子树
if(node.right!=null){
list.addAll(preIterator(node.right));
}
return list;
}
二. 深度优先遍历的图.(15)
如果从节点A开始进行深度优先遍历:DFS序列为:A B C D E F G H I
三. 代码实现.(这里实现的是从第一个节点开始的DFS序列).递归实现.间接明了.
1. 定义一位数组存储点集.
2. 定义变量指定节点数.
3. 定义Boolean型数组指定是否访问的标志.
4. 定义二维数组保存边的信息(邻接矩阵实现).考虑时间复杂度.
5. 初始化DFS.
6. 实现深度优先遍历的核心代码.
public class DFS {
/** 存储节点信息*/
private char[] vertices;
/** 存储边信息(邻接矩阵)*/
private int[][] arcs;
/** 图的节点数*/
private int vexnum;
/** 记录节点是否已被遍历*/
private boolean[] visited;
/** 初始化*/
public DFS(int n) {
vexnum = n;
vertices = new char[n];
arcs = new int[n][n];
visited = new boolean[n];
for (int i = 0; i < vexnum; i++) {
for (int j = 0; j < vexnum; j++) {
arcs[i][j] = 0;
}
}
}
/** 添加边*/
public void addEdge(int i, int j){
if(i==j){
return ;
}
// 无向图对称的.
arcs[i][j]=1;
arcs[j][i]=1;
}
/** 设置节点集*/
public void setVertices(char[] vertices){
this.vertices=vertices;
}
/** 设置节点访问标记*/
public void setVisited(boolean[] visited){
this.visited=visited;
}
/** 打印遍历节点*/
public void visit(int i){
System.out.print(vertices[i]+ " ");
}
/**
* 输出邻接矩阵
*/
public void pritf(int[][] arcs){
for(int i=0;i<arcs.length;i++){
for(int j=0;j<arcs[0].length;j++){
System.out.print(arcs[i][j]+ "\t");
}
System.out.println();
}
}
/** 从第i节点开始深度优先遍历*/
public void traverse(int i){
// 标记第i个节点已遍历
visited[i]=true;
// 打印当前已经遍历的节点
visit(i);
// 遍历邻接矩阵中第i个节点的直接连通节点
for(int j=0;j<vexnum;j++){
if(arcs[i][j]==1&&visited[j]==false){
traverse(j);
}
}
}
public void dfs(){
// 初始化节点访问标记
for(int i=0;i<visited.length;i++){
visited[i]=false;
}
// 从没有被遍历的节点开始深度优先遍历
for(int i=0;i<vexnum;i++){
// 如果没有被访问过.
if(visited[i]==false){
traverse(i);
}
}
// 输出二维矩阵
System.out.println();
pritf(arcs);
}
public static void main(String[] args) {
DFS dfs = new DFS(9);
// 添加节点集
char[] vertices = {'A','B','C','D','E','F','G','H','I'};
// 设置顶点集
dfs.setVertices(vertices);
// 添加边
dfs.addEdge(0, 1);
dfs.addEdge(0, 5);
dfs.addEdge(1, 0);
dfs.addEdge(1, 2);
dfs.addEdge(1, 6);
dfs.addEdge(1, 8);
dfs.addEdge(2, 1);
dfs.addEdge(2, 3);
dfs.addEdge(2, 8);
dfs.addEdge(3, 2);
dfs.addEdge(3, 4);
dfs.addEdge(3, 6);
dfs.addEdge(3, 7);
dfs.addEdge(3, 8);
dfs.addEdge(4, 3);
dfs.addEdge(4, 5);
dfs.addEdge(4, 7);
dfs.addEdge(5, 0);
dfs.addEdge(5, 4);
dfs.addEdge(5, 6);
dfs.addEdge(6, 1);
dfs.addEdge(6, 3);
dfs.addEdge(6, 5);
dfs.addEdge(6, 7);
dfs.addEdge(7, 3);
dfs.addEdge(7, 4);
dfs.addEdge(7, 6);
dfs.addEdge(8, 1);
dfs.addEdge(8, 2);
dfs.addEdge(8, 3);
System.out.print("深度优先遍历(递归实现):");
dfs.dfs();
}
}
运行结果如下:
非递归实现.基于栈实现.
import java.util.Stack;
/**
* title: com.lx.algorithm.graph
* @author: lixing
* date: 2018/10/31 21:03
* description:
*/
public class DFSStack {
/** 存储节点信息*/
private char[] vertices;
/** 存储边信息(邻接矩阵)*/
private int[][] arcs;
/** 图的节点数*/
private int vexnum;
/** 记录节点是否已被遍历*/
private boolean[] visited;
/** 初始化*/
public DFSStack(int n) {
vexnum = n;
vertices = new char[n];
arcs = new int[n][n];
visited = new boolean[n];
for (int i = 0; i < vexnum; i++) {
for (int j = 0; j < vexnum; j++) {
arcs[i][j] = 0;
}
}
}
/** 添加边*/
public void addEdge(int i, int j){
if(i==j){
return ;
}
// 无向图对称的.
arcs[i][j]=1;
arcs[j][i]=1;
}
/** 设置节点集*/
public void setVertices(char[] vertices){
this.vertices=vertices;
}
/** 设置节点访问标记*/
public void setVisited(boolean[] visited){
this.visited=visited;
}
/** 打印遍历节点*/
public void visit(int i){
System.out.print(vertices[i]+ " ");
}
/**
* 输出邻接矩阵
*/
public void pritf(int[][] arcs){
for(int i=0;i<arcs.length;i++){
for(int j=0;j<arcs[0].length;j++){
System.out.print(arcs[i][j]+ "\t");
}
System.out.println();
}
}
public void dfs(){
// 初始化所有的节点的访问标志
for (int v = 0; v < visited.length; v++) {
visited[v] = false;
}
Stack<Integer> stack =new Stack<Integer>();
for(int i=0;i<vexnum;i++){
if(visited[i]==false){
visited[i]=true;
System.out.print(vertices[i]+" ");
stack.push(i);
}
while(!stack.isEmpty()){
// 当前出栈的节点
int k = stack.pop();
for(int j=0;j<vexnum;j++){
// 如果是相邻的节点且没有访问过.
if(arcs[k][j]==1&&visited[j]==false){
visited[j]=true;
System.out.print(vertices[j]+" ");
stack.push(j);
// 这条路结束,返回上一个节点.
break;
}
}
}
}
// 输出二维矩阵
System.out.println();
pritf(arcs);
}
public static void main(String[] args) {
DFSTest dfs = new DFSTest(9);
// 添加节点集
char[] vertices = {'A','B','C','D','E','F','G','H','I'};
// 设置顶点集
dfs.setVertices(vertices);
// 添加边
dfs.addEdge(0, 1);
dfs.addEdge(0, 5);
dfs.addEdge(1, 0);
dfs.addEdge(1, 2);
dfs.addEdge(1, 6);
dfs.addEdge(1, 8);
dfs.addEdge(2, 1);
dfs.addEdge(2, 3);
dfs.addEdge(2, 8);
dfs.addEdge(3, 2);
dfs.addEdge(3, 4);
dfs.addEdge(3, 6);
dfs.addEdge(3, 7);
dfs.addEdge(3, 8);
dfs.addEdge(4, 3);
dfs.addEdge(4, 5);
dfs.addEdge(4, 7);
dfs.addEdge(5, 0);
dfs.addEdge(5, 4);
dfs.addEdge(5, 6);
dfs.addEdge(6, 1);
dfs.addEdge(6, 3);
dfs.addEdge(6, 5);
dfs.addEdge(6, 7);
dfs.addEdge(7, 3);
dfs.addEdge(7, 4);
dfs.addEdge(7, 6);
dfs.addEdge(8, 1);
dfs.addEdge(8, 2);
dfs.addEdge(8, 3);
System.out.print("深度优先遍历(非递归实现):");
dfs.dfs();
}
}
运行结果:
至此完成基于JAVA的深度优先遍历实现.