图结构的邻接表表示及相关算法

图结构的邻接表表示

#include<iostream>
#include<queue>
using namespace std;

/*图的邻接表表示法*/

#define MaxVertexNum 100         /*最大顶点数设为100*/
typedef int Vertex;              /*用顶点下标表示顶点,为整型*/
typedef int WeightType; 		 /*边的权值设为整型*/
typedef char DataType;  		 /*顶点存储的数据类型设为字符型*/
bool Visited[MaxVertexNum]= {false};
/*边的定义*/
typedef struct ENode* PtrToENode;
struct ENode {
	Vertex V1,V2;				/*有向边<V1,V2>*/
	WeightType Weight ;			/*权重*/
};
typedef PtrToENode Edge;

/*邻接点的定义*/
typedef struct AdjVNode* PtrToAdjVNode;
struct AdjVNode {
	Vertex Adjx;				/*邻接点下标*/
	WeightType Weight;			/*边权重*/
	PtrToAdjVNode Next; 			/*指向下一个邻接点的指针*/
};

/*顶点表头结点的定义*/
typedef struct Vnode {
	PtrToAdjVNode FirstEdge;	/*边表头指针*/
	DataType Data;				/*存顶点的数据*/
	/*注意:很多情况下,顶点无数据,此时Data可以不用出现*/
} AdjList[MaxVertexNum];

/*图结点的定义*/
typedef struct GNode* PtrToGNode;
struct GNode {
	int Nv;			/*顶点数*/
	int Ne;			/*边数*/
	AdjList G; 		/*邻接表*/
};
typedef PtrToGNode LGraph;			/*以邻接表方式存储的图类型*/


LGraph CreateGraph(int VertexNum) {
	/*初始化一个有VertexNum个顶点但没有边的图*/
	Vertex V;
	LGraph Graph;

	Graph=(LGraph)malloc(sizeof(struct GNode));
	Graph->Ne=0;
	Graph->Nv=VertexNum;
	/*初始化邻接表表头指针*/
	/*注意:这里默认顶点表号从0开始,到(Graph->Nv-1)*/
	for(V=0; V<Graph->Nv; V++)
		Graph->G[V].FirstEdge=NULL;

	return Graph;
}

void InsertEdge(LGraph Graph,Edge E) {
	PtrToAdjVNode NewNode;
	/*插入边<V1,V2>*/
	/*为V2建立新的邻接点*/
	NewNode =(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
	NewNode->Adjx=E->V2;
	NewNode->Weight=E->Weight;
	/*将V2插入V1*/
	NewNode->Next=Graph->G[E->V1].FirstEdge;
	Graph->G[E->V1].FirstEdge=NewNode;

	/*若是无向图,还要插入边<V2,V1>*/
	/*为V1建立新的邻接点*/
	NewNode=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
	NewNode->Adjx=E->V1;
	NewNode->Weight=E->Weight;
	/*将V1插入V2的表头*/
	NewNode->Next=Graph->G[E->V2].FirstEdge;
	Graph->G[E->V2].FirstEdge=NewNode;
}

LGraph BuildGraph() {
	LGraph Graph;
	Edge E;
	Vertex V;
	int Nv,i;

	scanf("%d",&Nv);  /*读入顶点个数*/
	Graph=CreateGraph(Nv); /*初始化有Nv个顶点但没有边的图*/

	scanf("%d",&(Graph->Ne));  /*读入边数*/
	if(Graph->Ne!=0) {			/*如果有边*/
		E=(Edge)malloc(sizeof(struct ENode));				/*建立边结点*/
		/*读入边,格式为“起点 终点 权重”,插入邻接表*/
		for(i=0; i<Graph->Ne; i++) {
			scanf("%d %d %d",&E->V1,&E->V2,&E->Weight);
			/*注意:如果权重不是整型,Weight的读入格式要改*/
			InsertEdge(Graph,E);
		}
	}

	/*如果顶点有数据的话,读入数据*/
	for(V=0; V<Graph->Nv; V++)
		scanf("%c",&(Graph->G[V].Data));

	return Graph;
}

/*邻接表存储的图 - DFS*/
void Visit(Vertex V) {
	printf("正在访问顶点%d\n",V);
}

/*Visited[]为全局变量,已经初始化魏false*/
void DFS(LGraph Graph,Vertex V,void(*Visit)(Vertex)) {
	/*以V为出发点对邻接表存储的图Graph进行DFS搜索*/
	PtrToAdjVNode W;

	Visit(V); /*访问第V个顶点*/
	Visited[V]=true;
	for(W=Graph->G[V].FirstEdge; W; W=W->Next)
		if(!Visited[W->Adjx])
			DFS(Graph,W->Adjx,Visit);
}

/*;邻接表存储--无权图的单源最短路径算法*/

/*dist[]和path[]全部初始化为-1*/
void Unweighted(LGraph Graph,int dist[],int path[],Vertex S) {
	queue<int >Q;
	Vertex V;
	PtrToAdjVNode W;

	dist[S]=0;/*初始化源点*/
	Q.push(S);

	while(!Q.empty()) {
		V=Q.front();
		Q.pop();
		for(W=Graph->G[V].FirstEdge; W; W=W->Next) { /*对V的每个邻接点W->AdjV*/
			if(dist[W->Adjx]==-1) { /*若W->AdjV未被访问过*/
				dist[W->Adjx]=dist[V]+1;/*W->AdjV到S的距离跟新*/
				path[W->Adjx]=V; /*将V记录在S到W->AdjV的路径上*/
				Q.push(W->Adjx);
			}
		}

	} /*While结束*/

}

/*邻接表存储 - Kruskal最小生成树算法*/

/*-----------------顶点并查集定义----------------*/
typedef Vertex ELementType;/*默认元素可以用非负整数表示*/
typedef Vertex SetName; 	/*默认用根节点的下标作为集合名称*/
typedef ElementType SetType[MaxVertexNum];/*假设集合元素下标从0开始*/

void InitializeVSet(SetType S,int N) {
	/*初始化并查集*/
	ELementType X;
	for(X=0; X<N; X++)S[X]=-1;
}

void Union(SetType S,SetName Root1,SetName Root2) {
	if(S[Root2]<S[Root1]) { /*如果集合2比较大*/
		S[Root2]+=S[Root1];	/*集合1并入集合2*/
		S[Root1]=Root2;
	} else {					/*如果集合1比较大*/
		S[Root1]+=S[Root2];	/*集合2并入集合1*/
		S[Root2]=Root1;
	}
}

SetName Find(SetType S,ElementType X) {
	/*默认集合元素全部初始化为-1*/
	if(S[X]<0)/*找到集合根*/
		return X;
	else
		return S[X]=Find(S,S[X]); //路径压缩
}

bool CheckCycle(SetType Vset,Vertex V1,Vertex V2) {
	/*检查连接V1和V2的边是否在现有的最小生成树子集中构成回路*/
	Vertex Root1,Root2;
	Root1=Find(Vset,V1);/*得到V1所属的连通集名称*/
	Root2=Find(Vset,V2);/*得到V2所属的连通集名称*/

	if(Root1==Root2)	/*若V1和V2已经连通,则该边不能要*/
		return false ;
	else { /*否则该边可以被收集,同时将V1和V2并入同一连通集*/
		Union(Vset,Root1,Root2);
		return true;
	}
}
/*----------------------------并查集定义结束--------------*/

/*----------------------------边的最小堆定义--------------*/
void PercDown(Edge Eset,int p,int N) {
	/*改编代码4.24的PercDown(MaxHeap H,int p)*/
	/*将N个元素的边数组中以Eset[p]为根的子堆调整为关于Weight的最小堆*/
	int Parent,Child;
	struct ENode X;

	X=Eset[p];/*取出根节点存放的值*/
	for(Parent=p; (Parent*2+1)<N; Parent=Child) {
		Child=Parent*2+1;
		if(Child!=N-1)&&(Eset[Child].Weight>Eset[Child+1].Weight)
			Child++;/*Child指向左右子节点的较小者*/
		if(X.Weight<=Eset[Child].Weight)break;/*找到了合适的位置*/
		else Eset[Parent]=Eset[Child];
	}

	Eset[Parent]=X;

}

void InitializeESet(LGraph Graph, Edge Eset) {
	/*将图的边存在数组Eset,并且初始化为最小堆*/
	Vertex V;
	PtrToAdjVNode W;
	int ECount;

	/*将图的边存在数组Eset*/
	ECount=0;
	for(V=0; V<Graph->Nv; V++) {
		for(W=Graph->G[V].FirstEdge; W; W=W->Next)
			if(V<W.Adjx) { /*避免重复录入无向图的边,只收V1<V2的边*/
				Eset[ECount].V1=V;
				Eset[ECount].V2=W->Adjx;
				Eset[ECount++].Weight=W->Weight;
			}
	}
	/*初始最小堆*/
	for(ECount=Graph->Ne/2; ECount>=0; ECount--)
		PercDown(Eset,ECount,Graph->Ne);
}

int GetEdge(Edge Eset,int CurrentSize) {
	/*给定当前堆的大小CurrentSize,将当前最小位置弹出并调整堆*/

	/*将最小边与当前的最后一个位置的边交换*/
	Swap(&Eset[0],&Eset[CurrentSize-1]);
	/*将剩下的边继续调整成最小堆*/
	PercDown(Eset,0,CurrentSize-1);

	return CurrentSize-1;/*返回最小边所在位置*/
}
/*-----------------------------最小堆结束-------------------*/

int Kruskal(LGraph Graph,LGraph MST) {
	/*将最小生成树保存魏邻接表村存储的图MST,返回最小权重和*/
	WeightType TotalWeight;
	int ECount,NextEdge;
	SetType VSet;/*顶点数组*/
	Edge Eset;/*边数组*/

	InitializeVSet( VSet, Graph->Nv ); /* 初始化顶点并查集 */
	ESet = (Edge)malloc( sizeof(struct ENode)*Graph->Ne );
	InitializeESet( Graph, Eset ); /* 初始化边的最小堆 */
	/* 创建包含所有顶点但没有边的图。注意用邻接表版本 */
	MST = CreateGraph(Graph->Nv);
	TotalWeight = 0; /* 初始化权重和     */
	ECount = 0;      /* 初始化收录的边数 */

	NextEdge = Graph->Ne; /* 原始边集的规模 */
	while ( ECount < Graph->Nv-1 ) {  /* 当收集的边不足以构成树时 */
		NextEdge = GetEdge( ESet, NextEdge ); /* 从边集中得到最小边的位置 */
		if (NextEdge < 0) /* 边集已空 */
			break;
		/* 如果该边的加入不构成回路,即两端结点不属于同一连通集 */
		if ( CheckCycle( VSet, ESet[NextEdge].V1, ESet[NextEdge].V2 )==true ) {
			/* 将该边插入MST */
			InsertEdge( MST, ESet+NextEdge );
			TotalWeight += ESet[NextEdge].Weight; /* 累计权重 */
			ECount++; /* 生成树中边数加1 */
		}
	}
	if ( ECount < Graph->Nv-1 )
		TotalWeight = -1; /* 设置错误标记,表示生成树不存在 */

	return TotalWeight;
}


/* 邻接表存储 - 拓扑排序算法 */
 
bool TopSort( LGraph Graph, Vertex TopOrder[] )
{ /* 对Graph进行拓扑排序,  TopOrder[]顺序存储排序后的顶点下标 */
    int Indegree[MaxVertexNum], cnt;
    Vertex V;
    PtrToAdjVNode W;
       Queue Q = CreateQueue( Graph->Nv );
  
    /* 初始化Indegree[] */
    for (V=0; V<Graph->Nv; V++)
        Indegree[V] = 0;
         
    /* 遍历图,得到Indegree[] */
    for (V=0; V<Graph->Nv; V++)
        for (W=Graph->G[V].FirstEdge; W; W=W->Next)
            Indegree[W->AdjV]++; /* 对有向边<V, W->AdjV>累计终点的入度 */
             
    /* 将所有入度为0的顶点入列 */
    for (V=0; V<Graph->Nv; V++)
        if ( Indegree[V]==0 )
            AddQ(Q, V);
             
    /* 下面进入拓扑排序 */ 
    cnt = 0; 
    while( !IsEmpty(Q) ){
        V = DeleteQ(Q); /* 弹出一个入度为0的顶点 */
        TopOrder[cnt++] = V; /* 将之存为结果序列的下一个元素 */
        /* 对V的每个邻接点W->AdjV */
        for ( W=Graph->G[V].FirstEdge; W; W=W->Next )
            if ( --Indegree[W->AdjV] == 0 )/* 若删除V使得W->AdjV入度为0 */
                AddQ(Q, W->AdjV); /* 则该顶点入列 */ 
    } /* while结束*/
     
    if ( cnt != Graph->Nv )
        return false; /* 说明图中有回路, 返回不成功标志 */ 
    else
        return true;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值