1. 程式人生 > >看資料結構寫程式碼(36) 圖的鄰接表表示與實現

看資料結構寫程式碼(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資料結構和演算法——連結串列

目錄   前面部落格我們在講解陣列中,知道陣列作為資料儲存結構有一定的缺陷。在無序陣列中,搜尋效能差,在有序陣列中,插入效率又很低,而且這兩種陣列的刪除效率都很低,並且陣列在建立後,其大小是固定了,設定的過大會造成記憶體的浪費,過小又不能滿足資料量的儲存。