uf 

// quick find: 
// use int array id[] of length n
// p and q are connected if and only if they have the same id
// when merge components containing p and q, change all entris who
// id equals id[p] to id[q]



//time complexity
// find/connected is o(1)
// union is o(n)
// union is o(n^2) for a sequence of n union commands on n objects 

public class QuickFindUF{
  private int[] id;
  public QuickFindUF(int N){
    id = new int[N];
    for(int i = 0; i < N; i++){
      id[i] = i;
    }
  }
  
  public boolean connected(int p, int q){
    return id[p] == id[q];
  }
  
  public void union(int p, int q){
    int pid = id[p];
    int qid = id[q];
    for(int i = 0; i < id.length; i++){
      if(id[i] = pid){
        id[i] = qid;
      }
    }
  }
}



============================================================
// quick union 
// int array id[] of length n, where id[i] is parent of i 
// root of i is id[id[..id[i]]] (keep going until it doesnt change)
// for find: check if p and q have the same root
// for union: merge compnents containing p and q, set the id of 
// p's root to the id of q's root

public class QuickUnionUF{
  private int[] id; // id[i] is parent of i 
  
  public QuickUnionUF(int N){
    id = new int[N];
    for(int i = 0; i < N; i++){
      id[i] = i;
    }
  }
  
  private int root(int i){
    while( i != id[i]){
      i = id[i];
    }
    return i;
  }
  
  public boolean connected(int p, int q){
    return root(p) == root(q);
  }
  
  public void union(int p, int q){
    int j = root(p);
    int i = root(q);
    id[j] = i;
  }
}

// time complexity : the tree can get very tall 
// could be a linked list strutrue, then the find 
// takes o(n), n is the number of the nodes in total
// union takes o(n) , first find the root first 
// still not good

======================================================

// improvements 
// union by size: sz[i] to count number of objects in the 
// tree rooted at i. when do union, we link root of smaller 
// tree to root of larger tree, and update the sz[] array 
  
int i = root(p);
int j = root(q);

if( i == j ){
  return;
}else{
  if(sz[i] < sz[j]){
    id[i] = j;
    sz[j] += sz[i];
  }else{
    id[j] = i;
    sz[i] += sz[j];
  }
}

// time complexity is o(nlogn) for both union and find 

========================================================
  
// improve further 
// path compression : decrease the depth of the tree
// when performing the root operation, point every node  we are examing  
// to the root 
  
private int root(int i ){
  int root = i;
  while(root != id[root]){
    root = id[root];
  }
  
  while(id[i] != root){
    int next = id[i];
    id[i] = root;
    i = next;
  }
  return root;
}

==========================================================
// number of islands 1 and 2 
// Number of Connected Components in an Undirected Graph
  
// accout merge