題目:有向圖的簡單路徑求解問題
一、課題內容和要求
給定一個有向圖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;
}