1. 程式人生 > >資料結構之--圖的講解與C語言實現

資料結構之--圖的講解與C語言實現

資料結構–圖

圖是研究資料元素之間的多對多的關係。在這種結構中,任意兩個元素之間可能存在關係。即結點之間的關係可以是任意的,圖中任意元素之間都可能相關。圖的應用極為廣泛,已滲入到諸如語言學、邏輯學、物理、化學、電訊、電腦科學以及數學的其它分支。

圖的定義

一個圖(G)定義為一個偶對(V,E) ,記為G=(V,E) 。其中: V是頂(Vertex)的非空有限集合,記為V(G);E是無序集V&V的一個子集,記為E(G) ,其元素是圖的弧(Arc)。將頂點集合為空的圖稱為空圖。其形式化定義為:G=(V ,E),V={v|v in data object},
E={(v,w)| v,w in V and p(v,w)},P(v,w)表示從頂點v到頂點w有一條直接通路。

圖的結構

圖的儲存結構比較複雜,其複雜性主要表現在:
任意頂點之間可能存在聯絡,無法以資料元素在儲存區中的物理位置來表示元素之間的關係。
圖中頂點的度不一樣,有的可能相差很大,若按度數最大的頂點設計結構,則會浪費很多儲存單元,反之按每個頂點自己的度設計不同的結構,又會影響操作。
圖的常用的儲存結構有:鄰接矩陣、鄰接連結串列、十字連結串列、鄰接多重表和邊表。
這裡寫圖片描述

在圖的鄰接連結串列表示中,所有頂點結點用一個向量 以順序結構形式儲存,可以隨機訪問任意頂點的連結串列,該向量稱為表頭向量,向量的下標指示頂點的序號。
用鄰接連結串列儲存圖時,對無向圖,其鄰接連結串列是唯一的,對有向圖,其鄰接連結串列有兩種形式。
這裡寫圖片描述


這裡寫圖片描述

圖的遍歷

深度優先搜尋(DFS):深度優先搜尋(Depth First Search–DFS)遍歷類似樹的先序遍歷,是樹的先序遍歷的推廣。
設初始狀態時圖中的所有頂點未被訪問,則:
⑴ :從圖中某個頂點vi出發,訪問vi;然後找到vi的一個鄰接頂點vi1 ;
⑵:從vi1出發,深度優先搜尋訪問和vi1相鄰接且未被訪問的所有頂點;
⑶:轉⑴ ,直到和vi相鄰接的所有頂點都被訪問為止
⑷ :繼續選取圖中未被訪問頂點vj作為起始頂點,轉(1),直到圖中所有頂點都被訪問為止。
廣度優先搜尋(BFS):廣度優先搜尋(Breadth First Search–BFS)遍歷類似樹的按層次遍歷的過程。
設初始狀態時圖中的所有頂點未被訪問,則:
⑴ :從圖中某個頂點vi出發,訪問vi;
⑵:訪問vi的所有相鄰接且未被訪問的所有頂點vi1,vi2,…,vim;
⑶:以vi1,vi2, …,vim的次序,以vij(1≦j≦m)依此作為vi ,轉⑴;
⑷ :繼續選取圖中未被訪問頂點vk作為起始頂點,轉⑴,直到圖中所有頂點都被訪問為止。

程式碼實現–C語言實現

Graph.c

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "Queue.h"

#define MAX_VERTEX_NUM 10
#define INFINITY 32768
typedef enum{DG,DN,UDG,UDN} GraphKind;

#define ERROR 0
#define TRUE 1

typedef int status;
typedef char ElemType;


typedef struct MNode{
    ElemType vertex[MAX_VERTEX_NUM];  /*頂點*/
    int Arc[MAX_VERTEX_NUM][MAX_VERTEX_NUM];  /*弧*/
    int vexnum,arcnum;  /*弧個數,頂點個數*/
    GraphKind kind;
}GraphNode,*Graph;

int IsRead[MAX_VERTEX_NUM];  //標記頂點是否被遍歷過

/*初始化*/
status Init(Graph *G){
    (*G) = (GraphNode *)malloc(sizeof(GraphNode));
    (*G)->vexnum = 0;
    (*G)->arcnum = 0;
    if((*G))return TRUE;
    else{
        printf("初始化出錯...\n");
    }
    return ERROR;
}

/*查詢位置*/
void FindPos(Graph G,char a,char b,int *pos1,int *pos2){
    int i = 0;
    *pos1 = -1; *pos2 = -1; //初值
    for(i = 0; i<G->vexnum;i++){
        if(G->vertex[i]==a){
            (*pos1) = i;
            continue;
        }else if(G->vertex[i] == b){
            (*pos2) = i;
            continue;
        }
    }
}

/*建立圖*/
void BuildGraph(Graph G){
    int choice = 0,num = 0,pos1,pos2,i,j,weight;
    char a,b,ch;
    printf("請選擇建立的圖的型別:1:有向圖,2:有向網,3:無向圖,無向網:\n");
    scanf("%d",&choice);
    getchar();
    printf("\n");  //下一行
    if(choice == 1){  //有向圖

        for(i = 0;i<MAX_VERTEX_NUM;i++){ //初始化弧
            for(j = 0;j<MAX_VERTEX_NUM;j++){
                G->Arc[i][j] = 0;
            }
        }
        G->kind = DG; //設定圖的型別

        printf("請輸入頂點(不超過10個,以#結束):\n");
        scanf("%c",&ch);
        while(ch!='#' && num <10){
            G->vertex[num] = ch;
            scanf("%c",&ch);
            num++;
        }
        G->vexnum = num;  //頂點個數

        getchar();  //清除鍵盤緩衝區

        printf("請輸入對應的弧以a->b格式輸入(以#->#結束):\n");
        scanf("%c->%c",&a,&b);
        while(a!='#' && b!='#'){
            printf("%c,%c",a,b);
            FindPos(G,a,b,&pos1,&pos2);
            printf("位置a:%d,位置b:%d\n",pos1,pos2);
            if(pos1!= -1 && pos2!= -1){  //忽略不存在的頂點
                G->Arc[pos1][pos2] = 1;
                G->arcnum++;
            }
            scanf("%c->%c",&a,&b);
        }
        getchar(); //清空
    }
    else if(choice==2){ //有向網

        num = 0;  //個數初始化
        for(i = 0;i < MAX_VERTEX_NUM; i++){ //初始化弧
            for(j = 0;j<MAX_VERTEX_NUM; j++){
                G->Arc[i][j] = INFINITY;
            }
        }
        G->kind = DN; //設定圖的型別

        printf("請輸入頂點(不超過10個,以#結束):\n");
        scanf("%c",&ch);
        while(ch!='#' && num <10){
            G->vertex[num] = ch;
            scanf("%c",&ch);
            num++;
        }
        G->vexnum = num;  //頂點個數

        getchar();  //清除鍵盤緩衝區

        printf("請輸入對應的弧以a->b:weight格式輸入(以#->#:0結束):\n");
        scanf("%c->%c:%d",&a,&b,&weight);
        while(a!='#' && b!='#'){
            printf("%c,%c",a,b);
            FindPos(G,a,b,&pos1,&pos2);
            printf("位置a:%d,位置b:%d\n",pos1,pos2);
            if(pos1!= -1 && pos2!= -1){  //忽略不存在的頂點
                G->Arc[pos1][pos2] = weight;
                G->arcnum++;
            }
            scanf("%c->%c:%d",&a,&b,&weight);
        }
        getchar(); //清空
    }
    else if(choice == 3){//無向圖

        num = 0;
        for(i = 0;i<MAX_VERTEX_NUM;i++){ //初始化弧
            for(j = 0;j<MAX_VERTEX_NUM;j++){
                G->Arc[i][j] = 0;
            }
        }
        G->kind = UDG; //設定圖的型別

        printf("請輸入頂點(不超過10個,以#結束):\n");
        scanf("%c",&ch);
        while(ch!='#' && num <10){
            G->vertex[num] = ch;
            scanf("%c",&ch);
            num++;
        }
        G->vexnum = num;  //頂點個數

        getchar();  //清除鍵盤緩衝區

        printf("請輸入對應的弧以a-b格式輸入(以#-#結束):\n");
        scanf("%c-%c",&a,&b);
        while(a!='#' && b!='#'){
            printf("%c,%c",a,b);
            FindPos(G,a,b,&pos1,&pos2);
            printf("位置a:%d,位置b:%d\n",pos1,pos2);
            if(pos1!= -1 && pos2!= -1){  //忽略不存在的頂點
                G->Arc[pos1][pos2] = 1;
                G->Arc[pos2][pos1] = 1;
                G->arcnum++;
            }
            scanf("%c-%c",&a,&b);
        }
        getchar(); //清空
    }
    else if(choice == 4){ //無向網

        num = 0;  //個數初始化
        for(i = 0;i < MAX_VERTEX_NUM; i++){ //初始化弧
            for(j = 0;j<MAX_VERTEX_NUM; j++){
                G->Arc[i][j] = INFINITY;
            }
        }
        G->kind = DN; //設定圖的型別

        printf("請輸入頂點(不超過10個,以#結束):\n");
        scanf("%c",&ch);
        while(ch!='#' && num <10){
            G->vertex[num] = ch;
            scanf("%c",&ch);
            num++;
        }
        G->vexnum = num;  //頂點個數

        getchar();  //清除鍵盤緩衝區

        printf("請輸入對應的弧以a-b:weight格式輸入(以#-#:0結束):\n");
        scanf("%c->%c:%d",&a,&b,&weight);
        while(a!='#' && b!='#'){
            printf("%c,%c",a,b);
            FindPos(G,a,b,&pos1,&pos2);
            printf("位置a:%d,位置b:%d\n",pos1,pos2);
            if(pos1!= -1 && pos2!= -1){  //忽略不存在的頂點
                G->Arc[pos1][pos2] = weight;
                G->Arc[pos2][pos1] = weight;
                G->arcnum++;
            }
            scanf("%c-%c:%d",&a,&b,&weight);
        }
        getchar(); //清空
    }
    else {  //非法輸入的選擇
        printf("輸入非法,請輸入正確的數字!!\n");
        return;
    }
}

void DepthFS(Graph G,int pos){ /*深度優先搜尋for圖*/
    int i = 0,j = 0;
    if(IsRead[pos] == 0){
        IsRead[pos] = 1; //從第一個開始
        printf("遍歷頂點%c\n",G->vertex[pos]);
    }

    for(i = 0;i<G->vexnum;i++){
        if(G->Arc[pos][i] == 1 && IsRead[i] ==0){  //存在弧
            DepthFS(G,i);
        }
    }
}

void DepthFS1(Graph G,int pos){ /*深度優先搜尋for網*/
    int i = 0,j = 0;
    if(IsRead[pos] == 0){
        IsRead[pos] = 1; //從第一個開始
        printf("遍歷頂點%c\n",G->vertex[pos]);
    }

    for(i = 0;i<G->vexnum;i++){
        if(G->Arc[pos][i] !=INFINITY && IsRead[i] ==0){  //存在弧且未被遍歷
            DepthFS1(G,i);
        }
    }
}

/*深度優先搜尋*/
void DFS(Graph G){
    if(G->kind == DG || G->kind == UDG){  //圖
        DepthFS(G,0);
    }else if(G->kind == DN || G->kind == UDN){ //網
        DepthFS1(G,0);
    }
}

void BFS1(Graph G,int pos){ //廣度優先搜尋for圖
    int i = 0,temp;
    Queue Q;
    InitQueue(&Q);
    for(i = 0; i <G->vexnum;i++){  //清零
        IsRead[i] = 0;
    }
    if(IsRead[pos] ==0){
        IsRead[pos] = 1;
        printf("遍歷頂點:%c\n",G->vertex[pos]);
    }
    EnterQueue(Q,pos);
    while(!isEmpty(Q)){//當佇列不為空
        OutQueue(Q,&temp);
        for(i = 0; i< G->vexnum;i++){
            if(G->Arc[temp][i] == 1 && IsRead[i] == 0){
                IsRead[i] = 1;
                printf("遍歷頂點:%c\n",G->vertex[i]);
                EnterQueue(Q,i);
            }
        }
    }
    free(Q);
}

void BFS2(Graph G,int pos){ //廣度優先搜尋for圖
    int i = 0,temp;
    Queue Q;
    InitQueue(&Q);
    for(i = 0; i <G->vexnum;i++){  //清零
        IsRead[i] = 0;
    }
    if(IsRead[pos] ==0){
        IsRead[pos] = 1;
        printf("遍歷頂點:%c\n",G->vertex[pos]);
    }
    EnterQueue(Q,pos);
    while(!isEmpty(Q)){//當佇列不為空
        OutQueue(Q,&temp);
        for(i = 0; i< G->vexnum;i++){
            if(G->Arc[temp][i] != INFINITY && IsRead[i] == 0){
                IsRead[i] = 1;
                printf("遍歷頂點:%c\n",G->vertex[i]);
                EnterQueue(Q,i);
            }
        }
    }
    free(Q);
}

void BFS(Graph G){
    if(G->kind == DG || G->kind == UDG){  //圖
        BFS1(G,0);
    }else if(G->kind == DN || G->kind == UDN){ //網
        BFS2(G,0);
    }
}

void main(){
    int i = 0;
    Graph G = NULL;
    Init(&G);
    BuildGraph(G);
    for(i = 0; i<MAX_VERTEX_NUM; i++){
        IsRead[i] = 0;  //未被遍歷
    }
    printf("\n深度優先搜尋:\n");
    DFS(G,0);
    printf("\n廣度優先搜尋:\n");
    BFS(G,0);

}

Queue.h

#include <stdio.h>
#include <stdlib.h>

#define TRUE 1
#define ERROR 0
#define MAX_SIZE 20

typedef int status;

/*定義佇列*/
typedef struct QNode{
    int Qarr[MAX_SIZE];
    int tail,head;
    int size;
}QNode,*Queue;

void InitQueue(Queue *Q){
    (*Q) = (QNode *)malloc(sizeof(QNode));
    if(*Q){
        (*Q)->size = 0;
        (*Q)->head = 0;
        (*Q)->tail = 0;
    }
}

/*入佇列*/
void EnterQueue(Queue Q, int data){
    if((Q->tail +1) % MAX_SIZE == Q->head){
        printf("佇列已經滿!!\n");
        return;
    }
    Q->Qarr[Q->tail] = data;
    Q->size++;
    Q->tail = (Q->tail +1) % MAX_SIZE;
    return;
}

void OutQueue(Queue Q, int *data){
    if(Q->head == Q->tail){
        printf("佇列為空!!\n");
        return;
    }
    (*data) = Q->Qarr[Q->head];
    Q->size--;
    Q->head = (Q->head +1) % MAX_SIZE;
    return;
}

int isEmpty(Queue Q){
    if(Q->head == Q->tail)return 1;
    return 0;
}