图G=(V,E)。要表示一个图,通常有两种方法:邻接表和邻接矩阵。两种方法都既可以表示有向图,也可以表示无向图。
邻接表表示由一个包含|V|个列表的数组组成,其中每个列表对应V中的一个顶点。每个邻接表中的顶点一般以任意顺序存储。
实例:
图一 无向图的邻接矩阵表示
图二 无向图的邻接表表示
图三 有向图的邻接矩阵
图四 有向图的邻接表表示
图五 带权图的邻接矩阵表示
邻接表适合表示稀疏图。所需要的存储空间是O(V+E)。
邻接矩阵所需存储空间:O(V*V)。
算法思想:
广度优先搜索假设从图中某个顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后再分别从这些邻接点出发依次访问它们的邻接点,并使先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问(因此需要用队列来存储顶点),直到图中所有已被访问的顶点的邻接点都被访问为止。如果此时图中还有未被访问的顶点,则另选图中未被访问的顶点作为起点,重复上述过程,直到图中所有顶点都被访问为止。
通常采用队列作为辅助结构。
图示:
图六 用于广度优先搜索的图-德国城市分布图
图七 广度优先搜索结果
注意:广度优先搜索计算出来的每个顶点到源顶点之间的距离就是最短路径距离。
时间复杂度:O(V+E)。
采用了队列辅助数据结构。每个顶点只入队列一次,也最多出队列一次。入队和出队的时间复杂度均为O(1)。因此队列操作所需的时间O(V)。当每个顶点出队时才需要扫描链表,每个顶点的邻接表只被扫描一次。所有邻接表长度为O(E),因此扫描邻接表时间复杂度为O(E)。初始化开销为O(V)。
简易代码:
std::queue visited, unvisited;
node nodes[9];
node* current;
unvisited.push(&nodes[0]); //先把root放入unvisited queue
while(!unvisited.empty()) //只有unvisited不空
{
current = (unvisited.front()); //目前應該檢驗的
if(current -> left != NULL)
unvisited.push(current -> left); //把左邊放入queue中
if(current -> right != NULL) //右边压入。因为QUEUE是一个先进先出的结构,所以即使后面再压其他东西,依然会先访问这个。
unvisited.push(current -> right);
visited.push(current);
cout << current -> self << endl;
unvisited.pop();
}
算法思想
对于最新发现的顶点,如果它还有以此起点而未探测到的边,就沿此边继续探测下去。当顶点v的所有边都已被探寻过后,搜索将回溯到发现顶点v有起始点的那些边。这个过程一直进行到已发现从源顶点可达的所有顶点时为止。如果还存在未被发现的顶点,则选择其中一个作为源顶点,并重复以上过程。
深度优先搜索的先辈子图形成一个由数棵深度优先树组成的深度优先森林。
伪代码:
DFS(G,s)
for each vertex v in V(G)
status[v] = WHITE
/******其他初始化******/
for each vertex v in V(G)
if(status[v]==WHITE)
DFS-VISIT(v)
DFS-VISIT(v)
status[v] = GRAY
for each vertex t in Adj(v)
if status[t] = WHITE
DFS-VISIT(t)
/******其他操作******/
status[v] = BLACK
时间复杂度:O(V+E)。采用聚集分析方法。
DFS中两个循环所花时间为O(V),其中不包括调用DFS-VISIT()的时间。对于每个顶点,DFS-VISIT()只被调用一次。在这个函数的一次执行过程中,其中的循环执行的次数是与当前顶点v相邻的顶点的个数。一次所有调用函数DFS-VISIT()所花时间是O(E)。
所以时间复杂度是O(V+E)。
Graph.h
//图相关算法编程实现《算法导论(第二版)》P322 第22章 图的基本算法
//Date:2013-03-27
//Author:江南烟雨(E-Mail:[email protected])
#include
#include
//图的基本算法类封装实现
//这里图用邻接表表示法(且是不带权的无向图)
template
class GraphClass{
public:
//图邻接表表示中节点数据结构
typedef struct StructGraphNode{
ElemType elem;
struct StructGraphNode *next;
}GraphNode,*GraphNodeLink;
//图遍历时节点颜色标记
enum NodeColor{
WHITE,//未被发现
GRAY,//已被发现但是未访问
BLACK//已被访问
};
static const int MaxVal = 99999;
GraphClass();//默认构造函数
~GraphClass();//析构函数
//依据图中包含的节点以及邻接矩阵创建邻接链表表示的图
void createGraph(ElemType *a,int n,char *matrix);
void BFSGraph();//图的广度优先搜索
void DFSGraph();//图的深度优先搜索
void __printAdjacencyList();//输出图的当前邻接表
private:
GraphNodeLink *root;//邻接表,包含了对应于每个节点的列表
int num_nodes;//图中节点个数
int __getNodeIndex(GraphNodeLink node);//得到某个顶点在颜色等数组中的索引
void __DFSSubGraph(GraphNodeLink u,int &time,int *d,int *f,enum NodeColor *color,GraphNodeLink *parent);//从某个子节点开始深度优先遍历
//删除邻接表所占空间
void __deleteAdjacencyList();
void __deleteSingleLinkList(GraphNodeLink head);//删除一个单链表
};
template
GraphClass::GraphClass()
{
root = NULL;
}
//析构函数
template
GraphClass::~GraphClass()
{
__deleteAdjacencyList();
}
//函数:依据图的邻接矩阵表示创建图的临界表表示
//参数:
//matrix:图的邻接矩阵,行优先,以一维数组表示
template
void GraphClass::createGraph(ElemType *a,int n,char *matrix)
{
num_nodes = n;
root = new GraphNodeLink[n];
for (int i = 0;i < n;++i)
root[i] = NULL;
//创建邻接表中的每个列表,每个列表对应一个顶点
for (int i = 0;i < n;++i)
{
root[i] = new GraphNode;
root[i]->elem = *(a + i);
root[i]->next = NULL;
GraphNodeLink loopNode = root[i];
for (int j = 0;j < n;++j)
{
if (*(matrix + i * n + j) == 1)
{
GraphNodeLink newNode = new GraphNode;
newNode->elem = *(a + j);
newNode->next = NULL;
//寻找插入的正确位置
while(loopNode->next != NULL)
loopNode = loopNode->next;
loopNode->next = newNode;
}
}
}
}
//图的广度优先遍历
template
void GraphClass::BFSGraph()
{
if (NULL == root)
{
cout << "The graph is empty!" << endl;
return;
}
cout << "BFS :" << endl;
//标记每个顶点的颜色,表示是否被访问过、被发现
enum NodeColor *color = new enum NodeColor[num_nodes];
//记录遍历时源顶点到其他顶点的距离
int *d = new int[num_nodes];
//记录每个顶点的父节点
GraphNodeLink *parentNode = new GraphNodeLink[num_nodes];
for(int i = 0;i < num_nodes;++i)
{
*(color + i) = WHITE;
*(d + i) = MaxVal;
*(parentNode + i) = NULL;
}
//从源顶点(邻接表中第一个列表首节点开始遍历)
int index = __getNodeIndex(*(root + 0));
*(color + index) = GRAY;
*(d + index) = 0;
*(parentNode + index) = NULL;
std::queue BFSQueue;//辅助数据结构:队列
BFSQueue.push(*(root + 0));//源节点入队列
while(!BFSQueue.empty())
{
GraphNodeLink tempNode = BFSQueue.front();
cout << tempNode->elem << " ";
BFSQueue.pop();
int tempIndex = __getNodeIndex(tempNode);
*(color + tempIndex) = BLACK;
GraphNodeLink loopNode = (*(root + tempIndex))->next;//找到邻接表中对应的列表
while(loopNode)
{
int index = __getNodeIndex(loopNode);
if (WHITE == *(color + index))//当前节点未被发现
{
*(d + index) = *(d + tempIndex) + 1;
*(parentNode + index) = tempNode;
*(color + index) = GRAY;
BFSQueue.push(loopNode);
}
loopNode = loopNode->next;
}
}
cout << endl;
cout << "distance from the source node : " << endl;
for(int i = 0;i < num_nodes;++i)
{
if(MaxVal == *(d + i))
cout << "The node cannot be visited from the source node!" << endl;
else
cout << "node " << (*(root + i))->elem << " has distance :" << *(d + i) << " from the source node"<< endl;
}
}
//图的深度优先遍历
template
void GraphClass::DFSGraph()
{
if (NULL == root)
{
cout << "The graph is empty!" << endl;
return;
}
cout << "DFS :" << endl;
//标记每个顶点的颜色,表示是否被访问过、被发现
enum NodeColor *color = new enum NodeColor[num_nodes];
//记录每个顶点的父节点
GraphNodeLink *parentNode = new GraphNodeLink[num_nodes];
//时间戳:顶点第一次被发现的时间以及被访问的时间
int *d = new int[num_nodes];
int *f = new int[num_nodes];
for(int i = 0;i < num_nodes;++i)
{
*(color + i) = WHITE;
*(parentNode + i) = NULL;
}
int time = 0;//标记访问时间戳
//从图中未被发现的节点开始,调用深度优先搜索函数
for (int i = 0;i < num_nodes;++i)
{
GraphNodeLink currentNode = *(root + i);
while(currentNode)
{
int tempIndex = __getNodeIndex(currentNode);
if(WHITE == *(color + tempIndex))
__DFSSubGraph(currentNode,time,d,f,color,parentNode);
currentNode = currentNode->next;
}
}
cout << endl;
cout << "time of nodes :(first find the node,end of checking) " << endl;
for (int i = 0;i < num_nodes;++i)
{
cout << "(" << *(d + i) << ", " << *(f + i) << ") ";
}
cout << endl;
}
//从某个子节点开始深度优先搜索
template
void GraphClass::__DFSSubGraph(typename GraphClass::GraphNodeLink u,int &time,int *d,int *f,
enum NodeColor *color,typename GraphClass::GraphNodeLink *parent)
{
cout << u->elem << " ";
++time;
int currentIndex = __getNodeIndex(u);
*(color + currentIndex) = GRAY;
*(d + currentIndex) = time;
GraphNodeLink loopNodeLink = (*(root + currentIndex))->next;
while(loopNodeLink)
{
int tempIndex = __getNodeIndex(loopNodeLink);
//与当前节点相邻的节点未被发现
if (WHITE == *(color + tempIndex))
{
*(parent + tempIndex) = u;
__DFSSubGraph(loopNodeLink,time,d,f,color,parent);
}
loopNodeLink = loopNodeLink->next;
}
*(color + currentIndex) = BLACK;
*(f + currentIndex) = ++time;
}
template
int GraphClass::__getNodeIndex(GraphNodeLink node)
{
for (int i = 0;i elem == node->elem)
return i;
}
return -1;
}
template
void GraphClass::__printAdjacencyList()
{
for (int i = 0;i < num_nodes;++i)
{
GraphNodeLink loopNode = *(root + i);
while(loopNode)
{
cout << loopNode->elem << " ";
loopNode = loopNode->next;
}
cout << endl;
}
}
//空间释放:删除邻接表所占空间
template
void GraphClass::__deleteAdjacencyList()
{
if(NULL == root)
return;
for(int i = 0;i < num_nodes;++i)
{
GraphNodeLink head= *(root + i);
if(head)
__deleteSingleLinkList(head);
}
}
//删除一个单链表所占空间
template
void GraphClass::__deleteSingleLinkList(typename GraphClass::GraphNodeLink head)
{
if(NULL == head)
return;
__deleteSingleLinkList(head->next);
delete head;
}
#include "Graph.h"
using namespace std;
int main()
{
//《算法导论(第二版)》P322 图22-1无向图测试例子
//const int n = 5;
//int a[n] = {1,2,3,4,5};
//char matrix[n * n] = {0,1,0,0,1,
// 1,0,1,1,1,
// 0,1,0,1,0,
// 0,1,1,0,1,
// 1,1,0,1,0};
//《算法导论(第二版)》P322 图22-2有向图测试例子
const int n = 6;
int a[n] = {1,2,3,4,5,6};
char matrix[n * n] = {0,1,0,1,0,0,
0,0,0,0,1,0,
0,0,0,0,1,1,
0,1,0,0,0,0,
0,0,0,1,0,0,
0,0,0,0,0,1};
GraphClass *graphObj = new GraphClass;
graphObj->createGraph(a,n,matrix);
graphObj->__printAdjacencyList();
graphObj->BFSGraph();
graphObj->DFSGraph();
return 0;
}