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;
}
}
}