现在有 105 个用户,编号为 1- 105,现在已知有 m 对关系,每一对关系给你两个数 x 和 y ,代表编号为 x 的用户和编号为 y 的用户是在一个圈子中,例如: A 和 B 在一个圈子中, B 和 C 在一个圈子中,那么 A , B , C 就在一个圈子中。现在想知道最多的一个圈子内有多少个用户。
数据范围:
进阶:空间复杂度 ,时间复杂度
进阶:空间复杂度 ,时间复杂度
第一行输入一个整数T,接下来有T组测试数据。
对于每一组测试数据:第一行输入1个整数n,代表有n对关系。接下来n行,每一行输入两个数x和y,代表编号为x和编号为y的用户在同一个圈子里。1 ≤ T ≤ 101 ≤ n ≤ 2*1061 ≤ x, y ≤ 105
对于每组数据,输出一个答案代表一个圈子内的最多人数
2 4 1 2 3 4 5 6 1 6 4 1 2 3 4 5 6 7 8
4 2
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; class UnionFind { public static int[] parent = new int[10000001]; public static int[] personNums = new int[10000001]; public UnionFind(){ for(int i = 1; i <= 10000000; i++){ parent[i] = i; personNums[i] = 1; } } public int find(int x) { while(x != parent[x]){ parent[x] = parent[parent[x]]; // 路径压缩 x = parent[x]; } return x; } public void union(int x, int y){ int rootX = find(x); int rootY = find(y); if(rootX == rootY) return; parent[rootY] = rootX; personNums[rootX] += personNums[rootY]; } } public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int T = Integer.parseInt(br.readLine()); while(T -- > 0){ int n = Integer.parseInt(br.readLine()); UnionFind uf = new UnionFind(); for(int i = 0; i < n; i++){ String[] params = br.readLine().trim().split(" "); int x = Integer.parseInt(params[0]); int y = Integer.parseInt(params[1]); uf.union(x, y); } int max = 0; for(int i = 1; i < uf.personNums.length; i++) max = Math.max(max, uf.personNums[i]); System.out.println(max); } } }
#include <iostream> #include <unordered_map> using namespace std; unordered_map<int, int> fa; //用fa来存储父节点 unordered_map<int, int> cnt; //用cnt来存储节点的子节点数量 int find(int x) //寻找父节点 { if (fa.find(x) == fa.end()) { //fa[x]尚未存在表中时 fa[x] = x; //将fa[x]的父节点设置为自己 cnt[x] = 0; } if (x == fa[x]) { return x; } return fa[x] = find(fa[x]); //将x的根节点设置为父节点(路径压缩),并返回根节点 } void Union(int x, int y) //合并 { int fx = find(x); //fx为x的父节点 int fy = find(y); //fy为y的父节点 fa[fx] = fy; //将fx的父节点设为fy } int main() { int T; cin >> T; while (T--) { int n; cin >> n; while (n--) { int x, y; cin >> x >> y; Union(x, y); } int ans = 0; //每个节点子节点数至少为1(其自身),故将初始ans设置为0用于后续比较 for (unordered_map<int,int>::iterator it=fa.begin();it!=fa.end(); it++) { int fi = find(it->second); cnt[fi]++; //每到节点i就往其根节点cnt[]+1 if (cnt[fi] > ans) ans = cnt[fi]; } cout << ans << endl; fa.clear(); cnt.clear(); } return 0; }
// 邻接表 + dfs #include<iostream> #include<vector> using namespace std; int cnt,ans; int v[100001]; vector<int>a[100001]; void dfs(int x) { int i,num; v[x]=1; cnt++; for(i=0;i<a[x].size();i++) { num=a[x][i]; if(v[num]==0)dfs(num); } } int main() { int i,k,n,x,y,T; scanf("%d",&T); for(i=0;i<T;i++) { scanf("%d",&n); for(k=1;k<=1e5;k++) { v[k]=0; a[k].clear(); } for(k=1;k<=n;k++) { scanf("%d %d",&x,&y); a[x].push_back(y); a[y].push_back(x); } ans=0; for(k=1;k<=1e5;k++) { cnt=0; dfs(k); ans=max(ans,cnt); } printf("%d\n",ans); } return 0; }
使用并查集,并添加路径压缩。关于并查集和路径压缩的讲解见并查集
详细注释代码如下:
import java.util.HashMap; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int t = sc.nextInt(); // t组数据 for (int i = 0; i < t; i++) { int n = sc.nextInt(); // n对关系 HashMap<Integer,Integer> map = new HashMap<>(); // 用户数字 到 数组位置 的映射 int[] check = new int[n * 2]; // 并查集数组(n对,最多 n * 2 个用户) for (int j = 0; j < n * 2; j++) { // 初始化并查集,指向自己 check[j] = j; } for (int j = 0; j < n; j++) { // 建立并查集 int a = sc.nextInt(); // 读入用户对 int b = sc.nextInt(); int x = map.getOrDefault(a, map.size()); // 找到映射 map.put(a, x); int y = map.getOrDefault(b, map.size()); map.put(b, y); if (find(check, x) != x && find(check, y) != y) { // 当都有根,从根部联通两并查集,能减少find的次数 check[find(check, x)] = find(check, y); // (连接方式未优化) } else if (find(check, x) != x) { // 只有一方有根,另一方直接连到根;都没根时一样 check[y] = find(check, x); } else { check[x] = find(check, y); } } HashMap<Integer,Integer> res = new HashMap<>(); // 并查集根 和 并查集节点个数 的映射 for (int j = 0; j < n * 2; j++) { // 遍历并查集 int k = find(check, j); // 找到当前节点的根 res.put(k, res.getOrDefault(k, 0) + 1); // 节点个数加一 } int max = 0; for (Integer value : res.values()) { // 遍历各个并查集的节点个数,找出最大者 max = Math.max(max, value); } System.out.println(max); // 打印 } } // 查找并查集的根 private static int find(int[] check, Integer i) { if (check[i] != i) { // 当前节点非根 check[i] = find(check, check[i]); // 当前节点指向根(路径压缩) } return check[i]; // 返回根 } }
"""使用并查集解题""" """ 运行时间: 2834ms 占用内存: 14236KB """ class UnionFind(): def __init__(self, **kwargs): """构造函数""" self.fa = [i for i in range(int(1e5+1))] #存储每个数据的根节点 self.num = [1] * int(1e5+1) #存储根节点的子节点数目 self.max_num = 1 def find(self, p): """查找根节点""" if self.fa[p] == p: return p self.fa[p] = self.find(self.fa[p])#把父节点的根节点当作父节点 return self.fa[p] #返回父节点,此时父节点就是根节点 def merge(self, p, q): """连通p,q 让q指向p""" proot = self.find(p) qroot = self.find(q) if proot != qroot: self.fa[qroot] = proot self.num[proot] += self.num[qroot] self.max_num = max(self.max_num, self.num[proot]) def get_max(self): return self.max_num def reset(self): self.fa = [i for i in range(int(1e5+1))] self.num = [1] * int(1e5+1) self.max_num = 1 T = int(input().strip()) UF = UnionFind() for i in range(T): # print(f'第{i+1}组测试数据') UF.reset() n = int(input().strip()) ii = 0 while ii < n: p, q = map(int, input().split()) UF.merge(p, q) ii += 1 print(UF.get_max())
importjava.util.*; importjava.io.*; importjava.lang.Math; public class Main { public static void main(String[] args) { Scanner cin=new Scanner(new BufferedInputStream(System.in)); int t=cin.nextInt(); int[] res=new int[t]; for (int i = 0; i < t; ++i) { int n = cin.nextInt(); int[][] relationships = new int[n][2]; Set<Integer> UFS=new HashSet<>(); //收集这一组中出现过的元素,用于下面建立并查集 for (int j = 0; j < n; ++j) { //自定义格式化每一组的关系 relationships[j][0] = cin.nextInt(); relationships[j][1] = cin.nextInt(); UFS.add(relationships[j][0]); UFS.add(relationships[j][1]); } res[i]=maxCircle(relationships,UFS); //计算这一组中最大圈子的数量 } for (int re : res) { System.out.println(re); } cin.close(); } private static int maxCircle(int[][] relationships,Set<Integer> UFS) { Map<Integer,Integer> ufs=new HashMap<>(); for (Integer uf : UFS) //初始化并查集 ufs.put(uf,-1); for (int[] relationship : relationships) ufsUnion(ufs,relationship[0],relationship[1]); //根据关系调整并查集 int min = 0; for (Integer uf : UFS) min=Math.min(ufs.get(uf),min); return (-1)*min; } private static void ufsUnion(Map<Integer, Integer> ufs, int a, int b) { int root1=ufsFind(ufs,a); int root2=ufsFind(ufs,b); if (root1==root2) return; int temp1=ufs.get(root1); int temp2=ufs.get(root2); //路径压缩,让小树并到大树下,尽量减少树的高度 if (temp1<temp2) { ufs.put(root2,root1); ufs.put(root1,temp1+temp2); } else { ufs.put(root1,root2); ufs.put(root2,temp1+temp2); } } private static int ufsFind(Map<Integer,Integer> ufs,int a) { int temp=a; while (ufs.get(temp)>0) temp=ufs.get(temp); return temp; } }
#include <bits/stdc++.h> using namespace std; int total_t, n; int NumofLargestGroup(vector<int>* mat, int n) { if(!n) return n; vector<bool> visited(10000005, false); int ans = 0; stack<int> s; for(int i = 1; i <= 10000000; ++i) { if(visited[i] || mat[i].empty()) continue; s.push(i); int cur_ans = 0; while(!s.empty()) { int cur = s.top(); s.pop(); if(visited[cur]) continue; visited[cur] = true; ++cur_ans; for(const auto& fr : mat[cur]) { //cout << "cur " << cur <<" fr " << fr << endl; if(visited[fr]) continue; s.push(fr); } } ans = max(ans, cur_ans); } return ans; } int main() { ios::sync_with_stdio(false), cin.tie(0); cin >> total_t; while(total_t--) { vector<int> mat[10000005]; cin >> n; int a, b; for(int i = 1; i <= n; ++i) { cin >> a >> b; mat[a].push_back(b); mat[b].push_back(a); } cout << NumofLargestGroup(mat, n) << endl; } }
public class Solution { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int graph_nums = sc.nextInt(); int T = 0; while (T < graph_nums){ LinkedHashSet<edge> edges = new LinkedHashSet<>(); int edges_nums = sc.nextInt(); for (int i = 0; i < edges_nums; i++) { edge edge = new edge(sc.nextInt(),sc.nextInt()); edges.add(edge); } int pyq = pyq(edges); System.out.println(pyq); T++; } sc.close(); } //求最大连通分量 public static int pyq(LinkedHashSet<edge> edges){ // 1. 构建邻接表 Map<Integer, List<Integer>> adjacencyList = new HashMap<>(); for (edge e : edges) { adjacencyList.putIfAbsent(e.node1, new ArrayList<>()); adjacencyList.putIfAbsent(e.node2, new ArrayList<>()); adjacencyList.get(e.node1).add(e.node2); adjacencyList.get(e.node2).add(e.node1); } // 2. 遍历每个连通分量,记录最大值 int maxComponentSize = 0; Set<Integer> visited = new HashSet<>(); for (int node : adjacencyList.keySet()) { if (!visited.contains(node)) { int componentSize = dfs(adjacencyList, node, visited); maxComponentSize = Math.max(maxComponentSize, componentSize); } } return maxComponentSize; } private static int dfs(Map<Integer, List<Integer>> adjacencyList, int node, Set<Integer> visited) { visited.add(node); int componentSize = 1; for (int neighbor : adjacencyList.getOrDefault(node, new ArrayList<>())) { if (!visited.contains(neighbor)) { componentSize += dfs(adjacencyList, neighbor, visited); } } return componentSize; } } class edge{ int node1,node2; edge(int node1,int node2){ this.node1 = node1; this.node2 = node2; } public boolean equals(edge e) { if ((e.node1 == this.node1 && e.node2 == this.node2) || (e.node1 == this.node2 && e.node2 == this.node1)){ return true; } return false; } }
可以使用并查集(Union-Find)来解决此问题。首先将所有用户看作一个单独的圈子,然后遍历所有关系对,将它们所在的圈子合并起来。最终,统计每个圈子的大小并找到最大值即可。
以下是Python代码的示例实现:
class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.size = [1] * n def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): root_x, root_y = self.find(x), self.find(y) if root_x == root_y: return if self.size[root_x] < self.size[root_y]: root_x, root_y = root_y, root_x self.parent[root_y] = root_x self.size[root_x] += self.size[root_y] def max_circle(m): uf = UnionFind(105) for x, y in m: uf.union(x-1, y-1) return max(uf.size) # Example usage: m = [(1, 2), (2, 3), (4, 5), (6, 7), (7, 8), (9, 10), (100, 101)] max_size = max_circle(m) print(max_size) # Output: 4
其中,UnionFind类实现了并查集,其中parent数组用于记录每个节点的父节点,size数组用于记录每个圈子的大小。find方法实现了路径压缩,使查找操作的时间复杂度接近常数。union方法实现了按秩合并和路径压缩,使合并操作的时间复杂度接近常数。
max_circle函数使用UnionFind类来处理关系对,然后找到最大圈子的大小并返回。
================================#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAX_N = 1e5 + 5; int parent[MAX_N]; int sz[MAX_N]; int find(int x) { if (x == parent[x]) { return x; } parent[x] = find(parent[x]); return parent[x]; } void merge(int x, int y) { int px = find(x); int py = find(y); if (px == py) { return; } parent[py] = px; sz[px] += sz[py]; } int main() { cin.tie(0); cout.tie(0); ios::sync_with_stdio(0); int t; cin >> t; while (t--) { int m; cin >> m; unordered_set<int> set; for (int i = 0; i < MAX_N; i++) { sz[i] = 1; parent[i] = i; } for (int i = 0; i < m; i++) { int x, y; cin >> x >> y; set.insert(x); set.insert(y); merge(x, y); } int ans = 0; for (auto x : set) { ans = max(ans, sz[find(x)]); } cout << ans << endl; } return 0; }
#参考https://oi-wiki.org/ds/dsu/ class Dsu: def __init__(self,size): self.pa = list(range(size)) self.size = [1]*size self.max_size=1 def find(self,x): #沿着树向上找到x的根节点,并将查询过程中经过的每个元素都归到根节点(路径压缩) if self.pa[x]!=x: self.pa[x] = self.find(self.pa[x]) return self.pa[x] def union(self,x,y):# 将x的根节点接到y, #启发式合并,将小的树合并到大的树 x,y = self.find(x),self.find(y) if x==y: return if self.size[x]<self.size[y]: x,y=y,x self.pa[y]=x self.size[x]+=self.size[y] if self.size[x]>self.max_size: self.max_size=self.size[x] while True: try: T=int(input()) for _ in range(T): n = int(input()) dsu = Dsu(100000+1) for _ in range(n): x,y=list(map(int,input().split())) dsu.union(x,y) print(dsu.max_size) del dsu except: break
# 深度优先算法 t=int(input()) from collections import defaultdict import sys # 导入sys模块 sys.setrecursionlimit(30000) def rec(num): if dic.get(num): return dic.get(num) visitd.add(num) find_count=1 for p in edge[num]: if p not in visitd: find_count+=rec(p) dic[num]=find_count return dic[num] for i in range(t): edge=defaultdict(list) n,nums,dic=int(input()),set(),{} for j in range(n): x,y=[m for m in input().split()] nums.add(x) nums.add(y) edge[x].append(y) edge[y].append(x) visitd,max_count=set(),2 for q in nums: if q not in visitd and (len(nums)-len(visitd))>max_count: tem=rec(q) max_count=max(max_count,tem) print(max_count) #广度优先算法 t=int(input()) from collections import defaultdict for i in range(t): edge,max_count=defaultdict(list),2 n,nums,visited=int(input()),set(),set() for j in range(n): x,y=[m for m in input().split()] nums.add(x) nums.add(y) edge[x].append(y) edge[y].append(x) for i in nums: if i not in visited: count,stack=1,[i] visited.add(i) while stack: cur=stack.pop() for j in edge[cur]: if j not in visited: visited.add(j) stack.append(j) count+=1 max_count=max(max_count,count) print(max_count)
import java.util.*; public class Main { /* 整体背景就是一堆孩子 认爹 然后看哪个爹孩子最多(还要加上老爹) */ public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); //一共几组数据 for (int i = 0; i < n; i++) { //找自己的爹节点 如果parent中不存在自己 那么说明自己没有爹节点 //自己就是自己的爹 Map<Integer, Integer> parent = new HashMap<Integer, Integer>(); //哪个爹有多少个孩子 key是爹 value是孩子的数量 Map<Integer, Integer> sizeMap = new HashMap<Integer, Integer>(); int m = sc.nextInt(); for (int j = 0; j < m; j++) { int a = sc.nextInt(); int b = sc.nextInt(); //分别找爹 看看谁的爹大 int pa= find(parent,a); int pb= find(parent,b); //如果你爹不等于我爹 if (pa!=pb){ //那么你pb要认我为爹 parent.put(pb, pa); //你pb家族的孩子都归我们家族了 int pbSize = sizeMap.getOrDefault(pb, 1); int paSize = sizeMap.getOrDefault(pa, 1); sizeMap.put(pa,paSize+pbSize); //pb家族从此除根 sizeMap.remove(pb); } } //找哪个家族最庞大 孩子最多 基本就是一map遍历过程 Iterator<Map.Entry<Integer, Integer>> it = sizeMap.entrySet().iterator(); int res=1; while (it.hasNext()){ res=Math.max(res,it.next().getValue()); } System.out.println(res); } } //找爹的函数 这里必须扁平化 否则时间过不了 //解释一下扁平化 就是说1是2爹 2是3爹 //这样3要找爹的话 肯定3->2 2->1 分两步 //如果链子很长就不行了 所以现在要乱辈 大家都认一个最大是爹 剩下都是孩子 //可以想象优化后就是一个N叉树 就两层的 根节点是爹 剩下一排孩子 private static int find(Map<Integer, Integer> parent, int i) { //保存沿途想认最大的爹为爹的孩子节点 Stack<Integer> s=new Stack<>(); //循环 只要我还能找到爹 就继续往下找 while (parent.containsKey(i)){ s.push(i); i=parent.get(i); } //扁平化 while (!s.isEmpty()){ parent.put(s.pop(),i); } //返回家族最大的爹 return i; } }
简单并查集模型
#include <iostream> const int M = 2000010,N = 100010; using namespace std; int t; int p[N]; int sum[N]; int find(int x) { if(p[x] != x) p[x] = find(p[x]); return p[x]; } int main() { scanf("%d",&t); while(t--) { int n; scanf("%d",&n); for(int i = 1;i <= N;i++) { p[i] = i; sum[i] = 1; } int res = 0; while(n--) { int x,y; scanf("%d%d",&x,&y); int px = find(x),py = find(y); if(px != py) { p[px] = py; sum[py] += sum[px]; } res = max(res,sum[py]); } printf("%d\n",res); } return 0; }
用了并查集模板就是舒服
import java.util.*; public class Main{ static int N = 100010; // 习惯了超出10个 static int[] p = new int[N + 1]; // 每个用户的父节点 static int[] size = new int[N + 1]; // 只保证根节点的数量正确有效即可 public static void main(String[] args) { Scanner sc = new Scanner(System.in); int T = sc.nextInt(); for (int k = 0; k < T; k++) { // 每次都初始化所有用户(因为题目给了 T 组测试数据) for (int i = 1; i <= N; i++) { p[i] = i; size[i] = 1; } int n = sc.nextInt(); // n对关系 for (int i = 0; i < n; i++) { int x = sc.nextInt(); int y = sc.nextInt(); if(find(x) == find(y)) continue; // 不能重复挂 // x挂到y上 之前,先统计x、y两个集合人数,再把x挂到y size[find(y)] += size[find(x)]; //先统计两个集合点数 p[find(x)] = find(y); // 把x挂到y的根节点上 } // 统计那个最大朋友圈的用户数 int res = 0; for (int i : size) { res = Math.max(res, i); } System.out.println(res); } } // 找出x的根节点(带路径压缩) public static int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } }
n=100001 T=int(input()) #寻找根节点 def getFather(x): a=x while father[x]!=x: x=father[x] #路径压缩 while father[a]!=a: z=a a=father[a] father[z]=x return x for _ in range(T): #初始化 father = [i for i in range(n)] isroot=[0]*(n) m=int(input()) for _ in range(m): i,j=map(int,input().strip().split()) if getFather(i)!=getFather(j): i=getFather(i) j=getFather(j) father[j]=i res=1 for i in range(1,n): isroot[getFather(i)]+=1 res=max(res,isroot[getFather(i)]) print(res)
#include<bits/stdc++.h> using namespace std; unordered_map<int, int>fa; unordered_map<int, int>cnt; int find(int x){ if(fa.find(x)==fa.end()){ fa[x]=x; cnt[x]=0; } if(x==fa[x]){ return x; } return fa[x]=find(fa[x]); } void Union(int x,int y){ int fx=find(x); int fy=find(y); fa[fx]=fy; } int main(){ int T;cin>>T; while(T-- >0){ int n;cin>>n; while(n-- >0){ int x,y; cin>>x>>y; Union(x,y); } int ans=0; for(unordered_map<int, int>::iterator it=fa.begin();it!=fa.end();it++){ int fi=find(it->second); cnt[fi]++; ans=max(ans,cnt[fi]); } cout<<ans<<endl; fa.clear(); cnt.clear(); } }
using namespace std; #include<iostream> #include<algorithm> #include<numeric> #include<vector> //#include<bits/stdc++.h> vector<int>id(100001, 0); vector<int>counts(100001, 1); int maxrs = 0; int find(int x) { while (x!=id[x]) { id[x] = id[id[x]]; x = id[x]; } return x; } void unionxy(int x, int y) { x = find(x); y = find(y); if (x != y) { if (counts[x] > counts[y]) { counts[x] += counts[y]; counts[y] = 0; id[y] = id[x]; if (maxrs < counts[x]) maxrs = counts[x]; } else { counts[y] += counts[x]; counts[x] = 0; id[x] = id[y]; if (maxrs < counts[y]) maxrs = counts[y]; } } } int main() { int T; cin >> T; while (T--) { int n; cin >> n; iota(id.begin(), id.end(), 0); fill(counts.begin(), counts.end(), 1); maxrs = 0; for (int i = 0; i < n; i++) { int x, y; cin >> x >> y; unionxy(x, y); } cout << maxrs << endl; } return 0; }
#include<iostream> #include<vector> #include<unordered_map> using namespace std; int deep(int key, unordered_map<int, vector<int>> &mp, unordered_map<int, int> &visit){ vector<int> tmp = mp[key];//访问当前节点是否有其他子节点 visit[key] = 1;//并且标志这个节点已经访问过了 int d = 1;//然后 深度 加1 for(auto t:tmp){ if(!visit[t])//如果没有访问 d += deep(t,mp,visit);//那么当前节点就加上 子节点的深度 } return d;//返回当前节点的深度 } void func(unordered_map<int, vector<int>> &mp){ int ans = 0; unordered_map<int, int> visit; for(auto [key, vval]:mp){ if(!visit[key]){//如果这个节点没有访问过,那么这个节点连接的树就是一棵独立的树 ans = max(ans, deep(key, mp, visit));//求最大深度 } } cout<<ans<<endl; } int main(){ std::ios::sync_with_stdio(0); cin.tie(0);//加快输入 int T; cin>>T; while(T--){ int n; cin>>n; int x,y; unordered_map<int, vector<int>> mp; for(int i = 0; i < n; i++){ cin>>x>>y; mp[x].emplace_back(y);//由单向图建立双向图 mp[y].emplace_back(x); } func(mp); } return 0; }