Leetocode 每日一题 并查集 题目整理

547. 省份数量
题目链接:
547
思路:
并查集裸题
代码如下

class Solution {
    int[] pre;
    private int find(int x)
    {
        if(x==pre[x]) return x;
        int temp = find(pre[x]);
        return temp;
    }
    private void union(int a,int b)
    {
        int pa = find(a);
        int pb = find(b);
        if(pa!=pb) pre[pa]=pb;
    }
    public int findCircleNum(int[][] isConnected) {
        pre = new int[isConnected.length];
        for (int i=0;i<pre.length;i++)
        {
            pre[i]=i;
        }
        int ans=0;
        for (int i=0;i<isConnected.length;i++)
        {
            for (int j=i+1;j<isConnected.length;j++)
            {
                if(isConnected[i][j]==1)
                {
                    union(i,j);
                }
            }
        }
        for (int i=0;i<pre.length;i++)
            if(pre[i]==i) ans++;
        return ans;
    }
}

1202. 交换字符串中的元素
题目链接:
1202
思路:
由于可以进行无限次交换,所以同处于同一个集合的元素可以交换到集合中的任意位置,所以这个题目可以记下每个集合中的字符串,并将同属于一个集合中的字符串放入一个优先队列中,依次输出同一个集合中的最小元素
代码如下

class Solution {
    private int find(int x,int[] pre)
    {
        if(x==pre[x]) return x;
        int temp = find(pre[x],pre);
        pre[x] = temp;
        return temp;
    }
    private void unite(int a,int b,int[]pre)
    {
        int para = find(a,pre);
        int parb = find(b,pre);
        if(para!=parb) pre[para] = parb;
    }
    public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) {
        int size = s.length();
        int[] pre =new int[size+5];
        for (int i=0;i<size;i++)
        {
            pre[i]=i;
        }
        for (int i=0;i<pairs.size();i++)
        {
            int a = pairs.get(i).get(0);
            int b = pairs.get(i).get(1);
            unite(a,b,pre);
        }
        Map<Integer,PriorityQueue<Character>> map = new HashMap<>();
        for (int i=0;i<size;i++)
        {
            int par = find(i,pre);
            PriorityQueue<Character> queue = map.getOrDefault(par,new PriorityQueue<Character>());
            queue.offer(s.charAt(i));
            map.put(par,queue);
        }
        StringBuilder sb = new StringBuilder();
        for (int i=0;i<size;i++)
        {
            int par = find(i,pre);
            PriorityQueue<Character> queue = map.get(par);
            sb.append(queue.poll());
        }
        return sb.toString();
    }
}

947. 移除最多的同行或同列石头
题目链接:
947
思路:
因为同行与同列只能保证一个石头,所以可以将同行,同列的石头放入同一个并查集集合中,移出的最大石头数为石头总数-有多少个连通集集合,因为每个连通集最后只会剩余1个石头
代码

 class Solution {

    public int removeStones(int[][] stones) {
        UnionFind unionFind = new UnionFind();

        for (int[] stone : stones) {
            unionFind.union(stone[0] + 10001, stone[1]);
        }
        return stones.length - unionFind.getCount();
    }

    private class UnionFind {

        private Map<Integer, Integer> parent;
        private int count;

        public UnionFind() {
            this.parent = new HashMap<>();
            this.count = 0;
        }

        public int getCount() {
            return count;
        }

        public int find(int x) {
            if (!parent.containsKey(x)) {
                parent.put(x, x);
                count++;
            }

            if (x != parent.get(x)) {
                parent.put(x, find(parent.get(x)));
            }
            return parent.get(x);
        }

        public void union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {
                return;
            }

            parent.put(rootX, rootY);
            count--;
        }
    }
}

721. 账户合并
题目链接
721
思路:
1.建立一个hash表h1存储每个邮箱的编号
2.建立一个hash表h2建立邮箱与名字之间的映射
3.通过h1这个映射建立并查集
4.将同一集合的邮箱排序和名字一起放入一个List中
代码

class Solution {
    class Union
    {
        private int[] pre;
        public Union(int count)
        {
            pre = new int[count];
            for (int i=0;i<count;i++)
            {
                pre[i]=i;
            }
        }
        public int find(int x)
        {
            if(x==pre[x]) return x;
            int temp = find(pre[x]);
            return temp;
        }

        public void merge(int a,int b)
        {
            int pa = find(a);
            int pb = find(b);
            if(pa!=pb) pre[pa]=pb;
        }
    }
    public List<List<String>> accountsMerge(List<List<String>> accounts) {
        List<List<String>> ans = new ArrayList<>();
        Map<String,Integer> indexToEmail = new HashMap<>();
        Map<String,String> indexToName = new HashMap<>();
        int emailCount = 0;
        for (List<String> account: accounts) {
            String name = account.get(0);
            for (int i=1;i<account.size();i++)
            {
                String email = account.get(i);
                if(!indexToEmail.containsKey(email))
                {
                    indexToEmail.put(email,emailCount++);
                    indexToName.put(email,name);
                }
            }
        }

        Union union = new Union(emailCount);
        for (List<String> account:accounts) {
            String email1 =account.get(1);
            int pre1 = indexToEmail.get(email1);
            for (int i=2;i<account.size();i++)
            {
                String emaili = account.get(i);
                int prei = indexToEmail.get(emaili);
                union.merge(pre1,prei);
            }
        }
        Map<Integer,List<String>> indexEmail = new HashMap<>();
        for (String email: indexToEmail.keySet()) {
            int index = union.find(indexToEmail.get(email));
            List<String> indexSameEmail = indexEmail.getOrDefault(index,new ArrayList<>());
            indexSameEmail.add(email);
            indexEmail.put(index,indexSameEmail);
        }
        for (List<String> emails:indexEmail.values()) {
            Collections.sort(emails);
            String name = indexToName.get(emails.get(0));
            List<String> account = new ArrayList<>();
            account.add(name);
            account.addAll(emails);
            ans.add(account);
        }
        return  ans;

    }
}

1584. 连接所有点的最小费用
题目链接
1584
思路:
Krusal算法,建立各点的图,对图的权值按从小到大排序,执行krusal算法

class Solution {
    int[] pre;
    class Adj
    {
        int va,vb,adj;
        Adj(int va,int vb,int adj)
        {
            this.va=va;
            this.vb=vb;
            this.adj=adj;
        }

    }
    private int find(int x)
    {
        if(x==pre[x]) return x;
        int temp = find(pre[x]);
        return temp;
    }
    private int union(int a,int b)
    {
        int pa = find(a);
        int pb = find(b);
        if(pa!=pb){
            pre[pa]=pb;
            return 0;
        }
        return 1;
    }
    public int minCostConnectPoints(int[][] points) {
        List<Adj> adjs= new ArrayList<>();
        pre = new int[points.length];
        for (int i=0;i<points.length;i++) pre[i]=i;
        for (int i=0;i<points.length;i++)
        {
            for (int j=i+1;j<points.length;j++)
            {
                int dis = Math.abs(points[i][0]-points[j][0])+
                        Math.abs(points[i][1]-points[j][1]);
                adjs.add(new Adj(i,j,dis));
            }
        }
        Collections.sort(adjs, new Comparator<Adj>() {
            @Override
            public int compare(Adj o1, Adj o2) {
                return o1.adj-o2.adj;
            }
        });
        int ans = 0,count=0;
        for (int i=0;i<adjs.size();i++)
        {
            Adj a = adjs.get(i);
            if(count==points.length-1) break;
            if(union(a.va,a.vb)==0)
            {
                count++;
                ans+=a.adj;
            }
        }
        return ans;
    }
}

1489. 找到最小生成树里的关键边和伪关键边
题目链接:
1489
思路:
关键边: 去掉这条边生成树的权值会变大或不能生成树
非关键边: 一开始加入这条边之后的生成树权值和最小生成树相同
先计算出最小生成树的权值,然后依次对每条边进行关键边和非关键边的判定
某条边是关键边后不用进行非关键边的判定
代码如下:

class Solution {
    public List<Integer> addToArrayForm(int[] A, int K) {

        List<Integer> arr = new ArrayList<>();
        int count=0;
        int num=K,i=0;
        for (;i<A.length&&num>0;i++)
        {
            int ge = num%10;
            count=(ge+A[i])/10;
            arr.add((ge+A[i])%10);
            num/=10;
        }
        if(i<A.length)
        {
            while(i<A.length)
            {
                arr.add((count+A[i])%10);
                count = (count+A[i])/10;
            }
        }
        if(num>0)
        {
            while(num>0)
            {
                int ge = num%10;
                arr.add((count+ge)%10);
                count = (count+ge)/10;
                num/=10;
            }
        }
        if(count>0)
        {
            arr.add(count);
        }


    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值