數(shù)據(jù)結構與算法- 圖

(Graph)是一種較線性表和樹更復雜的數(shù)據(jù)結構奏候。在圖形結構中,結點之間的關系可以是任一的,圖中任意兩個數(shù)據(jù)元素之間都可能相關获印。

圖的存儲結構

鄰接矩陣

鄰接矩陣是表示頂點之間相鄰關系的矩陣品山。設G=(V,E)是一個圖胆建,其中V={v1,v2,…,vn} 。G的鄰接矩陣是一個具有下列性質的n階方陣:

  • 對無向圖而言肘交,鄰接矩陣一定是對稱的笆载,而且主對角線一定為零(在此僅討論無向簡單圖),副對角線不一定為0,有向圖則不一定如此凉驻。
  • 在無向圖中腻要,任一頂點i的度為第i列(或第i行)所有非零元素的個數(shù),在有向圖中頂點i的出度為第i行所有非零元素的個數(shù)涝登,而入度為第i列所有非零元素的個數(shù)闯第。
  • 用鄰接矩陣法表示圖共需要n^2個空間,由于無向圖的鄰接矩陣一定具有對稱關系缀拭,所以扣除對角線為零外咳短,僅需要存儲上三角形或下三角形的數(shù)據(jù)即可,因此僅需要n(n-1)/2個空間蛛淋。
#define INFINITYC 0
#define MAX_VERTEX_NUM 50

typedef int Status;
typedef char VertexType;
typedef int EdgeType;

typedef struct MGraph {
    VertexType vexs[MAX_VERTEX_NUM];
    EdgeType arc[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
    int numNodes, numEdges;
} MGraph;

void CreateMGraph(MGraph *G) {
    printf("輸入頂點數(shù)和邊數(shù):\n");
    scanf("%d,%d", &G->numNodes, &G->numEdges);
    printf("頂點數(shù):%d,邊數(shù):%d\n", G->numNodes, G->numEdges);
    
    int i, j, w;
    for (i = 0; i < G->numNodes; i++) {
        scanf("%c", &G->vexs[i]);
        getchar();
    }
    
    for (i = 0; i < G->numNodes; i++) {
        for (j = 0; j < G->numNodes; i++) {
            G->arc[i][j] = INFINITYC;
        }
    }
    
    for (int k = 0; k < G->numEdges; k++) {
        printf("輸入邊(vi, vj)上的下標i, 下標j, 權w\n");
        scanf("%d,%d,%d", &i, &j, &w);
        G->arc[i][j] = w;
        G->arc[j][i] = G->arc[i][j];
    }
    
    for (int i = 0; i < G->numNodes; i++) {
        printf("\n");
        for (int j = 0; j < G->numNodes; j++) {
            printf("%d ",G->arc[i][j]);
        }
    }
    printf("\n");
}

鄰接表

鄰接表(Adjacency List)是圖的一種鏈式存儲結構咙好。在連接表中,對圖中的每個頂點建立一個單鏈表褐荷,第i個單鏈表中的結點表示依附于頂點v1的邊勾效。每個結點由3個域組成,其中鄰接點域(adjvex)指示與頂點v1鄰接的點在圖中的位置叛甫,鏈域(nextarc)指示下一條邊或弧的結點层宫。數(shù)據(jù)域存儲和邊或弧相關的信息。每個鏈表附設一個表頭結點其监。

#define MAX_VERTEX_NUM 50
#define true 1
#define false 0

typedef char Element;
typedef int BOOL;

typedef struct ArcNode {
    int adjvex;
    Element data;
    struct ArcNode *nextArc;
} ArcNode;

typedef struct VNode {
    Element data;
    ArcNode * firstedge;
} VertexNode, Adjlist[MAX_VERTEX_NUM];

typedef struct ALGraph {
    Adjlist adjlist;
    int arc_num;
    int node_num;
    BOOL is_directed;
} ALGraph, *GraphLink;

void CreateGraph(GraphLink *G) {
    printf("輸入頂點數(shù)目, 邊數(shù)和方向萌腿?:\n");
    scanf("%d %d %d", &(*G)->node_num, &(*G)->arc_num, &(*G)->is_directed);
    
    printf("輸入頂點信息:\n");
    int i, j;
    for (i = 0; i < (*G)->node_num; i++) {
        getchar();
        scanf("%c", &(*G)->adjlist[i].data);
        (*G)->adjlist[i].firstedge = NULL;
    }
    
    ArcNode *p;
    printf("輸入邊信息:\n");
    for (int k = 0; k < (*G)->arc_num; k++) {
        getchar();
        scanf("%d %d", &i, &j);
        
        p = (ArcNode *)malloc(sizeof(ArcNode));
        p->adjvex = j;
        p->nextArc = (*G)->adjlist[i].firstedge;
        (*G)->adjlist[i].firstedge = p;
        
        if (!(*G)->is_directed) {
            p = (ArcNode *)malloc(sizeof(ArcNode));
            p->adjvex = i;
            p->nextArc = (*G)->adjlist[j].firstedge;
            (*G)->adjlist[j].firstedge = p;
        }
    }
}

void putGraph(GraphLink G){
    printf("鄰接表中存儲信息:\n");
    for (int i = 0; i < G->node_num; i++) {
        ArcNode *p = G->adjlist[i].firstedge;
        while (p) {
            printf("%c->%c ", G->adjlist[i].data, G->adjlist[p->adjvex].data);
            p = p->nextArc;
        }
        printf("\n");
    }
}

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    GraphLink g = (ALGraph *)malloc(sizeof(ALGraph));
    CreateGraph(&g);
    putGraph(g);
    return 0;
}

圖的遍歷

和樹的遍歷類似,從圖中某一個頂點出發(fā)訪遍圖中其余頂點抖苦,且使每一個頂點緊被訪問一次毁菱,這一過程叫圖的遍歷

深度優(yōu)先搜索

鄰接矩陣深度優(yōu)先搜索

/*
 鄰接矩陣深度優(yōu)先搜索
 */

Boolean visited[MAX_VERTEX_NUM];

void DFS(MGraph G, int v) {
    visited[v] = TRUE;
    
    printf("%c ", G.vexs[v]);
    for (int i = 0; i < G.numNodes; i++) {
        if (!visited[i] && G.arc[v][i] == 1) {
            DFS(G, i);
        }
    }
}

void DFSTraverse(MGraph G) {
    for (int i = 0; i < G.numNodes; i++) {
        visited[i] = FALSE;
    }
    
    for (int i = 0; i < G.numNodes; i++) {
        if (!visited[i]) {
            DFS(G, i);
        }
    }
    printf("\n");
}

鄰接表深度優(yōu)先搜索

/*
 鄰接表深度優(yōu)先搜索
 */

BOOL visited[MAX_VERTEX_NUM];

void DFS(ALGraph G, int v) {
    visited[v] = true;
    printf("%c", G.adjlist[v].data);
    
    ArcNode *p;
    p = G.adjlist[v].firstedge;
    
    while (p) {
        if (!visited[p->adjvex]) {
            DFS(G, p->adjvex);
        }
        p = p->nextArc;
    }
}

void DFSTraverse(ALGraph G) {
    for (int i = 0; i < G.node_num; i++) {
        visited[i] = false;
    }
    
    for (int i = 0; i < G.node_num; i++) {
        if (!visited[i]) {
            DFS(G, i);
        }
    }
    printf("\n");
}

分析上述算法锌历,在遍歷圖時贮庞,對圖中每個頂點至多調用一次DFS函數(shù),因為一旦某個頂點被標志成已被訪問究西,就不再從它出發(fā)進行搜索窗慎。因此,遍歷圖的過程實質上是對每個頂點查找其鄰接點的過程卤材。其耗費的時間則取決于所采用的存儲結構遮斥。

  • 當用二維數(shù)組表示鄰接矩陣圖的存儲結構時,查找每個頂點的鄰接點所需要的時間為O(n^2),其中n為圖中頂點數(shù)商膊。
  • 當用鄰接表作圖的存儲結構時伏伐,找鄰接點所需要的時間為O(e)宠进,其中e為無向圖中邊的數(shù)或有向圖中弧的數(shù)晕拆,因此,當以鄰接表作為存儲結構時,深度優(yōu)化搜索遍歷圖的時間浮渣度為O(n + e)实幕。

廣度優(yōu)先搜索

鄰接矩陣的廣度優(yōu)先搜索

/*
鄰接矩陣廣度優(yōu)先搜索
*/

void BFSTraverse(MGraph G) {
    for (int i = 0; i < G.numNodes; i++) {
        visited[i] = FALSE;
    }
    
    SqQueue Q;
    InitQueue(&Q);
    
    for (int i = 0; i < G.numNodes; i++) {
        if (!visited[i]) {
            visited[i] = TRUE;
            printf("%c ", G.vexs[i]);
            
            EnQueue(&Q, i);
            while (!QueueEmpty(Q)) {
                DeQueue(&Q, &i);
                for (int j = 0; j < G.numNodes; j++) {
                    if (G.arc[i][j] == 1 && !visited[j]) {
                        visited[j] = TRUE;
                        printf("%c ", G.vexs[j]);
                        EnQueue(&Q, j);
                    }
                }
            }
        }
    }
}

鄰接表的廣度優(yōu)先搜索

/*
鄰接表廣度優(yōu)先搜索
*/

void BFSTraverse(ALGraph G) {
    for (int i = 0; i < G.node_num; i++) {
        visited[i] = FALSE;
    }
    
    SqQueue Q;
    InitQueue(&Q);
    ArcNode *p;
    
    for (int i = 0; i < G.node_num; i++) {
        if (!visited[i]) {
            visited[i] = TRUE;
            printf("%c ", G.adjlist[i].data);
            
            EnQueue(&Q, i);
            while (!QueueEmpty(Q)) {
                DeQueue(&Q, &i);
                p = G.adjlist[i].firstedge;
                while (p) {
                    if (!visited[p->adjvex]) {
                        visited[p->adjvex] = TRUE;
                        printf("%c ", G.adjlist[p->adjvex].data);
                        EnQueue(&Q, p->adjvex);
                    }
                    p = p->nextArc;
                }
            }
        }
    }
}

分析上述算法吝镣,每個頂點至多進一次隊列。遍歷圖的過程實質上是通過邊和弧找鄰接點的過程昆庇。因此廣度優(yōu)先搜索遍歷圖的時間復雜度和深度優(yōu)先搜索遍歷相同末贾,兩者不同之處僅僅在于對頂點訪問的順序不同。


Demo:https://github.com/ShoukaiWang/DataStructuresAndAlgorithms

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末整吆,一起剝皮案震驚了整個濱河市拱撵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌表蝙,老刑警劉巖拴测,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異府蛇,居然都是意外死亡集索,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門汇跨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來务荆,“玉大人,你說我怎么就攤上這事穷遂『埃” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵蚪黑,是天一觀的道長浦箱。 經(jīng)常有香客問我,道長祠锣,這世上最難降的妖魔是什么酷窥? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮伴网,結果婚禮上蓬推,老公的妹妹穿的比我還像新娘。我一直安慰自己澡腾,他們只是感情好沸伏,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著动分,像睡著了一般毅糟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上澜公,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天姆另,我揣著相機與錄音,去河邊找鬼。 笑死迹辐,一個胖子當著我的面吹牛蝶防,可吹牛的內容都是我干的。 我是一名探鬼主播明吩,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼间学,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了印荔?” 一聲冷哼從身側響起低葫,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仍律,沒想到半個月后氮采,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡染苛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年鹊漠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茶行。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡躯概,死狀恐怖,靈堂內的尸體忽然破棺而出畔师,到底是詐尸還是另有隱情娶靡,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布看锉,位于F島的核電站姿锭,受9級特大地震影響,放射性物質發(fā)生泄漏伯铣。R本人自食惡果不足惜呻此,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腔寡。 院中可真熱鬧焚鲜,春花似錦、人聲如沸放前。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凭语。三九已至葱她,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間似扔,已是汗流浹背吨些。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工搓谆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锤灿。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像辆脸,于是被迫代替她去往敵國和親但校。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350