图的基本操作

1. 图的基本概念

图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E),

 顶点集合V = {x|x属于某个数据对象集}是有穷非空集合;

 E = {(x,y)|x,y属于V}或者E = {<x, y>|x,y属于V && Path(x, y)}是顶点间关系的有穷集合, 也叫做边的集合。 

(x, y)表示x到y的一条双向通路,即(x, y)是无方向的;

 Path(x, y)表示从x到y的一条单向通路,即Path(x, y)是有方向的。

图中结点称为顶点,第i个顶点记作vi。两个顶点vi和vj相关联称作顶点vi和顶点vj之 间有一条边,图中的第k条边记作ek,ek = (vi,vj)或<vi,vj>。

2. 图的分类

图分为有向图和无向图。

有向图(如左图):顶点对<x, y>是有序的,顶点对<x,y>称为顶点x到顶点y的一条 边(弧),<x, y>和<y, x>是两条不同的边。


无向图(如右图):顶点对(x, y)是无序的,顶点对(x,y) 称为顶点x和顶点y相关联的一条边,这条边没有特定方向,(x, y)和(y,x)是同一条边。无向边 (x, y)等于有向边<x, y>和<y, x>。

3. 图的基本性质

(1)邻接顶点:在无向图中G中,若(u, v)是E(G)中的一条边,则称u和v互为邻接顶点,并称边(u, v)依附于顶点u和v;

                         在有向图G中,若<u, v>是E(G)中的一条边,则称顶点u邻接到v,顶点v邻接 自顶点u,并称边<u, v>与顶点u和顶点v相关联。

(2)顶点的度:顶点v的度是指与它相关联的边的条数,记作deg(v)。

在有向图中,顶点的度等于该顶 点的入度(indev)与 出度(outdev)之和,即 dev(v) = indev(v) + outdev(v) ;

对于无向图,顶点的度等于该顶点的入度和出度,即dev(v) = indev(v) = outdev(v);

(3)路径:在图G = (V, E)中,若从顶点vi出发有一组边使其可到达顶点vj,则称顶点vi到顶点vj的顶点序列为从顶点vi到顶点vj的路径。例如左图:顶点0到顶点3的路径为{0,1,3};

         

(4)路径长度:对于不带权的图,一条路径的路径长度是指该路径上的边的条数,如左图:顶点0到3的路径长度为2;对于带权的图,一条路径的路径长度是指该路径上各个边权值的总和,如右图:顶点A到E的路径长度为75。

(5)简单路径与回路:若路径上各顶点v1,v2,v3,…,vm均不重复,则称这样的路径为简单路径(如左图)。若路 径上第一个顶点v1和最后一个顶点vm重合,则称这样的路径为回路或环(如右图)。

                      

(6)子图:设图G = {V, E}和图G1 = {V1,E1},若V1属于V且E1属于E,则称G1是G的子图。

(7)连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。

        如果图中任意一 对顶点都是连通的,则称此图为连通图。

        任意N顶点的连通图有N-1条边。

(8)强连通图:在有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj到 vi的路径,则称此图是强连通图。

         任意N顶点的强连通图有N条边。 

(9)生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边。

4. 图的存储结构
  邻接矩阵

(1)将所有顶点的信息组织成一个顶点表,然后利用一个矩阵来表示个顶点之间的邻接关系。 设图G = (V, E)包含n个顶点,则A的邻接矩阵是一个二维数组G.Edge[n][n]。

(2)无向图的邻接矩阵是对称的,第i行(列)元素之和,就是顶点i的度。

        有向图的邻接矩阵则不一定 是对称的,第i行(列)元素之后就是顶点i 的出(入)度。

(3)利用邻接矩阵实现图的基本操作

template <class T,class W,bool Isdirector=true>
class Graph
{
public:
	Graph()
	{}
	Graph(const T* arr,const size_t& size)
	{
		_v.resize(size);
		for (size_t i = 0; i < size; i++)
		{
			_v[i] = arr[i];
		}
		//动态开辟二维数组
			_Edge.resize(size);
			for (size_t i = 0; i < size; i++)
			{
				(_Edge[i]).resize(size);
			}

		
		
	}
	size_t Getsign(const T& value)
	{
		int size = _v.size();
		int index = 0;
		for (index = 0; index < size; index++)
		{
			if (_v[index] == value)
				return index;
		}
		return -1;
	}
	void Insert(const T& value1, const T& value2, const W&weight)
	{
		int index1 = Getsign(value1);
		int index2 = Getsign(value2);
		if (index1 != -1 && index2 != -1)
		{
			_Edge[index1][index2] = weight;  //有向图

			if (Isdirector)   //无向图
				_Edge[index2][index1] = weight;
		}
	}
	int GetWeight(const T& value1, const T& value2)
	{
		int index1 = Getsign(value1);
		int index2 = Getsign(value2);
		if (index1 == -1 || index2 == -1)
		{
			cout << "顶点不存在!" << endl;
		}
		else
		{
			return _Edge[index1][index2];
		}
	}
	//获取顶点的度
	int Getdev(const T& value)
	{
		int size = _v.size();
		int index = Getsign(value);
		int dev = 0;
		if (index != -1)
		{
			//无向图
			for (size_t i = 0; i < size; i++)
			{
				if (_Edge[index][i] != 0)
					dev++;
			}
			//有向图
			if (!Isdirector)
			for (size_t j = 0; j < size; j++)
			{
				if (_Edge[j][index] != 0)
					dev++;
			}
			return dev;
		}
		return -1;
	}
	void showGraph()
	{
		size_t size = _v.size();
		for (size_t i = 0; i < size; i++)
		{
			for (size_t j = 0; j < size; j++)
			{
				printf("%-2d ", _Edge[i][j]);
			}
			cout << " " << endl;
		}
	}
private:
	vector<T> _v; //图的顶点
	vector<vector<W> > _Edge; //任意两个顶点的边的权值
};

注:在使用邻接矩阵存储边时,如果是无向图资源浪费问题不大,但是对于有向图来说,若只有极少数边的话,即当e远远小于n^2时,空间浪费比较严重。

邻接表

(1)邻接表:使用数组表示顶点的集合,使用链表表示边的关系

(2)无向图

无向图中同一条边在邻接表中出现了两次。 如果想知道顶点vi的度,只需要知道顶点vi边链表集合中结点的数目即可。


(3)有向图

有向图中每条边在邻接表中只出现一次。 与顶点vi对应的邻接表所含结点的个数,就是该顶点的出度,也称出度表。 要得到vi顶点的入度,必须检测其他所有顶点对应的边链表,看有多少边顶点的dst取值是i

        


(4)利用邻接表实现图的基本操作

template<class W,bool Isdirector=true>
struct GraphNode
{
	GraphNode(const size_t& src,const size_t& dest,const W& weight)
	:_src(src)
	, _dest(dest)
	, _weight(weight)
	,_PNext(NULL)
	{}
	GraphNode<W>* _PNext;
	size_t  _src;  //起点下标
	size_t _dest; //终点下标
	W _weight; //边的权值
};

template <class W>
class Compare
{
	typedef GraphNode<W> * PNode;
public:
	bool operator()(PNode Left, PNode Right)
	{
		return Left->_weight < Right->_weight;
	}
};
template<class T,class W,bool Isdirector=true>
class Graph
{
	typedef GraphNode<W> Node;
	typedef Node* PNode;
public:
	Graph(const T* arr, const size_t& size)
	{
		_v.resize(size);
		_Edge.resize(size);
		for (size_t i = 0; i < size; i++)
		{
			_v[i] = arr[i];
		}
	}
	size_t Getsign(const T& value)
	{
		int size = _v.size();
		int index = 0;
		for (index = 0; index < size; index++)
		{
			if (_v[index] == value)
				return index;
		}
		return -1;
	}
	void Insert(const T& value1, const T& value2,const W& weight)
	{
		int src = Getsign(value1);
		int dest = Getsign(value2);
		PNode Pcursrc = _Edge[src];
		PNode Pcurdest = _Edge[dest];
		//看当前边是否在邻接表中
		/*while (Pcursrc)
		{
			if (Pcursrc->_dest == dest)
				return;
			Pcursrc = Pcursrc->_PNext;
		}*/
		//有向图
		if (src != -1 && dest != -1)
		{
			PNode newNode = new Node(src,dest,weight);
		    newNode->_PNext = Pcursrc;
			_Edge[src] = newNode;

			if (Isdirector)//无向图
			{
				PNode newNode = new Node(src,dest,weight);
				newNode->_PNext =Pcurdest;
				_Edge[dest] = newNode;
			}
		}
	}
	int Getdev(const T& value)
	{
		int index = Getsign(value);
		size_t size = _v.size();
		PNode Pcur = _Edge[index];
		int count = 0;
		//无向图
		while (Pcur != NULL)
		{
			count++;
			Pcur = Pcur->_PNext;	
		}

		if (!Isdirector)
		{
			for (size_t i = 0; i < size && i!=index; i++)
			{
				PNode pcur = _Edge[i];
				while (pcur)
				{
					if (pcur->_dest == index)
						count++;
				}
			}
		}
		return count;
	}
	int GetWeight(const T& value1, const T& value2)
	{
		int src = Getsign(value1);
		int dest = Getsign(value2);

		if (src != -1 && dest != -1)
		{
			PNode pcur = _Edge[src];
			while (pcur)
			{
				if (pcur->_dest == dest)
					return pcur->_weight;
				pcur = pcur->_PNext;
			}
		}
		return -1;
		
	}
	void showGraph()
	{
		size_t size = _v.size();
		for (size_t i = 0; i < size; i++)
		{
			PNode pcur = _Edge[i];
			while (pcur)
			{
				cout << _v[pcur->_src] << " "<<_v[pcur->_dest]<<" " << pcur->_weight << endl;
				pcur = pcur->_PNext;
			}
		}
	}
private:
	vector<T> _v; //顶点
	vector<PNode> _Edge; //存储边

};


  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值