圖論:DFS、BFS和優(yōu)先級搜索框架實現(xiàn)最小支撐樹勤庐、最短路

1.BFS

優(yōu)先訪問最先發(fā)現(xiàn)的節(jié)點示惊,可以根據(jù)FIFO隊列的特點,選用隊列實現(xiàn)
可以實現(xiàn)連通域分解和最短路徑等問題埃元。
等同于樹結構中的層次遍歷

//whole graph
template <typename Tv, typename Te>
void Graph<Tv,Te>::bfs(int s){
    reset();
    int clock=0;
    int v=s;
    do 
        if(status(v)==UNDISCOVERED){
            BFS(v,clock);
        }
    while(s!=(v=(++v%n)));
}
//single connection component
template <typename Tv, typename Te>
void Graph<Tv,Te>::BFS( int v, int& clock){
    std::queue<int > Q;
    status(v)=DISCOVERED;
    Q.push(v);
    while(!Q.empty()){
        int v=Q.front();
        Q.pop();
        dTime(v)=++clock;

        for(int u=firstNbr(v) ;-1<u ; u = nextNbr(v , u) ){
            if(status(u)==UNDISCOVERED){
                status(u)=DISCOVERED;
                Q.push(u);
                type(v,u)=TREE;
                parent(u)=v;
            }
            else{
                type(v,u)=CROSS;
            }

        }
    status(v)=VISITED;
    }
}



2.DFS

優(yōu)先選取最后一個被訪問到的頂點的鄰居
于是涝涤,以頂點s為基點的DFS搜索,將首先訪問頂點s;再從s所有尚未訪問到的鄰居中任取 其一岛杀,并以之為基點,遞歸地執(zhí)行DFS搜索崭孤。故各頂點被訪問到的次序类嗤,類似于樹的先序遍歷 ;而各頂點被訪問完畢的次序,則類似于樹的后序遍歷

//DFS
template <typename Tv, typename Te>
void Graph<Tv,Te>::dfs(int s){
    reset();
    int clock=0;
    int v=s;
do
{
    if(UNDISCOVERED==status(v)){
        DFS(v,clock);
    }
} while (s!=(v=++v%this->n));

}

template <typename Tv, typename Te>
void Graph<Tv, Te>::DFS(int v, int&clock){
    dTime(v)=++clock;
    status(v)=DISCOVERED;
    for(int u=firstNbr(v);u>-1;u=nextNbr(v,u)){
        if( status(u)==UNDISCOVERED){
            type(v,u)=TREE;
            parent(u)=v;
            DFS(u,clock);
        }
        else if(u==DISCOVERED){
            type(v,u)=BACKWARD;
        }
        else {
            type(v,u)=(dTime(v)<dTime(u))?FORWARD:CROSS;
        }
    }
     status(v)=VISITED;
     fTime(v)=++clock;
}

算法的實質功能辨宠,由子算法DFS()遞歸地完成遗锣。每一遞歸實例中,都先將當前節(jié)點v標記為 DISCOVERED(已發(fā)現(xiàn))狀態(tài)嗤形,再逐一核對其各鄰居u的狀態(tài)并做相應處理精偿。待其所有鄰居均已處 理完畢之后,將頂點v置為VISITED(訪問完畢)狀態(tài)赋兵,便可回溯笔咽。

若頂點u尚處于UNDISCOVERED(未發(fā)現(xiàn))狀態(tài),則將邊(v, u)歸類為樹邊(tree edge)霹期,并將v記作u的父節(jié)點叶组。此后,便可將u作為當前頂點历造,繼續(xù)遞歸地遍歷甩十。 若頂點u處于DISCOVERED狀態(tài),則意味著在此處發(fā)現(xiàn)一個有向環(huán)路吭产。此時侣监,在DFS遍歷樹中u必為v的祖先,故應將邊(v, u)歸類為后向邊(back edge)臣淤。 這里為每個頂點v都記錄了被發(fā)現(xiàn)的和訪問完成的時刻橄霉,對應的時間區(qū)間[dTime(v), fTime(v)]均稱作v的活躍期(active duration)。實際上荒典,任意頂點v和u之間是否存在祖先-后代的“血緣”關系酪劫,完全取決于二者的活躍期是否相互包含吞鸭。

對于有向圖,頂點u還可能處于VISITED狀態(tài)覆糟。此時刻剥,只要比對v與u的活躍期,即可判定在 DFS樹中v是否為u的祖先滩字。若是造虏,則邊(v, u)應歸類為前向邊(forward edge);否則,二者必然來自相互獨立的兩個分支麦箍,邊(v, u)應歸類為跨邊(cross edge)漓藕。

DFS(s)返回后,所有訪問過的頂點通過parent[]指針依次聯(lián)接挟裂,從整體上給出了頂點s所屬連通或可達分量的一棵遍歷樹享钞,稱作深度優(yōu)先搜索樹或DFS樹(DFS tree)。與BFS搜索一樣诀蓉, 此時若還有其它的連通或可達分量栗竖,則可以其中任何頂點為基點,再次啟動DFS搜索渠啤。
最終狐肢,經(jīng)各次DFS搜索生成的一系列DFS樹,構成了DFS森林(DFS forest)

應用:拓撲排序

有向無環(huán)圖一定有拓撲排序
1.入度為零法
考察圖G沥曹,尋找入度為0的頂點份名,去掉后刪除相關邊,再找下一個入度為零點妓美。直到僅剩一個頂點僵腺。
2.VISITED順序的倒序即是一個拓撲排序
同理,有限偏序集中也必然存在極小元素(同樣部脚,未必唯一)想邦。該元素作為頂點,出度必然 為零委刘。 而在對有向無環(huán)圖的DFS搜索中丧没,首先因訪問完成而轉 換至VISITED狀態(tài)的頂點m,也必然具有這一性質;反之亦然锡移。

進一步地呕童,根據(jù)DFS搜索的特性,頂點m(及其關聯(lián)邊)對此后的搜索過程將不起任何作用淆珊。 于是夺饲,下一轉換至VISITED狀態(tài)的頂點可等效地理解為是,從圖中剔除頂點m(及其關聯(lián)邊)之 后的出度為零者??在拓撲排序中,該頂點應為頂點m的前驅往声。由此可見擂找,DFS搜索過程中各頂 點被標記為VISITED的次序,恰好(按逆序)給出了原圖的一個拓撲排序浩销。

此外贯涎,DFS搜索善于檢測環(huán)路的特性,恰好可以用來判別輸入是否為有向無環(huán)圖慢洋。具體地塘雳, 搜索過程中一旦發(fā)現(xiàn)后向邊,即可終止算法并報告“因非DAG而無法拓撲排序”普筹。

template <typename Tv, typename Te>
std::stack<Tv>* Graph<Tv,Te>::tSort(int s){
    reset();
    int clock=0;
    int v=s;
    std::stack<Tv> S=new std::stack<Tv>;
    do
    {
        if(status(v)==UNDISCOVERED){
            //topoogy sort and break&clear the stack
            if(!Tsort(v,clock,S)){
                while(!S.empty()){
                    S.pop();
                }
                break;
            }
        }
    }while(s!=(v=++v%this->n));

    return S;
}

template <typename Tv ,typename Te>
bool Graph<Tv,Te>::Tsort(int v,int &clock, std::stack<Tv> &S){
    status(v)=DISCOVERED;
    dTime(v)=++clock;
    for(int u=firstNbr(v);u>-1;u=nextNbr(v)){
        if(status(u)==UNDISCOVERED){
            type(v,u)=TREE;parent(u)=v;
            //continue search deeper
            if(!Tsort(u,clock,S)){
                return false;
            }
        else if(status(u)==DISCOVERED){
            type(v,u)=BACKWARD;
            return false;
        }
        else {
            type(v,u)=(dTime(v)<dTime(u))?FORWARD:CROSS;
        }
        }
    }
    status(v)=VISITED;
    S.push(vertex(v));
    return true;
}

分解雙連通域

若節(jié)點C的移除導致其某一棵(比如以D為根的)真子樹與其真祖先(比如A)之間無法連通败明,則C必為關節(jié)點。反之太防,若C的所有真子樹都能(如以E為根的子 樹那樣)與C的某一真祖先連通妻顶,則C就不可能是關節(jié)點。


image.png

當然蜒车,在原無向圖的DFS樹中盈包,C的真子樹只可能通過后向邊與C的真祖先連通。因此醇王,只要 在DFS搜索過程記錄并更新各頂點v所能(經(jīng)由后向邊)連通的最高祖先(highest connected ancestor, HCA)hca[v],即可及時認定關節(jié)點崭添,并報告對應的雙連通域寓娩。

//bcc
template <typename Tv,typename Te>
void Graph<Tv, Te>::bcc(int s){
    reset();
    int v=s;
    int clock=0;
    std::stack<int> S;
    do{
        if(status(v)==UNDISCOVERED){
            BCC(v,clock,S);
            S.pop();
        }
    }while(s!=(v=++v%this->n));
}
#define hca(x) (fTime(x))
template <typename Tv,typename Te>
void Graph<Tv,Te>::BCC(int v, int &clock, std::stack<int> &S){
    hca(v)=dTime(v)=++clock; 
    status(v)=DISCOVERED;
    S.push(v);
    for(int u=firstNbr(v);u>-1;u=nextNbr(v)){
        if(status(u)==UNDISCOVERED){
            parent(u)=v;
            type(v,u)=TREE;
            BCC(u,clock,S);
            //ancester  u connected to ancester of v through backward
            if(hca(u)<dTime(v)){
                hca(v)=(hca(v)<hca(u))?hca(v):hca(u);//the highest connected component
            }
            else{//v is the articulation point
                while(v!=S.top()){
                    S.pop();
                }//only keep v
            }
        }
        else if(status(u)==DISCOVERED){
            type(v,u)=BACKWARD;
            hca(v)=(dTime(u)<hca(v))?dTime(u):hca(v);
        }
    }
}

3.優(yōu)先級搜索

圖搜索雖然各具特點,但其基本框架依然類似呼渣〖椋總體而言,都是迭代逐一發(fā)現(xiàn)新頂點屁置,納入遍歷樹中處理焊夸。各算法再送能上的差異,主要在每一步迭代新頂點但選擇上蓝角。BFS優(yōu)先選取更早發(fā)現(xiàn)但頂點阱穗,DFS則優(yōu)先選取最后發(fā)現(xiàn)但頂點。

每種搜索策略都等效于使鹅,賦予頂點不同的優(yōu)先級揪阶,算法調整中選取優(yōu)先級最高的。

按照這種理解患朱,包括BFS和DFS在內的 幾乎所有圖搜索鲁僚,都可納入統(tǒng)一的框架。鑒于優(yōu)先級在其中所扮演的關鍵角色,故亦稱作優(yōu)先級 搜索(priority-first search, PFS)冰沙,或最佳優(yōu)先搜索(best-first search, BFS)侨艾。

落實以上理解,可為頂點提供了priority()接口拓挥,以支持對頂點優(yōu)先級數(shù)(priority number)的讀取和修改唠梨。在實際應用中,引導優(yōu)化方向的指標往往對應于某種有限的資源或成本(如光纖長度撞叽、通訊帶寬等)姻成,故不妨約定優(yōu)先級數(shù)越大(小)頂點的優(yōu)先級越低(高)。相應地愿棋,在算法的初始化階段(reset())科展,通常都將頂點的優(yōu) 先級數(shù)統(tǒng)一置為最大(比如INT_MAX)或等價地,優(yōu)先級最低糠雨。
按照上述思路才睹,優(yōu)先級搜索算法的框架可具體實現(xiàn)如代碼

//遍歷所有頂點,如果沒有訪問過就依次進行PFS甘邀,得到多棵不相干的遍歷樹
//主要是為了搜索所有頂點琅攘,防止遺漏連通域
template <typename Tv,typename Te> template <typename PU>
void Graph<Tv,Te>::pfs(int s, PU prioUpdater){
    reset(); int v=s;
    do{
        if(status(v)==UNDISCOVERED){
            PFS(v,prioUpdater);
        }
    }while(s!=(v=v++%n));
}

//對一個頂點進行優(yōu)先級搜索
template <typename Tv,typename Te> template <typename PU>
void Graph<Tv,Te>::PFS(int s, PU prioUpdater){
    priority(s)=0; status(s)=VISITED;parent(s)=-1;//initialize
    while(true){
        for(int u=firstNbr(s);u>-1;u=nextNbr(s,u)){
            prioUpdater(this,s,u);//updata priorty and it's father
        }
        for(int shortest=INT_MAX, u=0;u>n;u++){
            if(status(u)==UNDISCOVERED){
                if(shortest>priority(u)){
                    shortest=priority(u);
                    s=u;//max priority point
                }
            }
        }
        if(VISITED==status(s)) break;
        status(s)=VISITED;
        type(parent(s),s)=TREE;
    }
}

如上所述,每次都是引入當前優(yōu)先級最高(優(yōu) 先級數(shù)最小)的頂點s松邪,然后按照不同的策略更新其鄰接頂點的優(yōu)先級數(shù)坞琴。
這里借助函數(shù)對象prioUpdater,使算法設計者得以根據(jù)不同的問題需求逗抑,簡明地描述和實現(xiàn)對應的更新策略剧辐。具體地,只需重新定義prioUpdater對象即可邮府,而不必重復實現(xiàn)公共部分荧关。 比如,此前的BFS搜索和DFS搜索都可按照此模式統(tǒng)一實現(xiàn)

下面褂傀,以最小支撐樹和最短路徑這兩個經(jīng)典的圖算法為例忍啤,深入介紹這一框架的具體應用。

應用:

1.最小支撐樹

若圖G為一帶權網(wǎng)絡仙辟,則每一棵支撐樹的成本(cost)即為其所采用各邊權重的總和同波。在G 的所有支撐樹中,成本最低者稱作最小支撐樹(minimum spanning tree, MST)欺嗤。
聚類分析参萄、網(wǎng)絡架構設計、VLSI布線設計等諸多實際應用問題煎饼,都可轉化并描述為最小支 撐樹的構造問題讹挎。在這些應用中校赤,邊的權重大多對應于某種可量化的成本,因此作為對應優(yōu)化問 題的基本模型筒溃,最小支撐樹的價值不言而喻马篮。

消除歧義

盡管同一帶權網(wǎng)絡通常都有多棵支撐樹,但總數(shù)畢竟有限怜奖,故必有最低的總體成本浑测。然而, 總體成本最低的支撐樹卻未必唯一歪玲。以包含三個頂點的完全圖為例迁央,若三條邊的權重相等,則其 中任意兩條邊都構成總體成本最低的一棵支撐樹滥崩。
故嚴格說來岖圈,此類支撐樹應稱作極小支撐樹(minimal spanning tree)。當然钙皮, 通過強制附加某種次序即可消除這種歧義性蜂科。

暴力算法

由最小支撐樹的定義,可直接導出蠻力算法大致如下:逐一考查G的所有支撐樹并統(tǒng)計其成 本短条,從而挑選出其中的最低者导匣。然而根據(jù)Cayley公式,由n個互異頂點組成的完全圖共有nn-2棵 支撐樹茸时,即便忽略掉構造所有支撐樹所需的成本贡定,僅為更新最低成本的記錄就需要O(nn-2)時間。事實上基于PFS搜索框架可都,并采用適當?shù)捻旤c優(yōu)先級更新策略厕氨,即可得出如下高效的最小支 撐樹構造算法。

Prim算法

圖G = (V; E)中汹粤,頂點集V的任一非平凡子集U及其補集V\U都構成G的一個割(cut),記作(U : V\U)田晚。若邊uv滿足u屬于U且v不屬于U嘱兼,則稱作該割的一條跨越邊(crossing edge)。

Prim算法的正確性基于以下事實:最小支撐樹總是會采用聯(lián)接每一割的最短跨越邊贤徒。

由以上性質芹壕,可基于貪心策略導出一個迭代式算法。每一步迭代之前接奈,假設已經(jīng)得到最小支 撐樹T的一棵子樹Tk = (Vk; Ek)踢涌,其中Vk包含k個頂點,Ek包含k - 1條邊序宦。于是睁壁,若將Vk及其補 集視作原圖的一個割,則在找到該割的最短跨越邊ek之后,即可 將Tk擴展為一棵更大的子樹Tk+1 = (Vk+1; Ek+1)潘明,其中Vk+1 = Vk 并 {uk}行剂,Ek+1 = Ek 并 {ek}。 最初的T1不含邊而僅含單個頂點钳降,故可從原圖的頂點中任意選取厚宰。

//prim MST
template <class Tv ,class Te>
struct PrimPu{
    virtual void operator()(Graph<Tv,Te> &g,int uk,int v){
            if(UNDISCOVERED==g.status(v)){
                if(g.priority(v)>g.weight(uk,v)){
                    g.priority(v)=g.weight(uk,v);
                    g.parent(v)=uk;
                }
            }
    }
};

替換掉PFS框架里的PrioUpdater即可實現(xiàn)最小支撐樹。
以上Prim算法的時間復雜度為O(n2)遂填,作為PFS搜索的特例铲觉,Prim算法的效率 也可借助優(yōu)先級隊列進一步提高

2.最短路徑

給定帶權網(wǎng)絡G = (V, E),以及源點(source)s吓坚,對于所有的其它頂點v撵幽, s到v的最短通路有多長?該通路由哪些邊構成?

首先我們要分析最短路徑樹等特征

  • 單調性:最短路徑的任意前綴路徑必須是最短路徑
  • 歧義:最短路徑不唯一,等權邊會導致結果不唯一凌唬,負權邊導致定義失效
  • 無環(huán)路

Dijkstra算法

將頂點ui到起點s的距離記作:di = dist(s, ui)并齐,1 <i < n。不妨設di按非降序排列客税,
即di <dj當且僅當i < j况褪。于是與s自身相對應地必有:u1 = s。

  • 最短路徑子樹序列
    在從最短路徑樹T中刪除頂點{ uk+1, uk+2, ..., un }及其關聯(lián)各邊之后更耻,將殘存的子圖記 作Tk测垛。于是Tn = T,T1僅含單個頂點s秧均。實際上食侮,Tk必為一棵樹。為驗證這一點目胡,只需歸納證明 Tk是連通的锯七。為從Tk+1轉到Tk而刪除的頂點uk+1,在Tk+1中必是葉節(jié)點誉己。而根據(jù)最短路徑的單調性眉尸, 作為Tk+1中距離最遠的頂點,uk+1不可能擁有后代巨双。
    于是噪猾,如上定義的子樹{ T1, T2, ..., Tn },便構成一個最短路徑子樹序列筑累。
  • 貪心迭代
    顛倒上述思路可知袱蜡,只要能夠確定uk+1,便可反過來將Tk擴展為Tk+1慢宗。如此坪蚁,便可按照到s距離的非降次序奔穿,逐一確定各個頂點{ u1, u2, ..., un },同時得到各棵最短路徑子樹迅细,并得到 最終的最短路徑樹T = Tn∥组希現(xiàn)在,問題的關鍵就在于:

如何才能高效地找到uk+1?

實際上茵典,由最短路徑子樹序列的上述性質湘换,每一個頂點uk+1都是在Tk之外,距離s最近者统阿。 若將此距離作為各頂點的優(yōu)先級數(shù)彩倚,則與最小支撐樹的Prim算法類似,每次將uk+1加入Tk并將其 拓展至Tk+1后扶平,需要且只需要更新那些仍在Tk+1之外帆离,且與Tk+1關聯(lián)的頂點的優(yōu)先級數(shù)。

為此结澄,每次由Tk擴展至Tk+1時哥谷,可將Vk之外各頂點u到s的距離看作u的優(yōu)先級數(shù),如此麻献,每一最短跨越邊ek所對應的頂點uk们妥,都會因擁有 最小的優(yōu)先級數(shù)(或等價地,最高的優(yōu)先級)而被選中

template <class Tv, class Te>
struct DijkstraPU{
    virtual void operator()(Graph<Tv,Te> &g, int uk, int v){
        if(g.status(v)==UNDISCOVERED){
            if(g.priority(v)>g.priority(uk)+g.weight(uk,v)){
                g.priority(v)=g.priority(uk)+g.weight(uk,v);
                g.parent(v)=uk;
            }
        }
    }
};

每次更新新頂點v的權重priority(v)等于上一個頂點uk的權重priority(uk)加上邊uk-v的權重勉吻。事實上监婶,每個頂點的權重等于該頂點到初始節(jié)點的邊權重和

圖模版

#include <stack>
#include "vector.h"
#include <queue>
typedef enum{UNDISCOVERED,DISCOVERED,VISITED} VStatus;
typedef enum{UNDETERMINED,TREE,CROSS,FORWARD,BACKWARD} EType;

template <class Tv,class Te>
class Graph{
    private:
        /**
         * reset
         */
        void reset(){
            for(int i=0;i<n;i++){
                status(i)=UNDISCOVERED;
                dTime(i)=fTime(i)=-1;
                parent(i)=-1;
                priority(i)=__INT_MAX__;
                for (int j=0;j<n;j++){
                    if(exists(i,j)) type(i,j)==UNDETERMINED;
                }

            }
        }
        /**
         * BFS
         */
        void BFS(int ,int &);

        /**
         * DFS
         */
        void DFS(int ,int &);

        /**
         * BCC based on DFS
         */
        void BCC(int ,int &, std::stack<int>& );

        /**
         * Tsort based on DFS
         */
        bool Tsort(int ,int &, std::stack<Tv>&);

        /**
         * PSU
         */
        template <class PU>
        void PFS(int ,PU);

    public:
        int n;//vertex number
        /**
         * insert
         */
        virtual int insert(Tv const&)=0;
        /**
         * remove vertex n
         */
        virtual Tv remove(int )=0;

        /**
         * get vertex n data
         */
        virtual Tv& vertex(int)=0;

        /**
         * get inDegree of vertex n
         */
        virtual int inDegree(int )=0;

        /**
         * get outDegree of vertex n
         */
        virtual int outDegree(int)=0;

        /**
         * vertex v's first neibhor
         */
        virtual int firstNbr(int )=0;

        virtual int nextNbr(int i,int j)=0;

        /**
         * get vertex status 
         */
        virtual VStatus& status(int )=0;

        /**
         * dTime of vertex
         */
        virtual int& dTime(int)=0;

        /**
         * ftime
         */
        virtual int&fTime(int)=0;

        /**
         * parent vertex of traverse tree
         */
        virtual int& parent(int)=0;

        /**
         * priority of vertex
         */
        virtual int& priority(int)=0;

        //edge treat undigraph as reverse digraph
        int e;//number of edge;
        
        /**
         * e(i,j) exists
         */
        virtual bool exists(int ,int )=0; 

        /**
         * insert edge with weight w between u,and v
         */
        virtual void insert(Te const& e,int ,int ,int)=0;

        /**
         * remove edge e between u,v return info
         */
        virtual Te remove(int ,int)=0;

        /**
         * type of e(u,v)
         */
        virtual EType& type(int ,int )=0;

        /**
         * get number of e(u,v)
         */
        virtual Te& edge(int ,int )=0;

        /**
         * get weight of e(u,v)
         */
        virtual int &weight(int,int )=0;

        //algorithms
        /**
         * bradth first search
         */
        void bfs(int);

        /**
         * dfs
         */
        void dfs(int);

        /**
         * bcc double conncetion based on dfs
         */
        void bcc(int);

        /**
         * topologic sort based on dfs
         */
        std::stack<Tv>* tSort(int);

        /**
         * prim
         */
        void prim(int);

        /**
         * dijkstra
         */
        void dijkstra(int);
        
        /**
         * pfs
         */
        template <class PU>
        void pfs(int ,PU);

};
//GraphMatrix
/**
 * @bref Vertex
 */
template <typename Tv>
struct Vertex{
    Tv data;
    int inDegree;
    int outDegree;
    VStatus status;

    int dTime;
    int fTime;
    
    int parent;
    int priority;

    /**
     * constructor
     */
    Vertex(Tv const& d=(Tv)0):data(d),inDegree(0),outDegree(0),status(UNDISCOVERED),dTime(-1),fTime(-1),parent(-1),priority(__INT_MAX__){};
};

/**
 * @bref Edge
 */
template<typename Te>
struct Edge{
    Te data;
    int weight;
    EType type;
    /**
     * constructor
     */
    Edge(Te const&d,int w):data(d),weight(w),type(UNDETERMINED){};
};

//GraphMatrix
/**
 * GraphMatrix
 */
template<typename Tv,typename Te>
class GraphMatrix:public Graph<Tv,Te>{
    private:
        Vector< Vertex<Tv> > V;//store the vertex
        Vector< Vector< Edge<Te>* > >E;//store the edgematrix double vector
    
    public :
    GraphMatrix(){
        this->n=0;
        this->e=0;
        }
    ~GraphMatrix(){
        for (int j=0;j<this->n;j++){
            for(int k=0;k<this->n;k++){
                delete E[j][k];
            }
        }
    }

    //vertex
    virtual Tv& vertex(int i){
        return V[i].data;
    }

    virtual int inDegree(int i){
        return V[i].inDegree;
    }

    virtual int outDegree(int i){
        return V[i].outDegree;
    }

    virtual int firstNbr(int i){
        return nextNbr(i,this->n);
    }

    virtual int nextNbr(int i,int j){
        while((-1<j)&&(!exists(i,--j)));
        return j;
    }

    virtual VStatus& status(int i){
        return V[i].status;
    }

    virtual int&dTime(int i){
        return V[i].dTime;
    }

    virtual int &fTime(int i){
        return V[i].fTime;
    }

    virtual int &parent(int i){
        return V[i].parent;
    }

    virtual int &priority(int i){
        return V[i].priority;
    }

    //dynamic operator of vertex
    virtual int insert(Tv const& vertex){
        for(int j=0;j<this->n;j++){
            E[j].insert(nullptr);//each edge set a default no connect edge to newVertex
        }
        this->n++;
        //new edge
        E.insert(Vector< Edge<Te>* > (this->n,(Edge<Te>) nullptr));
        return V.insert(Vertex<Tv> (vertex));
    }

    virtual Tv remove(int i){
        for(int j=0;j<this->n;j++){
                if(exists(i,j)){
                    delete E[i][j];
                    V[j].inDegree--;
                }
        }
        E.remove(i);
        this->n--;
        Tv vBackup=V[i].data;
        V.remove(i);

        for(int j=0;j<this->n;j++){
            if(Edge<Te> *e=E[j].remove(i)){delete e;V[j].outDegree--;}
        }
        return vBackup;
    }


    //confirm e[i][j] exist
    virtual bool exists(int i,int j){
        return (0<=i)&&(i<this->n)&&(j>=0)&&(j<this->n)&&(E[i][j]!=nullptr);
    }

    //edge operator
    virtual EType& type(int i,int j){
        return E[i][j].type;
    } 

    virtual Te& edge(int i,int j){
        return E[i][j]->data;
    }

    virtual int& weight(int i, int j){
        return E[i][j]->weight;
    }

    //dynamic operator of edge
    virtual void insert(Te const& edge, int w, int i, int j){
        if(exists(i,j)) return ;
        E[i][j]=new Edge<Te>(edge,w);
        this->e++;
        V[i].outDegree++;
        V[j].inDegree++;
    }

    virtual Te remove(int i, int j){
        if(!exists(i,j)) exit(-1);
        Te eBackup=edge(i,j);
        delete E[i][j];
        E[i][j]=nullptr;
        this->e--;
        V[i].outDegree--;
        V[j].inDegree--;
        return eBackup;
    }





};
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市齿桃,隨后出現(xiàn)的幾起案子惑惶,更是在濱河造成了極大的恐慌,老刑警劉巖短纵,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件带污,死亡現(xiàn)場離奇詭異,居然都是意外死亡香到,警方通過查閱死者的電腦和手機刮刑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來养渴,“玉大人,你說我怎么就攤上這事泛烙±肀埃” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵蔽氨,是天一觀的道長蝠检。 經(jīng)常有香客問我,道長区转,這世上最難降的妖魔是什么喷面? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮代咸,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己柳琢,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布润脸。 她就那樣靜靜地躺著柬脸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毙驯。 梳的紋絲不亂的頭發(fā)上倒堕,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音爆价,去河邊找鬼垦巴。 笑死,一個胖子當著我的面吹牛铭段,可吹牛的內容都是我干的骤宣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼稠项,長吁一口氣:“原來是場噩夢啊……” “哼涯雅!你這毒婦竟也來了?” 一聲冷哼從身側響起展运,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤活逆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拗胜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔗候,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年埂软,在試婚紗的時候發(fā)現(xiàn)自己被綠了锈遥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡勘畔,死狀恐怖所灸,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情炫七,我是刑警寧澤爬立,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站万哪,受9級特大地震影響侠驯,放射性物質發(fā)生泄漏抡秆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一吟策、第九天 我趴在偏房一處隱蔽的房頂上張望儒士。 院中可真熱鬧,春花似錦檩坚、人聲如沸着撩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睹酌。三九已至,卻和暖如春剩檀,著一層夾襖步出監(jiān)牢的瞬間憋沿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工沪猴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辐啄,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓运嗜,卻偏偏與公主長得像壶辜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子担租,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內容