数据结构——图(Graph)

目录

图的概念

概括

基本术语

图的存储结构

邻接矩阵

邻接矩阵的基本结构运行算法代码

邻接矩阵代码实现

邻接表

图的基本运算在邻接表中的实现

邻接表存储结构 

输出图邻接表

邻接表计算无向图某个顶点的度

邻接表计算有向图某个顶点的出度和入度

图的遍历

 深度优先遍历

深度优先遍历算法(邻接表)

深度优先遍历算法(邻接矩阵)

广度优先遍历

广度优先遍历算法(邻接表)

广度优先遍历算法(邻接矩阵)

生成树和最小生成树

 两种常用的构造最小生成树的算法:

Prim算法

kruskal算法


图的概念

概括

图都由顶点(Vertex)和边(Edge)有限集合而成
图分为无向图和有向图
无向图:代表边的顶点对(或序偶)是无序的,则称为无向图。代表边的无序顶点对通常用圆括号括起来,以表示一条无向边。(i,j)
有向图:表示边的顶点对(或序偶)是有序的,则称为有向图。代表边的无序顶点对通常用尖括号括起来,以表示一条有向边。<i,j>

图1:无向图和有向图的示意图

基本术语

1、端点和邻接点
在一个无向图中,若存在一条边(i,j),则称顶点“i”和顶点“j“为该边的两个端点,并称它们互为邻接点,即顶点”i“是顶点”j“的一个邻接点,顶点j也是顶点i的一个邻接点。
在一个有向图中,若存在一条边<i,j>,则称此边是顶点”i“的一条出边,同时也是顶点”j“的一条入边;称”i“和”j“分别为此边的起始端点(简称为起点)和终止端点(简称为终点),并称顶点”j“是”i“的出边邻点,顶点”i“是”j“的入边邻接点。
2、顶点的度、入度和出度
在无向图中, 顶点所关联的边的数目称为该顶点的度。在有向图中,顶点”i“的度又分为入度和出度,以顶点”i“为终点的入边的数目称为该顶点的入度,以顶点”i“为起点的出边的数目称为该顶点的出度。一个顶点的入度与出度的和为该顶点的度。一个图中所有顶点的度之和等于边数的两倍。因为图中的每条边分别作为两个邻接点的度各计一次。
3、完全图
若无向图中的每两个顶点之间都存在一条边 ,有向图中的每两个顶点之间都存在方向相反的两条边,则称此图为完全图。显然,含有n个顶点的完全无向图有n(n-1)/2条边,含有n个顶点的完全有向图有n(n-1)条边。
4、连通、连通图和连通分量
在无向图G中,若从顶点”i“到顶点”j”有路径,则称顶点“i”和顶点“j”是连通的。若图G中的任意两个顶点都连通,则称G为连通图,否则称为非连通图。无向图G中的极大连通子图称为G的连通分量。显然,任何连通图的连通分量只有一个,即它本身,而非连通图有多个连通分量。
4、强连通图和强连通分量
在有向图G中,若从顶点“i”到顶点“j”有路径,则称从顶点“i”到顶点“j”连通的。若图G中的任意两个顶点“i”和“j”都连通,即从顶点“i”到顶点“j”和从顶点“j”到顶点“i’都存在路径,则称图G是强连通图。有向图G中的极大强连通子图称为G的强连通分量。显然,强连通图只有一个强连通分量,即它本身,非强连通图有多个强连通分量。一般地,单个顶点自身就是一个强连通分量。
5、权和网
图中的每一条边都可以附有一个对应的数值。 这种与边相关的数值称为权。权可以表示从图中顶点到另一个顶点的距商或花费的代价。边上带有权的图称为带权图。也称作网。


图的存储结构

邻接矩阵

邻接矩阵是表示顶点之间邻接关系的矩阵。设G=(V ,E)是含有n(设n>0)个顶占的图,各顶点的编号为0~n-1,则G的邻接矩阵数组A是n阶方阵,其定义如下
如果G是不带权图(或无权图),则:

如果G是带权图(或有权图),则:

 图2:2个邻接矩阵

邻接矩阵的基本结构运行算法代码

const int MAXV=100;        //图中最多的顶点数
const int INF=0x3f3f3f3f;    //用INF表示∞
class MaxGraph
{
public:
    int edges[MAXV][MAXV];    //邻接矩阵数组,假设元素为int
    int n,e;                    //顶点数和边数
    string vexs[MAXV];        //存放顶点信息
};

图的邻接矩阵存储结构的特点如下:
1、图的邻接矩阵表示是唯一的。
2、对于含有n个顶点的图,在采用邻接矩阵存储时,无论是有向图还是无向图,也无论边的数目是多少,其存储空间均为O(n^2)所以邻接矩阵适合干存储边数较多的调密图
3、无向图的邻接矩阵一定是对称矩阵,因此在顶点个数n很大时可以采用对称矩阵的压缩存储方法减少存储空间。有向图的邻接矩阵不一定是对称矩阵
4、对于无向图,邻接矩阵的第i行(或第i列)非零/非∞元素的个数正好是顶点i的度;对于有向图,邻接矩阵的第i行(或第i列)非零/非∞元素的个数正好是顶点i的出度(或入度)
5、用邻接矩阵存储图时,确定任意两个顶点之间是否有边相连的时间为0(1)

邻接矩阵代码实现

创建邻接矩阵

void CreateMatGraph(int a[][MAXV],int n,int e)
{
    this->n=n;
    this->e=e;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
              this->edges[i][j]=a[i][j];
        }
     }
}      

输出图

void DispMatGraph()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(edge[i][j]==INF)
                 printf("%4s","∞");
            else
                 printf("%4d",edges[i][j]);     
           printf("\n");
         }
     }
}          

邻接表

在含n个顶点的图G的邻接表中,顶点i(0≤i≤n-1)的每一条出边<i,j>(权值为W)对应一个边结点,顶点i的所有出边的边结点构成一个单链表,其中边结点的类型ArcNode
定义如下

struct ArcNode        //边结点类型
{
    int adjvex;        //邻接点
    int weight;        //权值
    ArcNode* nextarc; //指向下一边的边结点
};

给顶点i的单链表加上一个头结点,包含顶点i的信息和指向第一条边的边结点的指针,头结点的类型如下

struct HNode            //头结点类型
{
    string info;        //顶点信息
    ArcNode* firstarc;  //指向第一条边的边结点
};

图3:有向图的邻接表和有向图包含权值的邻接表

设计图的邻接表类AdjGraph如下

template<class i>
class AdjGraph		//图邻接表类
{
public:
	HNode adjlist[MAXV];	//头结点数组
	int n, e;
	AdjGraph()
	{
		for (int i = 0; i < MAXV; i++)
		{
			adjlist[i].firstarc = NULL;
		}
	}
	~AdjGraph()
	{
		ArcNode* pre, * p;
		for (int i = 0; i < n; i++)		//遍历所有头结点
		{
			pre = adjlist[i].firstarc;	//用一个ArcNode指针指向头结点的fitst区域
			if (pre != NULL)			//若不为空
			{
				p = pre->nextarc;		//则用另外一个ArcNode指针指向头结点的下一个结点
				while (p != NULL)		//释放过程,循环删除
				{
					delete pre;			
					pre = p; p = p->nextarc;	//两个指针同步后移
				}
				delete pre;
			}
		}
	}
	void CreateAdjGraph(int a[][MAXV], int n, int e);	//通过a,n,e来建立图的邻接表	
	void DispAdjGraph();								//输出图的邻接表
	int Degree1(AdjGraph* G, int v);					//计算无向图某个顶点的度,v为顶点名称
	i Degree2(AdjGraph& G, int v);				//计算有向图某个顶点的出度和入度,v为顶点名称
};

图的基本运算在邻接表中的实现

假设给定邻接矩阵数组啊,顶点数n和边数e来建立图的。

邻接表存储结构 
template<typename i>
void AdjGraph<i>::CreateAdjGraph(int a[][MAXV], int n, int e)
{
	ArcNode* p;
	this->n = n; this->e = e;
	for (int i = 0; i < n; i++)
	{
		for (int j = n - 1; j >= 0; j--)
		{
			if (a[i][j] != 0 && a[i][j] != INF)
			{
				p = new ArcNode;					//指针类型的构造函数
				p->adjvex = j;
				p->weight = a[i][j];
				p->nextarc = adjlist[i].firstarc;	//采用头插法插入p
				adjlist[i].firstarc = p;

			}
		}
	}
}
输出图邻接表
template<typename i>
void AdjGraph<i>::DispAdjGraph()
{
	ArcNode* p;
	for (int i = 0; i < n; i++)						//遍历每一个头结点
	{
		printf(" [%d]", i);
		p = adjlist[i].firstarc;					//p指向第一个邻接点
		if (p != NULL)
			printf("->");
		while (p!= NULL)
		{
			printf("(%d,%d)", p->adjvex, p->weight);
			p = p->nextarc;							//p移向下一个邻接点
		}
		printf("\n");
	}
}
邻接表计算无向图某个顶点的度
template<typename i>
int AdjGraph<i>::Degree1(AdjGraph* G, int v)			//主要是用来统计某个顶点v的边数的算法
{
	int d = 0;
	ArcNode* p = G->adjlist[v].firstarc;
	while (p != NULL)								//对顶点v中的边数进行统计
	{
		d++;
		p = p->nextarc;
	}
	return d;
}
邻接表计算有向图某个顶点的出度和入度
template<typename i>
i AdjGraph<i>::Degree2(AdjGraph& G, int v)
{
	i ans = { 0,0 };						//ans[o]累计出度,ans[i]累计入读
	ArcNode* p = G.adjlist[v].firstarc;
	while (p != NULL)						//统计单链表v中顶点边结点个数
	{
		ans[0]++;
		p = p->nextarc;
	}
	for (int i = 0; i < G.n; i++)			//统计所有adjvex为v的边结点个数即为顶点v的入读
	{
		p = G.adjlist[i].firstarc;
		while (p != NULL)
		{
			if (p->adjvex == v)
			{
				ans[1]++;
				break;						//在一个单链表中最多只有一个这样的结点
			}
			p = p->nextarc;
		}
	}
	return ans;								//返回出度和入读
}

图的遍历

从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点访问一次且仅访问一次,这个过程称为图的遍历。图的遍历得到的顶点序列称为图遍历序列。

抽象操作,可以是对结点进行的各种处理,这里简化为输出结点的数据信息。
为了保证图中的各顶点在遍历过程中访问到且仅访问一次,需要为每个顶点设一个访问标志;
可以为图设置一个访问标志数组visited[n],用于标示图中每个顶点是否被访问过,它的所有数组元素初始值为0,表示顶点均未被访问,一旦访问过顶点vi,则置访问标志数组中的visited[i]为1,以表示该顶点已访问。 
 

 深度优先遍历

思想(先深搜索):
1、深度优先遍历是指在图中任选一个顶点V,访问V并以V作为初始点
2、选择一个与顶点V相邻且未曾访问过的顶点W,访问W,并以W作为新的出发点,继续进行深度优先遍历;
3、若访问到某个顶点时,发现该顶点的所有邻接点均已访问过,则回溯到前一个访问过的顶点,从该顶点出发继续进行深度优先遍历。直到一直回溯到出发顶点V,并且图中与顶点V邻接的所有顶点均被访问过
4、若此时图中任有未访问的顶点(说明是非连通图),则另选一个尚未访问的顶点作为新的出发点,重复上述过程,直到图中所有顶点均已被访问为止。

基本定义的结构体和类如下

#include<iostream>
#include<string>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXV = 1000;
struct ArcNode
{
	int adjvex;		//邻接点
	int weight;		//权值
	ArcNode* nextarc;	//指向下一条边的边结点
};
struct HNode
{
	string info;	//顶点信息
	ArcNode* firstarc;	//指向第一条边的边结点
};

class AdjGraph		//图邻接表类
{
public:
	HNode adjlist[MAXV];	//头结点数组
	int n, e;
	AdjGraph()
	{
		for (int i = 0; i < MAXV; i++)
		{
			adjlist[i].firstarc = NULL;
		}
	}
	~AdjGraph()
	{
		ArcNode* pre, * p;
		for (int i = 0; i < n; i++)		//遍历所有头结点
		{
			pre = adjlist[i].firstarc;	//用一个ArcNode指针指向头结点的fitst区域
			if (pre != NULL)			//若不为空
			{
				p = pre->nextarc;		//则用另外一个ArcNode指针指向头结点的下一个结点
				while (p != NULL)		//释放过程,循环删除
				{
					delete pre;
					pre = p; p = p->nextarc;	//两个指针同步后移
				}
				delete pre;
			}
		}
	}
	void DFS(AdjGraph& G, int v);		//深度优先遍历(邻接表)
    void BFS(AdjGraph& G, int v);		//广度优先遍历(邻接表)
};

深度优先遍历算法(邻接表)

int visited[MAXV];						//全局标志数组,分辨顶点是否访问
void AdjGraph::DFS(AdjGraph& G, int v)
{
	cout << v << " ";							//输出访问的顶点
	visited[v] = 1;								//将访问的顶点置1
	ArcNode* p = G.adjlist[v].firstarc;			//将p指向顶点v的第一个邻接点
	while (p != NULL)
	{
		int w = p->adjvex;						//邻接点为w
		if (visited[w] == 0)					//若w顶点未被访问,递归访问
		{
			DFS(G, w);
		}
		p = p->nextarc;							//将p置为下一个邻接点
	}
}

深度优先遍历算法(邻接矩阵)

邻接矩阵结构类型代码见上——邻接矩阵的基本结构运行算法代码

该代码是类内声明类外定义

int visited[MAXV];
void MaxGraph::DFS(MaxGraph& g, int v)
{
    cout << v << "  ";                  //访问顶点v
    visited[v] = 1;                     //置顶点v为1,表示已经访问
    for (int w = 0; w < g.n; w++)
    {
        if (g.edges[v][w] != 0 && g.edges[v][w] != INF)
        {
            if (visited[w] == 0)           //存在边<v,w>并且w没有被访问
            {
                DFS(g, w);                  //若w顶点没有被访问,递归访问它
            }
        }        
    }
}

广度优先遍历

广度优先遍历算法(邻接表)

void AdjGraph::BFS(AdjGraph& G, int v)
{
	ArcNode* p;  int  queue[MAXV], first = 0, last = 0;//定义循环队列并初始化first=0,last=0,first指针指向队头元素的前一个位置,last指向队尾元素
	int w;
	cout << G.adjlist[v].info<< " ";  //  访问v
	visited[v] = 1;
	//v进队
	last = (last + 1) % MAXV;
	queue[last] = v;
	while (first != last)//若循环队列不为空
	{
		first = (first + 1) % MAXV;
		w = queue[first];//出队并赋值给w
		p = G.adjlist[w].firstarc;//找到与顶点w邻接的第一个顶点
		while (p != NULL)//当p=NULL时,表示顶点的所有邻接点都被访问过,继续判断循环队列是否为空
		{
			if (visited[p->adjvex] == 0)//如果当前邻接顶点未被访问
			{
				cout << G.adjlist[p->adjvex].info << " ";  //  访问相邻顶点
				visited[p->adjvex] = 1;//  置该顶点已被访问
				last = (last + 1) % MAXV;// 该顶点进队
				queue[last] = p->adjvex;
			}
			p = p->nextarc;  //查看下一个邻接点
		}
	}
}

广度优先遍历算法(邻接矩阵)

该代码是类内声明类外定义

void MaxGraph::BFS(MaxGraph& g, int v)
{
    int visited[MAXV];
    memset(visited, 0, sizeof(visited));        //初始化visited数组
    queue<int>qu;                               //定义一个队列
    cout << v << " ";                           //访问顶点v
    visited[v] = 1;                             //置已访问标记
    qu.push(v);                                 //顶点v入队
    while (!qu.empty())
    {
        int u = qu.front(); qu.pop();           //出队顶点u
        for (int i = 0; i < g.n; i++)
        {
            if (g.edges[u][i] != 0 && g.edges[u][i] != INF)
            {
                if (visited[i] == 0)            //存在边<u,i>并且顶点i未被访问
                {
                    cout << i << " ";           //访问邻接点i
                    visited[i] = 1;             //置已访问标记
                    qu.push(i);                 //邻接点i入队
                }
            }
        }
    }
}

生成树和最小生成树

生成树:包含了图中全部顶点的一个极小连通子图,所有顶点连通但又不形成回路,至少要有n-1条边,才可以使得全部顶点连通
由生成树的定义,可以看出生成树具有以下特点:
1、n个顶点(全部顶点)2、是连通的 3、有n-1条边  4、无回路 
避免:少——不连通    多——构成回路,不满足极小 
最小生成树:在连通网(带权图)中,各边权值之和最小的生成树称为最小生成树
 

 两种常用的构造最小生成树的算法:

Prim算法

#include <iostream>
using namespace std;
#define MAXVEX 6//定义顶点最大个数
#define INF 1000//定义无穷大为1000
typedef char vertexType;//顶点类型定义为字符型
struct Graph {
	vertexType vexs[MAXVEX];//存放顶点的一维数组
	int arc[MAXVEX][MAXVEX];//邻接矩阵,存放边
	int nVex, nEdge;//存放图中的顶点数和边数
};
void  Prim(Graph g, int v)//Prim算法从顶点v出发构造最小生成树
{
	int lowcost[MAXVEX];//lowcost数组,存储候选最短边的权值
	int min;
	int closest[MAXVEX];//closest数组,存储候选最短边,例如closest[i]=k,表示候选最短边(vi, vk)
	int i, j, k;

	for (i = 0; i < g.nVex; i++)	//给lowcost[]和closest[]置初值
	{
		lowcost[i] = g.arc[i][v];
		closest[i] = v;
	}
	lowcost[v] = 0;	//lowcost[v] = 0表示顶点v属于集合U
	for (i = 1; i <= g.nVex - 1; i++)	  	//输出(n-1)条最小生成树的边
	{
		min = INF;//给min初值
		for (j = 0; j < g.nVex; j++)//在lowcost中找最小值,即在候选边中找最短边
			if (lowcost[j] != 0 && lowcost[j] < min)
			{
				min = lowcost[j];
				k = j;			//记录最短边的下标
			}
		cout << " 边(" << closest[k] << "," << k << ")权为:" << min;
		cout << "      即:边(" << g.vexs[closest[k]] << "," << g.vexs[k] << ")权为:" << min << endl;
		lowcost[k] = 0;		//标记顶点k已经加入U
		for (j = 0; j < g.nVex; j++)  //新顶点k从集合V-U进入集合U后,修改更新数组lowcost和closest
			if (lowcost[j] != 0 && g.arc[j][k] < lowcost[j])
			{
				lowcost[j] = g.arc[j][k];
				closest[j] = k;
			}
	}
}
int main()
{
	Graph g;//定义结构体变量g,用于存储图
	g.nVex = 6;//存储顶点个数
	g.nEdge = 10;//存储边的个数
	int i, j;
	for (i = 0; i < g.nVex; i++)   //初始化邻接矩阵
		for (j = 0; j < g.nVex; j++)
		{
			g.arc[i][j] = INF;//边的权值初始化为无穷大
		}
	//存顶点
	g.vexs[0] = 'A'; g.vexs[1] = 'B'; g.vexs[2] = 'C';
	g.vexs[3] = 'D'; g.vexs[4] = 'E'; g.vexs[5] = 'F';
	//存边
	g.arc[0][1] = 6; g.arc[0][2] = 1; g.arc[0][3] = 5;
	g.arc[1][0] = 6; g.arc[1][2] = 5; g.arc[1][4] = 3;
	g.arc[2][0] = 1; g.arc[2][1] = 5; g.arc[2][3] = 5;
	g.arc[2][4] = 6; g.arc[2][5] = 4;
	g.arc[3][0] = 5; g.arc[3][2] = 5; g.arc[3][5] = 2;
	g.arc[4][1] = 3; g.arc[4][2] = 6; g.arc[4][5] = 6;
	g.arc[5][2] = 4; g.arc[5][3] = 2; g.arc[5][4] = 6;

	cout << " 最小生成树的边依次是:" << endl;
	Prim(g, 0);//从顶点0出发构造最小生成树

	return 0;
}

kruskal算法

最小生成树的初始状态为只有n个顶点而无边。此时,图中每个顶点自成一个连通分量
然后,按照边的权值由小到大的顺序,考察G的边集E中的各条边。
若被考察的边的两个顶点属于T的两个不同的连通分量,则将此边作为最小生成树的边加入到T中,同时把两个连通分量连接为一个连通分量
若被考察边的两个顶点属于同一个连通分量,则不能选择此边,以免造成回路,继续考察下一条边,如此下去,当T中的连通分量个数为1时,此连通分量便为G的一棵最小生成树 。

#include <iostream>
using namespace std;
#define MAXUEX 6//定义顶点最大个数
#define INF 1000//定义无穷大为1000
typedef char vertexType;//顶点类型定义为字符型
struct Graph {
	vertexType vexs[MAXUEX];//顶点表,存放顶点的一维数组
	int arc[MAXUEX][MAXUEX];//邻接矩阵,存放边
	int nVex, nEdge;//存放图中的顶点数和边数
};
struct Edge
{
	int u;//边的起始顶点
	int v;//边的终止顶点
	int w;//边的权值
};

void InsertSort(Edge E[], int n)//插入排序,对E数组所存的边按权值递增排序
{
	int i, j;   //循环变量
	Edge temp;     //存储待排序元素
	for (i = 1; i < n; i++) {
		j = i;
		temp = E[i];                         //待排序元素赋值给临时变量
		while (j > 0 && temp.w < E[j - 1].w) {   //当未达到数组的第一个元素或者待插入元素小于当前元素
			E[j] = E[j - 1];             //就将该元素后移
			j--;          //下标减1,继续比较
		}
		E[j] = temp;                 //插入位置已经找到,立即插入
	}
}

void Kruskal(Graph g)
{
	int i, j, u1, v1, sn1, sn2, k;
	int vset[MAXUEX];//记录每个顶点所在的连通分量编号
	Edge E[10];//定义结构体数组E,用于存储边
	k = 0;	//E数组从下标0开始存储边
	for (i = 0; i < g.nVex; i++)	//将邻接矩阵arc[][]中的边存储到数组E
		for (j = 0; j <= i; j++)
			if (g.arc[i][j] != INF)
			{
				E[k].u = i;  E[k].v = j;  E[k].w = g.arc[i][j];
				k++;
			}
	InsertSort(E, g.nEdge);		//对E数组按边权值递增排序

	for (i = 0; i < g.nVex; i++) 	//初始化辅助数组,记录每个顶点的连通分量编号
		vset[i] = i;//每个顶点各自为一个连通分量

	k = 1;		//k表示当前构造生成树的第几条边,用于控制循环次数
	j = 0;		//从E数组中下标0处开始选边
	while (k <= g.nVex - 1)		//循环n-1次,生成最小生成树的n-1条边
	{
		u1 = E[j].u; v1 = E[j].v;	//取一条边的头尾顶点
		sn1 = vset[u1];
		sn2 = vset[v1];		//分别得到两个顶点所属的连通分量编号
		if (sn1 != sn2)  		//如果两顶点属于不同的连通分量,这条边可以选为最小生成树的边
		{
			cout << " 边(" << u1 << "," << v1 << ")权为:" << E[j].w;
			cout << "      即:边(" << g.vexs[u1] << "," << g.vexs[v1] << ")权为:" << E[j].w << endl;
			k++;		   	//最小生成树的边数增1
			for (i = 0; i < g.nVex; i++)  	//两个不同的连通分量统一编号
				if (vset[i] == sn2) 	//连通分量编号为sn2的顶点改为sn1
					vset[i] = sn1;
		}
		j++;			//找E数组中的下一条边
	}
}
int main()
{
	Graph g;//定义结构体变量g,用于存储图
	g.nVex = 6;//存储顶点个数
	g.nEdge = 10;//存储边的个数
	for (int i = 0; i < g.nVex; i++)   //初始化邻接矩阵
		for (int j = 0; j < g.nVex; j++)
		{
			g.arc[i][j] = INF;//边的权值初始化为无穷大
		}
	//存顶点
	g.vexs[0] = 'A'; g.vexs[1] = 'B'; g.vexs[2] = 'C';
	g.vexs[3] = 'D'; g.vexs[4] = 'E'; g.vexs[5] = 'F';
	//存边
	g.arc[0][1] = 6; g.arc[0][2] = 1; g.arc[0][3] = 5;
	g.arc[1][0] = 6; g.arc[1][2] = 5; g.arc[1][4] = 3;
	g.arc[2][0] = 1; g.arc[2][1] = 5; g.arc[2][3] = 5;
	g.arc[2][4] = 6; g.arc[2][5] = 4;
	g.arc[3][0] = 5; g.arc[3][2] = 5; g.arc[3][5] = 2;
	g.arc[4][1] = 3; g.arc[4][2] = 6; g.arc[4][5] = 6;
	g.arc[5][2] = 4; g.arc[5][3] = 2; g.arc[5][4] = 6;
	cout << " 最小生成树的边依次是:" << endl;
	Kruskal(g);
	system("pause");

	return 0;
}

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Dijkstra算法可以用来求带权有向上的最短路径,下面是Java实现: ```java import java.util.*; public class DijkstraAlgorithm { private static final int MAX = Integer.MAX_VALUE; // 定义无穷大 public static void dijkstra(int[][] graph, int start) { int n = graph.length; // 的大小 int[] dist = new int[n]; // 存储起点到各个点的最短距离 boolean[] visited = new boolean[n]; // 标记各个结点是否已经访问过 int[] prev = new int[n]; // 存储到达各个结点的前驱结点 // 初始化 for (int i = 0; i < n; i++) { dist[i] = MAX; visited[i] = false; prev[i] = -1; } dist[start] = 0; // 循环n-1次,每次确定一个顶点的最短路径 for (int i = 0; i < n - 1; i++) { int minDist = MAX; int u = -1; // 找到当前未访问的结点中距离起点最近的结点 for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < minDist) { minDist = dist[j]; u = j; } } if (u == -1) { break; } visited[u] = true; // 更新与u相邻的结点的最短距离 for (int j = 0; j < n; j++) { if (!visited[j] && graph[u][j] != MAX) { int newDist = dist[u] + graph[u][j]; if (newDist < dist[j]) { dist[j] = newDist; prev[j] = u; } } } } // 输出结果 System.out.println("起点为" + start + "的最短路径如下:"); for (int i = 0; i < n; i++) { if (i != start && dist[i] != MAX) { System.out.print("从" + start + "到" + i + "的最短路径为:" + start); int j = i; while (j != start) { System.out.print(" -> " + j); j = prev[j]; } System.out.println(",路径长度为:" + dist[i]); } } } public static void main(String[] args) { int[][] graph = { {0, 1, 12, MAX, MAX}, {MAX, 0, 9, 3, MAX}, {MAX, MAX, 0, MAX, 5}, {MAX, MAX, 4, 0, 13}, {MAX, MAX, MAX, MAX, 0} }; dijkstra(graph, 0); } } ``` 运行结果: ``` 起点为0的最短路径如下: 从0到1的最短路径为:0 -> 1,路径长度为:1 从0到2的最短路径为:0 -> 1 -> 3 -> 2,路径长度为:12 从0到3的最短路径为:0 -> 1 -> 3,路径长度为:4 从0到4的最短路径为:0 -> 1 -> 3 -> 2 -> 4,路径长度为:17 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值