求矩阵的连通分支数问题

有这样一个题目

给出一个mxn的矩阵,矩阵中的元素为0或1,。称位置(x,y)与其上下左右四个位置(x,y+1)、(x,y-1)、(x+1,y)、(x-1,y)是相邻的。如果矩阵中有若干个1是相邻的(不必两两相邻),那么称这些1构成了一个“块”(可以理解为连通分量)。求给定的矩阵中“块”的个数。
          0 1 1 1 0 0 1
          0 0 1 0 0 0 0
          0 0 0 0 1 0 0
          0 0 0 1 1 1 0
          1 1 1 0 1 0 0
          1 1 1 1 0 0 0
例如上面的6x7矩阵中,“块”的个数是4。

对于这个问题,基本思想是枚举每一个位置的元素,如果为1且未访问过(用一个二维bool数组标记是否访问过),说明这是个新“块”,块数增1,访问该位(置对应bool二维数组元素为true),并对其进行深度搜索或者广度搜索,即同样地查询与之相邻的四个位置,枚举和遍历都结束后输出块数即可。
为方便查询相邻的四个位置,可以建立两个增量数组,表示四个方向。
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};
竖着来看即(0,1)、(0,-1)、(1,0)、(-1,0)
这样就可以用for循环来枚举四个方向,以确定与当前坐标(nowX,nowY)相邻的4个位置,如下所示:

for(int i=0;i<4;i++){
        nowX=nowx+X[i];
        nowY=nowy+Y[i];
    }

广度遍历算法如下:

#include<cstdio>
const int maxn=100;
using namespace std;
int n,m;
int matrix[maxn][maxn];//0,1矩阵
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};//增量数组
bool inq[maxn][maxn]={false};//记录是否访问
bool judge(int x,int y){//判断坐标(x,y)是否需要访问
    if(x<0||x>n||y<0||y>m)
    //越界,返回false
    return false;
    //已经访问过或当前位为0,返回false
    if(inq[x][y]==true||matrix[x][y]==0)
        return false;
        //除去以上情况,返回true
    return true;
}
void dfs(int x,int y){//深度遍历算法
    if(!judge(x,y)) return ;//递归出口,当前位不需要访问
    inq[x][y]=true;//置访问位为true
    for(int i=0;i<4;i++){//查询相邻的位置
        dfs(x+X[i],y+Y[i]);
    }
}
int main(){
    scanf("%d%d",&m,&n);//输入行列
    for(int i=0;i<m;i++){//输入矩阵
        for(int j=0;j<n;j++){
            scanf("%d",&matrix[i][j]);
        }
    }
    int ans=0;//块数
    for(int i=0;i<m;i++){
        for(int j=0;j<n;j++){//枚举每一位置
            if(matrix[i][j]==1&&inq[i][j]==false){//当前位为1且未访问
            //说明找到新"块"
                    ans++;
                    dfs(i,j);
            }
        }
    }
    printf("%d",ans);
    return 0;
}

这道题也可以用广度遍历算法来做,广度遍历算法需用到队列,枚举思想同上,如遇到为1且未访问的位,说明是新块,块数要加1,还要将这块中的所有元素都访问。广度遍历的做法是将当前位入队,访问队首元素并出队,然后将与队首相邻的需要访问的位入队,此后不断地进行出队入队操作,直到队空为止,说明本块中全部访问完了。最后输出块数。
算法只有BFS部分不同,代码如下:

#include<cstdio>
#include<queue>
const int maxn=100;
using namespace std;
int n,m;
int matrix[maxn][maxn];
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};
bool inq[maxn][maxn]={false};
struct node{
    int x,y;
}Node;
queue<node> q;
bool judge(int x,int y){
    if(x<0||x>n||y<0||y>m)
    return false;
    if(inq[x][y]==true||matrix[x][y]==0)
        return false;
    return true;
}
void bfs(int x,int y){
    queue<node> q;//定义队列
    Node.x=x;Node.y=y;//为结点赋值横纵坐标
   q.push(Node);//将该结点入队列
   inq[x][y]=true;//设置该结点已经入过队
   while(!q.empty()){//队空即结束
    node top=q.front();//取队首结点
    q.pop();//将队首结点出队
    for(int i=0;i<4;i++){//访问相邻位置
        int nowX=top.x+X[i];
        int nowY=top.y+Y[i];
        if(judge(nowX,nowY)){//如果该位置需要访问
            Node.x=nowX;//设置Node坐标
            Node.y=nowY;
            q.push(Node);//将结点入队
            inq[Node.x][Node.y]=true;//设置该结点已入过队
        }
    }
   }

}
int main(){
    scanf("%d%d",&m,&n);
    for(int i=0;i<m;i++){
        for(int j=0;j<n;j++){
            scanf("%d",&matrix[i][j]);
        }
    }
    int ans=0;
    for(int i=0;i<m;i++){
        for(int j=0;j<n;j++){
            if(matrix[i][j]==1&&inq[i][j]==false){
                    ans++;
                    bfs(i,j);
            }
        }
    }
    printf("%d",ans);
    return 0;
}

最后再提醒一下,BFS中设置的inq数组的含义是判断结点是否入队,而不是是否访问过,如果含义为是否访问,考虑这种情形:当前结点已在队中,但还未访问,故inq的相应位不会置为true,此时被其他与其相邻的结点查询到,从而导致其重复入队,计算量大增。
还有一点就是STL的 queue的push操作只是制造了一个该元素的一个副本入队,因此在入队后对原元素的访问不会影响队中的副本,对队中副本的修改也不会改变原元素,这点和传递指针是相反的。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要计算无向图的连通分支,可以使用深度优先搜索(DFS)算法。 具体实现步骤如下: 1. 定义一个组visited,用来记录每个顶点是否被访问过。 2. 定义一个变量count,用来记录连通分支,初值为0。 3. 对于每个未被访问过的顶点v,执行以下操作: 1)将v标记为已访问。 2)递归访问v的所有邻接点u,如果u未被访问过,则将u标记为已访问,并递归访问u的邻接点。 3)当所有与v连通的顶点都被访问过后,将count加1。 4. 返回count,即为无向图的连通分支。 以下是C语言实现代码: ```c #include <stdio.h> #include <stdbool.h> #define MAX_N 100 // 最大顶点 int graph[MAX_N][MAX_N]; // 无向图的邻接矩阵 bool visited[MAX_N]; // 记录顶点是否被访问过 // 深度优先搜索 void dfs(int v, int n) { visited[v] = true; for (int i = 0; i < n; i++) { if (graph[v][i] && !visited[i]) { dfs(i, n); } } } // 计算连通分支 int count_components(int n) { int count = 0; for (int i = 0; i < n; i++) { if (!visited[i]) { dfs(i, n); count++; } } return count; } int main() { int n, m; // n为顶点,m为边 scanf("%d%d", &n, &m); for (int i = 0; i < m; i++) { int u, v; // 边的两个端点 scanf("%d%d", &u, &v); graph[u][v] = graph[v][u] = 1; // 无向图的邻接矩阵是对称的 } int components = count_components(n); printf("The number of connected components is %d\n", components); return 0; } ``` 注意:该代码只适用于稠密图,即顶点和边都比较大的情况。对于稀疏图,应该使用邻接表来表示图,以避免浪费空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值