JAVA實現圖的深度優先遍歷.
一:深度優先遍歷介紹.
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的深度優先遍歷實現.