class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { //map用来保存每个课程的入度,先完成1才能完成2的话,2的入度是1,1的入度是0 Map<Integer,Integer> map=new HashMap<>(); for(int i=0;i<numCourses;i++){ map.put(i,0); } //保存一个path记录,比如先完成2才能完成34 2->3、4 Map<Integer, List<Integer>>path=new HashMap<>(); for (int[] prerequisite : prerequisites) { int pre=prerequisite[1]; int next=prerequisite[0]; map.put(next,map.getOrDefault(next,0)+1); if(!path.containsKey(pre)) { path.put(pre, new ArrayList<>()); } path.get(pre).add(next); } //将入度为0的课程入队列 Deque<Integer>queue=new LinkedList<>(); for (Integer integer : map.keySet()) { if(map.get(integer)==0){ queue.addLast(integer); } } while (!queue.isEmpty()){ int pre=queue.poll(); if(!path.containsKey(pre)){ continue; } for (Integer integer : path.get(pre)) { //将课程的入度-1, map.put(integer,map.getOrDefault(integer,0)-1); if(map.get(integer)==0){ //如果该课程的入度为0,就添加到队列中 queue.addLast(integer); } } } for (Integer integer : map.keySet()) { //如果map中还有入度>0的课程,就表示不能完成所有的课程 if(map.get(integer)>0){ return false; } } return true; }}
//弗洛伊德算法 public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) { double[]res=new double[queries.size()]; int count=0; Map<String, Integer> map = new HashMap<>(); for (List<String> equation : equations) { String a = equation.get(0); String b = equation.get(1); if(!map.containsKey(a)){ map.put(a,count++); } if(!map.containsKey(b)){ map.put(b,count++); } } //构建二维的graph,用来存储变量之间的除法取值 double[][]graph=new double[count][count]; for(int i=0;i<count;i++){ graph[i][i]=1; } int index=0; for (List<String> equation : equations) { String a = equation.get(0); String b = equation.get(1); int aa = map.get(a); int bb = map.get(b); graph[aa][bb]=values[index]; graph[bb][aa]=1/values[index]; index++; } //弗洛伊德算法求解所有可能 for(int i=0;i<count;i++){ for(int j=0;j<count;j++){ for(int k=0;k<count;k++){ if(graph[j][i]>0&&graph[i][k]>0) { graph[j][k] = graph[j][i] * graph[i][k]; } } } } index=0; for (List<String> query : queries) { String a = query.get(0); String b = query.get(1); if(map.containsKey(a)&&map.containsKey(b)){ int aa=map.get(a); int bb=map.get(b); res[index++]=graph[aa][bb]==0?-1.0:graph[aa][bb]; }else{ res[index++]=-1.0; } } return res; }}
//简单并查集的套路,合并就完事了。最后的size表示最终能够分成多少个区域class Solution { public int findCircleNum(int[][] isConnected) { int n=isConnected.length; UnionFind uf = new UnionFind(n); for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ if(isConnected[i][j]==1){ uf.union(i,j); } } } return uf.size; } class UnionFind{ int[] parent; int size; public UnionFind(int size) { this.parent = new int[size]; for(int i=0;i<parent.length;i++){ parent[i]=i; } this.size=size; } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public void union(int x,int y){ int fatherX=findFather(x); int fatherY=findFather(y); if(fatherX!=fatherY){ parent[fatherY]=fatherX; size--; } } }}
//也是简单的并查集套路,如果无法union,就代表这条边重复class Solution { public int[] findRedundantConnection(int[][] edges) { int n=edges.length; UnionFind uf = new UnionFind(n + 1); for (int[] edge : edges) { int x=edge[0]; int y=edge[1]; if(uf.union(x,y)==null){ return new int[]{x,y}; } } return null; } class UnionFind{ int[]parent; int size; public UnionFind(int size) { this.parent = new int[size]; for(int i=0;i<size;i++){ parent[i]=i; } this.size=size; } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public int[] union(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX!=fatherY){ if(fatherX<fatherY){ parent[fatherY]=fatherX; return new int[]{fatherX,fatherY}; }else{ parent[fatherX]=fatherY; return new int[]{fatherY,fatherX}; } }else{ return null; } } }}
//优先级队列class Solution { public int swimInWater(int[][] grid) { PriorityQueue<Node> queue=new PriorityQueue<>(new Comparator<Node>() { @Override public int compare(Node o1, Node o2) { return o1.val-o2.val; } }); int row=grid.length; int col=grid[0].length; //构建已访问坐标的二维图形 boolean[][]vis=new boolean[row][col]; Node root = new Node(0, 0, grid[0][0]); queue.add(root); int res=root.val; int[][]direction=new int[][]{{0,1},{1,0},{0,-1},{-1,0}}; while (!queue.isEmpty()){ Node node = queue.poll(); int i=node.i; int j=node.j; if(vis[i][j]){ continue; } vis[i][j]=true; res=Math.max(node.val,res); //如果已到达终点就停止遍历 if(i==row-1&&j==col-1){ break; } //从上下左右四个方向中去加入坐标进队列中 for (int[] ints : direction) { int newI=i+ints[0]; int newJ=j+ints[1]; if(newI>=0&&newI<row&&newJ>=0&&newJ<col&&!vis[newI][newJ]){ queue.add(new Node(newI,newJ,grid[newI][newJ])); } } } return res; } class Node{ int i; int j; int val; public Node(int i, int j, int val) { this.i = i; this.j = j; this.val = val; } }}
//简单并查集套路class Solution { public int numSimilarGroups(String[] strs) { int n=strs.length; UnionFind uf = new UnionFind(n); for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ if(check(strs[i],strs[j])){ uf.union(i,j); } } } return uf.size; } //如果相似则能合并,否则不能合并 private boolean check(String a,String b){ int count=0; for(int i=0;i<a.length();i++){ if(a.charAt(i)!=b.charAt(i)){ count++; } if(count>2){ return false; } } return true; } class UnionFind{ int[]parent; int size; public UnionFind(int size) { this.size = size; parent=new int[size]; for(int i=0;i<size;i++){ parent[i]=i; } } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public void union(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX!=fatherY){ if(fatherX<fatherY){ parent[fatherY]=fatherX; }else{ parent[fatherX]=fatherY; } size--; } } }}
class Solution { public int removeStones(int[][] stones) { int n=stones.length; UnionFind uf = new UnionFind(n); for(int i=0;i<stones.length;i++){ for(int j=i+1;j<stones.length;j++){ if(check(stones[i],stones[j])){ uf.union(i,j); } } } //最终返回移除了多少块石头 return n-uf.size; } //如果同行或者同列,则能合并 private boolean check(int[]a,int[]b){ if(a[0]==b[0]||a[1]==b[1]){ return true; } return false; } class UnionFind{ int[]parent; int size; public UnionFind(int size) { this.size = size; parent=new int[size]; for(int i=0;i<size;i++){ parent[i]=i; } } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public void union(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX!=fatherY){ if(fatherX<fatherY){ parent[fatherY]=fatherX; }else{ parent[fatherX]=fatherY; } size--; } } }}

//这道题有点难度,需要拆分成4个区域来做class Solution { public int regionsBySlashes(String[] grid) { int n=grid.length; UnionFind uf = new UnionFind(n*n*4); for(int i=0;i<n;i++){ char[] chars = grid[i].toCharArray(); for(int j=0;j<n;j++){ //index标识该区域的首区,比如0、4、8、12 int index=4*(n*i+j); if(chars[j]=='/'){ //如果是/,合并0、3区域以及1、2区域 uf.union(index,index+3); uf.union(index+1,index+2); }else if(chars[j]=='\\'){ //如果是\\,合并0、1区域以及2、3区域 uf.union(index,index+1); uf.union(index+2,index+3); }else{ //如果是空格,合并所有区域 uf.union(index,index+1); uf.union(index+1,index+2); uf.union(index+2,index+3); } if(j+1<n){ //如果j+1<n,需要合并1、7区域 uf.union(index+1,4*(n*i+j+1)+3); } if(i+1<n){ //如果i+1<n,需要合并2、8区域 uf.union(index+2,4*(n*(i+1)+j)); } } } return uf.size; } class UnionFind{ int[]parent; int size; public UnionFind(int size) { this.size = size; parent=new int[size]; for(int i=0;i<size;i++){ parent[i]=i; } } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public void union(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX!=fatherY){ if(fatherX<fatherY){ parent[fatherY]=fatherX; }else{ parent[fatherX]=fatherY; } size--; } } }}
class Solution { public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) { int n=s.length(); UnionFind uf = new UnionFind(n); for (List<Integer> pair : pairs) { int a=pair.get(0); int b=pair.get(1); //合并区域 uf.union(a,b); } //构建优先级队列 Map<Integer, PriorityQueue<Character>> map=new HashMap<>(); for(int i=0;i<s.length();i++){ int father = uf.findFather(i); if(!map.containsKey(father)){ map.put(father,new PriorityQueue<>()); } //向优先级队列中添加字符 map.get(father).add(s.charAt(i)); } StringBuilder builder = new StringBuilder(); for(int i=0;i<s.length();i++){ int father = uf.findFather(i); //从优先级队列中取出最小值来 builder.append(map.get(father).poll()); } return builder.toString(); } class UnionFind{ int[]parent; int size; public UnionFind(int size) { this.size = size; parent=new int[size]; for(int i=0;i<size;i++){ parent[i]=i; } } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public void union(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX!=fatherY){ if(fatherX<fatherY){ parent[fatherY]=fatherX; }else{ parent[fatherX]=fatherY; } } } }}
class Solution { public int makeConnected(int n, int[][] connections) { if(connections.length+1<n){ return -1; } UnionFind uf = new UnionFind(n); for (int[] connection : connections) { int computeA=connection[0]; int computeB=connection[1]; uf.union(computeA,computeB); } return uf.size-1; } class UnionFind{ int[]parent; int size; public UnionFind(int size) { parent=new int[size]; this.size = size; for(int i=0;i<size;i++){ parent[i]=i; } } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public void union(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX!=fatherY){ if(fatherX<fatherY){ parent[fatherY]=fatherX; }else{ parent[fatherX]=fatherY; } size--; } } }}
//这道题挺难的,用克鲁斯卡尔算法。先构建最小生成树,然后再判断是否是伪关键边、关键边class Solution { public List<List<Integer>> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { UnionFind ufMST = new UnionFind(n); int count=0; Map<int[],Integer> map=new HashMap<>(); for (int[] edge : edges) { //存储边的下标 map.put(edge,count++); } //按边的dis进行排序 Arrays.sort(edges, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { return o1[2]-o2[2]; } }); //最小生成树的边 List<Integer>mst=new ArrayList<>(); int minDis=0; //构建最小生成树 for(int i=0;i<edges.length;i++){ int from=edges[i][0]; int to=edges[i][1]; int dis=edges[i][2]; if(ufMST.union(from,to)){ minDis+=dis; mst.add(i); } } //存储关键边集合 List<Integer>importantEdge=new ArrayList<>(); //存储伪关键边集合 List<Integer>fakeEdge=new ArrayList<>(); for(int i=0;i<edges.length;i++){ UnionFind uf = new UnionFind(n); int curCount=0; int curDis=0; //如果mst中不包含这条边,判断它是否是伪关键边 if(!mst.contains(i)){ int from=edges[i][0]; int to=edges[i][1]; int dis=edges[i][2]; uf.union(from,to); curCount++; curDis+=dis; } //如果mst中包含这条边,判断它是否是关键边 for(int j=0;j<edges.length;j++){ //如果j与i相等的话,则表示当前已在遍历中 if(j==i){ continue; } int from=edges[j][0]; int to=edges[j][1]; int dis=edges[j][2]; if(uf.union(from,to)){ curCount++; curDis+=dis; if(curCount==n-1){ break; } } } if(mst.contains(i)&&(curCount<n-1||curDis>minDis)){ importantEdge.add(map.get(edges[i])); }else if(curCount==n-1&&curDis==minDis){ fakeEdge.add(map.get(edges[i])); } } List<List<Integer>>res=new ArrayList<>(); res.add(importantEdge); res.add(fakeEdge); return res; } class UnionFind{ int[]parent; int[]rank; int size; public UnionFind(int size) { this.size = size; parent=new int[size]; rank=new int[size]; for(int i=0;i<size;i++){ parent[i]=i; rank[i]=1; } } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public boolean union(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX==fatherY){ return false; }else{ if(rank[fatherY]<=rank[fatherX]){ parent[fatherY]=fatherX; rank[fatherX]++; }else{ parent[fatherX]=fatherY; rank[fatherY]++; } } return true; } }}
class Solution { public int maxNumEdgesToRemove(int n, int[][] edges) { int res=0; UnionFind ufAlice = new UnionFind(n + 1); UnionFind ufBob = new UnionFind(n + 1); //优先Alice和Bob都能遍历的边 for (int[] edge : edges) { int type=edge[0]; int pointA=edge[1]; int pointB=edge[2]; if(type==3){ if(ufAlice.isConnected(pointA,pointB)){ res++; }else{ ufAlice.union(pointA,pointB); ufBob.union(pointA,pointB); } } } for (int[] edge : edges) { int type=edge[0]; int pointA=edge[1]; int pointB=edge[2]; if(type==1){ if(ufAlice.isConnected(pointA,pointB)){ res++; }else{ ufAlice.union(pointA,pointB); } }else if(type==2){ if(ufBob.isConnected(pointA,pointB)){ res++; }else{ ufBob.union(pointA,pointB); } } } if(ufAlice.size==2&&ufBob.size==2){ return res; }else{ return -1; } } class UnionFind{ int[]parent; int[]rank; int size; public UnionFind(int size) { this.size = size; parent=new int[size]; rank=new int[size]; for(int i=0;i<size;i++){ parent[i]=i; rank[i]=1; } } public int findFather(int x){ if(x==parent[x]){ return x; }else{ return findFather(parent[x]); } } public void union(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX==fatherY){ return; } if(rank[fatherY]<=rank[fatherX]){ parent[fatherY]=fatherX; rank[fatherX]++; }else if(rank[fatherY]>rank[fatherX]){ parent[fatherX]=fatherY; rank[fatherY]++; } size--; } public boolean isConnected(int x,int y){ int fatherX = findFather(x); int fatherY = findFather(y); if(fatherX==fatherY){ return true; }else{ return false; } } }}
//普利姆算法class Solution { public int minCostConnectPoints(int[][] points) { int n=points.length; int[][]graph=new int[n][n]; for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ graph[i][j]=graph[j][i]=Math.abs(points[i][0]-points[j][0])+Math.abs(points[i][1]-points[j][1]); } } //构建优先级队列 PriorityQueue<Node>queue=new PriorityQueue<>(new Comparator<Node>() { @Override public int compare(Node o1, Node o2) { if(o1.dis==o2.dis){ return o1.val-o2.val; }else{ return o1.dis-o2.dis; } } }); for(int i=1;i<n;i++){ queue.add(new Node(i,graph[0][i])); } int res=0; while (!queue.isEmpty()){ Node node = queue.poll(); //如果为0表示当前节点已遍历过,或者是a->a为0的这种,就不需要遍历 if(graph[0][node.val]==0){ continue; } res+=node.dis; graph[0][node.val]=0; for(int i=0;i<n;i++){ //如果在这些节点中找到比当前遍历小的路径,就加入到优先级队列中去 if(graph[node.val][0]!=0&&graph[node.val][i]<graph[0][i]){ queue.add(new Node(i,graph[node.val][i])); graph[0][i]=graph[node.val][i]; } } } return res; } //用Node节点封装边的长度与索引 class Node{ int val; int dis; public Node(int val, int dis) { this.val = val; this.dis = dis; } }}