图的存储之十字链表
對于有向圖來說,鄰接表是有缺陷的。關心了出度問題,要獲取入度就必須要遍歷整個圖才能知道。在這里我們將要介紹十字鏈表。
1.知識點講解
我們需要重新定義表結點結構如下圖:
其中 firstin 表示邊表頭指針,指向該頂點的入邊表中第一個結點,firstout 表示出邊表的表頭指針,指向該頂點的出邊表中的第一個結點。
重新定義邊表結點如下圖:
其中 tailvex 是指弧起點在頂點表的下標,headvex 是指弧終點在頂點表中的下標,headlink 是指入邊表指針域,指向終點相同的下一條邊,taillink 是邊表指針域,指向起點相同的下一條邊。如果是網,可以再加一個 weight 域來存儲權值。
如下圖 A,頂點依然存入一個數組{V0,V1,V2,V3}。以頂點 V0 來說,firstout 指向出邊表中的第一個結點 V3。所以 V0 邊結點 的 headvex = 3,而 tailvex 其實就是當前結點 V0 的下標0,由于 V0 只有一個出邊頂點,所以 headlink 和 taillink 都是空。
?
?我們重點講解一下藍色箭頭的含義,他其實此圖的逆鄰接表的表示。對于 V0 來說,它有兩個頂點V1 和 V2 的入邊。因此 V0 的 firstin 指向頂點 V1 的邊表結點中 headvex? 為 0 的結點,就是①。接著由入邊結點的 headlink 指向下一個入邊頂點 V2,如②。對于 V1,有一個入邊頂點 V2,所以它的 firstin 指向 V2 的邊表結點中 headvex 為1的結點,如圖中的③。頂點 V2 和 V3 也是同樣有一個入邊頂點,④和⑤。
十字鏈表的好處就是把鄰接表和逆鄰接表整合在了一起,這樣既容易找到以 Vi 為尾的弧,也容易找到以 Vi 為頭的弧,因而容易求得頂點的入度和出度。它除了復雜一點外,創建圖算法的時間復雜度是和鄰接表相同的,因此在有向圖的應用中,十字鏈表是非常好的數據結構模型。
2.代碼實現
這是測試圖:
代碼:
/*** */ package datastructure.graph;/******************************* TODO* @author Chen Fan* @version 1.0* time 2022年1月23日*****************************/ public class OrthogonalList {// 內部結點class OrthogonalNode{// 行索引int row;// 列索引int column;// 下一個出結點OrthogonalNode nextOutNode;// 下一個入結點OrthogonalNode nextInNode;// 構造方法public OrthogonalNode(int paraRow, int paraColumn) {row = paraRow;column = paraColumn;nextInNode = null;nextOutNode = null;} // Of OrthogonalNode} // Of class OrthogonalNode// 結點的數目,可能是多余的,因為結點數始終等于headers.lengthint numNodes;// 每一行的表頭OrthogonalNode[] headers;// 十字鏈表的構造方法public OrthogonalList(int[][] paraMatrix) {numNodes = paraMatrix.length;// 第一步:初始化十字鏈表,表頭的數據域沒有意義OrthogonalNode tempPreViousNode,tempNode;headers = new OrthogonalNode[numNodes];// 第二步:鏈接外部結點for(int i = 0;i<numNodes;i++) {headers[i] = new OrthogonalNode(i, -1);tempPreViousNode = headers[i];for (int j = 0; j < numNodes; j++) {if(paraMatrix[i][j] == 0) {continue;} // Of if// 創建一個新結點tempNode = new OrthogonalNode(i, j);// linktempPreViousNode.nextOutNode = tempNode;tempPreViousNode = tempNode;} // Of for j} // Of for i// 第三步:鏈接入結點,這一步更難OrthogonalNode[] tempColumnNodes = new OrthogonalNode[numNodes];for (int i = 0; i < numNodes; i++) {// 復制一遍表頭數組tempColumnNodes[i] = headers[i]; } // Of for ifor(int i = 0;i < numNodes;i++) {tempNode = headers[i].nextOutNode; // 表頭第一個出結點while(tempNode != null) {// tempNode.column 列索引,就是回到入結點的位置,給它的入結點設置引用tempNodetempColumnNodes[tempNode.column].nextInNode = tempNode;tempColumnNodes[tempNode.column] = tempNode;tempNode = tempNode.nextOutNode;} // Of while } // Of for i} // Of constructorpublic String toString() {String resultString = "Out arce: \n";OrthogonalNode tempNode;for (int i = 0; i < numNodes; i++) {tempNode = headers[i].nextOutNode;while(tempNode != null) {resultString +="(" + tempNode.row + "," + tempNode.column +")";tempNode = tempNode.nextOutNode;} // Of whileresultString += "\r\n";} // Of for iresultString += "\r\n In arcs: \n";for (int i = 0; i < numNodes; i++) {tempNode = headers[i].nextInNode;while(tempNode != null) {resultString += " (" + tempNode.row + "," + tempNode.column + ")";tempNode = tempNode.nextInNode;} // Of whileresultString += "\r\n";}// Of for ireturn resultString;} // Of toString// 測試程序入口public static void main(String args[]) {int[][] tempMatrix = { { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 0 }, { 0, 1, 1, 0 } };OrthogonalList tempList = new OrthogonalList(tempMatrix);System.out.println("The data are:\r\n" + tempList);}// Of main }代碼需要講解的在評論區留言哦!
總結
- 上一篇: 在centos8 stream启用 Ex
- 下一篇: 01-subgradients_note