leetcode 547.省份数量(C\C++\java\python)

PS:算法并非原创,仅作个人学习记录使用,侵删。

题目描述
在这里插入图片描述

算法思路分析
首先看到这道题目的时候,个人的想法就是使用图论里面的“连通分量”这一个概念,这在最小生成树的prim算法中也有使用到。所以就用一个二维矩阵存储邻接矩阵,然后再用一个联通数组指明哪些结点属于一个连通分量,之后再统一计数即可。
而通过浏览前辈们的算法思想之后,这道题目的解题算法主要有三类:
深度优先遍历(dfs),广度优先遍历(bfs),并查集,
其中深度优先和广度优先有思想上的相似性,先建立好图的连接,然后遍历顶点集,当遇到一个新的结点时,以它为组长找到剩余的组内结点,并做好标记,不同的只是找到剩余组内结点的过程。
而并查集思想则是先将它们全部分散为单独的组,之后如果有联系,则合并两个结点。在这个合并的过程中,组长永远是小组的最后一个成员,而之前的成员会一个指向一个,最终指向组长的标号。

代码实现:
【C】

/*
算法简介:
深度优先遍历方法
通过visitd数组,从头遍历城市数组,每个城市都通过深度遍历查找所有连接城市,并将这些城市做上“记号”。
这样每个连通分量只统计一次,且每个连通分量都不会有遗漏
*/

void dfs(int** isConnected, int* visited, int provinces, int i) {//递归算法
//邻接矩阵——isConnected
//访问数组——visited
//城市数量——provinces
//i当前城市编号
    for (int j = 0; j < provinces; j++) {//遍历所有城市
        if (isConnected[i][j] == 1 && !visited[j]) {//如果城市i和遍历到的城市j之间有连接,并且j没有访问过
            visited[j] = 1;//j和i合并,此刻把j置为“已访问”,之后就不会在涉及它的连通分量查找。
            //j与i直接连接
            dfs(isConnected, visited, provinces, j);//再寻找与城市i间接连接的城市,可以想见,这些城市也会设置为“已访问”,之后不会再加入统计
        }
    }
}

int findCircleNum(int** isConnected, int isConnectedSize, int* isConnectedColSize) {
    //isConnected表示二维邻接矩阵; 
    //isConnetedSize为矩阵大小,即n; 
    //isConnectedColSize为每行的长度
    int provinces = isConnectedSize;//省数量
    int visited[provinces];//表示某节点有没有是否访问过
    memset(visited, 0, sizeof(visited));//初始化visited访问数组,全部置0
    int circles = 0;//连通分量的数量
    for (int i = 0; i < provinces; i++) {//遍历所有的结点,
        if (!visited[i]) {//当某个结点已经访问过,跳过,当某个结点还没有访问过,进行dfs深度遍历
            dfs(isConnected, visited, provinces, i);//寻找i的所有连接城市(直接+间接)
            circles++;
        }
    }
    return circles;
}

C语言参考网址

【C++】

/*
并查集思想:
初始时,每个城市都属于不同的连通分量。
遍历矩阵isConnected,如果两个城市之间有相连关系,则它们属于同一个连通分量,对它们进行合并。
*/

class Solution {
public:
  int Find(vector<int>& parent, int index) {//查找某城市的“队长”
    if (parent[index] != index) {//如果是组员的话
      parent[index] = Find(parent, parent[index]);//组员的组长不再是自己,而是组长的编号。
    }
    return parent[index];
  }

  void Union(vector<int>& parent, int index1, int index2) {//合并函数,
  //parent数组,index1和index2表示两个需要合并的连通分量。
    parent[Find(parent, index1)] = Find(parent, index2);//parent[城市1的父节点编号] = 城市2的父节点编号 
    /*
    城市5和城市1,城市3都相同,城市7和城市5相通,那么
    parent[1]=3,parent[3]=5, parent[5[]=7,parent[7]=7(队长);
    */
  }

  int findCircleNum(vector<vector<int>>& isConnected) {//主要算法
    int cities = isConnected.size();//城市数量是矩阵的大小(行数)
    vector<int> parent(cities);//vector(int nSize):创建一个vector结构,元素个数为nSize
    //最开始每一个都是单独的集合,parent表示他们小组的“队长”,一开始队长都是自己
    for (int i = 0; i < cities; i++) {
      parent[i] = i;
    }
    //进行并查集的合并
    for (int i = 0; i < cities; i++) {//遍历城市
      for (int j = i + 1; j < cities; j++) {//遍历城市i之后的后续城市
        if (isConnected[i][j] == 1) {//i,j两城市间有联通则合并
          Union(parent, i, j);
        }
      }
    }
    int provinces = 0;//最终的省份数量
    for (int i = 0; i < cities; i++) {//遍历所有城市
      if (parent[i] == i) {//找到“队长”的话,组数++
        provinces++;
      }
      //如果找到的不是组长,而是组员,那么直接跳过。
    }
    return provinces;//返回省份数量
  }
};

C++参考网址

【JAVA】

/*
广度优先遍历
通过广度优先搜索的方法得到省份的总数。
对于每个城市,如果该城市尚未被访问过,则从该城市开始广度优先搜索,直到同一个连通分量中的所有城市都被访问到,即可得到一个省份。
*/
class Solution {
    public int findCircleNum(int[][] isConnected) {
        int n=isConnected.length;//城市数量
        boolean[] visited=new boolean[n];//访问数组
        LinkedList<Integer> list=new LinkedList<>();//链表,此处用作栈
        /*
        ArrayList 采用的是数组bai形式来保存对象的,这du种方式将对象放在连zhi续的位dao置中,所以最zhuan大的缺点就是插入删除时shu非常麻烦
        LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引 但是缺点就是查找非常麻烦 要从第一个索引开始.
        所以 LinkedList 在逻辑上是有序的,会按照你的插入顺序存储的。但物理上是不连续的。,也就是链表
        */
        int count=0;//总的省份数量
        //开始遍历
        for(int i=0;i<n;i++){//遍历所有城市
            //如果之间没有访问过
            if(visited[i]==false){
                list.addLast(i);//添加在末尾的位置
                while(!list.isEmpty()){//当栈中还有没处理的顶点
                    int a=list.removeFirst();//取出栈顶的顶点
                    visited[a]=true;//该顶点设为访问过
                    for(int j=0;j<n;j++){//遍历成熟
                        if(isConnected[a][j]==1&&visited[j]==false){//所有和顶点相连并且没有访问过的城市,都加入栈中
                            list.addLast(j);
                        }
                    }
                }//处理栈结束,这时栈为空,i的所有连接城市都已经设为已访问,进入下一轮循环,在此之前,统计省份数量
                count++;//
            }
            //如果之前已经访问过了城市i,那么跳过该城市
        }
        return count;
    }
}

Java参考网址
【python】

##图的深度优先遍历(dfs)
class Solution:
    def findCircleNum(self, isConnected):
        n,ans = len(isConnected),0#n——城市数量,ans——最终结果
        visited = set()#访问数字

        def dfs(i):#深度优先遍历函数定义
            visited.add(i)#i标记为已经访问
            for j in range(n):#遍历城市,找到和i相连的未访问城市,继续深度遍历
                if isConnected[i][j]==1 and j not in visited:
                    dfs(j)

        for i in range(n):#遍历城市
            if i not in visited:#尚未访问的城市表示一个新的连通分量
                ans += 1#省份+1
                dfs(i)#找到i的所有连接城市,做好标记
        return ans

python参考网址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值