C语言数据结构:无向图连通子图

目录

无向图连通子图

问题

参考:

使用队列的代码

遇到的一些问题:

使用栈的代码

正确代码 


无向图连通子图

求无向图连通子图个数

问题

测试数据由m+1行构成,第一行为两个正整数n(1<n<=30)和m(1<m<100),分别表示顶点数(顶点编号为1,2,…,n)和边数,其后是m行数据,每行数据是一条边的信息,包括两个数字,分别表示该边关联的两个顶点。

输出两行信息,第一行输出该图中连通子图的个数。第二行按照升序输出每个连通子图中顶点个数。

参考:

求大神赐教:找出无向图的所有连通子图算法? - 知乎

<数据结构>无向连通子图个数求解(C语言版)_djs_snowyyy的博客-CSDN博客_无向图的连通子图个数

理解题意:

 连通图是“任意两点之间都可以联通的”。

 我感觉我的思路还是比较混乱的,这份代码最后的分数也只有60。目前还没改好。

使用队列的代码

for(i=0;i<n;i++)// use BFS
	{
		if(visit[i]==0)//Have not been visited
		{
			//enqueue current element
			visit[i]=1;
			len=1;//use to count the num of vertices
			while(queue)// while not empty
			{	
				//enqueue all the adjacent vertices of the first element
				visit[elem]=1;
				//dequeue the first element	
			} 
				
		 } 
		outcome[j]=len;j++;//mark visit results 
		
	} 
*****************************************************************
#include<stdio.h>
#include <stdlib.h>

typedef struct QNode{
    int elem;
    struct QNode * next;
}QNode;
//创建链式队列的函数
QNode * initQueue(){
    //创建一个头节点
    QNode * queue=(QNode*)malloc(sizeof(QNode));
    //对头节点进行初始化
    queue->next=NULL;
    return queue;
}
//入队 
QNode* enQueue(QNode * rear,int elem){  
    QNode * enElem=(QNode*)malloc(sizeof(QNode));
    enElem->elem=elem;enElem->next=NULL;//1、用节点包裹入队元素
    rear->next=enElem;//2、新节点与rear节点建立逻辑关系
    rear=enElem;//3、rear指向新节点
    //返回新的rear,为后续新元素入队做准备
    return rear;
}
//出队
int DeQueue(QNode * top){
    if (top->next==NULL) {
        //printf("队列为空\n");
        return 0;
    }
    QNode * p=top->next;
    top->next=p->next;
    free(p);
    return 1;
} 
//————————————————
int main()
{	//BFS 
	int n,m,t,i,j,count=0;
	int graph[31][31]={0};//图的邻接矩阵
	int outcome[20]={0};//存放结果的顶点数 
	int mark[31]={0};//用来标记 

	scanf("%d%d",&n,&m);
	for(t=0;t<m;t++)
	{
		scanf("%d%d",&i,&j);
		graph[i][j]=1;
		graph[j][i]=1;//避免数据混乱的? 
		graph[i][0]=1;
		graph[j][0]=1;
	}
	
	int len,a;
	j=0;
	for(i=1;i<=n;i++)//  BFS
	{
		if(graph[i][0]&&mark[i]==0)//Have not been visited
		{
			QNode *queue=initQueue();	//创建队列 
			QNode*top=queue,*rear=queue,*p=queue;
			rear=enQueue(rear,i);	//enqueue current element
			mark[i]=1;
			len=1;//use to count the num of vertices
			while(1)// while not empty
			{	
				if(top->next==NULL)
					break;
				p=top->next;//查看此时队列第一个元素 
				t=p->elem;
				for(a=1;a<=n;a++)//判断邻接 
				{
					if(graph[t][a]&&mark[a]==0&&t!=a)
					{//邻接+未访问+非本身  
						mark[a]=1;
						len++;
						rear=enQueue(rear,a);//入队 
					}
				}
				DeQueue(top);//首出队 
			}
				outcome[j]=len;j++;count++;//visit results  		
		 } 
	} 
	printf("%d\n",count);
	//起泡排序
	for(i=0;i<count;i++)
	{
		len=1;
		for(j=count-1;j>i;j--)
		{
			if(outcome[j-1]>outcome[j])
			{
				t=outcome[j-1];
				outcome[j-1]=outcome[j];
				outcome[j]=t;
				len=0;
			}
		}
		if(len)
			break;
	}
	 
	for(i=0;i<count;i++)
		printf("%d ",outcome[i]);
	return 1;
}

遇到的一些问题:

①计数从0开始还是1开始,全局记得统一 。
②出现了变量太多不知道含义的错误,并且分不清参量的用途。写着的感受就是:乱。
③没有办法进入下一轮 --->每一次新建队列而不是用旧的、deQuenue完的空队。(好像也不一定,好像是我的问题) 
④用1维的数组标记就可以,不用2维数组。因为如果出现,那么它必然只能出现一次。本质上来说,只需要遍历一次就可以。
⑤注意考虑只有一个点情况,即从a点出发的边又指向a点。 

使用栈的代码

同样也是60分 

①多余的mark数组和计算顶点数目的数组完全没有必要,合理充分利用邻接矩阵,同时也减少了变量数目

②用stack,这样我就少些了队列定义的代码,我不会队列嘛:D

③没有多余的子函数,全部在主函数中用循环结构遍历

#include<stack>
#include<iostream>
using namespace std;

int main()
{
	int n,m,t,i,j,count=0;
	int array[31][31]={0};
	cin >> n >> m;
	for (t = 1; t <= m; t++) 
	{
		cin >> i >> j;
		array[j][i] = 1;array[i][j] = 1;
		array[i][0] =-1;array[j][0] =-1;
	}
	
	for(i=1;i<=n;i++)
	{
		if(array[i][0]==-1)//有节点,并且没有被访问过 
		{ 
			stack<int> graph;//使用栈的数据结构进行dfs搜索 
			graph.push(i);//这个节点入栈
			count++;
			array[i][0]++;//入栈时标记该节点已经被访问过 
			array[0][i]++;//此位置记录此子图所含元素个数,入栈时标记 
			while(graph.size())//循环:非空
			{
				j=graph.top();
				graph.pop();
				for(t=1;t<=n;t++)
				{//条件:连通+未访问过 
					if(array[j][t]&&array[t][0]==-1)
					{
						//先标记
						array[0][i]++;array[t][0]++;
						graph.push(t); 
					}
				}
			 } 
		}
	//	printf("%d号子图含有顶点%d\n",i,array[0][i]);
	}
	printf("%d\n",count);
	//需要对答案排序之后输出
	//虽然要求从小到大,但是可以嗯反过来 
	int flog;
	for(i=1;i<=n;i++)
	{
		flog=1;
		for(j=n;j>i;j--)
		{
			if(array[0][j-1]<array[0][j])
			{
				flog=0;
				t=array[0][j];
				array[0][j]=array[0][j-1];
				array[0][j-1]=t;
			}
		}
		if(flog)
			break; 
	 } 
	 for(i=count;i>0;i--)
	 	printf("%d ",array[0][i]);
	return 0;
}

正确代码 

 不改了,附上我同学正确代码。好羡慕别人提交一次就对一次。

#include <iostream>
#include <stack>
using namespace std;
int main() {
	int m, n;
	cin >> n >> m; //顶点数,边数
	
	int a[n + 1][n + 1] = {0};//邻接矩阵表示
	int b[n + 1] = {0}; //存放每个连通子图顶点数
	
	for (int i = 1; i <= m; i++) 
	{
		int x, y;
		cin >> x >> y;
		a[x][y] = 1;
		a[y][x] = 1;
	}
	
	int q = 0; //当前遇到过q个连通子图
	for (int i = 1; i <= n; i++) 
	{
		if (a[i][0] == 0) //条件限制重复访问。同时第一次是能够进去的。 
		{ //这个顶点目前不属于任何连通子图
		q++;//连通子图+1
		stack<int> s;
		s.push(i);//子图第一个定点入栈
		//b[i]++; //每入栈一个元素,说明子图顶点数+1
			while (!s.empty()) 
			{ //寻找与q相连的所有顶点
		
				int now = s.top(); //出栈
				s.pop();
				if (a[now][0] == 0) 
				{//防止栈中元素为12344时对“4“的重复处理
					b[i]++; //说明子图顶点数+1
					a[now][0] = q; //标记这个顶点属于第q个子图
				
					for (int j = 1; j <= n; j++) 
					{ //遍历与他相连的顶点,把不属于任何子图的进栈
						if (a[now][j] == 1 && now != j && a[j][0] == 0) {
						s.push(j);//入栈
						//b[i]++; //每入栈一个元素,说明子图顶点数+1
						}//if
					}//for
				}//if
			}//while
		}//if
	}//for
	
	cout << q << endl;
	for (int i = 1; i <= n - 1; i++)
	{ //选择排序从小到大
		for (int j = i + 1; j <= n; j++) 
		{
			if (b[j] < b[i]) 
			{
				int t = b[i];
				b[i] = b[j];
				b[j] = t;
			}//if
		}//for
	}//for
	for (int i = 1; i <= n; i++)
		if (b[i] != 0)
			cout << b[i] << " ";
	return 0;
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值