データ構造検討ノート - 図

1. 絵の紹介

グラフは比較的複雑なデータ構造です。線形テーブル内のデータ要素間には線形関係のみがあります。各要素には直接の先行要素と直接の後続要素が 1 つだけあります (要素間には 1 対 1 の関係のみがあります)。ツリー内では構造 中間の要素間には明らかな階層関係があり、各層の要素は下位層の複数の要素とのみ関係を持つことができます (要素間には 1 対多の関係があります)。グラフ構造では、任意の 2 つのノード間に関係があり、関係 (要素間の多対多の関係) が存在する場合もあります。

2. グラフの定義と用語

**頂点:** グラフ内のデータ要素は頂点と呼ばれ、V は通常、グラフの有限で空でない頂点の集合を表すために使用されます。

**円弧:** 2 つの頂点間の関係は <v, w> で示されます。これは、v は頂点 w に到達できる、つまり、v は w に到達できるが、w は v に到達できない可能性があることを意味します。v を円弧と呼びます。尾部または始点、w は円弧頭または終点と呼ばれます。

**有向グラフ:** 円弧 + 頂点で構成されるグラフは有向グラフと呼ばれます。つまり、頂点間に一方通行の通りがあります。

**エッジ:** 2 つの頂点 (v、w) 間の関係レコード。これは、v から w、または w から v に進むことができることを意味します。v と w の間の関係をエッジと呼びます。

**無向グラフ: **エッジ + 頂点で構成されるグラフには、頂点間の双方向ストリートである無向グラフがあります。

**注:** 通常、グラフを表すには G、頂点の集合を表すには V、円弧またはエッジの集合を表すには VR、頂点の数を表すには n、エッジまたは円弧の数を表すには e を使用します。また、頂点自体のエッジや円弧については説明しません。

**完全なグラフ:** 無向グラフでは、e の値の範囲は 0 ~ n/2(n-1) であり、無向グラフの辺の数が最大値に達すると、その無向グラフは完全であると呼ばれます。写真。

**有向完全グラフ:**有向グラフでは、e の値の範囲は 0 ~ n(n-1) であり、有向グラフ内のアークの数が最大値に達した場合、この有向グラフは有向グラフと呼ばれます. 完全なグラフに向けて。

**疎グラフと密グラフ:** グラフにエッジと円弧がほとんどない場合、そのようなグラフは疎グラフと呼ばれ、そうでない場合は密グラフと呼ばれます。

**重みとネットワーク:** グラフ内の頂点が別の頂点に移動するためにコスト (距離またはコスト) が必要な場合、エッジまたは円弧を表すときに追加のデータが必要になります。追加のデータは重みと呼ばれ、重み付きグラフは通常ネットと呼ばれ、インターネットの起源でもあります。

**サブグラフ:** G1 の頂点セットが G2 の頂点セットのサブセットであり、G1 のエッジまたはアーク セットが G2 のエッジまたはアーク セットのサブセットである場合、2 つのグラフ G1 と G2 があると仮定します。 G2 を G1 と呼び、G2 の部分グラフになります。

**隣接点:** 無向グラフにエッジ (v, w) がある場合、v と w の 2 つの頂点は互いに隣接する点です。つまり、v と w は互いに隣接しており、エッジ (v, w) は頂点 v, w にアタッチされているか、または (v, w) は頂点 v, w に関連付けられています。

**頂点 v の次数:** 無向グラフ内の頂点 v に関連付けられたエッジの数。

**頂点 v の入次数と出力次数:** 有向グラフでは、頂点 v を弧の頭とする弧の数を頂点の入次数と呼び、頂点 v を含む弧の数を次数と呼びます。円弧の尾部は頂点のアウト次数と呼ばれます。

**パス:** 頂点 v から頂点 w までの一連の頂点はパスと呼ばれ、パスの長さはエッジまたは円弧の数になります。

**サーキットまたはリング:** 始点と終点が同じパスをループまたはリングと呼びます。

**単純なパス: **パス内に頂点が繰り返し出現しないパスは、単純なパスと呼ばれます。

**単純回路または単純リング: **始点と終点が同じで、残りの頂点が繰り返し出現しないものを、単純回路または単純リングと呼びます。

**接続されたグラフ:** 無向グラフでは、頂点 v から頂点 w へのパスがある場合、v と w は接続されていると言われます。グラフ内の 2 つの頂点が接続されている場合、そのグラフは接続されていると呼ばれます。グラフ。

**連結成分: **G1 と G2 は両方とも連結グラフであり、G1 は G2 のサブグラフである場合、G1 は G2 の連結成分または最大連結サブグラフと言われます。

強接続グラフ: 有向グラフでは、頂点のペアが双方向に接続されている場合、そのグラフは強接続グラフと呼ばれます。

強連結成分: G1 と G2 は両方とも強連結グラフであり、G1 が G2 のサブグラフである場合、G1 は G2 の強連結成分または非常に強連結のサブグラフと言われます。

3. グラフの保存構造:

隣接行列:

2 つの配列を使用して、データ要素 (頂点) とデータ要素間の関係 (エッジまたは円弧) に関する情報を保存します。

1 次元配列: 頂点を格納するために使用されます。

2 次元配列: 円弧またはエッジを保存するために使用されます。

隣接マトリックスの利点:

アクセス速度が速く、ノードの次数、入次数、出次数の計算に便利です。

隣接マトリックスの欠点:

頂点の容量には制限があり、拡張が非常に面倒なので、密なグラフの保存にのみ適しており、疎なグラフを保存するとメモリの無駄が多くなります。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 无向图
typedef struct Grahh
{
    
    
	size_t cal;	  // 顶点容量
	size_t cnt;	  // 顶点数量
	char *vertex; // 存储顶点的一维数组
	char **edge;  // 存储边的二维数组
} Graph;

Graph *create_graph(size_t cal)
{
    
    
	Graph *graph = malloc(sizeof(Graph));
	graph->vertex = malloc(sizeof(char) * cal);
	graph->cal = cal;
	graph->cnt = 0;

	graph->edge = malloc(sizeof(char *) * cal);
	for (int i = 0; i < cal; i++)
	{
    
    
		graph->edge[i] = calloc(sizeof(char), cal);
	}

	return graph;
}

bool add_vertex_graph(Graph *graph, char vertex)
{
    
    
	if (graph->cnt >= graph->cal)
		return false;

	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (graph->vertex[i] == vertex)
			return false;
	}

	graph->vertex[graph->cnt++] = vertex;
	return true;
}

bool add_edge_graph(Graph *graph, char v1, char v2)
{
    
    
	int v1_index = -1, v2_index = -1;
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (graph->vertex[i] == v1)
			v1_index = i;
		if (graph->vertex[i] == v2)
			v2_index = i;
	}

	if (-1 == v1_index || -1 == v2_index || v1_index == v2_index)
		return false;

	graph->edge[v1_index][v2_index] = 1;
	graph->edge[v2_index][v1_index] = 1;
	return true;
}

// 查询顶点在一维数组中的位置,如果不存在则返回-1
int query_vertex_graph(Graph *graph, char vertex)
{
    
    
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (vertex == graph->vertex[i])
			return i;
	}
	return -1;
}

// 查询顶点第一个邻接点,如果不存在则返回'\0'
char first_vertex_graph(Graph *graph, char vertex)
{
    
    
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return '\0';

	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (1 == graph->edge[index][i])
			return graph->vertex[i];
	}

	return '\0';
}

// 删除顶点
bool del_vertex_graph(Graph *graph, char vertex)
{
    
    
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return false;

	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		graph->edge[index][i] = 0;
		graph->edge[i][index] = 0;
	}

	return true;
}

void _dfs_graph(Graph *graph, int index, bool *flags)
{
    
    
	if (!flags[index])
		return;

	printf("%c\n", graph->vertex[index]);
	flags[index] = false;

	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (1 == graph->edge[index][i])
			_dfs_graph(graph, i, flags);
	}
}

// 深度优先遍历,与树的前序遍历类似
void dfs_graph(Graph *graph)
{
    
    
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	for (int i = 0; i < graph->cnt; i++)
		_dfs_graph(graph, i, flags);
}

// 广度优先遍历,与树的层序遍历一样,需要队列结构配合
void bfs_graph(Graph *graph)
{
    
    
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	int queue[graph->cnt], front = 0, rear = 0;

	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (flags[i])
		{
    
    
			flags[i] = false;
			queue[rear++] = i;
		}

		while (front != rear)
		{
    
    
			int index = queue[front++];
			printf("%c ", graph->vertex[index]);

			for (int j = 0; j < graph->cnt; j++)
			{
    
    
				if (1 == graph->edge[index][j] && flags[j])
				{
    
    
					flags[j] = false;
					queue[rear++] = j;
				}
			}
		}
	}
}

// 销毁图
void destroy_graph(Graph *graph)
{
    
    
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		free(graph->edge[i]);
	}
	free(graph->edge);
	free(graph->vertex);
	free(graph);
}

int main(int argc, const char *argv[])
{
    
    
	Graph *graph = create_graph(5);
	add_vertex_graph(graph, 'A');
	add_vertex_graph(graph, 'B');
	add_vertex_graph(graph, 'C');
	add_vertex_graph(graph, 'D');
	add_vertex_graph(graph, 'E');

	add_edge_graph(graph, 'A', 'C');
	add_edge_graph(graph, 'A', 'D');
	add_edge_graph(graph, 'C', 'B');
	add_edge_graph(graph, 'B', 'D');
	// del_vertex_graph(graph,'C');
	printf("------------------------\n");
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		for (int j = 0; j < graph->cnt; j++)
		{
    
    
			printf("%hhd ", graph->edge[i][j]);
		}
		printf("\n");
	}
	// destroy_graph(graph);
	// printf("%c\n",first_vertex_graph(graph,'D'));
	// dfs_graph(graph);
	bfs_graph(graph);
	return 0;
}

隣接リスト:

格納および格納には、1 次元配列と一方向リンク リストを使用します。

1次元配列: 頂点の先頭ポインタと一方向リンクリストを格納するために使用されます。

一方向リンクリスト: 頂点から始まる円弧またはエッジを保存します。

隣接リストの利点:

隣接行列と比較して、メモリを節約し、エッジまたはアークと同じ数のリンク リスト ノードを作成できるため、スパース グラフの保存に適しています。

隣接リストの欠点:

入次数を計算してノードを削除するのは面倒です。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 弧
typedef struct Bow
{
    
    
	int dest;
	struct Bow *next;
} Bow;

// 顶点
typedef struct Vertex
{
    
    
	char data;
	Bow first; // 单链表的空白头节点
} Vertex;

// 有向图
typedef struct Graph
{
    
    
	Vertex *vertex;
	size_t cal;
	size_t cnt;
} Graph;

// 创建图
Graph *create_graph(size_t cal)
{
    
    
	Graph *graph = malloc(sizeof(Graph));
	graph->vertex = malloc(sizeof(Vertex) * cal);
	graph->cal = cal;
	graph->cnt = 0;
	return graph;
}

// 添加顶点
bool add_vertex_graph(Graph *graph, char vertex)
{
    
    
	if (graph->cnt >= graph->cal)
		return false;

	// 顶点不能重复
	for (int i = 0; i < graph->cnt; i++)
		if (vertex == graph->vertex[i].data)
			return false;

	graph->vertex[graph->cnt].data = vertex;
	graph->vertex[graph->cnt++].first.next = NULL;
	return true;
}

// 查询顶点
int query_vertex_graph(Graph *graph, char vertex)
{
    
    
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (graph->vertex[i].data == vertex)
			return i;
	}

	return -1;
}

// 添加弧
bool add_bow_graph(Graph *graph, char bow_tail, char bow_head)
{
    
    
	int index_tail = query_vertex_graph(graph, bow_tail);
	if (-1 == index_tail)
		return false;

	int index_head = query_vertex_graph(graph, bow_head);
	if (-1 == index_head)
		return false;

	Bow *bow = malloc(sizeof(Bow));
	bow->dest = index_head;

	bow->next = graph->vertex[index_tail].first.next;
	graph->vertex[index_tail].first.next = bow;
	return true;
}

// 删除顶点
bool del_vertex_graph(Graph *graph, char vertex)
{
    
    
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return false;

	if (NULL != graph->vertex[index].first.next)
	{
    
    
		Bow *bow = graph->vertex[index].first.next;
		graph->vertex[index].first.next = bow->next;
		free(bow);
	}
	graph->vertex[index].data = '\0';

	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		for (Bow *bow = &graph->vertex[i].first; NULL != bow->next; bow = bow->next)
		{
    
    
			if (index == bow->next->dest)
			{
    
    
				Bow *tmp = bow->next;
				bow->next = tmp->next;
				free(tmp);
			}
		}
	}
	return true;
}

// 计算入度,计算出有多个弧的弧头是某顶点
int indegree_vertex_graph(Graph *graph, char vertex)
{
    
    
	// 打到顶点,遍历所有弧,dest等于vertex下的弧有多少条,如果找不到则返回-1
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return -1;

	int indegree = 0;
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		Bow *bow = graph->vertex[i].first.next;
		while (NULL != bow)
		{
    
    
			if (index == bow->dest)
				indegree++;
			bow = bow->next;
		}
	}

	return indegree;
}

// 计算出度
int outdegree_vertex_graph(Graph *graph, char vertex)
{
    
    
	// 找到顶点,从它出发的弧有多少条,如果找不到则返回-1
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return -1;

	int outdegree = 0;
	Bow *bow = graph->vertex[index].first.next;
	while (NULL != bow)
	{
    
    
		outdegree++;
		bow = bow->next;
	}

	return outdegree;
}

// 深度优先遍历
void _dfs_graph(Graph *graph, int index, bool *flags)
{
    
    
	flags[index] = false;
	printf("%c ", graph->vertex[index].data);

	Bow *bow = graph->vertex[index].first.next;
	while (NULL != bow)
	{
    
    
		if (flags[bow->dest])
			_dfs_graph(graph, bow->dest, flags);

		bow = bow->next;
	}
}

void dfs_graph(Graph *graph)
{
    
    
	// 参考昨天邻接矩阵的业务逻辑
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	for (int i = 0; i < graph->cnt; i++)
		if (flags[i])
			_dfs_graph(graph, i, flags);
	printf("\n");
}

// 广度优先遍历
void bfs_graph(Graph *graph)
{
    
    
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	int queue[graph->cnt], front = 0, rear = 0;

	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (flags[i])
		{
    
    
			flags[i] = false;
			queue[rear++] = i;
		}

		while (front != rear)
		{
    
    
			int index = queue[front++];
			printf("%c ", graph->vertex[index].data);

			Bow *bow = graph->vertex[index].first.next;
			while (NULL != bow)
			{
    
    
				if (flags[bow->dest])
				{
    
    
					flags[bow->dest] = false;
					queue[rear++] = bow->dest;
				}
				bow = bow->next;
			}
		}
	}
	printf("\n");
}

// 销毁图
void destroy_graph(Graph *graph)
{
    
    
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		Bow *bow = &graph->vertex[i].first;
		while (NULL != bow->next)
		{
    
    
			Bow *tmp = bow->next;
			bow->next = tmp->next;
			free(tmp);
		}
	}
	free(graph->vertex);
	free(graph);
}

int main(int argc, const char *argv[])
{
    
    
	Graph *graph = create_graph(10);
	add_vertex_graph(graph, 'A');
	add_vertex_graph(graph, 'B');
	add_vertex_graph(graph, 'C');
	add_vertex_graph(graph, 'D');
	add_vertex_graph(graph, 'E');

	add_bow_graph(graph, 'A', 'D');
	add_bow_graph(graph, 'A', 'E');
	add_bow_graph(graph, 'A', 'C');
	add_bow_graph(graph, 'C', 'B');
	add_bow_graph(graph, 'B', 'D');
	// add_bow_graph(graph,'E','D');
	/*
	del_vertex_graph(graph,'E');
	for(int i=0; i<graph->cnt; i++)
	{
		printf("%d %c first->%p",i,graph->vertex[i].data,graph->vertex[i].first.next);
		for(Bow* bow=graph->vertex[i].first.next; NULL!=bow; bow=bow->next)
		{
			printf(" %p dest:%d ",bow,bow->dest);
		}
		printf("\n");
	}
	printf("%d\n",outdegree_vertex_graph(graph,'B'));
	printf("%d\n",indegree_vertex_graph(graph,'A'));
	*/
	dfs_graph(graph);
	bfs_graph(graph);
	destroy_graph(graph);
	return 0;
}

クロスリスト:

1 次元配列と 2 つのクロスリンク リストがストレージに使用されます。クロスリンク リストと呼ばれる理由は、そのリンク リストのアーク ノードが 2 つのリンク リストによってポイントされ、アーク ノードには 2 つのリンク リストがあるためです。後続のアーク ノードを指すポインター。

1 次元配列: 頂点データ、2 つのリンクされたリストのヘッド ポインターを格納します。

アウト次数リンク リスト: ノードが円弧の終端となる円弧ノードを指します。

イン次数リンク リスト: このノードを円弧の頭とする円弧ノードを指します。

クロスリンクリストの利点:

隣接行列と比較すると、メモリ効率が高く、隣接リストよりも頂点の出次数を計算する方が便利です。

相互リンクリストの欠点:

頂点の削除や円弧の削除の方が面倒で、無向グラフの表現・実現も可能ですが、有向グラフの表現・実現の方が適しています。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 弧
typedef struct Bow
{
    
    
	int tailvex;	   // 弧尾的顶点下标
	int headvex;	   // 弧头的顶点下标
	struct Bow *tlink; // 指向相同弧尾的弧节点
	struct Bow *hlink; // 指向相同弧头的弧节点
} Bow;

// 顶点
typedef struct Vertex
{
    
    
	char data;
	Bow *inlink;
	Bow *outlink;
} Vertex;

// 图
typedef struct Graph
{
    
    
	Vertex *vertex;
	size_t cal;
	size_t cnt;
} Graph;

// 创建图
Graph *create_graph(size_t cal)
{
    
    
	Graph *graph = malloc(sizeof(Graph));
	graph->vertex = malloc(sizeof(Vertex) * cal);
	graph->cal = cal;
	graph->cnt = 0;
	return graph;
}
// 查询顶点下标
int query_vertex_graph(Graph *graph, char vertex)
{
    
    
	for (int i = 0; i < graph->cnt; i++)
		if (vertex == graph->vertex[i].data)
			return i;

	return -1;
}

// 添加顶点
bool add_vertex_graph(Graph *graph, char vertex)
{
    
    
	// 如果顶点已经满,或顶点已经存在则返回false
	if (graph->cnt >= graph->cal || -1 != query_vertex_graph(graph, vertex))
		return false;

	graph->vertex[graph->cnt].data = vertex;
	graph->vertex[graph->cnt].inlink = NULL;
	graph->vertex[graph->cnt++].outlink = NULL;
	return true;
}

// 添加弧
bool add_bow_graph(Graph *graph, char tailvex, char headvex)
{
    
    
	// 如果弧头或弧尾的顶点不存在则添加失败
	int tailindex = query_vertex_graph(graph, tailvex);
	int headindex = query_vertex_graph(graph, headvex);
	if (-1 == tailindex || -1 == headindex)
		return false;

	// 创建一个弧
	Bow *bow = malloc(sizeof(Bow));
	bow->tailvex = tailindex;
	bow->headvex = headindex;

	bow->tlink = graph->vertex[tailindex].outlink;
	graph->vertex[tailindex].outlink = bow;

	bow->hlink = graph->vertex[headindex].inlink;
	graph->vertex[headindex].inlink = bow;

	return true;
}

// 删除顶点
bool del_vertex_graph(Graph *graph, char vertex)
{
    
    
	// 找到顶点的下标,如果找不到则返回false
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return false;

	// 以它作为弧尾的弧要全部断开
	graph->vertex[index].inlink = NULL;

	// 以它作为弧头的弧要全部删除
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		Bow *out = graph->vertex[i].outlink;
		while (NULL != out && NULL != out->tlink)
		{
    
    
			if (out->tlink->headvex == index)
			{
    
    
				Bow *tmp = out->tlink;
				out->tlink = tmp->tlink;
				free(tmp);
			}
			out = out->tlink;
		}
		if (NULL != graph->vertex[i].outlink &&
			index == graph->vertex[i].outlink->headvex)
		{
    
    
			Bow *tmp = graph->vertex[i].outlink;
			graph->vertex[i].outlink = tmp->tlink;
			free(tmp);
		}
	}
	return true;
}

// 删除弧
bool del_bow_graph(Graph *graph, char tailvex, char headvex)
{
    
    
	// 找到弧头、弧尾顶点的下标,如果有一个找不到就返回false
	int tailindex = query_vertex_graph(graph, tailvex);
	int headindex = query_vertex_graph(graph, headvex);
	if (-1 == tailindex || -1 == headindex)
		return false;

	// 删除弧,并且要保证两个链表不断开
	Bow *out = graph->vertex[tailindex].outlink;
	while (NULL != out && NULL != out->tlink)
	{
    
    
		if (out->tlink->headvex == headindex)
		{
    
    
			out->tlink = out->tlink->tlink;
			break;
		}
		out = out->tlink;
	}
	if (NULL != graph->vertex[tailindex].outlink &&
		headindex == graph->vertex[tailindex].outlink->headvex)
	{
    
    
		graph->vertex[tailindex].outlink = graph->vertex[tailindex].outlink->tlink;
	}

	Bow *in = graph->vertex[headindex].inlink;
	while (NULL != in && NULL != in->hlink)
	{
    
    
		if (in->hlink->tailvex == tailindex)
		{
    
    
			Bow *tmp = in->hlink;
			in->hlink = tmp->hlink;
			free(tmp);
			break;
		}
		in = in->hlink;
	}

	if (NULL != graph->vertex[headindex].outlink &&
		tailindex == graph->vertex[headindex].outlink->tailvex)
	{
    
    
		Bow *tmp = graph->vertex[headindex].inlink;
		graph->vertex[headindex].inlink = tmp->hlink;
		free(tmp);
	}
	return true;
}

// 计算入度
int indegree_vertex_graph(Graph *graph, char vertex)
{
    
    
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return -1;

	int indegree = 0;
	Bow *in = graph->vertex[index].inlink;
	while (NULL != in)
	{
    
    
		indegree++;
		in = in->hlink;
	}

	return indegree;
}
// 计算出度
int outdegree_vertex_graph(Graph *graph, char vertex)
{
    
    
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return -1;

	int outdegree = 0;
	Bow *out = graph->vertex[index].outlink;
	while (NULL != out)
	{
    
    
		outdegree++;
		out = out->tlink;
	}
	return outdegree;
}

// 深度优先
void _dfs_graph(Graph *graph, int index, bool *flags)
{
    
    
	printf("%c ", graph->vertex[index].data);
	flags[index] = false;

	Bow *out = graph->vertex[index].outlink;
	while (NULL != out)
	{
    
    
		if (flags[out->headvex])
			_dfs_graph(graph, out->headvex, flags);
		out = out->tlink;
	}
}

void dfs_graph(Graph *graph)
{
    
    
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	for (int i = 0; i < graph->cnt; i++)
		if (flags[i])
			_dfs_graph(graph, i, flags);
	printf("\n");
}

// 广度优先
void bfs_graph(Graph *graph)
{
    
    
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	int queue[graph->cnt], front = 0, rear = 0;
	for (int i = 0; i < graph->cnt; i++)
	{
    
    
		if (flags[i])
		{
    
    
			queue[rear++] = i;
			flags[i] = false;
		}
		while (front != rear)
		{
    
    
			int index = queue[front++];
			printf("%c ", graph->vertex[index].data);

			Bow *out = graph->vertex[index].outlink;
			while (NULL != out)
			{
    
    
				if (flags[out->headvex])
				{
    
    
					queue[rear++] = out->headvex;
					flags[out->headvex] = false;
				}
				out = out->tlink;
			}
		}
	}
	printf("\n");
}

// 销毁图
void destroy_graph(Graph *graph)
{
    
    
	for (int i = 0; i < graph->cnt; i++)
		del_vertex_graph(graph, graph->vertex[i].data);

	free(graph->vertex);
	free(graph);
}

int main(int argc, const char *argv[])
{
    
    
	Graph *graph = create_graph(10);
	add_vertex_graph(graph, 'A');
	add_vertex_graph(graph, 'B');
	add_vertex_graph(graph, 'C');
	add_vertex_graph(graph, 'D');
	add_vertex_graph(graph, 'E');
	add_bow_graph(graph, 'A', 'D');
	add_bow_graph(graph, 'A', 'E');
	add_bow_graph(graph, 'A', 'C');
	add_bow_graph(graph, 'C', 'B');
	add_bow_graph(graph, 'B', 'D');
	add_bow_graph(graph, 'E', 'D');
	dfs_graph(graph);
	bfs_graph(graph);
	destroy_graph(graph);
	// del_vertex_graph(graph,'D');
	// del_bow_graph(graph,'A','D');
	/*
	printf("%d\n",indegree_vertex_graph(graph,'B'));
	for(int i=0; i<graph->cnt; i++)
	{
		printf("vex:%c",graph->vertex[i].data);
		printf("\n\t");
		Bow* out = graph->vertex[i].outlink;
		while(NULL != out)
		{
			printf("%d->%d ",out->tailvex,out->headvex);
			out = out->tlink;
		}
		printf("\n\t");
		Bow* in= graph->vertex[i].inlink;
		while(NULL != in)
		{
			printf("%d<-%d ",in->headvex,in->tailvex);
			in = in->hlink;
		}
		printf("\n");
	}

	*/
	return 0;
}

隣接マルチテーブル:

は隣接リストの拡張であり、隣接リストのエッジに基づいて、アクセス タグ、エッジを構成する 2 つの頂点の添え字、および 2 つの頂点に接続されている次のエッジへのポインタが追加されます。

隣接リストを使用すると、エッジの 1 つの 2 つのノードがそれぞれ i 番目と j 番目のリンク リストに含まれるため、グラフの操作に不都合が生じ、リンク リストのノードがマルチテーブルに隣接します。各種メンバーを追加するなど、グラフ上で便利な操作です。

交流');
add_bow_graph(グラフ, 'C', 'B');
add_bow_graph(グラフ, 'B', 'D');
add_bow_graph(グラフ, 'E', 'D');
dfs_graph(グラフ);
bfs_graph(グラフ);
destroy_graph(グラフ);
// del_vertex_graph(graph,'D');
// del_bow_graph(graph,'A','D');
/*
printf(“%d\n”,indegree_vertex_graph(graph,'B'));
for(int i=0; icnt; i++)
{ printf(“vex:%c”,graph->vertex[i].data); printf(“\n\t”); Bow* out = グラフ->頂点[i].outlink; while(NULL != out) { printf(“%d->%d “,out->tailvex,out->headvex); アウト = アウト -> トゥリンク; printf (”\n\t”); Bow* in= グラフ->頂点[i].inlink;










while(NULL != in)
{ printf(“%d<-%d “,in->headvex,in->tailvex); in = in->hlink; printf (”\n”); }




*/
return 0;

}


#### 邻接多重表:

​	是对邻接表一种扩展,在邻接表边的基础上,增加了访问标记、组成边的两个顶点的下标、指向依附这两个顶点的下一条边的指针。

​	使用邻接表时,它的一条边的两个节点,分别在第i和第j的两个链表中,这给图的操作带来不便,而邻接多重表的链表节点增加各项成员,方便对图的操作。















おすすめ

転載: blog.csdn.net/m0_62480610/article/details/126459521