哈密顿回路【转载】
第一种情况:
在Dirac定理的前提下构造哈密顿回路的过程:
1)任意找两个相邻的节点S和T,在其基础上扩展出一条尽量长的没有重复结点的路径。即如果S与结点v相邻,而且v不在路径S -> T上,则可以把该路径变成v -> S -> T,然后v成为新的S。从S和T分别向两头扩展,直到无法继续扩展为止,即所有与S或T相邻的节点都在路径S -> T上。
2)若S与T相邻,则路径S -> T形成了一个回路。
3) 若S与T不相邻,可以构造出来一个回路.设路径S -> T上有k+2个节点,依次为S, v1, v2, ..., vk, T.可以证明存在节点vi(i属于[1, k]),满足vi与T相邻,且vi+1与S相邻.找到这个节点vi,把原路径变成S -> vi -> T -> vi+1 ,即形成了一个回路。
4)到此为止,已经构造出来了一个没有重复节点的的回路,如果其长度为N,则哈密顿回路就找到了。如果回路的长度小于N,由于整个图是连通的,所以在该回路上,一定存在一点与回路之外的点相邻。那么从该点处把回路断开,就变回了一条路径,同时还可以将与之相邻的点加入路径。再按照步骤1的方法尽量扩展路径,则一定有新的节点被加进来。接着回到路径2。
哈密顿路径的伪代码如下:
算法:哈密顿路径
输入:哈密顿回路的起始点s,哈密顿回路中终点s之前的点t
输出:最终的哈密顿回路ans[ ]
1.初始化,令s = 1,t为s的任意一个邻接点;
2.如果ans[]中元素的个数小于n,则从t开始向外扩展,如果有可扩展点v,放入ans[]的尾部,并且t=v,并继续扩展,如无法扩展进入步骤3;
3.将当前得到的ans[]倒置,s和t互换,从t开始向外扩展,如果有可扩展点v,放入ans[]尾部,并且t=v,并继续扩展。如无法扩展进入步骤4;
3.1如果当前s和t相邻,进入步骤5。否则,遍历ans[],寻找点ans[i],使得ans[i]与t相连并且ans[i +1]与s相连,将从ans[i + 1]到t部分的ans[]倒置,t=ans[i +1],进如步骤5;
3.2如果当前ans[]中元素的个数等于n,算法结束,ans[]中保存了哈密顿回路(可看情况是否加入点s).否则,如果s与t连通,但是ans[]中的元素的个数小于n,则遍历ans[],寻找点ans[i],使得ans[i]与ans[]外的一点(j)相连,则令s=ans[i - 1],t = j,将ans[]中s到ans[i - 1]部分的ans[]倒置,将ans[]中的ans[i]到t的部分倒置,将点j加入到ans[]的尾部,转步骤2;
时间复杂度:
如果说每次到步骤5算一轮的话,那么由于每一轮当中至少有一个节点被加入到路径S -> T中,所以总的轮数肯定不超过n轮,所以时间复杂度为O(n^2).空间上由于边数非常多,所以采用邻接矩阵来存储比较适合。
C语言代码:
#include<stdio.h> #include<stdlib.h> #define Maxsize 10 #define MAX_NO_LIMIT 10000000 typedef char Datatype; int visited[Maxsize] = { 0 }; int s[Maxsize] = { 0 }; int count = 0; typedef struct { Datatype vertex[Maxsize]; int edge[Maxsize][Maxsize]; int vertexnum, edgenum; }graph; void creatgraph(graph *G, Datatype a[], int n, int e) { int i, j, k; G->vertexnum = n; G->edgenum = e; for (i = 0; i<G->vertexnum; i++) { G->vertex[i] = a[i]; } for (i = 0; i<G->vertexnum; i++) { for (j = 0; j<G->vertexnum; j++) { G->edge[i][j] = 0; } } for (k = 0; k<G->edgenum; k++) { printf("输入两个定点"); scanf_s("%d%d", &i, &j); G->edge[i][j] = 1; G->edge[j][i] = MAX_NO_LIMIT; } } void DFS(graph *G, int v) //count是全局变量并已初始化为0 { int j; visited[v] = 1; //对访问点进行标记 s[count++] = v;//S表示路径,这里为开头为v for (j = 0; j <G->vertexnum; j++) { if (G->edge[v][j] == 1 && visited[j] == 0) DFS(G, j); //这里对点进行递归 } if (j == G->vertexnum) { //取消回溯 visited[v] = 0; count--; } } int main() { int i; char ch[] = { 'a','b','c','d' }; graph MG; creatgraph(&MG, ch, 4, 5); DFS(&MG, 0); for (i = 0; i<4; i++) { printf("%d",s[i]); } printf("\n"); system("pause"); return 0; }
第二种情况:
C++代码:
#include <iostream> using namespace std; const int MAX_V = 50; void print(int path[], int V) { cout << "存在哈密顿回路" << endl; for (int i = 0; i < V; i++) cout << path[i] << " "; cout << path[0] << endl; } //path记录路径,visited记录顶点是否访问过,len记录当前路径的长度 bool hamCycle(int graph[][MAX_V], int V, int path[], bool visited[], int current) { if (current == V) { //访问到最后一个顶点 if (graph[path[current - 1]][0] == 1) return true;//有到0点的边 else return false; } //遍历起点外其它顶点 for (int v = 1; v < V; v++) { //如果没访问过,并且有边相连 if (!visited[v] && graph[path[current - 1]][v] == 1) { visited[v] = true; path[current] = v; //当本次递归的child也为true时返回true if (hamCycle(graph, V, path, visited, current + 1)) return true; //当本条递归线路失败时恢复原图 path[current] = -1; visited[v] = false; } } return false; } //从起点开始引导 bool hamCycleStart(int graph[][MAX_V], int V) { int path[MAX_V]; memset(path, -1, sizeof(path)); bool visited[MAX_V] = { 0 }; path[0] = 0; visited[V] = true; //把起点标记为访问过 //起点已确定,current从1开始 if (hamCycle(graph, V, path, visited, 1) == false) { cout << "哈密顿回路不存在" << endl; return false; } print(path, V); return true; } int main() { int graph[MAX_V][MAX_V]; int V; cout << "请输入点的个数:" << endl; cin >> V; for (int i = 0;i < V;++i) { cout << "请输入图的第" << i << "行" << endl; for (int j = 0;j < V;++j) { cin >> graph[i][j]; } } hamCycleStart(graph, V); return 0; }
原作者:黄油Chocolate ,CieloSun
原文:
https://blog.csdn.net/qq_41587612/article/details/86224564
http://www.cnblogs.com/cielosun/p/5654577.html