上一次我們介紹了圖的相關的資料結構,今天我們來介紹圖的有關算法。
接下來我将以如下的順序介紹算法:1.圖的周遊(廣度和深度)2.最小生成樹 3.最短路徑 4.拓撲排序 5.關鍵路徑
一、圖的周遊
1.基本思路
1).圖的周遊:從圖中某一個頂點出發周遊途中其餘結點,每一 個頂點僅僅被周遊 一次。
2).基本思路
(1)樹有四種周遊方式,因為根結點隻有一個。而圖的複雜情況是順着一個
點向下尋找,很有可能又找回到自己,容易形成回路造成死循環。
(2)是以要設定一個數組visited[n],n是圖中頂點的個數,初值為0,當該頂
點被周遊後,修改數組元素值為1.
(3)基于上面的思想,形成兩個周遊方案:深度優先周遊和廣度優先周遊。
2.深度優先周遊
深度優先周遊,就是從初始通路結點出發,我們知道初始通路結點可能有多個鄰接結點,深度優先搜尋的政策是首先通路第一個鄰接結點,再以這個被通路的鄰接結點作為初始結點,通路它的第一鄰接結點。總結來說:每次通路目前結點後首先通路目前結點的第一個鄰接結點。
這種政策是優先縱向深度,而不是對一個結點的鄰接結點做橫向通路。算法辨別如下:
(1)通路初始結點,并标記結點v為已通路。
(2)查找結點v的第一個鄰接結點w。
(3)若w存在,則繼續執行4,否則算法結束。
(4)若w未被通路,對w進行深度優先遞歸(即把w當做另一個v,執行算法1,2,3)。
(5)查找v的w鄰接結點的下一個鄰接結點,轉到步驟3。
例如下圖,深度優先周遊的順序是1->2->4->8->5->3->6->7
![]()
圖的相關算法參考文獻
深度優先算法代碼和廣度合在一起了,都在下面
3.廣度優先周遊
廣度優先周遊類似與一個分層搜尋的過程,廣度優先周遊需要使用一個隊列以保持通路過的結點的順序,以便按照這個順序來通路這個結點的鄰接結點。算法的表述如下:
(1)通路初始結點v并标記結點v為已通路。
(2)結點v入隊列。
(3)當隊列非空時,繼續執行,否則算法結束。
(4)出隊列,取得隊頭結點u。
(5)查找結點u的第一個鄰接結點w。
(6)若結點u的鄰接結點w不存在,則轉到步驟3;否則執行下面3個步驟:
1)若結點w未被通路,則通路結點w并标記為已通路。
2)結點w入隊列。
3)查找結點u的繼w鄰接結點後的下一個鄰接結點,轉到步驟6。
例如下圖,廣度優先周遊的順序為:1->2->3->4->5->6->7->8
![]()
圖的相關算法參考文獻
深度,廣度優先算法代碼,這個代碼用的鄰接矩陣
package com.xushu.Undirectedgraph;
/**
* Java: 鄰接表表示的"無向圖(List Undirected Graph)"
*/
import java.io.IOException;
import java.util.Scanner;
public class ListUDG {
// 鄰接表中表對應的連結清單的頂點
private class ENode {
int ivex; // 該邊所指向的頂點的位置
ENode nextEdge; // 指向下一條弧的指針
}
// 鄰接表中表的頂點
private class VNode {
char data; // 頂點資訊
ENode firstEdge; // 指向第一條依附該頂點的弧
};
private VNode[] mVexs; // 頂點數組
// /*
// * 建立圖(自己輸入資料)
// */
// public ListUDG() {
//
// // 輸入"頂點數"和"邊數"
// System.out.printf("input vertex number: ");
// int vlen = readInt();
// System.out.printf("input edge number: ");
// int elen = readInt();
// if ( vlen < 1 || elen < 1 || (elen > (vlen*(vlen - 1)))) {
// System.out.printf("input error: invalid parameters!\n");
// return ;
// }
//
// // 初始化"頂點"
// mVexs = new VNode[vlen];
// for (int i = 0; i < mVexs.length; i++) {
// System.out.printf("vertex(%d): ", i);
// mVexs[i] = new VNode();
// mVexs[i].data = readChar();
// mVexs[i].firstEdge = null;
// }
//
// // 初始化"邊"
// //mMatrix = new int[vlen][vlen];
// for (int i = 0; i < elen; i++) {
// // 讀取邊的起始頂點和結束頂點
// System.out.printf("edge(%d):", i);
// char c1 = readChar();
// char c2 = readChar();
// int p1 = getPosition(c1);
// int p2 = getPosition(c2);
// // 初始化node1
// ENode node1 = new ENode();
// node1.ivex = p2;
// // 将node1連結到"p1所在連結清單的末尾"
// if(mVexs[p1].firstEdge == null)
// mVexs[p1].firstEdge = node1;
// else
// linkLast(mVexs[p1].firstEdge, node1);
// // 初始化node2
// ENode node2 = new ENode();
// node2.ivex = p1;
// // 将node2連結到"p2所在連結清單的末尾"
// if(mVexs[p2].firstEdge == null)
// mVexs[p2].firstEdge = node2;
// else
// linkLast(mVexs[p2].firstEdge, node2);
// }
// }
/*
* 建立圖(用已提供的矩陣)
*
* 參數說明:
* vexs -- 頂點數組
* edges -- 邊數組
*/
public ListUDG(char[] vexs, char[][] edges) {
// 初始化"頂點數"和"邊數"
int vlen = vexs.length;
int elen = edges.length;
// 初始化"頂點"
mVexs = new VNode[vlen];
for (int i = 0; i < mVexs.length; i++) {
mVexs[i] = new VNode();
mVexs[i].data = vexs[i];
mVexs[i].firstEdge = null;
}
// 初始化"邊"
for (int i = 0; i < elen; i++) {
// 讀取邊的起始頂點和結束頂點
char c1 = edges[i][0];
char c2 = edges[i][1];
// 讀取邊的起始頂點和結束頂點
int p1 = getPosition(edges[i][0]);
int p2 = getPosition(edges[i][1]);
// 初始化node1
ENode node1 = new ENode();
node1.ivex = p2;
// 将node1連結到"p1所在連結清單的末尾"
if(mVexs[p1].firstEdge == null)
mVexs[p1].firstEdge = node1;
else
linkLast(mVexs[p1].firstEdge, node1);
// 初始化node2
ENode node2 = new ENode();
node2.ivex = p1;
// 将node2連結到"p2所在連結清單的末尾"
if(mVexs[p2].firstEdge == null)
mVexs[p2].firstEdge = node2;
else
linkLast(mVexs[p2].firstEdge, node2);
}
}
/*
* 将node節點連結到list的最後
*/
private void linkLast(ENode list, ENode node) {
ENode p = list;
while(p.nextEdge!=null)
p = p.nextEdge;
p.nextEdge = node;
}
/*
* 傳回ch位置
*/
private int getPosition(char ch) {
for(int i=0; i<mVexs.length; i++)
if(mVexs[i].data==ch)
return i;
return -1;
}
/*
* 讀取一個輸入字元
*/
private char readChar() {
char ch='0';
do {
try {
ch = (char)System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
} while(!((ch>='a'&&ch<='z') || (ch>='A'&&ch<='Z')));
return ch;
}
/*
* 讀取一個輸入字元
*/
private int readInt() {
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
/*
* 深度優先搜尋周遊圖的遞歸實作
*/
private void DFS(int i, boolean[] visited) {
visited[i] = true;
System.out.printf("%c ", mVexs[i].data);
ENode node = mVexs[i].firstEdge;
while (node != null) {
if (!visited[node.ivex])
DFS(node.ivex, visited);
node = node.nextEdge;
}
}
/*
* 深度優先搜尋周遊圖
*/
public void DFSTravel() {
boolean[] visited = new boolean[mVexs.length]; // 頂點通路标記
// 初始化所有頂點都沒有被通路
for (int i = 0; i < mVexs.length; i++)
visited[i] = false;
System.out.printf("DFS: ");
for (int i = 0; i < mVexs.length; i++) {
if (!visited[i])
DFS(i, visited);
}
System.out.printf("\n");
}
/*
* 廣度優先搜尋(類似于樹的層次周遊)
*/
public void BFSTravel() {
int head = 0;
int rear = 0;
int[] queue = new int[mVexs.length]; // 輔組隊列
boolean[] visited = new boolean[mVexs.length]; // 頂點通路标記
for (int i = 0; i < mVexs.length; i++)
visited[i] = false;
System.out.printf("BFS: ");
for (int i = 0; i < mVexs.length; i++) {
if (!visited[i]) {
visited[i] = true;
System.out.printf("%c ", mVexs[i].data);
queue[rear++] = i; // 入隊列
}
while (head != rear) { //當隊列不空
int j = queue[head++]; // 出隊列
ENode node = mVexs[j].firstEdge;
while (node != null) {
int k = node.ivex;
if (!visited[k])
{
visited[k] = true;
System.out.printf("%c ", mVexs[k].data);
queue[rear++] = k;
}
node = node.nextEdge;
}
}
}
System.out.printf("\n");
}
/*
* 列印矩陣隊列圖
*/
public void print() {
System.out.printf("List Graph:\n");
for (int i = 0; i < mVexs.length; i++) {
System.out.printf("%d(%c): ", i, mVexs[i].data);
ENode node = mVexs[i].firstEdge;
while (node != null) {
System.out.printf("%d(%c) ", node.ivex, mVexs[node.ivex].data);
node = node.nextEdge;
}
System.out.printf("\n");
}
}
public static void main(String[] args) {
char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
char[][] edges = new char[][]{
{'A', 'C'},
{'A', 'D'},
{'A', 'F'},
{'B', 'C'},
{'C', 'D'},
{'E', 'G'},
{'F', 'G'}};
ListUDG pG;
// 自定義"圖"(輸入矩陣隊列)
//pG = new ListUDG();
// 采用已有的"圖"
pG = new ListUDG(vexs, edges);
pG.print(); // 列印圖
pG.DFSTravel(); // 深度優先周遊
pG.BFSTravel(); // 廣度優先周遊
}
}