图可以作为表示指定环境内所有对象的关系的形式。树可以认为是图的子集,图中节点的联系可能是成环的。

图的元素包括边和点,按照不同的需要可能侧重表达点之间的关系或者点本身的信息。

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))    //对所有未访问过、且是当前点的邻接点的 wDFS(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!
他要通过一个大胆的计划逃离此地——从最近的鳄鱼脑袋上跳过!
Before the animal realized what was happening, James jumped again onto the next big head...
在这些动物意识到发生什么事之前,詹姆斯已经跳到下一个脑袋上……
Finally he reached the bank before the last crocodile could bite him
(actually the stunt man was caught by the big mouth and barely escaped with his extra thick boot).
最终他在被鳄鱼咬到之前跳上湖岸(事实上这个特工被大嘴钳住靠着他的厚鞋底险险逃脱)。Assume that the lake is a 100 by 100 square one.
假设这个湖是一个100×100的方形。
Assume that the center of the lake is at (0,0) and the northeast corner at (50,50).
假设湖中心坐标是 (0, 0),东北角坐标是 (50, 50)。
The central island is a disk centered at (0,0) with the diameter of 15.
湖心岛中心在 (0, 0) ,是一个直径15的圆。
A number of crocodiles are in the lake at various positions.
已知数量的鳄鱼在湖中的不同位置。
Given the coordinates of each crocodile and the distance that James could jump, you must tell him whether or not he can escape.
给出每条鳄鱼的坐标和詹姆斯能跳跃的距离,你要告诉他能否成功逃脱。Input Specification:
输入格式:Each input file contains one test case.
每个输入文件包含一个测试样例。
Each case starts with a line containing two positive integers N (≤100),
the number of crocodiles, and D, the maximum distance that James could jump.
每个测试样例中,第一行给定两个整数 N (≤100) 和 D,分别为鳄鱼的数量和詹姆斯一次跳跃的最远距离。
Then N lines follow, each containing the (x,y) location of a crocodile.
Note that no two crocodiles are staying at the same position.
之后 N 行,每行给出一对鳄鱼的坐标 (x, y)。任意两条鳄鱼不会出现在同样的位置。
*/

题目有“简单模式” 和“困难模式”。

“简单模式”的要求:

/*
Output Specification:
输出格式:For each test case, print in a line "Yes" if James can escape, or "No" if not.
对每个测试样例,根据詹姆斯能否成功逃脱,在一行内输出 "Yes" 或者 "No"。Sample Input 1:
14 20
25 -15
-25 28
8 49
29 15
-35 -2
5 28
27 -29
-8 -28
-20 -35
-25 -20
-13 29
-30 15
-35 40
12 12
Sample Output 1:
Yes
Sample Input 2:
4 13
-12 12
12 12
-12 -12
12 -12
Sample Output 2:
No
*/

可以在建立图后使用任意一种遍历方式判断:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<stdbool.h>#define MaxAssume 100
#define HALF_LAND 7.5       //小岛的半径int crocodiles[2][MaxAssume] = {0};     // 存放已经记录的鳄鱼坐标
int N;      // 存放鳄鱼数量
int D;      // 詹姆斯可行的的跳跃距离bool CanEscape(int x, int y)
{return x <= D || y <= D || (MaxAssume - x) <= D || (MaxAssume - y) <= D;
}//两点间距离公式,注意到传入的坐标数据在程序处理中 +50
bool CanJump(int x1, int y1, int x2, int y2)
{if (x1 == 50 && y1 == 50){return pow(x2-x1, 2) + pow(y2-y1,2) > pow(D+HALF_LAND, 2)? false: true;}return pow(x2-x1, 2) + pow(y2-y1, 2) > pow(D, 2)? false: true;
}//访问坐标点的标记,标记为 true 就是被詹姆斯踩过的鳄鱼
bool Visited[MaxAssume][MaxAssume] = {0};
void VisitNode(int x, int y)
{Visited[x][y] = true;//printf("Visit point(%d, %d)\n", x-50, y-50);return;
}//魔改的深度优先,把判断条件改成能够跳过去,如果判断可以逃脱就显示信息然后直接退出程序
void DFS(int x1, int y1)
{int count;int x2, y2;VisitNode(x1, y1);if (CanEscape(x1, y1)){printf("Yes\n");exit(0);}for (count = 0; count < N; count++){x2 = crocodiles[0][count];y2 = crocodiles[1][count];if(!Visited[x2][y2] && CanJump(x1, y1, x2, y2)){DFS(x2, y2);}}return;
}int main(void)
{int xc, yc;scanf("%d %d", &N, &D);getchar();int count;for(count = 0; count < N; count++){scanf("%d %d", &xc, &yc);getchar();crocodiles[0][count] = xc + 50;crocodiles[1][count] = yc + 50;}//从原点 (进入程序的坐标+50) 开始的深度优先访问,如果程序跑完了 DFS() 就说明到不了岸DFS(50, 50);printf("No\n");return 0;
}

“困难模式”的要求:

/*
Output Specification:
输出格式:For each test case, if James can escape, output in one line the minimum number of jumps he must make.
对每个测试样例,如果詹姆斯能逃脱,就在一行内输出他需要跳跃的最小次数。
Then starting from the next line, output the position (x,y) of each crocodile on the path,
each pair in one line, from the island to the bank.
在随后的若干行内输出每次跳过鳄鱼的坐标 (x, y),每个坐标占一行,方向从湖心岛到岸上。
If it is impossible for James to escape that way, simply give him 0 as the number of jumps.
如果詹姆斯没可能逃出生天,只给他一个“0”作为他跳跃的次数。
If there are many shortest paths, just output the one with the minimum first jump, which is guaranteed to be unique.
如果有多条最短路,只需要输出第一跳最短的路径,题目保证这一路径唯一。Sample Input 1:17 15
10 -21
10 21
-40 10
30 -50
20 40
35 10
0 -10
-25 22
40 -40
-30 30
-10 22
0 11
25 21
25 10
10 10
10 35
-30 10
Sample Output 1:4
0 11
10 21
10 35
Sample Input 2:4 13
-12 12
12 12
-12 -12
12 -12
Sample Output 2:0
*/

对“困难模式”,需要寻找一种最优的路径(幸运的是这里只需要找出第一跳所需最近的一步),所以要用到广度优先搜索。

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<stdbool.h>
#include<limits.h>#define MaxAssume 100
#define HALF_LAND 7.5       // 小岛的半径int crocodiles[2][MaxAssume+1] = {0};   // 存放已经记录的鳄鱼坐标,第0行存横坐标,第1行存纵坐标
int N;      // 存放鳄鱼数量
int D;      // 詹姆斯可行的的跳跃距离
int Path[MaxAssume+1] = {0};        // 詹姆斯逃脱的路径表示,以 crocodiles[][] 里鳄鱼的序号表示bool CanEscape(int x, int y)
{return (x <= D || y <= D || (MaxAssume - x) <= D || (MaxAssume - y) <= D);
}// 两点间距离公式,注意到传入的坐标数据在程序处理中 +50
bool CanJump(int x1, int y1, int x2, int y2)
{if (x1 == 50 && y1 == 50){if (pow(x2, 2) + pow(y2, 2) <= pow(HALF_LAND, 2))   // 鳄鱼在小岛内return false;return pow((x2 - x1), 2) + pow((y2-y1), 2) > pow(D+HALF_LAND, 2)? false: true;}return pow(x2-x1, 2) + pow(y2-y1, 2) > pow(D, 2)? false: true;
}// 访问坐标点的标记,标记为 true 就是被詹姆斯踩过的鳄鱼
bool Visited[MaxAssume][MaxAssume] = {0};
void VisitNode(int x, int y)
{Visited[x][y] = true;//printf("Visit point(%d, %d)\n", x-50, y-50);return;
}// 一个简单队列, 与函数 BFS() 耦合
int Queue[MaxAssume] = {0};
int Qhead = 0, Qtail = 0;
bool QEmpty = true;
void InQueue(int a)
{if (Qhead == Qtail && !QEmpty){printf("Queue was full.\n");exit(1);}Qtail = (Qtail + 1) % MaxAssume;Queue[Qtail] = a;QEmpty = false;return;
}int PopQueue(void)
{if (Qhead == Qtail && QEmpty)    // 队列空return INT_MAX;QEmpty = true;Qhead = (Qhead + 1) % MaxAssume;return Queue[Qhead];
}// 固定起点的广度优先访问(BFS). 使用队列, 类似二叉树中序遍历.
void BFS(int* pathTails)
{int croc;int V, W;// 访问原点,从原点能跳的进队列(按编号)VisitNode(50, 50);crocodiles[0][0] = 50;crocodiles[1][0] = 50;InQueue(0);Path[0] = -1;int x1, y1, x2, y2;// 队列不空,就找队列里记录的鳄鱼里能跳到的其它鳄鱼while (!QEmpty || Qhead != Qtail){V = PopQueue();for (W = 1; W <= N; W++){x1 = crocodiles[0][V], y1 = crocodiles[1][V];x2 = crocodiles[0][W], y2 = crocodiles[1][W];if ( CanJump(x1, y1, x2, y2) &&!Visited[x2][y2] ){VisitNode(x2, y2);InQueue(W);Path[W] = V;if ( CanEscape(x2, y2) ){pathTails[W] = 1;}}}}return;
}// 一个简单栈
int crocStack[MaxAssume];
int StackHead = 0;
void InStack(int a)
{if (StackHead >= MaxAssume){fprintf(stderr, "Stack was full.\n");exit(1);}crocStack[StackHead] = a;StackHead++;return;
}int PopStack(void)
{if (StackHead < 1)return INT_MIN;StackHead--;return crocStack[StackHead];
}int main(void)
{int count;int xc, yc;int PathTail;scanf("%d %d", &N, &D);getchar();// 能一步跳到岸的情形if (D >= 50){printf("1");exit(0);}for (count = 1; count <= N; count++){scanf("%d %d", &xc, &yc);getchar();crocodiles[0][count] = xc + 50;crocodiles[1][count] = yc + 50;}// Exit 保存可逃脱的路径尾,序号代表路径尾的鳄鱼,内容存储第一跳的距离。int* Exit = (int*)calloc(N+1, sizeof(int));for (count = 1; count <= N; count++)Exit[count] = -1;BFS(Exit);int dx, dy;int* minSteps = (int*)calloc(N+1, sizeof(int));for (count = 0; count <= N; count++)minSteps[count] = INT_MAX;for (count = 1; count <= N; count++){if (Exit[count] != -1)  // 选择出口{PathTail = count;while (PathTail != 0){InStack(PathTail);if (Path[PathTail] == 0)    // 判断第一跳{// 求算 PathTail 和 Path[PathTail] 之间的距离并赋值给 Exit[count]dx = crocodiles[0][PathTail] - crocodiles[0][Path[PathTail]];dy = crocodiles[1][PathTail] - crocodiles[1][Path[PathTail]];Exit[count] = dx * dx + dy * dy;minSteps[count] = StackHead+1;// printf("minSteps[%d] = %d\n", count, minSteps[count]);}PathTail = Path[PathTail];}}StackHead = 0;}// 比较最少跳数和最短第一跳int p_minFirstStep = 0, minFirstStep = INT_MAX;for (count = 1; count <= N; count++){// printf("%d ", Exit[count]);if (Exit[count] != -1){// 选出逃脱步数最少的路径尾if (minSteps[count] < minSteps[0]){minSteps[0] = minSteps[count];p_minFirstStep = count;minFirstStep = Exit[count];}// 逃脱步数同为目前的最小值,但第一跳更短else if (minSteps[count] == minSteps[0] &&Exit[count] < minFirstStep){minFirstStep = Exit[count];p_minFirstStep = count;}}}PathTail = p_minFirstStep;       // 逃脱前最后跳过鳄鱼的编号// printf("\nPathTail = %d\n", PathTail);while (PathTail != 0){InStack(PathTail);PathTail = Path[PathTail];}if (StackHead == 0)    // 无法逃脱的情形,栈内没有记录任何步骤{printf("0");}else{printf("%d\n", StackHead+1);// 显示完整的路径while (StackHead != 0){count = PopStack();printf("%d %d\n", crocodiles[0][count] - 50, crocodiles[1][count] - 50);}}free(Exit);free(minSteps);return 0;
}

图和两种遍历,Saving James Bond相关推荐

  1. 三十张图片让你彻底弄明白图的两种遍历方式:DFS和BFS

    1 引言   遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次.图的遍历.遍历过程中得到的顶点序列称为图遍历序列. 2 深度优先搜索 2.1 算法思想 ...

  2. lisp遍历表中所有顶点_三十张图片让你彻底弄明白图的两种遍历方式:DFS和BFS...

    1 引言   遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次.   在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点出发,按照一定的访问规则 ...

  3. 有向图的广度优先遍历_图的两种遍历方式

    1 引言 遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次. 在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点出发,按照一定的访问规则,依次访 ...

  4. 分别用邻接矩阵和邻接表实现图的深度优先遍历和广度优先遍历_数据结构与算法:三十张图弄懂「图的两种遍历方式」...

    原创: 进击的HelloWorld1 引言遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次. 在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点 ...

  5. c++层次遍历_数据结构与算法,弄懂图的两种遍历方式

    1 引言   遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次.  在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点出发,按照一定的访问规则, ...

  6. 深度优先遍历访问的边集合_数据结构与算法: 三十张图弄懂「图的两种遍历方式」...

    1 引言 遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次. 在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点出发,按照一定的访问规则,依次访 ...

  7. c++ stack 遍历_划重点啦!带你解读图的两种遍历方式

    01知识框架 02图的遍历 1 深度优先遍历基本思想:首先访问图中起始顶点v,然后由v出发,访问与v邻接且未被访问的顶点再访问与v相邻且未被访问的顶点 w1...重复上述过程.当不能再继续向下访问时, ...

  8. 图的两种遍历算法——BFS和DFS

    一.BFS,也称广度优先搜索,和二叉树的层次遍历算法类似 //BFS bool visited[MaxVertexNum]; void BFSTraverse(Graph G){for(i=0;i&l ...

  9. 用两种遍历方法判断图中两点是否有路径

    用两种遍历方法判断图中两点是否有路径(可直接测试) 邻接表.图.图的两种遍历以及图中路径的基本概念,可以去自行了解和学习(下面是代码实践)可直接在自己主机测试 #include <iostrea ...

最新文章

  1. 程序员奶爸用树莓派制作婴儿监护仪:哭声自动通知,还能分析何时喂奶
  2. ubuntu下解决oracle sqlplus不能查看历史命令问题
  3. 一些常用的IHTMLXX接口
  4. easyui combobox 左匹配模糊查询
  5. 文档屏幕水印_您的文档何时需要屏幕截图?
  6. Asp.Net SignalR 集群会遇到的问题
  7. eltable表头高度无法设置_厨房吊顶高度一般多少?厨房吊顶安装需要注意什?...
  8. Android系统服务分析与Native Service实例
  9. 牛客网利用C语言解兔子序列
  10. Apache Ambari官方文档中文版
  11. U-boot中控制台命令
  12. matlab cbfreeze,Matlab:如何在同一图中为不同的表面指定不同的色图/色块
  13. Ajax 改造,第 1 部分: 使用 Ajax 和 jQuery 改进现有站点
  14. 季节性ARIMA:时间序列预测
  15. PREP 语法笔记 10
  16. CnOpenData中国各城市基于一级分类绿色专利授权数量统计
  17. 学习练手的22个Python迷你程序
  18. 3 个鲜为人知的 Python 特性
  19. 【Week14实验】猫睡觉问题【模拟】
  20. 有点想法系列:借助海尔平台打造智能家居的一点想法

热门文章

  1. Android进阶2之有道词典开发
  2. 漫谈高数 特征向量物理意义
  3. Unveiling causal interactions in complex systems(揭示复杂系统中的因果交互作用)
  4. 工作态度决定了你的层次
  5. 设置docker容器时间
  6. Windows图标显示异常解决方法。桌面图标异常,开始菜单图标异常,任务栏图标异常。图标缓存位置。
  7. ROW_NUMBER、RANK、DENSE_RANK的用法(1)(转)
  8. 软件评测师考试学习计划
  9. js 幻灯片放映图片_如何制作数据库驱动的首页幻灯片放映
  10. 鸿蒙系统桌面加插件,华为鸿蒙OS 2系统最常用UI桌面模块化体验