算法导论-第22章-基本的图算法-22.1 图的表示

一、综述

图的表示方法通常有两种,即邻接表表示法和邻接矩阵表示法。这两种方法都可以表示有向图和无向图

1.邻接表表示法

(1)用邻接表表示无向图

(2)用邻接表表示有向图

(3)邻接表的优点及适用场合

使用邻接表表示稀疏图比较紧凑

2.邻接矩阵表示法

(1)用邻接矩阵表示无向图

(2)用邻接矩阵表示有向图

(3)邻接矩阵的优点与适用场合

用邻接矩阵表示稠密图可以很快地判别两个给定顶点是否存在邻接边

 

二、代码


1.邻接表表示法 "Link_Graph.h"


#include <iostream>
using namespace std;

#define N 100

struct Vertex;
struct Edge
{
	int start;//一条边的第一个编号,例如(1,2)的1
	int end;//一条边的第二个编号,(1,2)中的2
	int value;//边的权重,本程序默认为1
	Edge *next;//第start起点的链表的下一条边
	Edge(int s, int e, int v):start(s),end(e),value(v),next(NULL){}
};
struct Vertex
{
	Edge *head;
	Vertex():head(NULL){};
};
class Link_Graph
{
public:
	int n;
	Vertex *V;//邻接链表,相当于动态数组 Vertex V[n+1];V[1].head代表编号为1的点的链表,head为链表的第一个结点,每个结点代表一条边,有start和end
	Link_Graph(int num):n(num)
	{
		V = new Vertex[n+1];
	}
	~Link_Graph(){delete []V;}
	//把当前边(start,end)插入到编号为start的点的链表中,插入一条边意味着是有向图,单相插入
	void AddSingleEdge(int start, int end, int value = 1)
	{
		Edge *NewEdge = new Edge(start, end, value);
		//如果编号为start的点的链表为空或者他的第一个结点的end大于要插入的边的end,则把要插入的边放到第一个(保证每个点的链表的边的end按从小到大的顺序排列)
		if(V[start].head == NULL || V[start].head->end > end)
		{
			NewEdge->next = V[start].head;
			V[start].head = NewEdge;
		}
		else
		{
			Edge *e = V[start].head, *pre = e;
			//找到要插入的位置,pre的end小于要插入的边的end,e的end大于等于要插入的边的end
			while(e != NULL && e->end < end)
			{
				pre = e;
				e = e->next;
			}
			//遇到重复的边,则不插入
			if(e && e->end == end)
			{
				delete NewEdge;
				return;
			}
			//否则,插入到pre和e之间
			NewEdge->next = e;
			pre->next = NewEdge;
		}
	}
	//无向图的边的插入
	void AddDoubleEdge(int a, int b, int value = 1)
	{
		AddSingleEdge(a, b, value);
		AddSingleEdge(b, a, value);
	}
	//有向图的边删除
	void DeleteSingleEdge(int start, int end)
	{
		Edge *e = V[start].head, *pre = e;
		while(e && e->end < end)
		{
			pre = e;
			e = e->next;
		}
		//如果e为空,或者当前的e的end>end,则说明要删除的边不存在,直接返回
		if(e == NULL || e->end > end) return;
		//删除的是头结点时,需要先把后一个结点置为头结点
		if(e == V[start].head)
			V[start].head = e->next;
		else
			pre->next = e->next;//直接把前一个指向e的后一个结点
		delete e;
	}
	//无向图的边删除
	void DeleteDoubleEdge(int a, int b)
	{
		DeleteSingleEdge(a, b);
		DeleteSingleEdge(b, a);
	}
	//22.1-1,计算有向图的出度和入度
	void OutDegree();
	void InDegree();
	//22.1-2
	void Print();
	//22.1-3
	Link_Graph* Reverse();
	//22.1-5
	Link_Graph* Square();
};

void Link_Graph::OutDegree()
{
	int i, cnt = 0;
	Edge *e;
	for(i = 1; i <= n; i++)
	{
		cnt = 0;//保存编号为i的结点的链表中结点的个数,即出度
		e = V[i].head;
		while(e)
		{
			cnt++;
			e = e->next;
		}
		cout<<"顶点"<<i<<"的出度为"<<cnt<<endl;
	}
}

void Link_Graph::InDegree()
{
	//ret[i]保存编号为i的点的入度,即在别的点的链表中出现的次数
	int ret[N+1] = {0}, i;

	Edge *e;
	for(i = 1; i <= n; i++)
	{
		e = V[i].head;
		while(e)
		{
			ret[e->end]++;
			e = e->next;
		}
	}
	for(i = 1; i <= n; i++)
		cout<<"顶点"<<i<<"的入度为"<<ret[i]<<endl;
}

void Link_Graph::Print()
{
	int i;
	Edge *e;
	for(i = 1; i <= n; i++)
	{
		cout<<i;
		e = V[i].head;
		while(e)
		{
			cout<<"->"<<e->end;
			e = e->next;
		}
		cout<<endl;
	}
}
//转置,即把有向图的箭头反一下
Link_Graph* Link_Graph::Reverse()
{
	Link_Graph *ret = new Link_Graph(n);  
	int i;  
	//遍历图G中的每一条边,以终点为起点,以起点为终点,加入到新图RET中  
	for(i = 1; i <= n; i++)  
	{  
		Edge *e = V[i].head;  
		while(e)  
		{  
			ret->AddSingleEdge(e->end, e->start);  
			e = e->next;  
		}  
	}  
	return ret; 
}

Link_Graph* Link_Graph::Square()
{
	int i;
	Link_Graph *ret = new Link_Graph(n);  
	//遍历图中每个顶点  
	for(i = 1; i <= n; i++)  
	{  
		//遍历以该顶点为起点的边  
		Edge *e = V[i].head, *e2;  
		while(e)  
		{  
			//把原来有的边加入到新图中  
			ret->AddSingleEdge(e->start, e->end);  
			//把以e的终点为起点的边加入当前点的链表中  
			e2 = V[e->end].head;  
			while(e2)  
			{  
				ret->AddSingleEdge(e->start, e2->end);  
				e2 = e2->next;  
			}  
			e = e->next;  
		}     
	}  
	return ret;
}



2.邻接矩阵表示法


#include <iostream>
using namespace std;

#define N 100

class Mat_Graph
{
public:
	int n;
	int Map[N+1][N+1];

	Mat_Graph(int num):n(num)
	{
		memset(Map, 0, sizeof(Map));
	}
	void AddDoubleEdge(int a, int b, int value = 1)
	{
		AddSingleEdge(a, b, value);
		AddSingleEdge(b, a, value);
	}
	void AddSingleEdge(int start, int end, int value = 1)
	{
		Map[start][end] = value;
	}
	void DeleteDoubleEdge(int a, int b)
	{
		DeleteSingleEdge(a, b);
		DeleteSingleEdge(b, a);
	}
	void DeleteSingleEdge(int start, int end)
	{
		Map[start][end] = 0;
	}
	//22.1-2
	void Print();
	//22.1-3
	Mat_Graph* Reverse();
	//22.1-5
	Mat_Graph* Square();
	//22.1-6
	bool Universal_Sink();
};

void Mat_Graph::Print()
{
	int i, j;
	for(i = 1; i <= n; i++)
	{
		for(j = 1; j <= n; j++)
			cout<<Map[i][j]<<' ';
		cout<<endl;
	}
}

Mat_Graph* Mat_Graph::Reverse()
{
	int i, j;
	Mat_Graph *ret = new Mat_Graph(n);
	for(i = 1; i <= n; i++)
	{
		for(j = 1; j <= n; j++)
			if(Map[i][j])
				ret->AddSingleEdge(j, i);
	}
	return ret;
}

Mat_Graph* Mat_Graph::Square()
{
	int i, j, k;
	Mat_Graph *ret = new Mat_Graph(n);  
	//遍历图中每个顶点  
	for(i = 1; i <= n; i++)  
	{  
		for(j = 1; j <= n; j++)  
		{  
			if(Map[i][j])  
			{  
				//把原来有的边加入到新图中  
				ret->AddSingleEdge(i, j);
				//把以E的终点为起点的边加入新图中  
				for(k = 1; k <= n; k++)  
					if(Map[j][k])  
						ret->AddSingleEdge(i, k);  
			}  
		}  
	}  
	return ret;
}

bool Mat_Graph::Universal_Sink()
{
	//i指向有可能是汇的编号最小的点  
	int i = 1, j = 1;  
	while(j <= n)  
	{  
		//map[i][j] = 0,那么j没有i的入,一定不是汇,i可能是汇  
		if(Map[i][j] == 0)  
			j++;  
		//若map[i][j] = 1,则(1)i有出,i不是汇(2)map[i][i+1..j-1]=0,i+1..j-1都不是汇(3)j及j以后的点可能是汇  
		//若i=j,j也不是汇,j+1可能是汇      
		else if(i == j)  
		{  
			i++;  
			j++;  
		}  
		//若i!=j,j以前的点都不是汇,j可能是汇  
		else i = j;  
	}  
	//没有汇  
	if(i > n)  
		return false;  
	//找到有可能是汇的点,但是还是需要验证一下  
	else  
	{  
		//汇的纵向都是1,除了map[i][i]  
		for(j = 1; j <= i-1; j++)  
		{  
			if(Map[i][j] == 1)  
				return false;  
		}  
		//汇的横向都是0  
		for(j = 1; j <= n; j++)  
			if( i != j && Map[j][i] == 0)  
				return false;  
		return true;  
	}  
}

3.main.cpp 


#include <iostream>
using namespace std;

#include "Link_Graph.h"
#include "Mat_Graph.h"

void Test1()
{
	cout<<"Test1"<<endl;
	int n, a, b;//n为边的个数,a为一条边的前一个编号,b为一条边的后一个编号
	cin>>n;
	Link_Graph *LG = new Link_Graph(n);
	while(cin>>a>>b && a && b)//输入00时结束
		LG->AddSingleEdge(a, b);//增加一条边
	LG->OutDegree();//计算出度(有向图)
	LG->InDegree();//计算入度
	delete LG;
}
void Test2()
{
	cout<<"Test2"<<endl;
	Link_Graph *LG = new Link_Graph(6);
	Mat_Graph *MG = new Mat_Graph(6);
	int edge[6][2] = {{1,2},{1,3},{2,4},{2,5},{3,6},{3,6}};
	int i = 0;
	for(i = 0; i < 6; i++)
	{
		LG->AddSingleEdge(edge[i][0], edge[i][1]);
		MG->AddSingleEdge(edge[i][0], edge[i][1]);
	}
	LG->Print();
	MG->Print();
	delete LG;
	delete MG;
}
void Test3()
{
	cout<<"Test3"<<endl;
	int n, a, b;
	cin>>n;
	Link_Graph *LG = new Link_Graph(n);
	Mat_Graph *MG = new Mat_Graph(n);
	while(cin>>a>>b && a && b)//输入00时结束
	{
		LG->AddSingleEdge(a, b);
		MG->AddSingleEdge(a, b);
	}
	Link_Graph *NewLG = LG->Reverse();
	NewLG->Print();
	Mat_Graph *NewMG = MG->Reverse();
	NewMG->Print();
	delete LG;
	delete MG;
	delete NewLG;
	delete NewMG;
}
void Test5()
{
	cout<<"Test5"<<endl;
	int n, a, b;
	cin>>n;
	Link_Graph *LG = new Link_Graph(n);
	Mat_Graph *MG = new Mat_Graph(n);
	while(cin>>a>>b && a && b)//输入00时结束
	{
		LG->AddSingleEdge(a, b);
		MG->AddSingleEdge(a, b);
	}
	Link_Graph *NewLG = LG->Square();
	NewLG->Print();
	Mat_Graph *NewMG = MG->Square();
	NewMG->Print();
	delete LG;
	delete MG;
	delete NewLG;
	delete NewMG;
}

void Test6()
{
	cout<<"Test6"<<endl;
	int n, a, b;
	cin>>n;
	Mat_Graph *MG = new Mat_Graph(n);
	while(cin>>a>>b && a && b)//输入00时结束
		MG->AddSingleEdge(a, b);
	if(MG->Universal_Sink())
		cout<<"包含通用的汇"<<endl;
	else
		cout<<"不包含通用的汇"<<endl;
	delete MG;
}
/*
6
1 2
1 4
4 2
2 5
5 4
3 5
3 6
6 6
0 0
*/
int main()
{
	Test1();
	//Test2();
	//Test3();
	//Test5();
	//Test6();
	return 0;
}

转自: http://blog.csdn.net/mishifangxiangdefeng/article/details/7837650
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值