千里码:热点营销-2

上一篇博客笔记的末尾提到,碰到数据量较大的图,需要计算图的连通分支个数时,如果使用邻接矩阵来保存图,会发生内存不足的情况。下面记录一下使用并查集来解决该问题的办法。

题目如下:

本题与‘热点营销-1’题型一致,仅改变微信用户数量(100000)和关系列表。
请问,若需要使一个广告让这100000个微信用户都看到,请问初始至少将这条广告传播个几个人?

首先创建一个User类来表示用户

public class User{
    private int data;
    //@param rank is used to store the height of the set-tree 
    //when this is the root user
    private int rank;
    private User parent;
    //status is used to judge if this is the root user 
    private Boolean status;

    public User(int data){
        this.data = data;
        this.parent = this;
        this.rank = 0;
        this.status = false;
    }
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public int getRank() {
        return rank;
    }
    public void rankInc() {
        this.rank++;
    }
    public User getParent() {
        return parent;
    }
    public void setParent(User parent) {
        this.parent = parent;
    }
    public Boolean isRoot() {
        return status;
    }
    public void setStatus(Boolean status) {
        this.status = status;
    }   
}

并查集的查找操作

然后是写并查集的根节点查找算法,这个过程有点绕,需要仔细研究一下。该算法中还加了路径压缩过程,需要特别注意

    /**
     * find certain user's root parent and do path compression
     * @param user
     * @return the root parent of the user
     */
    public static User find(User user){
        User root = user;
        //in union-find root's parent is itself
        //if root's parent is not himself 
        //then find his parent until root's parent is himself
        while(root.getParent() != root ){
            root = root.getParent();
        }

        //do path compression to make the parent of each user in the path to be the root
        User copy = user,temp;
        while(copy != root){
            temp = copy.getParent();
            copy.setParent(root);
            copy = temp; 
        }
        //return the root parent of the user
        return root;
    }

并查集的合并操作

接下来是写并查集的合并操作算法,只需要找到想要合并的两个目标集合树的根节点,然后把矮的树添加为高的数的根节点的子树就行了(矮树的根节点的父节点指向高树的根节点)

    /**
     * merge two set-tree
     * @param x
     * @param y
     */
    public static void union(User x, User y){
        User xRoot = find(x);
        User yRoot = find(y);

        if(xRoot.getRank() > yRoot.getRank()){
            yRoot.setParent(xRoot); 
        }else{
            xRoot.setParent(yRoot);
            if(xRoot.getRank() == yRoot.getRank())
                yRoot.rankInc();
        }
    }

使用并查集求连通分支个数

  1. 开始的时候每一个用户就是一个集合,根节点是他自己,(并查集根节点的父节点是自己)
  2. 如果两个用户有联系,则合并这两个用户所在的集合
  3. 合并完成之后需要将根节点的status置为true,表示它是根节点,便于统计连通分支个数
  4. 统计连通分支个数,即计算根节点个数
    public static void main(String[] args){
        //initial every user's parent to themselves
        for(int i = 0; i < 100000; i++){
            users[i] = new User(i);         
        }

        try{
            FileReader fr = 
                new FileReader("E:/dev/java/javaweb/qlcoder/144341511030664.txt");
            BufferedReader br = new BufferedReader(fr);
            String in = null;
            while((in = br.readLine()) != null){
                String[] fragment = in.split(" ");
                int one = Integer.valueOf(fragment[0]) - 1;
                int two = Integer.valueOf(fragment[1]) - 1;
                union(users[one], users[two]);
            }
            br.close();
        }catch(IOException e){
            e.printStackTrace();
        }

        for(int i = 0; i < 100000; i++){
            find(users[i]).setStatus(true);
        }

        for(int i = 0; i < 100000; i++){
            if(users[i].isRoot()){
                count++;
            }           
        }

        System.out.println(count);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值