学习内容
1579.保证图可完全遍历
我的代码:
class Solution {public int maxNumEdgesToRemove(int n, int[][] edges) {UnionFind origin=new UnionFind(new int[n]);for (int i = 0; i < n; i++) {origin.data[i]=i;}int count=0;ArrayList<Integer> bob=new ArrayList<>();ArrayList<Integer> alice=new ArrayList<>();for (int i = 0; i < edges.length; i++) {switch (edges[i][0]){case 3:if(!origin.union(edges[i][1]-1,edges[i][2]-1)){count++;}break;case 1:alice.add(i);break;case 2:bob.add(i);break;default:System.out.println("error");break;}}UnionFind uBob=new UnionFind(Arrays.copyOfRange(origin.data,0,origin.data.length));UnionFind uAlice=new UnionFind(Arrays.copyOfRange(origin.data,0,origin.data.length));for(Integer index:bob){if(!uBob.union(edges[index][1]-1,edges[index][2]-1)){count++;}}for(Integer index:alice){if(!uAlice.union(edges[index][1]-1,edges[index][2]-1)){count++;}}if(uAlice.isConnected()&&uBob.isConnected()){return count;}else{return -1;}}}class UnionFind{int[] data;public UnionFind(int[] data) {this.data = data;}public Boolean union(int a,int b){int fa=find(a);int fb=find(b);if(fa!=fb){data[fb]=fa;return true;}else{return false;}}public int find(int a){if(data[a]==a){return a;}else{return find(data[a]);}}public boolean isConnected(){int temp=0;for (int i = 0; i < data.length; i++) {if(data[i]==i){temp++;}}return temp <= 1;}}
思路:
这是一个相当标准的并查集问题,使用三个并查集来分别代表三个集合,优先计算基础的连接,随后计算两个集合独有的连接,最后使用并查集的标准版方法来解决问题.
注意:
1.指向同一个对象的类,其的不同实例之间会通过这个对象来联系起来,换句话说,类中的对象在一个新的类中并不会被复制一个新,而是直接使用老的.
改进思路:
改进判断其是否连通的方法,减少其遍历的次数.
使用带有优化的并查集模板来减少计算次数,防止长串.
减少使用ArrayList,来减少计算时间,如果想要使用ArrayList,作为缓存,还不如直接使用数组来遍历,内存的分配才比较花费时间.
优解代码
主函数:
class Solution {public int maxNumEdgesToRemove(int n, int[][] edges) {UnionFind ufa = new UnionFind(n);UnionFind ufb = new UnionFind(n);//无用边的数量int count = 0;// 节点编号改为从 0 开始for (int[] edge : edges) {--edge[1];--edge[2];}// 公共边for (int[] edge : edges) {if (edge[0] == 3) {if (!ufa.unite(edge[1], edge[2])) {++count;} else {ufb.unite(edge[1], edge[2]);}}}// 独占边for (int[] edge : edges) {if (edge[0] == 1) {// Alice 独占边if (!ufa.unite(edge[1], edge[2])) {++count;}} else if (edge[0] == 2) {// Bob 独占边if (!ufb.unite(edge[1], edge[2])) {++count;}}}if (ufa.setCount != 1 || ufb.setCount != 1) {return -1;}return count;}}
并查集模板:
class UnionFind {int[] parent;int[] size;int n;int setCount;public UnionFind(int n) {this.n = n;this.setCount = n;//未被改变的节点数目this.parent = new int[n];this.size = new int[n];Arrays.fill(size, 1);//初值设定为1,作为每个串的初始长度:1for (int i = 0; i < n; ++i) {parent[i] = i;//标记值为1}}public int findSet(int x) {return parent[x] == x ? x : (parent[x] = findSet(parent[x]));//使用递归找根}public boolean unite(int x, int y) {x = findSet(x);y = findSet(y);if (x == y) {return false;}//如果两个根值的size不同的话,宁可让长的接在短的后面,也不要让短的接在长的后面if (size[x] < size[y]) {int temp = x;x = y;y = temp;}//y:短 x:长parent[y] = x;//短的那部分的size,加上长的那部分的size,成为新的sizesize[x] += size[y];//每次成功的连接都会让未被改变根的节点少一个,当这个值降为1时,全部连接.--setCount;return true;}//判断两个节点是否连接public boolean connected(int x, int y) {x = findSet(x);y = findSet(y);return x == y;}public boolean allConnected(){return setCount==1;}}
心得:
1.思路的差距不大,主要的差距是没有很好的利用并查集的模板,导致了主函数变得臃肿与不优雅,需要对于并查集的模板进行深入研究.
2.—a 比 a— 快
