学习内容

LeeCodeJava

1579.保证图可完全遍历

我的代码:

  1. class Solution {
  2. public int maxNumEdgesToRemove(int n, int[][] edges) {
  3. UnionFind origin=new UnionFind(new int[n]);
  4. for (int i = 0; i < n; i++) {
  5. origin.data[i]=i;
  6. }
  7. int count=0;
  8. ArrayList<Integer> bob=new ArrayList<>();
  9. ArrayList<Integer> alice=new ArrayList<>();
  10. for (int i = 0; i < edges.length; i++) {
  11. switch (edges[i][0]){
  12. case 3:
  13. if(!origin.union(edges[i][1]-1,edges[i][2]-1)){
  14. count++;
  15. }
  16. break;
  17. case 1:
  18. alice.add(i);
  19. break;
  20. case 2:
  21. bob.add(i);
  22. break;
  23. default:
  24. System.out.println("error");
  25. break;
  26. }
  27. }
  28. UnionFind uBob=new UnionFind(Arrays.copyOfRange(origin.data,0,origin.data.length));
  29. UnionFind uAlice=new UnionFind(Arrays.copyOfRange(origin.data,0,origin.data.length));
  30. for(Integer index:bob){
  31. if(!uBob.union(edges[index][1]-1,edges[index][2]-1)){
  32. count++;
  33. }
  34. }
  35. for(Integer index:alice){
  36. if(!uAlice.union(edges[index][1]-1,edges[index][2]-1)){
  37. count++;
  38. }
  39. }
  40. if(uAlice.isConnected()&&uBob.isConnected()){
  41. return count;
  42. }else{
  43. return -1;
  44. }
  45. }
  46. }
  47. class UnionFind{
  48. int[] data;
  49. public UnionFind(int[] data) {
  50. this.data = data;
  51. }
  52. public Boolean union(int a,int b){
  53. int fa=find(a);
  54. int fb=find(b);
  55. if(fa!=fb){
  56. data[fb]=fa;
  57. return true;
  58. }else{
  59. return false;
  60. }
  61. }
  62. public int find(int a){
  63. if(data[a]==a){
  64. return a;
  65. }else{
  66. return find(data[a]);
  67. }
  68. }
  69. public boolean isConnected(){
  70. int temp=0;
  71. for (int i = 0; i < data.length; i++) {
  72. if(data[i]==i){
  73. temp++;
  74. }
  75. }
  76. return temp <= 1;
  77. }
  78. }

思路:

这是一个相当标准的并查集问题,使用三个并查集来分别代表三个集合,优先计算基础的连接,随后计算两个集合独有的连接,最后使用并查集的标准版方法来解决问题.

注意:

1.指向同一个对象的类,其的不同实例之间会通过这个对象来联系起来,换句话说,类中的对象在一个新的类中并不会被复制一个新,而是直接使用老的.

改进思路:

改进判断其是否连通的方法,减少其遍历的次数.
使用带有优化的并查集模板来减少计算次数,防止长串.
减少使用ArrayList,来减少计算时间,如果想要使用ArrayList,作为缓存,还不如直接使用数组来遍历,内存的分配才比较花费时间.

优解代码

主函数:

  1. class Solution {
  2. public int maxNumEdgesToRemove(int n, int[][] edges) {
  3. UnionFind ufa = new UnionFind(n);
  4. UnionFind ufb = new UnionFind(n);
  5. //无用边的数量
  6. int count = 0;
  7. // 节点编号改为从 0 开始
  8. for (int[] edge : edges) {
  9. --edge[1];
  10. --edge[2];
  11. }
  12. // 公共边
  13. for (int[] edge : edges) {
  14. if (edge[0] == 3) {
  15. if (!ufa.unite(edge[1], edge[2])) {
  16. ++count;
  17. } else {
  18. ufb.unite(edge[1], edge[2]);
  19. }
  20. }
  21. }
  22. // 独占边
  23. for (int[] edge : edges) {
  24. if (edge[0] == 1) {
  25. // Alice 独占边
  26. if (!ufa.unite(edge[1], edge[2])) {
  27. ++count;
  28. }
  29. } else if (edge[0] == 2) {
  30. // Bob 独占边
  31. if (!ufb.unite(edge[1], edge[2])) {
  32. ++count;
  33. }
  34. }
  35. }
  36. if (ufa.setCount != 1 || ufb.setCount != 1) {
  37. return -1;
  38. }
  39. return count;
  40. }
  41. }

并查集模板:

  1. class UnionFind {
  2. int[] parent;
  3. int[] size;
  4. int n;
  5. int setCount;
  6. public UnionFind(int n) {
  7. this.n = n;
  8. this.setCount = n;//未被改变的节点数目
  9. this.parent = new int[n];
  10. this.size = new int[n];
  11. Arrays.fill(size, 1);//初值设定为1,作为每个串的初始长度:1
  12. for (int i = 0; i < n; ++i) {
  13. parent[i] = i;//标记值为1
  14. }
  15. }
  16. public int findSet(int x) {
  17. return parent[x] == x ? x : (parent[x] = findSet(parent[x]));
  18. //使用递归找根
  19. }
  20. public boolean unite(int x, int y) {
  21. x = findSet(x);
  22. y = findSet(y);
  23. if (x == y) {
  24. return false;
  25. }
  26. //如果两个根值的size不同的话,宁可让长的接在短的后面,也不要让短的接在长的后面
  27. if (size[x] < size[y]) {
  28. int temp = x;
  29. x = y;
  30. y = temp;
  31. }
  32. //y:短 x:长
  33. parent[y] = x;
  34. //短的那部分的size,加上长的那部分的size,成为新的size
  35. size[x] += size[y];
  36. //每次成功的连接都会让未被改变根的节点少一个,当这个值降为1时,全部连接.
  37. --setCount;
  38. return true;
  39. }
  40. //判断两个节点是否连接
  41. public boolean connected(int x, int y) {
  42. x = findSet(x);
  43. y = findSet(y);
  44. return x == y;
  45. }
  46. public boolean allConnected(){
  47. return setCount==1;
  48. }
  49. }

心得:

1.思路的差距不大,主要的差距是没有很好的利用并查集的模板,导致了主函数变得臃肿与不优雅,需要对于并查集的模板进行深入研究.
2.—a 比 a— 快