有向圖的簡單路徑求解問題(C/C++)

題目:有向圖的簡單路徑求解問題

一、課題內容和要求
給定一個有向圖G兩個頂點a和b,試編寫算法求a到b的簡單路徑的數量,並分別輸出最短的簡單路徑和最長的簡單路徑。
在這裏插入圖片描述

頭文件:

#ifndef PATH_HEADER_
#define PATH_HEADER_
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define ERROR 0
#define OK 1
#define Overflow 2   //Overflow表示上溢出
#define Underflow 3   //Underflow表示下溢出
#define NotPresent 4  //NotPresent表示元素不存在
#define Duplicate 5     //Duplicate 表示有重複的元素
#define STLEN 5
static int sum = 0;  //統計有幾條簡單路徑
static int min_number = 100;  //假設此圖最多包含100個頂點
typedef int Status;
typedef int ElemType;
typedef struct eNode
{
    int adjVex;   //與任意頂點u相鄰接的頂點
    ElemType w;     //邊的權值
    struct eNode* nextArc;  //指向下一個邊節點
}ENode;
typedef struct lGraph
{
    int n;      //圖的當前頂點數
    int e;          //圖的當前邊數
    ENode **a;      //指向一維指針數組
}LGraph;
 Status Init(LGraph *lg, int nSize);
 void Destroy(LGraph *lg);
 Status Exist(LGraph *lg, int u, int v);
 Status Insert(LGraph *lg, int u, int v,ElemType w); 
 Status Remove(LGraph *lg, int u, int v);
 void DFS(int v, int length, int visited[],LGraph *Lg);
 void DFSGraph(LGraph *Lg);
#endif

在這裏插入圖片描述

頭文件實現部分

#include <stdio.h>
#include "pathheader.h"
int min = INT_MAX;
int max = INT_MIN;
/**
 * 1、初始化
 * 鄰接表的初始化運算是構造一個有n個頂點,但不包含邊的鄰接表。使用動態分配數組空間方式構造一個長度爲
 * n的一維指針數組a
 * 
 * 算法步驟:
 *  1、對n、e的值進行初始化
 *  2、動態分配一維指針數組a,若失敗,則返回出錯信息;否則,對數組a中每個元素賦初值NULL。並且返回OK
 */
Status Init(LGraph *lg, int nSize)
{
    lg->n = nSize;
    lg->e = 0;
    lg->a = (ENode **)malloc(nSize * sizeof(ENode *)); //動態生成長度爲n的一維指針數組
    if (!lg->a)
    {
        return ERROR;
    }
    else
    {
        for (int i = 0; i < lg->n; i++)
        {
            lg->a[i] = NULL;
        }
        return OK;
    }
}
/**
 * 2、撤銷
 * 撤銷運算的主要功能是首先釋放鄰接表中每條單鏈表的所有邊節點,然後釋放一維指針數組a的存儲空間,以防止
 * 內存泄露
 * 
 * 算法步驟:
 *  1、釋放鄰接表中每條單鏈表的所有邊節點,即相當於lg->a[i] = NULL;
 *  2、釋放一維指針數組a的存儲空間
 */
void Destroy(LGraph *lg)
{
    ENode *p, *q;
    for (int i = 0; i < lg->n; i++)
    {
        p = lg->a[i];
        q = p;
        while (p)
        {
            p = p->nextArc;
            free(q);
            q = p;
        }
    }
    free(lg->a);
}

/**
 * 3、邊的搜索
 * 若參數u,v無效,則返回ERROR,否則,從a[u]指示的頂點u的單鏈表的第一個邊節點開始,搜索adjVex的值爲v
 * 邊節點,若找到此邊,但會OK,否則返回ERROR
 * 
 * 算法步驟:
 *  1、若參數u、v無效,則返回ERROR
 *  2、指針p指向頂點u的單鏈表的第一個邊節點
 *  3、搜索u的單鏈表中是否存在adjVex的值爲v的邊節點,若找到此邊,返回OK,否則返回ERROR 
 */
Status Exist(LGraph *lg, int u, int v)
{
    ENode *p;
    if (u < 0 || v < 0 || u > lg->n - 1 || v > lg->n - 1 || u == v)
    {
        return ERROR;
    }
    p = lg->a[u];
    while (p && p->adjVex != v)
    {
        p = p->nextArc;
    }
    if (!p)
    {
        return ERROR;
    }
    else
    {
        return OK;
    }
}
/**
 * 4、邊的插入
 * 若參數u、v無效,返回ERROR,反之,從a[u]指示的邊節點開始搜索adjVex的值爲v的邊節點,若找到,說明邊節點
 * <u,v>存在,返回Duplicate,否則,創建邊節點並在u的單鏈表的最前面插入此邊節點。
 * 
 * 算法步驟:
 *  1、若參數u、v無效,返回ERROR
 *  2、搜索u的單鏈表中是否存在adjVex值爲v的邊節點,若找到<u,v>,則返回Duplicate。
 *  3、爲新的邊節點分配存儲空間,將此節點插入u的單鏈表的最前面
 *  4、圖中邊數+1,返回OK
 */
Status Insert(LGraph *lg, int u, int v, ElemType w)
{
    ENode *p;
    if (u < 0 || v < 0 || u > lg->n - 1 || v > lg->n - 1 || u == v)
    {
        return ERROR;
    }
    if (Exist(lg, u, v))
    {
        return Duplicate;
    }
    p = (ENode *)malloc(sizeof(ENode *)); //爲新的節點分配內存
    p->adjVex = v;
    p->w = w;
    p->nextArc = lg->a[u]; //將新的邊節點插入單鏈表的最前面
    lg->a[u] = p;
    lg->e++;
    return OK;
}
/**
 * 5、邊的刪除
 * 若參數u、v無效,返回ERROR,否則,從a[u]指示邊節點開始搜索adjVex值爲v的邊節點,若找到邊<u,v>對應的
 * 邊節點,刪除之。
 * 
 * 算法步驟:
 *  1、若參數u、v無效,返回ERROR
 *  2、指針p指向頂點u的單鏈表的第一個邊節點
 *  3、搜索u的單鏈表中是否存在adjVex的值爲v的邊節點,若沒有找到邊<u,v>,則返回NotPresent
 * ,否則刪除。
 *  4、圖中邊數-1,返回OK
 */
Status Remove(LGraph *lg, int u, int v)
{
    ENode *p, *q;
    if (u < 0 || v < 0 || u > lg->n - 1 || v > lg->n - 1 || u == v)
    {
        return ERROR;
    }
    p = lg->a[u], q = NULL;
    while (p && p->adjVex != v)
    {
        q = p;
        p = p->nextArc;
    }
    if (!p)
    {
        return NotPresent; //p爲空,待刪除邊不存在
    }
    if (q)
    {
        q->nextArc = p->nextArc; //從單鏈表刪除此邊
    }
    else
    {
        lg->a[u] = p->nextArc; //此時爲第一個邊節點就是待刪除邊的情況
    }
    free(p);
    lg->e--;
    return OK;
}
/**
 * 在此採用鄰接表作爲圖的存儲結構,下面分別給出了優先深度遍歷的遞歸函數DFS和深度優先遍歷整個圖的DFSGraph
 * 函數。後者調用前者完成對整個圖的深度優先遍歷。若是非連通圖,執行一次DFS之後,圖中還有頂點未被訪問,需要
 * 再次從未被訪問的頂點出發,重複執行深度優先遍歷,直至圖中所有頂點均被訪問過。
 * 
 * 算法步驟:
 *  1、從頂點v出發,訪問頂點v,並且令visited[v] = 1;
 *  2、依次查找v的所有鄰接點w,若visited[w]的值爲0,則從w出發,深度優先遍歷G
 */
int a, b;
void DFS(int v, int length, int visited[], LGraph *Lg)
{
    ENode *p = NULL;
    if (v == b)
    {
        sum++;
        if (length < min)
        {
            min = length;
        }
        if (length > max)
        {
            max = length;
        }
        return;
    }
    for (p = Lg->a[v]; p; p = p->nextArc)
    {
        if (!visited[p->adjVex] && p)
        {
            visited[p->adjVex] = 1;
            DFS(p->adjVex, length + p->w, visited, Lg);
            visited[p->adjVex] = 0;
        }
    }
}
void DFSGraph(LGraph *Lg)
{
    int *visited = (int *)malloc(Lg->n * sizeof(int)); //動態生成標記數組visited
    for (int i = 0; i < Lg->n; i++)
    {
        visited[i] = 0; //初始化visited數組,令所有頂點都未被訪問過
    }
    printf("請輸入頂點a,b的值:");
    scanf("%d %d", &a, &b); //輸入始點和終點
    visited[a] = 1;         //爲此時此刻加入路徑標記爲1,即路徑不可再走此點
    DFS(a, 0, visited, Lg);
    printf("一共有%d條簡單路徑,其中最長的路徑長度是%d, 最短的路徑是%d\n", sum, max, min);
    free(visited);
}

在這裏插入圖片描述

主函數部分
/**
 * 2020-06-08 pm:20:01
 * 有向圖的簡單路徑求解問題:在一個有向圖中,尋找給定的兩個頂點a、b,求a到b的簡單路徑的數量,並且
 * 給出最短和最長的路徑
 * 關鍵字:有向圖、簡單路徑、最短、最長
 * 參考書:《數據結構》(C語言)慕課版、《大話數據結構》、《啊哈!算法》、《C Primer Plus》
 * 主要功能:初始化圖、爲圖增加邊及其權值、刪除誤增加的邊、進行深度優先遍歷實現簡單路徑的查詢
 * 主要算法:深度優先遍歷、圖的存儲結構之鄰接表、單鏈表的遍歷、增加、刪除
 */ 
#include <stdio.h>
#include "pathheader.h"
 int main()
 {
    LGraph Lg;  //聲明一個圖,名字爲Lg
    int n;  //此圖頂點的個數
    printf("請輸入需要此圖需要的頂點數:");
    scanf("%d",&n);
    getchar();
    int status = Init(&Lg, n);  //初始化圖,爲每個頂點分配內存空間,初始化成功,status爲1,反之返回0
    if(!status)
    {
        printf("沒有成功動態生成長度爲%d的一維指針數組,程序終止運行!\n\n",n);
    }
    else
    {
        printf("帶有%d個頂點的圖已經初始化完成,請插入邊<u,v>及權值w,以構成完整的圖\n\n",n);
    }
    int u, v, w;   //表示頂點u、v和權值w
    int i = 1;  //統計第幾條邊
    char flag[5];//接收輸入
    printf("是否準備爲初始化的圖插入邊(yes/no):");
    gets(flag);
    while(strcmp(flag, "yes") == 0)
    {
        printf("請插入第%d條邊<u,v>及權值w:",i);
        scanf("%d %d %d",&u,&v,&w);
        getchar();
        status = Insert(&Lg, u, v, w);  //檢查插入操作是否成功
        if(status == 5)  
        {
            printf("您輸入的頂點已存在,請重新輸入!\n\n");
            continue;
        }
        else if(status == 0)
        {
            printf("參數u=%d、v=%d無效,請重新輸入!\n\n",u, v);
            continue;
        }
        else
        {
            printf("第%d條邊已經成功插入完成!\n\n",i);
        }
        i++;  //爲即將插入的邊做好準備
        printf("是否繼續插入邊(yes/no):");
        gets(flag);  //接收輸入
        if(strcmp(flag, "yes") != 0)
        {
            printf("帶有%d個頂點的圖一共成功插入%d條邊\n\n",n, i-1);
            break;
        }
    }
    int m = i-1;   //爲了統計還剩下幾條邊
    printf("是否錯誤插入某條邊,請刪除!(yes/no):");
    gets(flag);
    i = 1;
    while(strcmp(flag, "yes") == 0)
    {
        printf("請輸入要刪除第%d條邊<u,v>:",i);
        scanf("%d %d",&u,&v);
        getchar();
        status = Remove(&Lg, u, v);  //調用remove操作,返回刪除操作的狀態
        if(status == 4)
        {
            printf("您輸入的頂點不存在,請重新輸入!\n\n");
            continue;
        }
        else if(status == 0)
        {
            printf("參數u=%d、v=%d無效,請重新輸入!\n\n",u, v);
            continue;
        }
        else
        {
            printf("第%d條邊已經成功刪除完成!\n\n",i);
        }
        i++;//爲繼續刪除邊做好準備
        printf("是否繼續刪除邊(yes/no):");
        gets(flag);
        if(strcmp(flag, "yes") != 0)
        {
            printf("帶有%d個頂點的圖一共刪除了%d條邊,還剩下%d條邊\n\n",n, i-1, m-(i-1));
            break;
        }
    }
    DFSGraph(&Lg);  //調用此圖的深度優先遍歷函數

    Destroy(&Lg);  //釋放內存空間
    return 0;
 }


在這裏插入圖片描述
在這裏插入圖片描述

請需要的小夥伴關注一下動態!你的隨手鼓勵,是我不斷創作的最大動力!感謝🙏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章