1. 程式人生 > >資料結構——圖—概念和儲存(鄰接矩陣,鄰接表)

資料結構——圖—概念和儲存(鄰接矩陣,鄰接表)

圖的概念

為什麼要有圖

在學習圖之前我們應該學習了,線性表和樹;但是我們有沒有考慮過為什麼要有圖,線性表和圖的侷限性優勢上面呢?
線性表他僅僅侷限於一個直接前驅和一個直接後繼的關係。
樹呢?樹也僅僅只能有一個直接前驅也就是父節點。

那麼這種多對多的關係怎麼處理麼? 這裡我們就用到了圖

圖的概念

這裡寫圖片描述
上面圖片就是一個圖
線上性表中我們把資料元素叫做元素,在樹種叫做結點,在圖中我們把他叫做頂點(Vertex);
兩個頂點之間的連線叫做邊;
有方向的叫做有向邊,沒方向的叫做無向邊;
邊上的數字代表邊的權值,當圖上有權值時他也叫做網
這些基本概念足以看懂一個圖了那麼上面怎麼儲蓄一個圖呢?

圖的儲存-鄰接矩陣

鄰接矩陣的概念

鄰接矩陣,它是什麼形式呢?
形式就是我上面給書的圖旁邊的那個圖;

首先 把圖中的各個頂點排成一個行列式
有邊的寫1(或者邊上的權值)沒有的寫無窮大(現在的圖中我們不考慮頂點和頂點自己邊)。
為什麼要無窮大而不取0 呢因為有的邊上的權值可能是0

鄰接矩陣的定義

/*
這裡首先定義一個vexs陣列來存放各個頂點,然後建立arc以頂點數為行和列的行列式 
vexs陣列也就是沒有顯示出來實際行列式的第0行和第0列
*/
typedef struct{

    char vexs[MAXSIZE];//頂點表
    int arc[MAXSIZE][MAXSIZE];//領接矩陣可以看成邊表
int N_v,N_e ; //定義頂點數字和邊數 }M_g;

鄰接矩陣建立

/*
這個函式大概意思
就是首先接收到頂點數個邊數,用頂點數為矩陣的長寬來初始化矩陣
然後接收某兩個頂點之間的關係,改變矩陣中的預設值
*/
void onCreateM_g(M_g *x)
{
    int i,j,k,w;
    printf("請輸入頂點數和邊數\n");
    scanf("%d%d",&x->N_v,&x->N_e);
    printf("請依次各輸入頂點\n");
    for(i=0;i < x->N_v;i++)
    {
        scanf("%d
"
,&x->vexs[i]); // puts("sdsdsds"); } // for(i = 0;i < x->N_v;i++){ for(j = 0;j < x->N_v;j++) { x->arc[i][j] = FALL; } } for( k = 0; k < x->N_e;k++) { printf("請輸入(vi,vj)的上標i,下標j 和權 w \n"); scanf("%d%d%d",&i,&j,&w); x->arc[i][j] = w; x->arc[j][i] = x->arc[i][j]; } }

實現領接矩陣


#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#define FALL 99999
//用99999來代表無窮大

/*
這裡首先定義一個vexs陣列來存放各個頂點,然後建立arc以頂點數為行和列的行列式 
vexs陣列也就是沒有顯示出來實際行列式的第0行和第0列
*/
typedef struct{

    char vexs[MAXSIZE];//頂點表
    int arc[MAXSIZE][MAXSIZE];//領接矩陣可以看成邊表
    int N_v,N_e ;  //定義頂點數字和邊數

}M_g;
//實現
void onCreateM_g(M_g *x)
{
    int i,j,k,w;
    printf("請輸入頂點數和邊數\n");
    scanf("%d%d",&x->N_v,&x->N_e);
    printf("請依次各輸入頂點\n");
    for(i=0;i < x->N_v;i++)
    {
        scanf("%d",&x->vexs[i]);
//        puts("sdsdsds");

    }
//
    for(i = 0;i < x->N_v;i++){
       for(j = 0;j < x->N_v;j++)
        {
          x->arc[i][j] = FALL;

        }
    }
    for( k = 0; k < x->N_e;k++)
          {
              printf("請輸入(vi,vj)的上標i,下標j 和權 w \n");
              scanf("%d%d%d",&i,&j,&w);
              x->arc[i][j] = w;
              x->arc[j][i] = x->arc[i][j];

          }

}
void  ergodic (M_g *x)
{   int i,j;
    for(i=0;i< x->N_v;i++)
    {
        for(j=0;j<x->N_v;j++)
        {
             printf("%d\t",x->arc[i][j]);
        }
        putchar('\n');
    }



}
int main(){
    M_g *p;
    p = (M_g*)malloc(sizeof(M_g));
    onCreateM_g(p);
    ergodic (p);

return 0;

}


/*
4 4
0 1 2 3
0 1 56
0 2 34
0 3 78
2 3 25
*/
//複製上面的輸入進去檢視輸出

圖的儲存-鄰接表

圖b

鄰接表的基本概念

1.為什麼要有鄰接表
可以想象如果有一個圖他的頂點特別多而邊只有一個?那麼是不是用鄰接矩陣特別浪費記憶體呢?
所以需要鄰接表(不同於鄰接表主要以連結串列形式實現)
2.怎麼理解鄰接表呢?
首先看上面b圖,最邊上的表我們給他起名——頂點表;其他的我們給另外的變我們給他起名邊表;
3.頂點表
頂點表用來開始儲存圖中的各個頂點,可以看到我們給頂點表開闢了兩個域,第一個域存放了頂點了第二個域呢?
我們可以看出來頂點表的第二個域指向了邊表,所以在頂點表的第二個域裡面我們存放邊表型別讓他變成邊表的頭結點指向邊表;
4.邊表
上圖中邊表有兩個域一個存放和頂點邊中有關係的頂點,另外一個也就是他的NEXT域和連結串列一樣在後面沒有時他指向為空,其實還可以給他加第三個域比如權值等等這個很簡單了
這樣各個就形成了一個鄰接表了

鄰接表的實現

首先是定義這個結構體型別

/*
下面程式碼的總體意思就是先定義邊表我們給了他3個域分別是頂點域 權值域和next域
在定義頂點表 他有兩個域分別是頂點域和代表邊表頭結點的first_v域
最後我們定義一個頂點表的順序表型別。類似於圖b中最左邊的那個順序表
這樣一個鄰接表就定義好了
*/


typedef int VertexType;/*頂點型別有使用者定義*/
typedef struct EdgeNode//定義邊表
{
    int adjvex;//鄰接點域 裡面放各個和頂點表對應節點有關的頂點
    int weight;//權值
    struct EdgeNode *next;

}EdgeNode;
typedef struct VertexNode /*定義頂點表*/
{
    VertexType data;//頂點域,儲存頂點資訊
     EdgeNode *first_v;//邊表的頭指標
}VertexNode;
typedef struct   //鄰接表
{
   VertexNode adjlist[M]; //用於存放頭結點和頂點的順序表;
   int n_v,n_e;

}LinkdeGraph;

建立鄰接表

/*
下面程式碼的主要含義是
1.先獲取到圖中的頂點數和邊數,然後以頂點數為最大建立頂點表陣列並且初始化
2.然後定義一個邊表型別,輸入某個邊上的兩個頂點,並且用頭插發插入
邊的表示(vi vj)表示連線vi vj的邊
3.函式解釋
大概意思就是接收到邊上的兩個頂點後,把和i有邊的j節點用頭插發插在頂點表中有i頂點那一段之後;

void onCreate_L(LinkdeGraph *p,int c)//等於0表示建立為無向圖
{
    int i,j,k;
    EdgeNode *e;
    printf("請輸入頂點數和邊數:\n");
    scanf("%d%d",&p->n_v,&p->n_e);
    printf("請依次輸入頂點\n");
    for(i = 0; i < p->n_v; i++ )//輸入頂點資訊,建立頂點表

    {
        scanf("%d",&p->adjlist[i].data);//輸入頂點資訊
        p->adjlist[i].first_v = NULL;   //初始狀態預設邊表為空

    }

    for(k = 0 ; k < p->n_e;k++)//建立邊表
    {
        printf("請輸入邊(vi,vj)上的頂點序號:\n");
        scanf("%d%d",&i,&j);//輸入邊vivj上的頂點序號
        e = (EdgeNode*)malloc(sizeof(EdgeNode));//開闢邊表的空間
        /*
                頭插法
        */
        e->adjvex = j; //頂點為j
        e->next = p->adjlist[i].first_v;//將邊表e作為頭指標放進頂點表的first域
        p->adjlist[i].first_v = e;
        if(c == 0)
            {
                 e = (EdgeNode*)malloc(sizeof(EdgeNode));
                 e ->adjvex = i;
                 e->next = p->adjlist[j].first_v;
                 p->adjlist[j].first_v = e;

            }


    }



}

實現領接表

#include<stdio.h>
#include<stdlib.h>
#define M 20
typedef int VertexType;/*頂點型別有使用者定義*/
typedef struct EdgeNode//定義邊表
{
    int adjvex;//鄰接點域 裡面放各個和頂點表對應節點有關的頂點
    int weight;//權值
    struct EdgeNode *next;

}EdgeNode;
typedef struct VertexNode /*定義頂點表*/
{
    VertexType data;//頂點域,儲存頂點資訊
     EdgeNode *first_v;//邊表的頭指標
}VertexNode;
typedef struct   //鄰接表
{
   VertexNode adjlist[M]; //用於存放頭結點和頂點的順序表;
   int n_v,n_e;

}LinkdeGraph;
//建立鄰接表
void onCreate_L(LinkdeGraph *p,int c)//等於0表示建立為無向圖
{
    int i,j,k;
    EdgeNode *e;
    printf("請輸入頂點數和邊數:\n");
    scanf("%d%d",&p->n_v,&p->n_e);
    printf("請依次輸入頂點\n");
    for(i = 0; i < p->n_v; i++ )//輸入頂點資訊,建立頂點表

    {
        scanf("%d",&p->adjlist[i].data);//輸入頂點資訊
        p->adjlist[i].first_v = NULL;   //初始狀態預設邊表為空

    }

    for(k = 0 ; k < p->n_e;k++)//建立邊表
    {
        printf("請輸入邊(vi,vj)上的頂點序號:\n");
        scanf("%d%d",&i,&j);//輸入邊vivj上的頂點序號
        e = (EdgeNode*)malloc(sizeof(EdgeNode));//開闢邊表的空間
        /*
                頭插法
        */
        e->adjvex = j; //頂點為j
        e->next = p->adjlist[i].first_v;//將邊表e作為頭指標放進頂點表的first域
        p->adjlist[i].first_v = e;
        if(c == 0)
            {
                 e = (EdgeNode*)malloc(sizeof(EdgeNode));
                 e ->adjvex = i;
                 e->next = p->adjlist[j].first_v;
                 p->adjlist[j].first_v = e;

            }


    }



}
int main(){
    int c = 0;
    LinkdeGraph *p;
    p = (LinkdeGraph*)malloc(sizeof(LinkdeGraph));
    onCreate_L(p,c);


return 0;

}