看資料結構寫程式碼(36) 圖的鄰接表表示與實現
圖的鄰接表表示法,是為每一個頂點建立一個連結串列,連結串列裡存放著相同弧尾的 弧的資訊,這些連結串列順序存放在陣列中。下面是無向圖g2的鄰接表
鄰接表 比 鄰接矩陣 節省空間,同時 也帶來一些操作上的 不便,例如 看 兩個頂點是否 相鄰,需要 遍歷 連結串列,在 求 無向圖頂點的度時,只需 遍歷 頂點的連結串列,而 求 有向圖 頂點的度 需要 遍歷 整個圖 查詢 弧頭 為這個頂點的 個數。 如果 不想這樣做,可以 建立 逆鄰接表,即 連結串列裡 存放著 相同 弧頭的 弧 的資訊。 下一節 要說的 十字連結串列 類似於這種結構。
下面 上程式碼:
執行截圖:// Graph.cpp : 定義控制檯應用程式的入口點。 //圖的鄰接表 表示法 #include "stdafx.h" #include <cstdlib> #include <climits> #define MAX_VERTEX_NUM 20 #define INFINITY INT_MAX enum E_Graph_Kind { DG = 0,//有向圖 DN,//有向網 UDG,//無向圖 UDN,//無向網 }; struct ArcNode//邊(弧) { int adjVex;//頂點在陣列中的位置 ArcNode * nextAdj; int weight;//權值 }; typedef struct VNode//頂點 { ArcNode * head;//頭指標 char vexName;//頂點名稱 }AdjList[MAX_VERTEX_NUM]; struct Graph//圖 { AdjList list;//鄰接表 int arcNum,vexNum; E_Graph_Kind kind; }; //頂點在陣列中的位置 int vexLocation(Graph g,char vex){ for (int i = 0; i < g.vexNum; i++){ if (g.list[i].vexName == vex){ return i; } } return -1; } ArcNode * getHeadNode(){//獲得頭節點.. ArcNode * node = (ArcNode*)malloc(sizeof(ArcNode)); if (node != NULL){ node->adjVex = -1; node->nextAdj = NULL; node->weight = INFINITY; } return node; } ArcNode * getArcNode(Graph g,char vexName){ ArcNode * node = getHeadNode(); if (node != NULL){ int location = vexLocation(g,vexName); node->adjVex = location; } return node; } void createDG(Graph * graph); void createDN(Graph * graph); void createUDG(Graph * graph); void createUDN(Graph * graph); void graphCreate(Graph * graph){ E_Graph_Kind kind; printf("請輸入要建立的圖的型別(有向圖:0,有向網:1,無向圖:2,無向網:3)\n"); scanf("%d",&kind); switch (kind){ case DG: createDG(graph); break; case DN: createDN(graph); break; case UDG: createUDG(graph); break; case UDN: createUDN(graph); break; default: break; } } //有向圖 void createDG(Graph * g){ g->kind = DG; printf("輸入圖的頂點樹 和 邊(弧)樹\n"); scanf("%d%d%*c",&g->vexNum,&g->arcNum); //構造頂點集 printf("請輸入頂點集\n"); for (int i = 0; i < g->vexNum; i++){ char name; scanf("%c",&name); g->list[i].vexName = name; g->list[i].head = getHeadNode();//頭指標指向頭節點. } //構造頂點關係 fflush(stdin); printf("請輸入頂點的關係\n"); for (int i = 0; i < g->arcNum; i++){ char vex1,vex2; scanf("%c%c%*c",&vex1,&vex2); int location1 = vexLocation(*g,vex1); ArcNode * node = getArcNode(*g,vex2); node->nextAdj = g->list[location1].head->nextAdj; g->list[location1].head->nextAdj = node; } } //有向網.. void createDN(Graph * g){ g->kind = DN; printf("輸入圖的頂點樹 和 邊(弧)樹\n"); scanf("%d%d%*c",&g->vexNum,&g->arcNum); //構造頂點集 printf("請輸入頂點集\n"); for (int i = 0; i < g->vexNum; i++){ char name; scanf("%c",&name); g->list[i].vexName = name; g->list[i].head = getHeadNode(); } //構造頂點關係 fflush(stdin); printf("請輸入頂點的關係\n"); for (int i = 0; i < g->arcNum; i++){ char vex1,vex2; int weight; scanf("%c%c%d%*c",&vex1,&vex2,&weight); int location1 = vexLocation(*g,vex1); ArcNode * node = getArcNode(*g,vex2); node->weight = weight; node->nextAdj = g->list[location1].head->nextAdj; g->list[location1].head->nextAdj = node; } } //無向圖 void createUDG(Graph * g){ g->kind = UDG; printf("輸入圖的頂點樹 和 邊(弧)樹\n"); scanf("%d%d%*c",&g->vexNum,&g->arcNum); //構造頂點集 printf("請輸入頂點集\n"); for (int i = 0; i < g->vexNum; i++){ char name; scanf("%c",&name); g->list[i].vexName = name; g->list[i].head = getHeadNode(); } //構造頂點關係 fflush(stdin); printf("請輸入頂點的關係\n"); for (int i = 0; i < g->arcNum; i++){ char vex1,vex2; scanf("%c%c%*c",&vex1,&vex2); int location1 = vexLocation(*g,vex1); ArcNode * node1 = getArcNode(*g,vex2); node1->nextAdj = g->list[location1].head->nextAdj; g->list[location1].head->nextAdj = node1; int location2 = vexLocation(*g,vex2); ArcNode * node2 = getArcNode(*g,vex1); node2->nextAdj = g->list[location2].head->nextAdj; g->list[location2].head->nextAdj = node2; } } //無向網 void createUDN(Graph * g){ g->kind = UDN; printf("輸入圖的頂點樹 和 邊(弧)樹\n"); scanf("%d%d%*c",&g->vexNum,&g->arcNum); //構造頂點集 printf("請輸入頂點集\n"); for (int i = 0; i < g->vexNum; i++){ char name; scanf("%c",&name); g->list[i].vexName = name; g->list[i].head = getHeadNode(); } //構造頂點關係 fflush(stdin); printf("請輸入頂點的關係\n"); for (int i = 0; i < g->arcNum; i++){ char vex1,vex2; int weight; scanf("%c%c%d%*c",&vex1,&vex2,&weight); int location1 = vexLocation(*g,vex1); ArcNode * node1 = getArcNode(*g,vex2); node1->weight = weight; node1->nextAdj = g->list[location1].head->nextAdj; g->list[location1].head->nextAdj = node1; int location2 = vexLocation(*g,vex2); ArcNode * node2 = getArcNode(*g,vex1); node2->weight = weight; node2->nextAdj = g->list[location2].head->nextAdj; g->list[location2].head->nextAdj = node2; } } void graphDestory(Graph * g){ for (int i = 0; i < g->vexNum; i++){ ArcNode * next = g->list[i].head; while (next != NULL){ ArcNode * freeNode = next; next = next->nextAdj; free(freeNode); } g->list[i].head = NULL; g->list[i].vexName = ' '; } g->vexNum = g->arcNum = 0; } //vex1 和 vex2是否相鄰.. bool grphIsAdj(Graph g,char vex1,char vex2){ int location = vexLocation(g,vex1); ArcNode * next = g.list[location].head->nextAdj;//第一個節點是頭結點的後繼 while (next != NULL){ if (g.list[next->adjVex].vexName == vex2){ return true; } next = next->nextAdj; } return false; } //頂點vex的度. //有向 = 出度 + 入度 //無向 = 出度 int graphDegree(Graph g,char vex){ int degree = 0; int location = vexLocation(g,vex); ArcNode * next = g.list[location].head->nextAdj; while (next != NULL){//出度 degree ++; next = next->nextAdj; } if (g.kind == DG || g.kind == DN){ //有向圖還需要遍歷圖,尋找入度. for (int i = 0; i < g.vexNum; i++){ ArcNode * next = g.list[i].head->nextAdj; while (next != NULL){ if (next->adjVex == location){ degree ++; } next = next->nextAdj; } } } return degree; } //vex 的第一個鄰接點 char firstAdj(Graph g,char vex){ int location = vexLocation(g,vex); ArcNode * next = g.list[location].head->nextAdj; if (next != NULL){ return g.list[next->adjVex].vexName; } return ' '; } //vex1 相對於 vex2 的下一個鄰接點。。。 char nextAdj(Graph g,char vex1,char vex2){ int location = vexLocation(g,vex1); ArcNode * next = g.list[location].head->nextAdj; while (next != NULL){//查詢到 vex2 if (g.list[next->adjVex].vexName == vex2){ break; } next = next->nextAdj; } if (next != NULL){ ArcNode * nextNode = next->nextAdj; if (nextNode != NULL){ return g.list[nextNode->adjVex].vexName; } } return ' '; } //插入頂點 void insertVex(Graph * g,char vex){ if (g->vexNum < MAX_VERTEX_NUM){ g->list[g->vexNum].vexName = vex; g->list[g->vexNum].head = getHeadNode(); g->vexNum++; } } //刪除頂點 void deleteVex(Graph * g,char vex){ int location = vexLocation(*g,vex); //釋放空間 ArcNode * next = g->list[location].head->nextAdj; int delNum = 0; while (next != NULL){ ArcNode * freeNode = next; next = next->nextAdj; free(freeNode); delNum++; } //vex下面的 頂點上移 for (int i = location + 1; i < g->vexNum; i++){ g->list[i-1] = g->list[i]; } g->vexNum --; //刪除與頂點vex 相關的邊(弧)(以及更新 所有節點的 adjVex )要遍歷圖.. for (int i = 0; i < g->vexNum; i++){ ArcNode * next = g->list[i].head->nextAdj; ArcNode * pre = g->list[i].head; while (next != NULL){ if (next->adjVex == location){ ArcNode * freeNode = next; next = next->nextAdj; pre->nextAdj = next; free(freeNode); delNum++; } else { if (next->adjVex > location){//在頂點下面的節點位置要減1 next->adjVex --; } pre = next; next = next->nextAdj; } } } g->arcNum -= delNum;//有向 if (g->kind == UDG || g->kind == UDN){ g->arcNum += delNum/2; } } //插入邊(弧) void insertArc(Graph * g,char vex1,char vex2){ int location1 = vexLocation(*g,vex1); ArcNode * node1 = getArcNode(*g,vex2); node1->nextAdj = g->list[location1].head->nextAdj; g->list[location1].head->nextAdj = node1; //無向圖需要插入另外一邊. if (g->kind == UDG || g->kind == UDN){ int location2 = vexLocation(*g,vex2); ArcNode * node2 = getArcNode(*g,vex1); node2->nextAdj = g->list[location2].head->nextAdj; g->list[location2].head->nextAdj = node2; } g->arcNum ++; } //刪除邊(弧) void deleteArc(Graph * g,char vex1,char vex2){ g->arcNum--; int location1 = vexLocation(*g,vex1); int location2 = vexLocation(*g,vex2); ArcNode * next = g->list[location1].head->nextAdj; ArcNode * pre = g->list[location1].head; while (next != NULL){ if (next->adjVex == location2){ pre->nextAdj = next->nextAdj; free(next); break; } pre = next; next = next->nextAdj; } if (g->kind == UDG || g->kind == UDN ){//無向圖還需要刪除 另外一邊 next = g->list[location2].head->nextAdj; pre = g->list[location2].head; while (next != NULL){ if (next->adjVex == location1){ pre->nextAdj = next->nextAdj; free(next); break; } pre = next; next = next->nextAdj; } } } void printGrahp(Graph g){ for (int i = 0; i < g.vexNum; i++){ printf("%c的鄰接點有:",g.list[i].vexName); ArcNode * next = g.list[i].head->nextAdj; while (next != NULL){ printf("%c",g.list[next->adjVex].vexName); next = next->nextAdj; } printf("\n"); } } int _tmain(int argc, _TCHAR* argv[]) { Graph g; graphCreate(&g); printGrahp(g); printf("圖的頂點數:%d,邊(弧)樹為:%d\n",g.vexNum,g.arcNum); char first = firstAdj(g,'a'); char next = nextAdj(g,'a','c'); char * isAdj = grphIsAdj(g,'c','d')? "相鄰" : "不相鄰"; int degree = graphDegree(g,'d'); printf("a的第一個鄰接點是%c,a的c鄰接點的下一個鄰接點是:%c\n",first,next); printf("c 和 d %s,d的度為:%d\n",isAdj,degree); insertVex(&g,'e'); printf("插入e頂點之後圖結構如下:\n"); printGrahp(g); insertArc(&g,'a','e'); printf("插入(a,e)邊(弧)之後圖結構如下:\n"); printGrahp(g); deleteArc(&g,'d','c'); printf("刪除(d,c)邊(弧)之後圖結構如下:\n"); printGrahp(g); deleteVex(&g,'a'); printf("刪除頂點a之後圖結構如下:\n"); printGrahp(g); printf("圖的頂點數:%d,邊(弧)樹為:%d\n",g.vexNum,g.arcNum); //及時銷燬記憶體是個好習慣 graphDestory(&g); return 0; }
最後 圖的 頂點樹為 4, 邊(弧) 數 為 1
相關推薦
看資料結構寫程式碼(36) 圖的鄰接表表示與實現
圖的鄰接表表示法,是為每一個頂點建立一個連結串列,連結串列裡存放著相同弧尾的 弧的資訊,這些連結串列順序存放在陣列中。下面是無向圖g2的鄰接表 鄰接表 比 鄰接矩陣 節省空間,同時 也帶來一些操作上的 不便,例如 看 兩個頂點是否 相鄰,需要 遍歷 連結串列,在 求 無
看資料結構寫程式碼(38) 圖的鄰接多重表表示法與實現
圖的鄰接多重表 是 無向圖的 另一種表示法。其與 鄰接表 的差別 僅僅 在於 ,鄰接表 用 兩個 頂點 來表示 一條邊,而 鄰接多重表 用一個 頂點來表示一條邊。這樣使得 鄰接多重表 在 某些操作 要 來的 方便。例如 將 搜尋過的邊 做記號 或者 刪除 一條邊。 下面是鄰
看資料結構寫程式碼(35) 圖的鄰接矩陣表示法
雜談:最近清明小長假,好好的放鬆了一下。節前 和 節後 都有點 鬆懈。不好,不好。貴在堅持。加油。 圖的鄰接矩陣表示法是用 兩個陣列 來表示 圖的資料結構。一個是頂點陣列,另一個是鄰接矩陣陣列。鄰接矩陣 裡存放著 頂點的關係。 用鄰接矩陣表示圖,在 看 頂點之間 是否有邊,
看資料結構寫程式碼(20)稀疏矩陣(順序儲存方式)
雜談:昨天辭職了,告別了繁重又無意義的工作。準備在家專心學習資料結構,好好磨練自己的基本功。 在寫這個小例子的時候遇到了 stack overflow(棧溢位)的問題,是 自己 分配了 過大的 棧變數,導致棧溢位。說實話,這還是 第一次 遇到,呵呵,別笑話我。看到網上的部落
看資料結構寫程式碼(63) 堆排序
// HeapSort.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include <cstdlib> #define LIST_MAX_SIZE 100 //順序表 struct sqList{ int bas
看資料結構寫程式碼(22) 二叉樹的順序儲存方式
二叉樹 是 一個 節點 的度最多是2 ,並且區分 左右子樹的 特殊樹。 二叉樹 有一些特性,這些特性 是 寫 二叉樹順序表的 重要依據,所以先介紹一下: 1.k層的二叉樹,最多有 2 的 k次方 -1 個節點,如果 節點數達到最大值,稱為 滿二叉樹。 2.第k層的二叉樹,
野生前端的資料結構基礎練習(8)——圖
網上的相關教程非常多,基礎知識自行搜尋即可。 習題主要選自Orelly出版的《資料結構與演算法javascript描述》一書。 參考程式碼可見:https://github.com/dashnowords/blogs/tree/master/Structure/graph 一.圖的基本
資料結構複習筆記(一)——靜態順序表(C語言)
定義結構體 typedef struct{ int data[MAXSIZE]; //MAXSIZE為最大容量 int length; //當前長度 } Array; 建立順序表 /* 需要接受一個已知的陣列,以及該陣列的長度 按順序將陣列內的值,賦給順
資料結構程式設計回顧(七)航班資訊的查詢與檢索
題目七:航班資訊的查詢與檢索 設計要求:對飛機航班資訊進行排序和查詢。可按航班號、 起點站、終點站、起飛時間及到達時間等資訊進行查詢。 每個航班記錄包括八項:航班號、起點站、終點站、航班期、 起飛時間、到達時間、機型以及票價。 航班資訊查詢系統內容: 1. 航班號 2. 起點站 3. 終點
資料結構學習筆記(四) 圖之鄰接表實現深度優先遍歷
一下是使用鄰接表儲存表示,實現圖的深度優先遍歷的示例。 用於遍歷的有向圖如下: #include<iostream> #define MaxVertexNum 6 using namespace std; //抽象資料型別 typedef c
資料結構學習筆記(20)---圖的應用(生成樹與最小生成樹)
上一篇部落格寫了圖的基本儲存於遍歷,在此基礎上,此篇部落格將會介紹圖的主要應用—–生成樹與最小生成樹。 (一)生成樹 定義:我總感覺書上定義比較繁瑣,因此就自己簡單定義了一下(可能不對哦),生成樹其實就是:對於一棵樹G,若頂點數為n,則在原來圖的基礎上把
野生前端的資料結構基礎練習(5)——雜湊
網上的相關教程非常多,基礎知識自行搜尋即可。 習題主要選自Orelly出版的《資料結構與演算法javascript描述》一書。 參考程式碼可見:https://github.com/dashnowords/blogs/tree/master/Structure/Hash 雜湊的基本知識
野生前端的資料結構基礎練習(6)——集合
網上的相關教程非常多,基礎知識自行搜尋即可。 習題主要選自Orelly出版的《資料結構與演算法javascript描述》一書。 參考程式碼可見:https://github.com/dashnowords/blogs/tree/master/Structure/Set [TOC] 集
資料結構上機題(週三)
週三,19號的上機題。 題目,如圖: 不多廢話,直接原始碼: #include<iostream> #include<stdlib.h> int a[100]; using namespace std; class node { public: node
hashmap資料結構詳解(五)之HashMap、HashTable、ConcurrentHashMap 的區別
【hashmap 與 hashtable】 hashmap資料結構詳解(一)之基礎知識奠基 hashmap資料結構詳解(二)之走進JDK原始碼 hashmap資料結構詳解(三)之hashcode例項及大小是2的冪次方解釋 hashmap資料結構詳解(四)之has
野生前端的資料結構基礎練習(7)——二叉樹
網上的相關教程非常多,基礎知識自行搜尋即可。 習題主要選自Orelly出版的《資料結構與演算法javascript描述》一書。 參考程式碼可見:https://github.com/dashnowords/blogs/tree/master/Structure/btree 一.二叉樹的
Java資料結構和演算法(一):簡介
本系列部落格我們將學習資料結構和演算法,為什麼要學習資料結構和演算法,這裡我舉個簡單的例子。 程式設計好比是一輛汽車,而資料結構和演算法是汽車內部的變速箱。一個開車的人不懂變速箱的原理也是能開車的,同理一個不懂資料結構和演算法的人也能程式設計。但是如果一個開車的人懂變速箱的原理,比如降低速
JAVA樹形結構 通用程式碼(高效能)
package com.test.main; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.ArrayList; import java.util.L
Python3&資料結構之合併(歸併)排序
合併(歸併)排序和快速排序一樣也採用了分而治之(divide and conquer,D&C)的思想 不過對比快速排序,mergesort沒有pivot(中心點) 分的部分: 它是把一個無序陣列按照陣列大小的中心數分為兩部分 if len(n) < 2
Java資料結構和演算法(七)——連結串列
目錄 前面部落格我們在講解陣列中,知道陣列作為資料儲存結構有一定的缺陷。在無序陣列中,搜尋效能差,在有序陣列中,插入效率又很低,而且這兩種陣列的刪除效率都很低,並且陣列在建立後,其大小是固定了,設定的過大會造成記憶體的浪費,過小又不能滿足資料量的儲存。