Java - exactly based on hints - Union by Rank and MST - 22 ms - beats 98% time
\nclass DisjointSetUnion{\n   private int N;\n   private int[] rank, parent;\n\n   public DisjointSetUnion(int N){\n       this.N = N;\n       this.rank = new i
```\nclass DisjointSetUnion{\n   private int N;\n   private int[] rank, parent;\n\n   public DisjointSetUnion(int N){\n       this.N = N;\n       this.rank = new int[N];\n       this.parent = new int[N];\n       this.reset();\n    }\n\n   public void reset(){\n       for(int i = 0; i < N; i++){\n           this.rank[i] = 1;\n           this.parent[i] = i;\n       }\n   }\n\n   public boolean union(int u, int v){\n      if(u != v){\n         int x = find(u);\n         int y = find(v);\n         if(x != y){\n            if(rank[x] > rank[y]){\n                rank[x] += rank[y];\n                parent[y] = x;\n            }else{\n                rank[y] += rank[x];\n                parent[x] = y;\n            }\n\n            return true;\n         }\n      }\n\n      return false;\n   }\n\n   private int find(int u){\n      int x = u;\n      while(x != parent[x]){\n         x = parent[x];\n      }\n\n      parent[u] = x;\n      return x;\n   }\n}\n\nclass Edge implements Comparable<Edge>{\n   private int start, end, weight, index;\n\n   public Edge(int start, int end, int weight, int index){\n       this.start = start;\n       this.end = end;\n       this.weight = weight;\n       this.index = index;\n   }\n\n   public int getIndex(){\n       return this.index;\n   }\n\n   public int getStart(){\n      return this.start;\n   }\n\n   public int getEnd(){\n      return this.end;\n   }\n\n   public int getWeight(){\n      return this.weight;\n   }\n\n   @Override\n   public int compareTo(Edge other){\n       return this.weight - other.weight;\n   }\n}\n\nclass Solution {\n    public List<List<Integer>> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) {\n        DisjointSetUnion set = new DisjointSetUnion(n);\n        int i, weight, E = edges.length;\n       Edge[] edgeArray = new Edge[E];\n       for(i = 0; i < E; i++){\n           edgeArray[i] = new Edge(edges[i][0], edges[i][1], edges[i][2], i);\n       } \n        \n       Arrays.sort(edgeArray);\n       int mst = getMstWeight(set, edgeArray, n, E, -1, -1);\n\n       List<Integer> critical = new ArrayList<Integer>();\n       List<Integer> nonCritical = new ArrayList<Integer>();\n\n       for(i = 0; i < E; i++){\n           // do not include this edge\n           weight = getMstWeight(set, edgeArray, n, E, -1, i);\n           if(weight > mst){\n               critical.add(edgeArray[i].getIndex());\n           }else{\n               // forcefully include this edge\n               weight = getMstWeight(set, edgeArray, n, E, i, -1);\n               if(weight == mst){\n                  nonCritical.add(edgeArray[i].getIndex());\n               }\n           }\n       }\n\n       List<List<Integer>> result = new ArrayList<List<Integer>>();\n       result.add(critical);\n       result.add(nonCritical);\n       return result;\n    }\n\n    private int getMstWeight(DisjointSetUnion set, Edge[] edgeArray, int n, int E, int include, int ignore){\n        set.reset();\n        \n        int weight = 0;\n        int edgeCount = 0;\n        Edge current;\n        if(include != -1){\n           current = edgeArray[include];\n           set.union(current.getStart(), current.getEnd());\n           weight += current.getWeight();\n           ++edgeCount;\n        }\n\n        for(int i = 0; (i < E) && (edgeCount < n - 1); i++){\n           if(i != ignore){\n               current = edgeArray[i];\n               if(set.union(current.getStart(), current.getEnd())){\n                   weight += current.getWeight();\n                   ++edgeCount;\n               }\n           }\n        }\n\n        return (edgeCount == n - 1) ? weight : Integer.MAX_VALUE; // important check for disconnected MST\n    }\n}\n\n\n```
[]