一.圖的定義
定義:圖(Graph)是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為:G(V,E),其中,G表示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。
圖的分類:
圖是按照無方向和有方向分為無向圖和有向圖。
左圖為無向圖是由頂點和邊構成,右圖為有向圖是由頂點和弧(有向邊構成)。弧有弧頭和弧尾區別
二、圖的存儲結構
1、鄰接矩陣
鄰接矩陣用兩個數組保存數據。一個一維數組存儲圖中頂點信息,一個二維數組存儲圖中邊或弧的信息。
2、鄰接表
鄰接表:數組和鏈表相結合的存儲方法為鄰接表。
三.鄰接矩陣的實現
public class Graph {
private ArrayList<String> vertexList;//存儲頂點集合
private int[][] edges; //存儲圖對應的鄰接矩陣
private int numOfEdge; //表示邊的數目
private boolean[] isVisited;
public static void main(String[] args) {
int n = 5;
Graph graph = new Graph(n);
String[] vertexValue = { "A", "B", "C", "D", "E" };
// 建立各頂點所對應的值
for (String vertex : vertexValue) {
graph.insertVertex(vertex);
}
// 構造邊的關系
graph.insertEdges(0, 0, 1);
graph.insertEdges(0, 2, 1);
graph.insertEdges(1, 2, 1);
graph.insertEdges(1, 3, 1);
graph.insertEdges(1, 4, 1);
graph.showGraph();
//深度優先遍歷
System.out.println("深度優先遍歷");
graph.dfs();
System.out.println();
//廣度優先遍歷
System.out.println("廣度優先遍歷");
graph.bfs();
}
/**
*
* @param //頂點個數
*/
public Graph(int n) {
edges = new int[n][n];
vertexList = new ArrayList<String>(n);
}
/**
*
* @Title: insertVertex
* @Description: 向頂點中加入該頂點的數據
* @param @param vertex 要插入的數據
* @return void 返回類型
*/
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
/**
*
* @Title: insertEdges
* @Description: 將鄰接矩陣各個結點之間的關系建立起來,1代表相連,0代表不相連
* @param @param v1 代表下標為v1的頂點
* @param @param v2 代表下標為v2的頂點
* @param @param weight 權值,不是0就是1
* @return void 返回類型
*/
public void insertEdges(int v1, int v2, int weight) {
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdge++;
}
// 返回結點數
public int getNumOfVertex() {
return vertexList.size();
}
// 返回邊數
public int getNumOfEdges() {
return numOfEdge;
}
// 返回i對應的數據
public String getValueByyIndex(int i) {
return vertexList.get(i);
}
// 返回v1和v2的權值
public int getWeight(int v1, int v2) {
return edges[v1][v2];
}
// 顯示圖對應的矩陣
public void showGraph() {
for (int[] link : edges) {
System.out.println(Arrays.toString(link));
}
}
}
四.圖的深度優先遍歷
深度優先遍歷(Depth-First-Search),從初始訪問結點出發,初始訪問結點可能有多個鄰接結點,深度優先遍歷的策略就是首先訪問,第一個鄰接結點,然后再以這個被訪問的鄰接結點作為初始結點,訪問它的第一個鄰接結點, 可以這樣理解每次都在訪問完當前結點后首先訪問當前結點的第一個鄰接結點。
深度優先搜索是一個遞歸的過程。
1. 深度優先遍歷算法步驟
1、訪問初始頂點x,訪問后需要標記x已經訪問過,不能再次重讀訪問
2、查找頂點x的第一個鄰接點y
3、如果y存在,則繼續執行下面步驟,如果y不存在,則回到第1步,將從x的下一個頂點繼續
4、如果y未被訪問過,對y進行深度優先遍歷,則是把y當做另一個x,然后執行123步
5、查找頂點x的y鄰接點的下一個鄰接點,轉到步驟3
//得到第一個臨結節點的下標w
//如果存在就返回對應的下標
public int getFirstNeighbor(int index){
for (int i = 0; i < vertexList.size(); i++) {
if (edges[index][i] > 0){
return i;
}
}
return -1;
}
//根據前一個鄰接節點的下標來獲取下一個鄰接節點
public int getNextNeighbor(int v1,int v2){
for (int i = v2+1; i < vertexList.size(); i++) {
if (edges[v1][i] > 0){
return i;
}
}
return -1;
}
//對dfs重載,遍歷所有的節點
public void dfs(){
isVisited = new boolean[5];
//遍歷所有節點進行dfs[回溯]
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]){
dfs(isVisited,i);
}
}
}
//深度優先遍歷
public void dfs(boolean[] isVisited,int i){
//首先訪問該節點
System.out.print(getValueByyIndex(i)+"->");
//將該節點設置為已訪問
isVisited[i] = true;
//查找節點i的第一個鄰接節點w
int w =getFirstNeighbor(i);
while (w!=-1){
if (!isVisited[w]){
dfs(isVisited,w);
}
//如果w節點被訪問過
w = getNextNeighbor(i,w);
}
}
五.廣度優先遍歷
類似於一個 分層搜索的過程,廣度優先遍歷(Breadth-First-Search)需要使用一個隊列以保持訪問過的結點的順序,以便按這個順序來訪問這些結點的鄰接結點
1 廣度優先遍歷算法分析
1)訪問初始結點 v 並標記結點 v 為已訪問。
-
結點 v 入隊列
-
當隊列非空時,繼續執行,否則算法結束。
-
出隊列,取得隊頭結點 u。
-
查找結點 u 的第一個鄰接結點 w。
-
若結點 u 的鄰接結點 w 不存在,則轉到步驟 3;否則循環執行以下三個步驟
6.1若結點 w 尚未被訪問,則訪問結點 w 並標記為已訪問。
6.2 結點 w 入隊列
6.3 查找結點 u 的繼 w 鄰接結點后的下一個鄰接結點 w,轉到步驟 6
//遍歷所有節點
public void bfs() {
isVisited = new boolean[5];
//遍歷所有節點進行bfs
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]) {
bfs(isVisited, i);
}
}
}
//進行廣度優先遍歷
private void bfs(boolean[] isVisited, int i) {
int u; //表示隊列頭節點對應下標
int w; //鄰接節點
//隊列,記錄節點訪問的順序
LinkedList list = new LinkedList();
//訪問節點 輸出節點信息
System.out.print(getValueByyIndex(i) + "->");
//標記為已訪問
isVisited[i] = true;
//將節點加入隊列
list.addLast(i);
while (!list.isEmpty()) {
//取出隊列頭結點下標
u = (Integer) list.removeFirst();
//得到第一個臨界點的下標
w = getFirstNeighbor(u);
while (w != -1) {
if (!isVisited[w]) {
System.out.print(getValueByyIndex(w) + "->");
//標記為已訪問
isVisited[w] = true;
//入隊
list.addLast(w);
}
//以u找w后面的下一個鄰接點
w = getNextNeighbor(u, w);
}
}
}