图的相关操作总结
- 递归深度优先遍历
- 非递归深度优先遍历
- 广度优先遍历
- 判断无向图是否有环
- 输出vi到vj的长度为l的路径
- 找出经过vi的回路
- 普利姆算法
- 拓扑排序
部分操作参考:http://blog.csdn.net/Kay_Sprint/article/category/851272/1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_SIZE 100
//邻接矩阵
struct vertex {//顶点
int num;//顶点的编号
char data;//顶点的信息
};
typedef struct graph{//图
int n;//图中顶点的个数
int e;//图中边的个数
struct vertex vexs[MAX_SIZE];//顶点集合
int edges[MAX_SIZE][MAX_SIZE];//边的集合;无权时为0,1;有权时是其:0;权值。
}AdjMaxix;
//邻接矩阵
typedef struct ArcNode{//表结点
int adjverx;//邻接点序号
char info;//邻接点信息
struct ArcNode *nextarc;//下一个邻接点
}ArcNode;
typedef struct verxnode{//顶点结点
char data;//顶点信息
struct ArcNode *firstarc;//指向第一个邻接点
}AdjList;
//初始化访问标志位为0
int visited[8] = {0,0,0,0,0,0,0,0};
void initVisited(){
int i = 0;
for (i = 0;i< 8;i++){
visited[i] = 0;
}
}
//建立邻接矩阵的图
AdjMaxix createGraphStoredByMatrix(){
AdjMaxix adj;
adj.n = 8;
adj.e = 10;
int i = 0,j = 0;
for (i = 0;i < adj.n;i++){
adj.vexs[i].num = i;
adj.vexs[i].data = 'a' + i;
for (j = 0;j< adj.n;j++){
adj.edges[i][j] = 0;
}
}
adj.edges[0][1]= 1;
adj.edges[0][2]= 1;
adj.edges[1][0]= 1;
adj.edges[1][3]= 1;
adj.edges[1][4]= 1;
adj.edges[2][0]= 1;
adj.edges[2][5]= 1;
adj.edges[2][6]= 1;
adj.edges[3][1]= 1;
adj.edges[3][7]= 1;
adj.edges[4][1]= 1;
adj.edges[4][7]= 1;
adj.edges[5][2]= 1;
adj.edges[5][7]= 1;
adj.edges[6][2]= 1;
adj.edges[6][7]= 1;
adj.edges[7][3]= 1;
adj.edges[7][4]= 1;
adj.edges[7][5]= 1;
adj.edges[7][6]= 1;
return adj;
}
//建立邻接表的图
AdjList* createGraphStoredByTable(){
AdjList *g = new AdjList[8];
int i = 0,j = 0;
for (i = 0;i <8 ;i++){
g[i].data = '0'+i;
g[i].firstarc = NULL;
}
ArcNode *node1 = (ArcNode*)malloc(sizeof(ArcNode));
node1->adjverx = 1;
node1->nextarc = NULL;
ArcNode *node2 = (ArcNode*)malloc(sizeof(ArcNode));
node2->adjverx = 2;
node2->nextarc = NULL;
node1->nextarc = node2;
g[0].firstarc = node1;
ArcNode *node3 = (ArcNode*)malloc(sizeof(ArcNode));
node3->adjverx = 0;
node3->nextarc = NULL;
ArcNode *node4 = (ArcNode*)malloc(sizeof(ArcNode));
node4->adjverx = 3;
node4->nextarc = NULL;
ArcNode *node5 = (ArcNode*)malloc(sizeof(ArcNode));
node5->adjverx = 4;
node5->nextarc = NULL;
node3->nextarc = node4;
node4->nextarc = node5;
g[1].firstarc = node3;
ArcNode *node6 = (ArcNode*)malloc(sizeof(ArcNode));
node6->adjverx = 0;
node6->nextarc = NULL;
ArcNode *node7 = (ArcNode*)malloc(sizeof(ArcNode));
node7->adjverx = 5;
node7->nextarc = NULL;
ArcNode *node8 = (ArcNode*)malloc(sizeof(ArcNode));
node8->adjverx = 6;
node8->nextarc = NULL;
node6->nextarc = node7;
node7->nextarc = node8;
g[2].firstarc = node6;
ArcNode *node9 = (ArcNode*)malloc(sizeof(ArcNode));
node9->adjverx = 1;
node9->nextarc = NULL;
ArcNode *node10 = (ArcNode*)malloc(sizeof(ArcNode));
node10->adjverx = 7;
node10->nextarc = NULL;
node9->nextarc = node10;
g[3].firstarc = node9;
ArcNode *node11 = (ArcNode*)malloc(sizeof(ArcNode));
node11->adjverx = 1;
node11->nextarc = NULL;
ArcNode *node12 = (ArcNode*)malloc(sizeof(ArcNode));
node12->adjverx = 7;
node12->nextarc = NULL;
node11->nextarc = node12;
g[4].firstarc = node11;
ArcNode *node13 = (ArcNode*)malloc(sizeof(ArcNode));
node13->adjverx = 2;
node13->nextarc = NULL;
ArcNode *node14 = (ArcNode*)malloc(sizeof(ArcNode));
node14->adjverx = 7;
node14->nextarc = NULL;
node13->nextarc = node14;
g[5].firstarc = node13;
ArcNode *node15 = (ArcNode*)malloc(sizeof(ArcNode));
node15->adjverx = 2;
node15->nextarc = NULL;
ArcNode *node16 = (ArcNode*)malloc(sizeof(ArcNode));
node16->adjverx = 7;
node16->nextarc = NULL;
node15->nextarc = node16;
g[6].firstarc = node15;
ArcNode *node17 = (ArcNode*)malloc(sizeof(ArcNode));
node17->adjverx = 3;
node17->nextarc = NULL;
ArcNode *node18 = (ArcNode*)malloc(sizeof(ArcNode));
node18->adjverx = 4;
node18->nextarc = NULL;
ArcNode *node19 = (ArcNode*)malloc(sizeof(ArcNode));
node19->adjverx = 5;
node19->nextarc = NULL;
ArcNode *node20 = (ArcNode*)malloc(sizeof(ArcNode));
node20->adjverx = 6;
node20->nextarc = NULL;
node17->nextarc = node18;
node18->nextarc = node19;
node19->nextarc = node20;
g[7].firstarc = node17;
return g;
}
//有向图
AdjList* createGraphStoredByTable2(){
AdjList *g = new AdjList[8];
int i = 0,j = 0;
for (i = 0;i <8 ;i++){
g[i].data = '0'+i;
g[i].firstarc = NULL;
}
ArcNode *node1 = (ArcNode*)malloc(sizeof(ArcNode));
node1->adjverx = 1;
node1->nextarc = NULL;
ArcNode *node2 = (ArcNode*)malloc(sizeof(ArcNode));
node2->adjverx = 7;
node2->nextarc = NULL;
node1->nextarc = node2;
//g[0].firstarc = node1;
g[0].firstarc = node1;
/*ArcNode *node3 = (ArcNode*)malloc(sizeof(ArcNode));
node3->adjverx = 0;
node3->nextarc = NULL;*/
ArcNode *node4 = (ArcNode*)malloc(sizeof(ArcNode));
node4->adjverx = 3;
node4->nextarc = NULL;
ArcNode *node5 = (ArcNode*)malloc(sizeof(ArcNode));
node5->adjverx = 4;
node5->nextarc = NULL;
//node3->nextarc = node4;
node4->nextarc = node5;
g[1].firstarc = node4;
/*ArcNode *node6 = (ArcNode*)malloc(sizeof(ArcNode));
node6->adjverx = 0;
node6->nextarc = NULL;*/
ArcNode *node7 = (ArcNode*)malloc(sizeof(ArcNode));
node7->adjverx = 1;
node7->nextarc = NULL;
ArcNode *node8 = (ArcNode*)malloc(sizeof(ArcNode));
node8->adjverx = 6;
node8->nextarc = NULL;
//node6->nextarc = node7;
node7->nextarc = node8;
g[2].firstarc = node7;
ArcNode *node9 = (ArcNode*)malloc(sizeof(ArcNode));
node9->adjverx = 5;
node9->nextarc = NULL;
//ArcNode *node10 = (ArcNode*)malloc(sizeof(ArcNode));
//node10->adjverx = 7;
//node10->nextarc = NULL;
//node9->nextarc = node10;
g[3].firstarc = node9;
ArcNode *node11 = (ArcNode*)malloc(sizeof(ArcNode));
node11->adjverx = 5;
node11->nextarc = NULL;
/*ArcNode *node12 = (ArcNode*)malloc(sizeof(ArcNode));
node12->adjverx = 7;
node12->nextarc = NULL;*/
//node11->nextarc = node12;
g[4].firstarc = node11;
ArcNode *node13 = (ArcNode*)malloc(sizeof(ArcNode));
node13->adjverx = 6;
node13->nextarc = NULL;
/*ArcNode *node14 = (ArcNode*)malloc(sizeof(ArcNode));
node14->adjverx = 7;
node14->nextarc = NULL;*/
//node13->nextarc = node14;
g[5].firstarc = node13;
ArcNode *node15 = (ArcNode*)malloc(sizeof(ArcNode));
node15->adjverx = 7;
node15->nextarc = NULL;
/*ArcNode *node16 = (ArcNode*)malloc(sizeof(ArcNode));
node16->adjverx = 7;
node16->nextarc = NULL;*/
//node15->nextarc = node16;
g[6].firstarc = node15;
ArcNode *node17 = (ArcNode*)malloc(sizeof(ArcNode));
node17->adjverx = 0;
node17->nextarc = NULL;
/*ArcNode *node18 = (ArcNode*)malloc(sizeof(ArcNode));
node18->adjverx = 4;
node18->nextarc = NULL;
ArcNode *node19 = (ArcNode*)malloc(sizeof(ArcNode));
node19->adjverx = 5;
node19->nextarc = NULL;
ArcNode *node20 = (ArcNode*)malloc(sizeof(ArcNode));
node20->adjverx = 6;
node20->nextarc = NULL;
node17->nextarc = node18;
node18->nextarc = node19;
node19->nextarc = node20;*/
g[7].firstarc = node17;
return g;
}
void printData(char c){
printf("%c",c);
}
void dfs(AdjList* list, int adjverx){
visited[adjverx] = 1;
printData(list[adjverx].data);
ArcNode* p = list[adjverx].firstarc;
//在这里访问的是边,因此为e
while (p != NULL){
if (!visited[p->adjverx]){
dfs(list,p->adjverx);
}
p = p ->nextarc;
}
}
//递归深度优先遍历 邻接矩阵 时间复杂度为O(n+e)
void dfsTraverse(){
AdjList* list = createGraphStoredByTable();
//printf("%c",list[0].data);
int i = 0;
int count = 0 ;
//dfs(list,7);
//顶点个数n
for(i = 0;i<8;i++){
if (!visited[i]){
count++;
dfs(list,i);
}
}
printf("\nThe parts of the graph is %d",count);
}
//非递归的深度优先遍历
//采用先访问,在入栈的方式。
void dfsTraverse2(){
int i = 0;
int stack[MAX_SIZE];
int top = -1;
AdjList* list = createGraphStoredByTable();
ArcNode* p = NULL;
int j = 0;
for (i = 0;i<8;i++){
if (!visited[i]){
//图的第一个元素入栈。
visited[i] = 1;
printData(list[i].data);
stack[++top] = i;
while(top > -1){
//访问栈顶而不是退出栈,因为,有可能这个点还有很多邻接点没有访问到。
j = stack[top];
//图的第一个邻接点
p = list[j].firstarc;
//关键点,这个地方非常厉害,从第一个点开始判断,直到其将所有的邻接点判断完毕。
while (p != NULL && visited[p->adjverx] == 1){
p = p->nextarc;
}
//如果为空,证明,这个点所在的所有邻接点都访问过了,因此退栈。
if (p == NULL){
top--;
} else {
//先访问再进栈,然后,将再从第一个邻接点开始访问。
j = p->adjverx;
printData(list[j].data);
visited[j] = 1;
stack[++top] = j;
}
}
}
}
}
//广度优先遍历 时间复杂度O(n+e)
void BFSTraverse(){
AdjList* list = createGraphStoredByTable();
ArcNode* queue[MAX_SIZE];
int front = 0,rear = 0;
ArcNode* p ;
int i = 0;
//顶点数n
for (i= 0 ;i<8;i++){
if (!visited[i]){
printf("%c",list[i].data);
visited[i] = 1;
//在这里不用判断是否访问过,因为没有一个结点与本连通,因此其肯定没有访问过。
if (list[i].firstarc == NULL ){
//除去只有一个结点的情况
continue;
}
//将第一个结点压入站
queue[rear++] = list[i].firstarc;
p = list[i].firstarc;
p = p->nextarc;
//这个是有记忆功能的对弧的遍历e
while(rear != front){
while(p != NULL){
//保证入栈的全部是没有访问的元素
if (!visited[p->adjverx]){
queue[rear++] = p;
}
p = p->nextarc;
}
//出栈
p = queue[front++];
if (!visited[p->adjverx]){
visited[p->adjverx] = 1;
printData(list[p->adjverx].data);
//p出栈之后,将p的所有自都压入栈中。
p = list[p->adjverx].firstarc;
}
}
}
}
}
//广度优先遍历 推荐
void BFSTraverse2(){
AdjList* list = createGraphStoredByTable();
AdjList queue[MAX_SIZE];
int front = 0,rear = 0;
AdjList temp ;
ArcNode* p ;
int i = 0;
//对于每个顶点
for (i= 0 ;i<8;i++){
if (!visited[i]){
//对每个顶点判断
printData(list[i].data);
visited[i] = 1;
queue[rear++] = list[i];
while(rear != front){
//出队列
temp = queue[front++];
p = temp.firstarc;
while(p != NULL){
//保证入栈的全部是没有访问的元素
if (!visited[p->adjverx]){
printData(list[p->adjverx].data);
visited[p->adjverx] = 1;
queue[rear++] = list[p->adjverx];
}
p = p->nextarc;
}
}
}
}
}
//判断无向图是否有环 第一种方案
/************************************************************************/
//递归依次删除度数为1的节点,如果最后仍然存在未删除的那么图中有环
/* 第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减一。
第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一。
如果最后还有未删除顶点,则存在环,否则没有环。 */
/************************************************************************/
//判断无向图是否有环 第二种方案(采用改进版的深度优先遍历)
//参考:http://bbs.csdn.net/topics/190057901 中willshy的解答;
//同时参考:http://daoluan.net/blog/%E3%80%90%E5%9B%BE%E8%AE%BA%E3%80%91%E5%88%A4%E6%96%AD%E6%9C%89%E5%90%91%E5%9B%BE%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8%E7%8E%AF/
/************************************************************************/
/*引入三种状态,而不是之前深度优先访问的两种方案。
0 白色,未被访问过的节点标白色
-1 灰色,已经被访问过一次的节点标灰色。主要是为了标识这个点,以这个点位根结点,然后访问其子结点,如果子结点能够回来,说明有环。
1 黑色,当该节点的所有后代都被访问过标黑色
仍然是按图的节点深度遍历,访问到V时,V若被访问过,那么有2种状态:
C[V]=-1,程序跳出,存在环
C[V]=1,程序继续,这不是环
时间复杂度O(n+e) */
/************************************************************************/
int dfsCycle(AdjList* list, int adjverx);
void isCycleOrNot(){
AdjList * list = createGraphStoredByTable();
int i = 0;
for (i = 0;i<8;i++){
if (visited[i] == 0){
if (!dfsCycle(list,i)){
printf("有环\n");
break;
}
}
}
if (i == 8){
printf("无环\n");
}
}
int dfsCycle(AdjList* list, int adjverx){
visited[adjverx] = -1;
ArcNode *p = list[adjverx].firstarc;
while(p != NULL){
if (visited[p->adjverx] == 0){
dfsCycle(list,p->adjverx);
//说明有环
} else if (visited[p->adjverx] == -1){
return 0;
}
p = p->nextarc;
}
//访问完了,将其置为1。
visited[adjverx] = 1;
return 1;
}
/************************************************************************/
/* 简单路径:如果一条路径上的顶点除了起点和终点可以相同外,其它顶点均不相同,
则称此路径为一条简单路径;起点和终点相同的简单路径称为回路(或环)。 */
/************************************************************************/
int path[20];
//输出vi到vj的长度为l的路径,参考:http://blog.csdn.net/jkay_wong/article/details/6968260
void Print_X_Y_Path(AdjList* vertices, int u,int v,int l,int d){
//求出一条长度为l的从u到v的路径,d刚进来的时候是-1
int m;
int i;
d++;
visited[u]=1;
path[d]=u;
if(u == v && d == l) { //找到一条路径
for(int i=0;i<l;i++) {
printData(vertices[ path[i] ].data);
}
printData('\n');
} else if(u == v && d!=l) {
visited[u]=0;
d--;
} else {
ArcNode *p=vertices[u].firstarc; //继续DFS
while(p) {
m=p->adjverx;
if(!visited[m])
Print_X_Y_Path(vertices,m,v,l,d);
p=p->nextarc;
}
}
//恢复环境,使顶点可重新使用
//路径长度减一
visited[u]=0;
d--;
}
//输出
void Print_X_X_Path(AdjList* vertices,int i,int j,int d) {
//找出从i到i的回路,思想和上面的类似
int v,k;
ArcNode *p;
visited[i]=1;
d++;
path[d]=i;
if(i == j && d>2){
for(k=0;k<d;k++){
printData(vertices[path[i]].data);
}
printData('\n');
} else if(i == j && d==2) {
visited[i]=0;
d--;
} else { //一条边只可以走一次
p=vertices[i].firstarc;
while(p)
{
v=p->adjverx;
if(!visited[v] || v == j)
Print_X_X_Path(vertices,v,j,d);
p=p->nextarc;
}
}
visited[i]=0;
d--;
}
typedef struct Tree_Edge{
int star;
int end;
int weight;
}Tree_Edge ;
int vexnum = 8;
int arcs[MAX_SIZE][MAX_SIZE];
//Prime 普利姆算法最小生成树
//从v点出发,输出g的最小生成树
void Prim_Min_Tree(int v, Tree_Edge c[]) {
int lowcast[MAX_SIZE]; //存放当前点到各个点的权值
int closet[MAX_SIZE]; //存放已经进入最小树的值
int i,min,j,k;
/* 初始化数组lowcast closet */
for(i=0;i<vexnum;i++) {
//放v到各个顶点的权值
lowcast[i]=arcs[v][i];
//现在最短路径集合中只有v顶点,下标为终点,值为起点。
closet[i]=v;
}
//将v到顶点的权值设置为0
lowcast[v]=0;
for(i=1;i<vexnum;i++){
min=MAX_SIZE;
for(j=0;j<vexnum;j++){
if(lowcast[j]!=0 && lowcast[j]<min) {
min=lowcast[j];
k=j;
}
}
lowcast[k]=0; //标志该顶点已经是U中顶点
c[i].star=closet[k];
c[i].end=k;
c[i].weight=min;
//下面是修改lowcast和closet
//因为终点为k,所以现在以k为起点,找到比原先小的值放入集合中
for(j=0;j<vexnum;j++){
if(arcs[k][j]<lowcast[j]){
lowcast[j]=arcs[k][j];
closet[j]=k;
}
}
}
printf("最小生成树 完成~~");
} /* 求最小生成树的Prime算法 */
/************************************************************************/
/* 克鲁斯卡尔算法 :主要使用了并查集的算法,用于判断新加入的结点是否已经在现有结点当中,防止出现环的情况。
1、首先对所有的路径排序
2、发现新加入结点的组号,(采用二叉树的parent-link改进的find方法)
3、如果两个组号相同,则表示已经在连接组内,如果不同,则放入连接组,并更改其中一个结点的组
4、判断连接组中的k值是否与图中结点数相同。
参考:http://blog.csdn.net/jkay_wong/article/details/6975151
并查集参考:http://blog.csdn.net/dm_vincent/article/details/7655764
*/
/************************************************************************/
int indegree[8];
//临界表查找指定结点的入度
void getInDegree(AdjList * list){
int i = 0;
for (i = 0;i<8;i++){
indegree[i] = 0;
}
ArcNode * p = NULL;
for (i = 0;i < 8 ;i++){
p = list[i].firstarc;
while(p != NULL){
indegree[p->adjverx]++;
p = p->nextarc;
}
}
}
//拓扑排序 aov
void Topo_Sort() {
//有向图
AdjList * list = createGraphStoredByTable2();
//得到所有结点的入度
getInDegree(list);
int queue[MAX_SIZE] ;
int front = 0,rear = 0,i = 0;
ArcNode * p ;
//将入度为0 的元素入队列
for (i = 0;i<8;i++){
if (indegree[i] == 0){
queue[rear++] = i;
}
}
int temp =0;
int count = 0;
while(front != rear){
temp = queue[front++];
printData(list[temp].data);
p = list[temp].firstarc;
count ++;
while(p != NULL){
//将以其为弧尾的弧所对应的弧头结点的入度 减一。
indegree[p->adjverx]--;
//判断入度是否为0
if (indegree[p->adjverx] == 0){
//如果为0则,将其入队列,进行下一次出队列操作。
queue[rear++] = p->adjverx;
}
p = p ->nextarc;
}
}
if (count < 8){
printf("\n改图有环\n");
}
}
/************************************************************************/
/*
* 求AOE网的关键路径参考:http://blog.csdn.net/jkay_wong/article/details/6696701
*/
/************************************************************************/
/************************************************************************/
/*求最短路径之——Dijkstra算法,参考:http://blog.csdn.net/jkay_wong/article/details/6692572
*/
/************************************************************************/
void main(){
printf("深度优先遍历:\n");
dfsTraverse();
printf("\n\n");
initVisited();
printf("广度优先遍历:\n");
BFSTraverse();
printf("\n\n");
initVisited();
BFSTraverse2();
printf("\n\n");
printf("检查无向图是否有环:\n");
initVisited();
isCycleOrNot();
printf("\n\n");
printf("拓扑排序:\n");
Topo_Sort();
printf("\n\n");
}