paypal笔试: 关联用户(并查集)

题目

题目链接

PayPal上海团队一直致力于风险控制,风控需要收集各种信息,有时需要通过地理位置找出用户与用户之间存在的关联关系,这一信息可能会用于找出用户潜在存在的风险问题。我们记两个用户的关联关系可以表示为:

(1) user1,user2与他们最常发生交易的地理位置分别为(x1, y1),(x2, y2),当这两个用户的欧氏距离不超过d时,我们就认为两个用户关联。

(2) 用户关联性具有传递性,若用户1与用户2关联,用户2与用户3关联,那么用户1,2,3均关联。

给定N个用户及其地理位置坐标,将用户按照关联性进行划分,要求返回一个集合,集合中每个元素是属于同一个范围的用户群。

输入描述:

d:欧式距离
N:用户数


之后的N行表示第0个用户到第N-1个用户的地理位置坐标

输出描述:

一个数组集合,所有关联的用户在一个数组中。


输出数组需要按照从小到大的顺序排序,每个集合内的数组也需要按照从小到大的顺序排序。

输入例子1:

2.0
5
3.0 5.0
6.0 13.0
2.0 6.0
7.0 12.0
0.0 2.0

输出例子1:

[[0, 2], [1, 3], [4]]

题解

本题采用了并查集的思想,需要为同一组的用户指定一个统一的“组长”。每一组根据“组长”来区分。

对应到数组中就是每一个用户用数组索引来表示,元素值表示其父节点(即“组长”)。

对应关系
如图,按照上述的说明,此数组中用户之间的分组关系就是:

[0, 5]
[1, 3]
[2, 4]

当遍历到某个位置时,用当前位置的用户来和前面的已分组用户进行判断,如果二者之间的距离判断为“关联”,那么就将此用户与已分组用户“联合”,将其归为已分组用户所属组,如果不关联,那么当前位置用户自己为一个组,且自己就是“组长”。

这里面有一些需要注意的地方。

  1. 在进行“联合”时,注意联合的是组,而不是用户。如果将用户A归为用户B所在的组(简称A组合B组),那么需要将当前所有用户中属于A组的用户全部归为B组,而不是仅仅将当前用户划过去。

  2. 再进行联合分组时,将组长索引大的归为组长号小的那一组。这样做的目的是在输出所有组的时候是有序输出,符合题意。


//采用并查集思想,将所有的关联节点父子关系使用数组索引表示

import java.util.*;

public class Main{
    static int[] users;
    static double d;  //欧式距离

    //判断相连
    private static boolean isConnected(double x1, double y1, double x2, double y2){
        double dis = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));  //计算两点距离
        return dis <= d;
    }

    //获取当前节点的根节点
    private static int getRoot(int i){
        return users[i];
    }

    //合并p、q,将q的根节点设置为q的根节点
    private static void union(int p, int q){
        int pRoot = getRoot(p);
        int qRoot = getRoot(q);
        if (pRoot < qRoot) {
            for (int i = 0; i < users.length; ++i) {
                if (users[i] == qRoot) {
                    users[i] = pRoot;
                }
            }
        } else {
            for (int i = 0; i < users.length; ++i) {
                if (users[i] == pRoot) {
                    users[i] = qRoot;
                }
            }
        }
    }

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        d = sc.nextDouble();
        int n = sc.nextInt();
        users = new int[n];

        for( int i=0; i<n; ++i ){
            users[i] = i;
        }

        double[][] pos = new double[n][2];
        //读取位置信息,存入二维数组
        for( int i=0; i<n; ++i ){
            pos[i][0] = sc.nextDouble();
            pos[i][1] = sc.nextDouble();
        }
        //遍历数组,建立联系
        for( int i=0; i<n; ++i ){
            for( int j=0; j<i; ++j ){
                if( isConnected(pos[i][0], pos[i][1], pos[j][0], pos[j][1]) ) {
                    union(i, j);
                }
            }
        }
        //将有联系的用户放进集合
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
        for( int i=0; i<n; ++i ){
            if( users[i] == i ){
                //如果这是一个根节点,建立一个关于她的数组集合
                ArrayList<Integer> temp = new ArrayList<>();
                for( int j=i; j<n; ++j ){
                    if( users[j]==i ) temp.add(j);
                }
                res.add(temp);
            }
        }
        System.out.println(res);
    }
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前Facebook工程师 打造王者级课程                            覃超 Facebook 早期员工&多年面试官、曾作为 Facebook Messenger Tech Lead,主导和参与了 Facebook App、Facebook Messenger、Facebook Phone 等产品的研发工作。 现在,覃超老师致力于计算机科学领域的培训指导,经他指导的学生,90%以上拿到了硅谷公司或国内顶级互联网公司的offer。 推动0到3岁程序员 打通职业发展路径 数据结构与算法计算机学科知识结构的核心和技术体系的基石,随着科技的飞速发展,数据结构的基础性地位反而更加坚固,是每一个程序员必须掌握的底层核心技能! 多岗位必备技能 快速提升编程内功   作为一名程序员,无论是任何岗位,掌握数据结构与算法,就可以在面试前建立自己的算法技术体系,同时有助于更好地阅读源码和设计编写一些复杂的工具,快速提升编程内功! 建立框架性知识体系 覆盖重要知识点 很多人认为可能除了面试,之后再也用不到据结构与算法了,但是如果程序员想拔高一个层次,数据结构与算法是很重要的一部分,《全解数据结构与算法》帮你完成角色的转变! 透彻讲解底层核心原理 拓展能力上限 很多人认为可能除了面试,之后再也用不到据结构与算法了,但是如果程序员想拔高一个层次,数据结构与算法是很重要的一部分,《全解数据结构与算法》帮你完成角色的转变!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值