1. 程式人生 > >資料結構之圖(鄰接表 稀疏圖)

資料結構之圖(鄰接表 稀疏圖)

<!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>