1. 程式人生 > >C 圖 鄰接表的基本操作

C 圖 鄰接表的基本操作

文章目錄

基本思路

強烈推薦大家看看,同胞寫的,講的特別清楚
圖 – 我的理解就是若干個節點,再加上他們之間的聯絡;這樣就是兩塊內容,一個放節點,一個放他們的關係。
最直觀就是弄一個二維陣列,行列為1的就是連著的,為0就是沒有。

但是太佔空間了,於是就有了這樣的,前面一列就是所有頂點排列,後面每個頂點跟著的就是和這個頂點連著的節點。
在這裡插入圖片描述
根據這個理解,就可以把鄰接表弄的七七八八了

儲存結構

#define M 100
#define true 1
#define false 0 typedef char Element; typedef int BOOL; //鄰接表的節點 typedef struct Node{ int adj_vex_index; //弧頭的下標,也就是被指向的下標 Element data; //權重值 struct Node * next; //邊指標 }EdgeNode; //頂點節點表 typedef struct vNode{ Element date; //頂點的權值 EdgeNode * firstedge; //頂點下一個是誰? }VertexNode,
Adjlist[M]; //總圖的一些資訊 typedef struct Graph{ Adjlist adjlist; //頂點表 int arc_num; //邊的個數 int node_num; //節點個數 BOOL is_directed; //是不是有向圖 }Graph, *GraphLink;

建立

void creatGraph(GraphLink *g){
    int i, j, k;
    EdgeNode *p;
    
    printf("輸入頂點數目,邊數和有向?:\n"
); scanf("%d %d %d", &(*g)->node_num, &(*g)->arc_num, &(*g)->is_directed); //安排一下頂點陣列,有幾個走幾個,安排的明明白白 printf("輸入頂點資訊:\n"); for (i = 0; i < (*g)->node_num; i++) { getchar(); scanf("%c", &(*g)->adjlist[i].date); (*g)->adjlist[i].firstedge = NULL; } printf("輸入邊資訊:\n"); //再安排下邊,想來想去,還是教程給的最簡便,就是我知道邊的兩端是誰就好了 for (k = 0; k < (*g)->arc_num; k++){ //第一個輸入的是弧尾下標,第二個是弧頭下標 // i ----> j getchar(); scanf("%d %d", &i, &j); //新建一個節點是必須的 p = (EdgeNode *)malloc(sizeof(EdgeNode)); //弧頭的下標 p->adj_vex_index = j; //頭插法插進去,插的時候要找到弧尾,那就是頂點陣列的下標i p->next = (*g)->adjlist[i].firstedge; (*g)->adjlist[i].firstedge = p; //如果無向的話得額外在加這一句,畢竟是實對稱矩陣 if(!(*g)->is_directed) { // j -----> i p = (EdgeNode *)malloc(sizeof(EdgeNode)); p->adj_vex_index = i; p->next = (*g)->adjlist[j].firstedge; (*g)->adjlist[j].firstedge = p; } printf("\n"); } }

列印

void putGragh(GraphLink g){
    int i;
    //遍歷一遍頂點座標,每個再進去走一次
    for (i = 0; i < g->node_num; i++) {
        EdgeNode * p = g->adjlist[i].firstedge;
        while (p) {
            printf("%c->%c ", g->adjlist[i].date, g->adjlist[p->adj_vex_index].date);
            p = p->next;
        }
        printf("\n");
    }
}

廣度遍歷

//廣度遍歷
void BFSTraverse(Graph *g)
{
    int i;
    int tmp;
    EdgeNode *p;
    //初始化佇列
    LinkQueue q;
    initQueue(&q);
    //防一手不連通的圖
    for (i = 0; i < g->node_num; i++) {
        if(!visited[i]){
            visited[i] = true;
            printf("%c ",g->adjlist[i].date);
            
            //佇列插進來一個節點
            InsertQueue(&q, i);
            //佇列不是空的時候就從佇列裡面操作
            while (!isEmpty(&q)) {
                //從佇列取一個就得把和他連著的點都填進去
                DeleteQueue(&q, &tmp);
                //新增和他連著的所有未點亮的點
                p = g->adjlist[tmp].firstedge;
                while (p) {
                    if (!visited[p->adj_vex_index]) {
                        visited[p->adj_vex_index] = true;
                        printf("%c ",g->adjlist[p->adj_vex_index].date);
                        InsertQueue(&q, p->adj_vex_index);
                    }
                    p = p->next;
                }
            }
        }
    }
}

深度遍歷

//得有個陣列記錄一下我走過的點點
int visited[M];
//經典演算法
/*
a| a->b a->d a->c 
b| b->c b->g b->a 
c| c->b c->a 
d| d->a 
e| e->f 
f| f->e 
g| g->b 

從頂點數組裡找,找第一個a,然後記錄了a已經點亮了
接著從a連著的點裡往下找,找到了b,b也被點亮
然後再從b裡往下找,找到c,c點亮
從c往下找,找到b,判斷並pass,又找到a並判斷,c連線的點已經都找過了
再回到b,往下找找到g...

就這樣以此類推
用遞迴很巧妙的解決了這個問題,遞迴真屌!!!
*/

void DFS(Graph *g, int i){
    visited[i] = true;
    printf("%c ",g->adjlist[i].date);
    EdgeNode *p = g->adjlist[i].firstedge;
    while (p) {
        if(visited[p->adj_vex_index] == 0){
            DFS(g, p->adj_vex_index);
        }
        p = p->next;
    }
}
//防一手不連通的圖
void DFSTraverse(Graph *g)
{
    int i;
    for (i = 0; i < g->node_num; i++)
    {
        if (!visited[i])
            DFS(g, i);
    }
}

完整程式碼

//
//  main.c
//  圖鄰接表
//
//  Created by 赫凱 on 2018/10/28.
//  Copyright © 2018 赫凱. All rights reserved.
//

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

#define M 10
#define true 1
#define false 0

////////////
typedef int Elemtype;
typedef struct QNode{
    Elemtype data;
    struct QNode *next;
}QNode, *QueuePrt;

typedef struct {
    QueuePrt front, rear;
}LinkQueue;
//初始化
void initQueue(LinkQueue *q){
    q->front = q->rear = (QueuePrt)malloc(sizeof(QNode));
    if(!q->front)
        exit(0);
    q->front->next = NULL;
}
//插入一個節點
void InsertQueue(LinkQueue *q, Elemtype e){
    QueuePrt p;
    p = (QueuePrt)malloc(sizeof(QNode));
    if(p == NULL)
        exit(0);
    p->data = e;
    p->next = NULL;
    
    //插進去
    q->rear->next = p;
    q->rear = p;
}
//出佇列
void DeleteQueue(LinkQueue *q, Elemtype *e){
    QueuePrt p;
    if( q->front == q->rear ){
        return;
    }
    
    p = q->front->next;
    *e = p->data;
    
    q->front->next = p->next;
    if(q->rear == p)
        q->rear = q->front;
    free(p);
}
//銷燬一個佇列
void DestroyQueue(LinkQueue *q){
    while (q->front) {
        q->rear = q->front->next;
        free(q->front);
        q->front = q->rear;
    }
}
//佇列是否為空
int isEmpty(LinkQueue* a)
{
    if(a->front == a->rear)
        return 1;
    return 0;
}

////////////
typedef char Element;
typedef int BOOL;
//鄰接表的節點
typedef struct Node{
    int adj_vex_index;  //弧頭的下標,也就是被指向的下標
    Element data;       //權重值
    struct Node * next; //邊指標
}EdgeNode;

//頂點節點表
typedef struct vNode{
    Element date;          //頂點的權值
    EdgeNode * firstedge;  //頂點下一個是誰?
}VertexNode, Adjlist[M];

//總圖的一些資訊
typedef struct Graph{
    Adjlist adjlist;       //頂點表
    int arc_num;           //邊的個數
    int node_num;          //節點個數
    BOOL is_directed;      //是不是有向圖
}Graph, *GraphLink;

void creatGraph(GraphLink *g){
    int i, j, k;
    EdgeNode *p;
    
    printf("輸入頂點數目,邊數和有向?:\n");
    scanf("%d %d %d", &(*g)->node_num, &(*g)->arc_num, &(*g)->is_directed);
    
    //安排一下頂點陣列,有幾個走幾個,安排的明明白白
    printf("輸入頂點資訊:\n");
    for (i = 0; i < (*g)->node_num; i++) {
        getchar();
        scanf("%c", &(*g)->adjlist[i].date);
        (*g)->adjlist[i].firstedge = NULL;
    }
    printf("輸入邊資訊:\n");
    //再安排下邊,想來想去,還是教程給的最簡便,就是我知道邊的兩端是誰就好了
    for (k = 0; k < (*g)->arc_num; k++){
        //第一個輸入的是弧尾下標,第二個是弧頭下標
        // i ----> j
        getchar();
        scanf("%d %d", &i, &j);
        
        //新建一個節點是必須的
        p = (EdgeNode *)malloc(sizeof(EdgeNode));
        //弧頭的下標
        p->adj_vex_index = j;
        //頭插法插進去,插的時候要找到弧尾,那就是頂點陣列的下標i
        p->next = (*g)->adjlist[i].firstedge;
        (*g)->adjlist[i].firstedge = p;
        
        //如果無向的話得額外在加這一句,畢竟是實對稱矩陣
        if(!(*g)->is_directed)
        {
            // j -----> i
            p = (EdgeNode *)malloc(sizeof(EdgeNode));
            p->adj_vex_index = i;
            p->next = (*g)->adjlist[j].firstedge;
            (*g)->adjlist[j].firstedge = p;
        }

    }
}

void putGragh(GraphLink g){
    int i;
    //遍歷一遍頂點座標,每個再進去走一次
    for (i = 0; i < g->node_num; i++) {
        EdgeNode * p = g->adjlist[i].firstedge;
        while (p) {
            printf("%c->%c ", g->adjlist[i].date, g->adjlist[p->adj_vex_index].date);
            p = p->next;
        }
        printf("\n");
    }
}

int visited[M];
//深度遍歷
void DFS(Graph *g, int i){
    visited[i] = true;
    printf("%c ",g->adjlist[i].date);
    EdgeNode *p = g->adjlist[i].firstedge;
    while (p) {
        if(visited[p->adj_vex_index] == 0){
            DFS(g, p->adj_vex_index);
        }
        p = p->next;
    }
}

void DFSTraverse(Graph *g)
{
    int i;
    for (i = 0; i < g->node_num; i++)
    {
        if (!visited[i])
            DFS(g, i);
    }
}

//廣度遍歷
void BFSTraverse(Graph *g)
{
    int i;
    int tmp;
    EdgeNode *p;
    //初始化佇列
    LinkQueue q;
    initQueue(&q);
    
    for (i = 0; i < g->node_num; i++) {
        if(!visited[i]){
            visited[i] = true;
            printf("%c ",g->adjlist[i].date);
            
            //佇列插進來一個節點
            InsertQueue(&q, i);
            //佇列不是空的時候就從佇列裡面操作
            while (!isEmpty(&q)) {
                //從佇列取一個就得把和他連著的點都填進去
                DeleteQueue(&q, &tmp);
                //新增和他連著的所有未點亮的點
                p = g->adjlist[tmp].firstedge;
                while (p) {
                    if (!visited[p->adj_vex_index]) {
                        visited[p->adj_vex_index] = true;
                        printf("%c ",g->adjlist[p->adj_vex_index].date);
                        InsertQueue(&q, p->adj_vex_index);
                    }
                    p = p->next;
                }
            }
        }
    }

}

int main(int argc, const char * argv[]) {
    // insert code here...
    
    GraphLink g = (Graph *)malloc(sizeof(Graph));
    //數目:7 6 0
    //頂點資訊:a b c d e f g
    //邊的資訊:0 2 0 3 0 1 4 5 1 6 1 2
    
    creatGraph(&g);
    putGragh(g);
    int i;
    for (i = 0; i < M; i++) {
        visited[i] = false;
    }
    printf("深度優先遍歷:");
    DFSTraverse(g);

    for (i = 0; i < M; i++) {
        visited[i] = false;
    }
    printf("廣度優先遍歷:");
    BFSTraverse(g