C++ solution (well commented, beginner-friendly)
```\nclass UnionFind {\npublic:\n    vectorparent;\n    vectorrank;\n    UnionFind(int n){\n        parent.resize(n);\n        rank.resize(n);\n        for(int 
```\nclass UnionFind {\npublic:\n    vector<int>parent;\n    vector<int>rank;\n    UnionFind(int n){\n        parent.resize(n);\n        rank.resize(n);\n        for(int i=0;i<n;i++){\n            //initially every vertex is parent of itself.\n            parent[i]=i;\n            rank[i]=0;\n        }\n    }\n    int Find(int x){\n        //This function finds the parent of a vertex.\n        //if a vertex is parent of itself return the vertex.\n        if(parent[x]==x){\n            return x;\n        }\n        else{\n            //else recursively call for parent of vertex.\n            return Find(parent[x]);\n        }\n    }\n    void Union(int x, int y){\n        //find parents of both vertices.\n        int parent_x=Find(x);\n        int parent_y=Find(y);\n        //if they have same parent, we don\'t need to unite them.\n        if(parent_x==parent_y)\n            return;\n        //each set is a tree in itself.\n        //rank is basically height of tree with parent as root.\n        //if height of tree1>tree2, make tree2 as child of tree1.\n        //as this would avoid chaining of nodes and hence time complexity.\n        if(rank[parent_x]>rank[parent_y]){\n            parent[parent_y]=parent_x;\n        }\n        else if(rank[parent_x]<rank[parent_y]){\n            parent[parent_x]=parent_y;\n        }\n        else{\n            parent[parent_y]=parent_x;\n            //if both trees have same height, make anyone as child of other.\n            //this would increase the overall height of resultant tree by 1.\n            rank[parent_x]++;\n        }\n    }\n};\nclass Solution {\npublic:\n    static bool cmp(vector<int>&a, vector<int>&b){\n        return a[2]<b[2];\n    }\n    int MST(vector<vector<int>>&edges, int n, int exclude, int include){\n        UnionFind uf(n); //create an object of UnionFind class with vertex n as constructor parameter.\n        int weight=0;//total weight of an MST.\n        if(include!=-1){\n            //if we need to include a particular edge without excluding anything.\n            weight+=edges[include][2];\n            uf.Union(edges[include][0],edges[include][1]);\n        }\n        //We unite u and v to form edges of MST.\n        //we construct rest of out MST.\n        for(int i=0;i<edges.size();i++){\n            //if exclude!=-1 it must be within range 0-n.\n            //otherwise this block will never be executed(in case of exclude=-1)\n            if(i==exclude)\n                continue;\n            //Now we check if adding this edge forms cycle in MST.\n            //In an MST, all vertices have same parent\n            //if these two vertices already have same parent, we don\'t need to add this edge,\n            //as this would form a cycle.\n            //both LHS and RHS return same value if they have a common parent.\n            if(uf.Find(edges[i][0])==uf.Find(edges[i][1]))\n                continue;\n            //if no cycle, add its weight to total and edge to MST.\n            uf.Union(edges[i][0],edges[i][1]);\n            weight+=edges[i][2];\n        }\n        //if in a special case, we exclude a vertex from MST,\n        //but there is no other edge connecting that vertex to rest of the graph\n        //then we may have two or more seperate (disjoint) sets\n        //and not a single MST.\n        //in that case we return a very large number to indicate that that vertex is critical.\n        for (int i=0;i<n;i++) {\n            if(uf.Find(i)!=uf.Find(0))\n                return INT_MAX;\n        }\n        return weight;\n    }\n    vector<vector<int>> findCriticalAndPseudoCriticalEdges(int n, vector<vector<int>>& edges){\n        for(int i=0;i<edges.size();i++){\n            edges[i].push_back(i);\n            //We assign a unique number to all edges.\n        }\n        sort(edges.begin(),edges.end(),cmp);\n        int bestMST=MST(edges,n,-1,-1);\n        //We take most optimal MST possible(no exclusion or inclusion of any particular edge).\n        vector<int>critical,pseudo_critical;\n        for(int i=0;i<edges.size();i++){\n            if(bestMST<MST(edges,n,i,-1)){\n                //We ignore edge no. i and check if new MST is greater than bestMST.\n                critical.push_back(edges[i][3]);\n                //If yes then this edge is critical.\n            }\n            else if(bestMST==MST(edges,n,-1,i)){\n                //We particularly include this edge i and do not exclude anything\n                //and check if this edge yields same MST or not.\n                pseudo_critical.push_back(edges[i][3]);\n                //this edge is pseudo critical.\n            }\n        }\n        return {critical,pseudo_critical};\n    }\n};\n\n\n\n\nIf you find this helpful, please upvote :-)
['Union Find']