資料結構之圖(鄰接表 稀疏圖)
<!DOCTYPE html>
<html>
<head>
<title>鄰接表</title>
<meta charset="utf-8">
<script type="text/javascript">
// 稀疏圖 鄰接表
class SparseGraph{
constructor(n, directed){
// n為定點數 m為邊數
this.n = n;
this.m = 0;
this.directed = directed;
this.data = new Array();
for(var i=0; i<n; i++){
this.data[i] = new Array();
}
// 判斷節點是否是訪問過得
this.visited = [];
// 用來記錄從哪個節點 到這個節點的值
this.from = [];
// 用來記錄從源點到本節點的長度
this.ord = [];
}V() { return this.n; }
E() { return this.m; }
result() { return this.data; }
// 新增一條邊
addEdge(v,w){
console.assert( v >= 0 && v < this.n);
console.assert( w >= 0 && w < this.n );// 判斷v和w是否有邊
if(this.hasEdge(v, w)){
return ;
}this.data[v].push(w);
if(v!=w && !this.directed){
this.data[w].push(v);
}
this.m++;
}// 判斷是否有邊
hasEdge(v, w){
console.assert( v >= 0 && v < this.n);
console.assert( w >= 0 && w < this.n )
return (this.data[v].length && this.data[v].indexOf(w) > 0);
}// 圖的深度優先遍歷
DFSTraverse(){
// 給每個節點設定 false(未遍歷)
for(var i=0; i<this.n; i++){
this.visited[i] = false;
}for(var i=0; i<this.n; i++){
if(!this.visited[i]){
// console.log(i)
this.DFS(i)
}
}
}DFS(v){
// 第v個頂點 應該是訪問過的
this.visited[v] = true;
console.log(v)for(var w = this.FirstAdjVex(v); w >= 0; w = this.NextAdjVex(v, w)){
// 如果v中的相鄰節點沒有遍歷的話 那麼就在遍歷這個節點中的相鄰節點
if(!this.visited[w]){
// 記錄從哪個節點遍歷 到這
this.from[w] = v;
this.visited[w] = true;
console.log(w)
// 獲得w中 第一個未遍歷的元素
var x = this.FirstFlase(w);
if(x >=0){
// 這個是由於 要走w的第一個元素 故要記錄 x的前一個是w
this.from[x] = w;
this.DFS(x)
}
}
}
}// 獲得w中的第一個未遍歷的元素
FirstFlase(w){
if(this.data[w]){
for(var i=0; i<this.data[w].length; i++){
if(!this.visited[this.data[w][i]]){
return this.data[w][i];
}
}
}
return -1;
}// 圖的 廣度優先遍歷
BFSTraverse(){
// 給每個節點設定 false (未遍歷)
for(var i=0; i<this.n; i++){
this.visited[i] = false;
}// 這個迴圈是 為了解決圖 有多個聯通分量 問題
for(var i=0; i<this.n; i++){
if(!this.visited[i]){
// 實現一個佇列 來廣度遍歷圖
this.visited[i] = true;
// 把源點的長度記錄為0 如果是多個聯通分量,初始的節點的長度也是0
this.ord[i] = 0;
var res = []
res.push(i);
var node = null;
while(node != null || res.length>0){
var node = res.shift()
if(node != null){
console.log(node)
}
for(var w = this.FirstAdjVex(node); w >= 0; w = this.NextAdjVex(node, w)){
// 如果v中的相鄰節點沒有遍歷的話 那麼就在遍歷這個節點中的相鄰節點
if(!this.visited[w]){
// 如果w 未遍歷 則標誌改變 且加入res中
this.visited[w] = true;
this.from[w] = node;
// 這個節點到源點的長度 等於它上個節點的長度 +1
this.ord[w] = this.ord[node] + 1;
res.push(w);
}
}
}
}
}
}// 深度遍歷 獲得任意節點的路徑
DFSPath(v, w){
// v -> w 的路徑
this.DFSTraverse();return this.Path(v, w);
}
// 廣度優先遍歷 獲得兩節點之間的路徑
BFSPath(v, w){
// 廣度遍歷
this.BFSTraverse();
return this.Path(v, w);
}//
Path(v, w){
// 儲存路徑
var ww = w;
var res = []while(v != w){
res.push(this.from[w])
w = this.from[w]
}
res.reverse().push(ww);
res = res.join(" -> ");return res;
}// 獲得v 的 第一個連線的節點
FirstAdjVex(v){// 檢測this.data[v]是否存在
if(this.data[v]){
var len = this.data[v].length;
if(len > 0){
var sum = this.data[v][0];
return sum;
}
}
return -1;
}// 獲得v的下一個連線的節點
NextAdjVex(v, w){
for(var i=0; i<this.data[v].length; i++){
if(this.data[v][i] == w){
//判斷v中的鄰接表w的下一個節點 是否存在
return this.data[v][i+1] ? this.data[v][i+1] : -1;
}
}
return -1;
}
}
var sGraph = new SparseGraph(7, false);var wdata = [[0, 1], [0, 2], [0, 5], [0, 6], [3, 4], [3, 5], [4, 5], [4, 6]]
for(var i=0; i<wdata.length; i++){
sGraph.addEdge(wdata[i][0], wdata[i][1]);
}
console.log(sGraph.result())
//sGraph.BFSTraverse();
//sGraph.DFSTraverse()console.log(sGraph.from)
console.log(sGraph.BFSPath(0, 6))
</script>
</head>
<body></body>
</html>