圖的數據結構,操作;數據結構用途。
結構說明
由多個頂點的結合及和點之間的關係集合組成的數據結構。
概念:
- 頂點的度 - 指與該頂點相關聯的邊的條數。
- 路徑 - 從一個頂點到另一個頂點的路徑,存在0個或多個。
- 權 - 路徑上附屬攜帶了數據信息。
- 連通圖指任意兩個頂點之間存在關係邊;
基本數據結構:
設計實現
圖的存儲結構主要有鄰接矩陣、鄰接表。
- 圖中頂點數小且邊較多時,採用鄰接矩陣存儲,即使用二維數組存儲
- 頂點數多且邊較少時,採用鄰接表存儲,即三元組鏈表存儲
實現鄰接矩陣存儲結構
結果說明:
-
頂點信息存儲在順序表中;JS 鏈表表實現 - 順序表部分,查看此地址
-
圖的邊信息存儲在二維數組中;
-
測試:
測試代碼:// 測試 let graph = new Graph(); graph.init(5); graph.inserPoint(1); graph.inserPoint(2); graph.inserPoint(3); graph.inserPoint(4); graph.inserPoint(5); graph.insertBorder(0,2,1); graph.insertBorder(0,1,1); graph.insertBorder(0,4,1); graph.insertBorder(0,3,1); graph.insertBorder(1,3,1); graph.insertBorder(2,4,1); console.log(graph.borderArr,graph.pointList);
function Graph(){
// 存儲頂點的順序表
this.pointList = new queueList();
// 存儲邊的二維數組
this.borderArr = new Array();
//
this.borderLen = 0;
}
Graph.prototype = {
init:function(n){
// 需要初始化二維數組
this.borderArr = new Array(n);
for(let start=0;start<n;start++){
// 如果默認 0 爲兩個頂點之間無關係邊時的標識;後續不拿 0 作爲權值
this.borderArr[start] = new Array(n).fill(0);
}
// 初始化 n 個頂點的
// for(let i=0;i<n;i++){
// for(let j=0;j<n;j++){
// if(i == j){
// this.borderArr[i][j] = 0;
// }
// }
// }
this.borderLen = 1;
},
inserPoint:function(p){
// 新增一個頂點
this.pointList.insert(this.pointList.size,p);
},
insertBorder:function(p1,p2,val){
// 插入一條有向邊;插入無向邊,插入兩條有向邊操作;
// p1 p2 指定一條邊,有關係連接
// val 爲這條邊的權值
if(p1<0||p1>this.pointList.size || p2<0 || p2>this.pointList.size){
console.error("參數不合法!");
return;
}
this.borderArr[p1][p2] = val;
this.borderArr[p2][p1] = val;
this.borderLen++;
},
deleteBorder:function(p1,p2){
// 刪除一條邊
if(p1<0||p1>this.pointList.size || p2<0 || p2>this.pointList.size || p1 ===p2){
console.error("參數合法!");
return;
}
if(this.borderArr[p1][p2] == Infinity || p1 ==p2){
console.error("該邊不存在!");
return;
}
this.borderArr[p1][p2] = Infinity;
this.borderLen--;
},
getFirstPoint:function(p){
// 獲取指定點的鄰接點
if(p<0 || p>=this.pointList.size){
console.error("參數不合法!");
return;
}
for(let i=0;i<this.pointList.size;i++){
if(this.borderArr[p][i]>0 && this.borderArr[p][i]<Infinity){
return i;
}
}
return -1;
}
}
實現鄰接表存儲結構:
結構說明:
-
頂點信息存儲在數組中;
-
頂點數組元素爲一個指定的數據對象;
function Point(){ // 頂點 this.point = null; // 下標值,指定 this.index = null; // 指向邊,爲一個鏈表結構 this.border = null; }
-
數組元素中執向邊爲一個鏈表結構,連接有關係的頂點;JS 實現鏈表 - 單鏈表部分,點此查看代碼
-
測試:無向圖可更改插入的方法,將 p2 和 p1 也建立關係。
// 測試 let graph = new GraphList(); // 初始化 默認大小 爲 10 ;可改爲動態傳入大小 graph.init(); // 插入頂點 graph.insertPoint(0,"A"); graph.insertPoint(1,"B"); graph.insertPoint(2,"C"); graph.insertPoint(3,"D"); graph.insertPoint(4,"E"); graph.insertPoint(5,"F"); // 插入關係邊 graph.insertBorder(0,2); graph.insertBorder(0,1); graph.insertBorder(0,3); graph.insertBorder(2,4); graph.insertBorder(3,5); graph.insertBorder(0,5); console.log(JSON.stringify(graph.pointArr));
代碼實現:邊的關係單鏈表是帶有頭部head
的
// 定義頂點的最大個數
const MAX_POINT = 10;
// 鄰接表存儲結構
function GraphList(){
// 頂點信息存儲的數組
this.pointArr = [];
// 頂點個數
this.pointLen = 0;
// 邊的個數
this.borderLen = 0;
}
GraphList.prototype = {
init(){
// 初始化鄰接表結構
for(let i=0;i<MAX_POINT;i++){
let point = new Point();
// 頂點下標
point.index = i;
// 頂點邊初始化爲單鏈表結構對象
point.border = new MyLinkedList();
point.border.init();
this.pointArr.push(point);
}
},
insertPoint(i,p){
// 在指定位置 i 插入頂點 p
if(i>=0&&i<MAX_POINT){
this.pointArr[i].point = p;
this.pointLen++;
}else{
console.error("參數不合法!");
}
},
insertBorder(p1,p2){
// 指定頂點 p1 p2 建立邊關係
if(p1<0 || p1>=this.pointLen || p2<0 || p2>=this.pointLen){
console.error("參數不合法!");
return;
}
// let border = new Node();
// border.val = p2;
// border.next = this.pointArr[p1].border;
// this.pointArr[p1].border = border;
// 調用鏈表自己的方法,實現頭部插入
this.pointArr[p1].border.addAtIndex(0,p2);
this.borderLen++;
},
deleteBorder(p1,p2){
// 刪除一條邊 p1 p2 的頂點關係
if(p1<0 || p1>this.pointLen || p2<0 || p2>this.pointLen){
console.error("參數不合法!");
return;
}
// 當前的頂點關係邊的 鏈表結構
let pre = null;
let curr = this.pointArr[p1].border;
while(curr!=null && curr.val!==p2){
// 無關係邊,或者 curr就是需要處理的邊
pre = curr;
curr = curr.next;
}
// 刪除操作
if(curr!==null && curr.val === p2 && pre ==null){
// p2 是 p1 的鄰接邊 ,且是第一個元素
this.pointArr[p1].border = curr.next;
this.borderLen--;
}else if(curr!==null && curr.val===p2 && pre!==null){
// p2 不是 p1 的鏈表裏的第一個元素
pre.next = curr.next;
this.borderLen--;
}else{
console.error(" p1/p2 無關係邊");
}
},
getFirstBorder(p){
// 獲取頂點 p 的第一個鄰接邊
if(p<0 || p>this.pointLen){
console.error("參數不合法!");
return;
}
let p = this.pointArr[p].border;
if(p!==null){
return p.val;
}
return false;
}
}
結構應用與算法
需要注意的問題:
- 圖的結構沒有首尾之分,初始是 需要 指定訪問的第一個節點。
- 需要處理死循環的問題,頂點關係之間可能會存在迴路;
- 確保定點的鄰接節點可被訪問;
深度優先遍歷(DFS)
指定訪問的頂點,並遞歸訪問他的鄰接點。
說明:
- 訪問頂點
p
, 標記頂點p
已被訪問。 - 訪問頂點
p
的第一個鄰接節點pn
; - 若
pn
存在,繼續執行;不存在則結束; - 若
pn
未被訪問,則遞歸遍歷訪問pn
; - 查找
p
的下一個鄰接點pm
; 轉步驟 3
廣度優先遍歷(BFS)
分層搜索的過程;按照路徑長度,從指定頂點開始訪問。
說明:
- 訪問指定頂點
p
,標記已被訪問;頂點p
入隊列; - 若隊列非空,則繼續執行;否則結束;
- 取隊列數據
p1
;查找p1
的第一個鄰接點pn
。 - 如果
pn
不存在,則轉到 3;否則循環執行:- 若
pn
違背訪問,則訪問pn
,標記已被訪問; - 將
pn
加入隊列。 - 查找
p1
的下一個頂點pn
,轉到 步驟 6
- 若