图可以作为表示指定环境内所有对象的关系的形式。树可以认为是图的子集,图中节点的联系可能是成环的。
图的元素包括边和点,按照不同的需要可能侧重表达点之间的关系或者点本身的信息。
int graph[100][100] = {0};
...
void InsertEdge(int ver_a, int ver_b, int weight)
{
graph[ver_a][ver_b] = weight;
return;
}
如上例所示为侧重表示边的图,可以使用二维数组存储这样的图,而数组的序号作为点的代号。上例中的 graph[1][2] 即表示“从 1 点 到 2 点的值(这个值可能是距离或者其他指定含义的值)”,而 graph[2][1] 就是“从 2 到 1 的值”了。
如果给出了一系列对象之间的联系,就可以简单地为这些对象编个号,然后在矩阵中定义它们之间的边。
还有一种表示形式是记录点而非边,这在一个给定的坐标系内比较常见。
int vertex[100][2] = {0};
bool isVertex[100] = {0};
...
void InsertVertex(int x, int y, int num)
{
vertex[num][0] = x;
vertex[num][1] = y;
isVertex[num] = true;
return;
}
和上一种形式相比它较不直观,如果要为这些点定义边可能需要其他手段 (比如根据两点间距离判断,如果它们足够近就认为是联通的)。
bool IsEdge(int x1, int x2, int y1, int y2, int lenth)
{
return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) <= lenth*lenth;
}
遍历整个图的方式包括 深度优先搜索 (Depth First Search, DFS) 和 广度优先搜索 (Breadth First Search, BFS)。
深度优先搜索,行为类似二叉树的前序遍历。即从起点开始,每访问一个节点便查询该节点的邻接点并访问找到的第一个邻接点,直到访问的节点没有未访问过的邻接点,再从最近访问过的点中寻找还可以访问的点。void BFS(int vertexNum)
{
Visited[vertexNum] = true; //访问当前节点
for (int w = 0; w < GraphLen; w++)
{
if (!Visited[w] && IsEdge(vertexNum, w)) //对所有未访问过、且是当前点的邻接点的 w
DFS(w);
}
return;
}
void BFS(int vertexNum)
{
visited[vertexNum] = true;
EnQueue(queue, vertexNum);
while (!EmptyQueue(queue))
{
v = PopQueue(queue); //从队列中弹出的点用于查找所有的邻接点进队列
for (w = 0; w < GraphLen; w++)
{
if (!Visited[w] && IsEdge(v, w));
{
Visited[w] = True;
EnQueue(w);
}
}
}
return;
}
下面是两道关于图遍历的题目,见于 中国大学MOOC-陈越、何钦铭-数据结构 在PTA发布的习题。题目背景如下:
/*
This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the world's most famous spy, was captured by a group of drug dealers.
现在让我们假设《Live and Die》中詹姆斯邦德的处境,世界顶级的间谍被一组毒贩俘虏。
He was sent to a small piece of land at the center of a lake filled with crocodiles.
他被扔到一个湖心的小孤岛上,湖中遍是鳄鱼。
There he performed the most daring action to escape -- he jumped onto the head of the nearest crocodile!
他要通过一