学习内容
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,作为每个串的初始长度:1
for (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,成为新的size
size[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— 快