实验要求:
写一个c程序判断输入的图是否为哈密尔顿图。
解题思路:
首先,要解决的问题是如何把图转化为可以从键盘上输入的内容,并且可以让电脑“读懂”这个图。
我采用的方式是:把每两个节点之前的关系都输进去,并用数组储存。
具体实现方法:先输入节点的个数,和边的个数,目的是为了计算需要多少个scanf来读取键盘输入,然后再把每条边两边的节点输入进去,及时地用数组保存,因为做的这个程序是、无向的(也可以改成有向的),所以输入的时候调换两个节点的位置也是可行的(只需要输入两个节点存在关系就可以了)。
其次,要解决的问题是如何去表示关系,比如1可以指向2,2也可以指向1,也就是1和2之前存在一条边,我采用的是a[x][++num[x]]=y; a[y][++num[y]]=x; 用num数组给当前节点编号。方便后面遍历边的时候,可以有序不重复地进行。当然,储存关系的时候用链表更加直观、自然,但是用数组更加地顺手,所以就用数组来储存。
把所有对应的关系都输入到数组里面,该数组就构成了一颗树。树的顶点(根节点)不是固定的,因为把任意一个节点作为根节点,都有可能经历所有其他点,并且不重复走边,回到原点。
接下来该问题就转化成为了如何走遍所有的节点,并回到原地。可以换句话说,就是寻找到最长的路径,并且回到原点,不重复走边。
所以转化为DFS算法(深度优先搜索Depth-First-Search),意思是寻找树的最大深度路径。DFS算法没有具体的格式化的代码,它更多的是一种思想,通过对节点、路径的标记,来判断路有没有走过(防止被重复走过),通过递归来遍历所有的路径,并从中找到一条最长的路径。
具体实现方法:
从第一个节点开始,遍历所有的节点。
每访问一个节点,调用一次Mydfs方法,并返回flag标记的值(flag为1代表找到了符合要求的路径,flag为0表示这不是哈密尔顿图)
再看Mydfs核心函数,首先这个函数要把main函数的x(遍历的开始位置),n(节点的个数),还有flag标记传入该函数。同时设定上一次访问的节点(为了避免走回头路,而且在最终判断是否构成哈密尔回路有很大的意义)
Mark数组用来标记节点是否已经被访问,主要是为了在后面的代码中访问其他未访问的节点。每找到一个未标记的点,就把这个节点保存在output数组里面,其中通过length来记录节点具体保存在数组的哪一个位置。如果一条路已经到了尽头,但是无法构成哈密尔回路,那么length就要减一,并且把Mark数组清零,意思是返回到上一个节点,继续寻找该节点对应的没有被访问过的节点。如果还是没有找到这样的节点,那么再返回到上一个节点,以此类推,直到遍历所有的节点为止。该方法用递归来实现。
判断构成哈密尔顿图的条件:
1、下一个点为最初的起点(即回到原点)
2、下一条路是之前没有走过的路
3、路径的长度为n+1(包含了起点,重复计算)
核心代码:(如果想要完整版的代码请加QQ1228333410)
int Mydfs(int last,int i,int x,int n,intflag)//i表示当前访问节点,last表示上次访问的节点
{
intj,s;
Mark[i]=1;//标记为已经访问过
output[++length]=i;//记录下答案
for(j=1;j<=num[i];j++)
{
if(!Mark[a[i][j]])//遍历与i相关联的所有未访问过的节点
{
flag=Mydfs(i,a[i][j],x,n,flag);
}
if(a[i][j]==x&&a[i][j]!=last&&length==n)//回到起点,构成哈密尔顿图
{
output[++length]=a[i][j];
flag=1;
length--;
break;
}
}
length--;
Mark[i]=0;//回溯
returnflag;
}