数据结构之图——模板实现

来源:互联网 发布:网站源码本地调试 编辑:程序博客网 时间:2024/06/07 05:53

时间:2014.04.07

地点:基地二楼

-------------------------------------------------------------

一、图的定义

1.无向图

无向图是顶点的有限集合和边的有限集合,两个集合可以为空,称为空图。每个节点称为顶点,每个连接称为边,每条边连接两个顶点,相连顶点的顺序并不重要。

图在问题求解中的作用

  通常可以使用图来表示一个问题,并且可以通过在相应图中求解问题得到该问题的解。比如状态机中各状态的切换。

2.有向图

有向图是顶点的有限集合和边的有限集合,每个集合可以为空,称为空图。每条边连接两个顶点,分别称为源顶点和目标顶点。相连顶点的顺序很重要,按箭头区别顶点顺序。

-------------------------------------------------------------

二、相关术语

  回路:一条连接顶点本身的边

  路径:一个顶点序列,每个顶点间有一条边相连

  多边:理论上图中允许两条或者多条边按同一方向连接两个不同的顶点。称为多边。

  简单图:最简单的图没有回路和多变,这样的图称呼为简单图。

 无向图中顶点的度:该顶点作为边的端点的次数,顶点处的回路会使得该顶点的度增加2

  有向图中的顶点有一个入度:该顶点作为目标顶点的次数

                                         出度:该顶点作为源顶点的次数

有向图中的环:一条起点和终点相同的路径。环的长度为边的条数。

-------------------------------------------------------------

三、图的实现

不同种类的图有不同的实现,介绍如下:

1.使用邻接矩阵表示图

图的边表示为布尔值矩阵。邻接矩阵的每个单元表示某个边是否存在。例如顶点0到顶点2的边,看0行2列的布尔值,true为存在,false为不存在。如果一个图包含n个顶点,那么邻接矩阵为nxn大小。

2.使用二维数组存储邻接矩阵

  C++中可以把邻接矩阵存储在二维数组中,每个数组元素有两个下标。可把二维数组看成是一张表,第一个表示行号,第二个表示列号。在邻接矩阵中,使用二维数组的一行一列表示图的一个顶点,数组元素为true或false。一旦邻接矩阵设置好了,应用程序就可以检查矩阵中的位置,判断哪些边存在,哪些边不存在。

3.使用边列表表示图

假设一个不存在多边的有向图,我们可以创建每个顶点的链表来 表示。比如编号为0的顶点的链表,它包含1和2,表示存在顶点为0到顶点1和顶点0到顶点2的边。抽象来说,就是n个顶点的有向图可以用n个不同的列表表示,列表i给出了顶点i的连接,对于列表i的每一项j,都有一条从i到j的边。

在一个n个顶点的图中,有n条边的列表,n个边列表的头指针存储在一个由n个头指针组成的数组中,当需要判断顶点i和顶点j之间是否存在一条边时,只需检查j是否出现在i的列表中。。

4.使用边集表示图

图的另一种实现方式是使用标准库中的set类,比如为表示一个具有10个顶点的图,可声明一个具有10个整数集的数组:

set<int> connections[10];


这样,connections[i]这样的集合会包含顶点i连接到所有顶点的编号。

5.评价各种图的表示方法

若不考虑空间,邻接矩阵比较容易实现,使用起来比边列表和边集合简单。考虑其他应用场景时,比如:

1.增加或删除边

2.检查某个边是否存在

3.迭代一个循环,为每条具体特定源顶点i的边执行一次循环。

当使用邻接矩阵,前两个操作只需要一个较小的时间常量,但在最坏情况下,如果使用边列表表示,1和2两种场景都需要O(n)次运算,而使用边集合,1和2场景时间有一定程序减小,因为标准集合实现一般将项存储在平衡树中。

  另一方面,使用边列表对于场景3非常有效,仅需一次遍历列表的一个元素即可。所需时间为O(e),e是使用定点i作为源顶点的边的条数,set类也提供每次遍历集合中一个元素的操作。这样边列表和边集合遍历使用某个特定源顶点的边都需要O(e)次操作,但于邻接矩阵而言,遍历源顶点i的边需要检查i行的每个元素,需要时间为O(n),n为顶点的个数。

  一般来说,如果每个顶点仅有很少的边,使用邻接矩阵表示会是一个稀疏矩阵,这太浪费空间,到底使用什么表示方法,视应用场景而定。

-------------------------------------------------------------

四、实现一个简单的有向图(Graph类)

1.图被初始化为没有顶点和边的图

2.有一个成员函数用于添加顶点,对于有n个顶点的图来说,这些顶点从0到n-1开始编号,一旦图有了一些顶点,其他成员函数就可以添加和删除边。

3.使用二维数组实现

4.为图的节点附加信息

template <class Item>class Graph{  public:    //MEMBRE CONSTANTS    static const std::size_t kMaximum=20;  private:    bool edges_[kMaximum][kMaximum];  Item labels_[kMaximum];  std::size_t many_vertices_;};
a.增加顶点的成员函数

当图使用构造函数初始化时,没有顶点和边。图的当期顶点数可使用常量成员函数size获得,于是刚创建的新图size()等于0。

成员函数AddVertex和AddEdge用来添加顶点和边,函数规范如下:

void AddVertex(const Item& label)//Precondition: size()<kMaximum//Postcondition: The size of the graph has been increased by adding one new vertex.This vew vertex has the specified lable and on edges.void AddEdge(zie_t source,size_t target)//Precondition:(source<size()) and (target<size()).//Postcondition:The graph has all the edges that it originally had,and it also has another edge from the specified source to the specified target.(If this edge was already present,then the graph is unchanged.)
比如按上述规范现在可以创建这样一个图:

Graph<double> t;t.AddVertex(3.14);t.AddVertex(2.17);t.AddEdge(1,0);

-------------------------------------------------------------

五、重载下标操纵符

  图创建好后,我们希望用[]操纵符来访问顶点的标签,比方说g是一个已建立好的图时,可以这样

cout<<g[3];        //输出的是标签值
重载下标操作符可以如下定义,返回的是一个引用的意图是允许使用操作符来改变一个标签,如赋值语句g[3]=42:

template <class Item>class Graph{  public:    ......    Item& operator[](std::size_t vertex;    ......);
可以如下实现:

template<classs Item>Item& Graph<Item>::operator[](std::size_t vertex){  assert(vertes<size());  return labels[vertex];}
下标操作符的常量版

上面的普通版返回的是对顶点标签的引用,允许改变标签的值,且不是const的,若要是图是const的,则该方法也不能访问到,因此还有提供const的方法,如下:

template<class Item>const Item& Graph<Item>::operator[](std::size_t vertex) const{  assert(vertex<size());  return labels[vertex];}

-------------------------------------------------------------

六、标签图

  类还提供一个成员函数neighbors,为一个给定的顶点计算边列表。边列表使用标准库set类以整数集合的形式给出,向量包含所有起始于某个源顶点的边的所有目标顶点编号。neighbors方法需要O(n)时间来计算一个集合,但是一旦有了数组,就可以快速遍历一个顶点的所有邻接点。

-------------------------------------------------------------

七、标枪图的实现:

//MEMBER CONSTANT for the graph<Item> template class://static const size_t kMaximum//Graph:::kMaximum is the maxismum number of vertices that a Graph can have.//CONSTRUCTOR for the graph<Item> template class//Graph()//Postcondition:The graph has been initialized with no vertices and no edges.//MODIFICATION MEMBER FUNCTIONS for the Graph<Item> template class://void AddVertex(const Item& label)//Preconditon:Size()<MAXIMUM.//...#ifndef GRAPH_H#define GRAPH_H#include<cstdlib>#include<set>#include<cassert>template<class Item>class Graph{public://MEMBER CONSTANTSstatic const std::size_t kMaximum = 20;//CONSTRUCTOR Graph(){ many_vertices_ = 0;many_edges_=0; }//MODIFICATION MEMBEW FUNCTIONSvoid AddVertex(const Item& label);void AddEdge(std::size_t source, std::size_t target);void RemoveEdge(std::size_t source, std::size_t target);void RemoveTopVertex();Item& operator[](std::size_t vertex);//CONSTANT MEMBER FUNCTIONSstd::size_t Size() const{ return many_vertices_; }std::size_t GetEdgeNum() const{ return many_edges_; }bool IsEdge(std::size_t source, std::size_t target) const;std::set<std::size_t> Neighbors(std::size_t vertex) const;Item operator[](std::size_t vertex) const;private:bool edges_[kMaximum ][kMaximum];Item labels_[kMaximum];std::size_t many_vertices_;std::size_t many_edges_;};template<class Item>void Graph<Item>::AddEdge(std::size_t source, std::size_t target)//Libaray facilities used::cassert,cstdlib{assert(source < Size());assert(target < Size());edges_[source][target] = true;many_edges_++;}template<class Item>void Graph<Item>::AddVertex(const  Item& label)//Library facilities used::cassert,cstdlib{std::size_t new_vertex_number = many_vertices_;std::size_t other_number;assert(Size() < kMaximum);many_vertices_++;for (other_number = 0; other_number < many_vertices_; ++other_number){edges_[other_number][new_vertex_number] = false;edges_[other_number][new_vertex_number] = false;}labels_[new_vertex_number] = label;}template<class Item>bool Graph<Item>::IsEdge(std::size_t source, std::size_t target) const//Bibrary facilities used:cassert,cstdlib{assert(source < Size());assert(target < Size());return edges_[source][target];}template<class Item>Item& Graph<Item>::operator[](std::size_t vertex)//Library facilities used:cassert,cstdlib{assert(vertex < Size());return labels_[vertex];}template<class Item>std::set<std::size_t> Graph<Item>::Neighbors(std::size_t vetex) const//libaray facilities used::cassert,cstdlib,set{std::set<std::size_t> answer;std::size_t i;assert(vertex < Size());for (i = 0; i < Size(); ++i){if (edges[vertex][i])answer.insert(i);}return answer;}template<class Item>void Graph<Item>::RemoveEdge(std::size_t source, std::size_t target)//Library facilities used:cassert,cstdlib{assert(source < Size());assert(target < Size());edges_[source][target] = false;}template<class Item>void Graph<Item>::RemoveTopVertex(){assert(many_vertices_ != 0);std::size_t other_vertex;for (other_vertex = 0; other_vertex < many_vertices_; other_vertex++){edges_[other_vertex][many_vertices_ - 1] = false;edges_[many_vertices_ - 1][other_vertex] = false;}--many_vertices_;}#endif





0 0
原创粉丝点击