1. 程式人生 > >C語言—鄰接矩陣和鄰接表的理解

C語言—鄰接矩陣和鄰接表的理解

要談鄰接表,那我們先談談鄰接矩陣,因為鄰接表就是因為鄰接矩陣對於稀疏圖造成記憶體的很大浪費。那麼它是如何浪費的哪? 別急慢慢來!

  • 什麼叫做鄰接矩陣?
    它就是儲存頂點是否存在連線關係的二維陣列如下(右),關係圖(左)
    在這裡插入圖片描述在這裡插入圖片描述
    解釋圖:如果a1->a2有線連線的話,用1表示反之用0表示。
    程式碼如下:
#define MAX_VERTEX_NUM 20  /*最多頂點數目*/

#define INFINITY 32768  /*表示最大值*/
#include <stdio.h>
#include <stdlib.h>

/*圖的種類: DG表示有向圖   DN表示有向網 UDG表示無向圖   UDN表示無向網*/

typedef  enum {DG,DN,UDG,UDN} GraphKind;

typedef  int VertexData;//假設頂點資料為字元型

typedef  struct ArcNode//儲存鄰接結點的資訊
{
	int adj;             //對於無權圖,用1或0表示是否相鄰;對帶權圖,則為表示權值型別
	int info;        //暫時不知道儲存什麼。
}ArcNode;
typedef struct 
{
	VertexData vertex[MAX_VERTEX_NUM];

	ArcNode   arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//鄰接矩陣 
	int vexnum, arcnum;//圖放入頂點數和弧數
	GraphKind  kind;//圖的種類標誌
}AdjMatrix;//鄰接矩陣資訊

int locateVertex(AdjMatrix *G, VertexData v)//找頂點函式
{
	int j = 0, k;
	for (k = 0; k < G->vexnum; k++)
	{
		if (G->vertex[k] == v)
		{
			j = k; break;
		}
	}
	return (j);
}

int main()
{
	AdjMatrix *G;
	G = (AdjMatrix *)malloc(sizeof(AdjMatrix));//定義物件
	int  i, j, k, weight;
	VertexData v1=0, v2=0;//兩頂點。
	printf("請輸入圖的頂點數和弧數\n");
	scanf_s("%d,%d", &G->vexnum, &G->arcnum);
	getchar();
	for (i = 0; i < G->vexnum; i++)//初始化鄰接矩陣
	{
		for (j = 0; j < G->vexnum; j++)
		{
			G->arcs[i][j].adj = 0;//權值最大
		}
	}
	printf("請輸入頂點的資料\n");
	for (i = 0; i < G->vexnum; i++)
	{
		scanf_s("%d", &G->vertex[i]);
	}
	printf("請輸入兩個頂點和權值\n");
	for (k = 0; k < G->arcnum; k++)//兩個頂點建立聯絡
	{
		scanf_s("%d,%d,%d", &v1, &v2, &weight);//讀入資料
		i = locateVertex(G, v1);
		j = locateVertex(G, v2);
		G->arcs[i][j].adj = weight;//賦權值
	}//建弧完成
	printf("打印出帶權圖結構如下\n");
	for (i = 0; i < G->vexnum; i++)
	{
		for (j = 0; j < G->vexnum; j++)
		{
			printf("%d  ", G->arcs[i][j].adj);
		}
		printf("\n");
	}
	system("pause");
	return 0;
}

執行結果如下:
認真分析下結果出現的原因
從圖中我們發現存了不少0,表示它們不是鄰接關係,但是我們並不需要記住它們不相鄰,所以這就大大浪費了記憶體。所以就有了鄰接表法儲存。

  • 鄰接法
    優點:只儲存關聯的資訊,對於圖中存在的邊資訊,則儲存,而不相鄰的頂點則不保留資訊。
    圖奉上如下:
    在這裡插入圖片描述在這裡插入圖片描述
    後面的結點我只分了兩份,實際還要給權值給開個空間儲存資訊。(我給省略了)。
    程式碼如下:
//鄰接表儲存結構的形式
#include <stdio.h>
#include<stdlib.h>

#define MAX_VERTEX_NUM 20
#define   VertexData  int 
#define max 100
#define size  4//儲存數字的大小
#define bian  4//儲存邊的大小

int edge[1000][2] = { 0 };
typedef enum{DG,DN,UDG,UDN} GraphKind;//圖的種類

typedef struct ArcNode
{
	int data;
	struct ArcNode *nextarc;  //指向下一條弧的指標
	//int weight;//權值  先不考慮
}ArcNode;
typedef struct VertexNode
{
	VertexData data;//頂點資料
	ArcNode *firstarc;//指向該頂點第一條弧的指標
}VertexNode;
typedef struct
{
	VertexNode vertex[MAX_VERTEX_NUM];//一維頂點陣列
	int vexnum, arcnum;//頂點個數和弧數
}AdjList;//鄰接目錄

int find_insert(ArcNode *edge, ArcNode *temp)
{
	ArcNode *p;
	p = edge;
	while (p->nextarc != NULL)//迴圈找到一維頂點陣列中的每一個頂點之後的鄰接點
	{
		p = p->nextarc;
	}
	p->nextarc = temp;//插入找到的邊
	return 0;
}

AdjList* CreGraph()//建立圖
{
	int i,c1,c2;
	ArcNode *Arc;
	Arc = (ArcNode*)malloc(sizeof(ArcNode));
	AdjList *picture;
	picture = (AdjList*)malloc(sizeof(AdjList));

	picture->vexnum = size;
	picture->arcnum = bian;
	printf("請輸入頂點\n");
	for (i = 1; i <= picture->vexnum; i++)
	{
		scanf_s("%d", &picture->vertex[i].data);
		picture->vertex[i].firstarc = NULL;//剛開始把一維陣列建立起來
	}
	//再確定哪兩個頂點連線需要存邊0代表出度連線1代表入度
	

    for (int i = 1; i <= picture->arcnum; i++)//建立聯絡
	{
		c1 = edge[i][0];
		c2 = edge[i][1];
		Arc = (ArcNode*)malloc(sizeof(ArcNode));//新申請的結點
		Arc->data = edge[i][1];//獲取頂點數
		Arc->nextarc = NULL;
   //從輸入的弧中將入度相同的結點連線起來
		if (picture->vertex[c1].firstarc == NULL)
		{
			picture->vertex[c1].firstarc = Arc;
		}
		else
		{
			find_insert(picture->vertex[c1].firstarc, Arc);
		}
	
    }
   return picture;
}
int main()
{
	int i = 0;
	AdjList* picture;
	
	printf("請輸入要連線的序號邊\n");
	for (i = 1; i <= bian ; i++)
	{
		scanf("%d",&edge[i][0]);//代表號,不是放權值
		scanf("%d", &edge[i][1]);
	}
	picture = CreGraph();
	printf("鄰接表如下\n");
    for (int i = 1; i <= picture->vexnum; i++)
	{
		ArcNode *p;
		p = (ArcNode*)malloc(sizeof(ArcNode));
		if (picture->vertex[i].firstarc == NULL)
		{
			printf("error");
		}
		else
		{
			p = picture->vertex[i].firstarc;
			printf("%d ", picture->vertex[i].data);
			while (p != NULL)
			{
				printf("%d ", p->data);
				p = p->nextarc;
			}
		}
		printf("\n");
	}
	system("pause");
	return 0;
}

程式碼有點長,但對於IT專業的我們來說,耐的住心慢慢理解,到最終get it,是非常重要的,加油!