You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现无权重图及关系建模?算法分析项目技术求助

基于无权重图构建密切联系人推荐系统实现方案

核心需求回顾

为未开启隐私设置的用户,推荐同一社区、学校或雇主的密切联系人;开启隐私的用户不参与推荐(既不接收推荐,也不被推荐)。目前已完成文件读取并映射到Person和Activities类,需实现无权重图结构来关联用户关系。


1. 先优化数据存储(解决原代码对象未持久化问题)

原代码中创建的Person和Activities对象未保存,需先将数据存入集合以便后续构建图结构。我们用HashMap以**姓名组合(firstname+lastname)**作为唯一键,快速查找用户:

public class InfoReader {
    // 存储所有用户:键为用户唯一标识(姓名组合),值为Person对象
    private Map<String, Person> personMap = new HashMap<>();
    // 存储用户活动:键为用户唯一标识,值为该用户的活动列表
    private Map<String, List<Activities>> activityMap = new HashMap<>();
    private ContactGraph contactGraph;

    public void ReadInfo(){
        // 读取人员文件
        try {
            String fileLocation = File.separator + "Users" + File.separator + "user" + File.separator + "Downloads" + File.separator + "SamplefilePersons2022Oct31text.csv";
            File personList = new File(fileLocation);
            Scanner personScanner = new Scanner(personList);

            while (personScanner.hasNextLine()) {
                String nextline = personScanner.nextLine();
                String[] personComponents = nextline.split(",");
                
                // 清洗数据(去除字段前后空格)
                String firstname = personComponents[0].trim();
                String lastname = personComponents[1].trim();
                String phone = personComponents[2].trim();
                String email = personComponents[3].trim();
                String community = personComponents[4].trim();
                String school = personComponents[5].trim();
                String employer = personComponents[6].trim();
                String privacy = personComponents[7].trim();

                // 生成用户唯一标识
                String userId = firstname + "_" + lastname;
                Person newPerson = new Person();
                newPerson.setFirstname(firstname);
                newPerson.setLastname(lastname);
                newPerson.setPhone(phone);
                newPerson.setEmail(email);
                newPerson.setCommunity(community);
                newPerson.setSchool(school);
                newPerson.setEmployer(employer);
                newPerson.setPrivacy(privacy);
                
                // 存入map
                personMap.put(userId, newPerson);
            }
            personScanner.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }

        // 读取活动文件
        try {
            String fileLocation = File.separator + "Users" + File.separator + "user" + File.separator + "Downloads" + File.separator + "SamplefileActivities2022Oct31text.csv";
            File activityList = new File(fileLocation);
            Scanner activityScanner = new Scanner(activityList);

            while (activityScanner.hasNextLine()) {
                String nextLine = activityScanner.nextLine();
                String[] activityComponents = nextLine.split(",");
                
                String firstname = activityComponents[0].trim();
                String lastname = activityComponents[1].trim();
                String activity = activityComponents[2].trim();
                String userId = firstname + "_" + lastname;

                Activities newActivity = new Activities();
                newActivity.setFirstname(firstname);
                newActivity.setLastname(lastname);
                newActivity.setActivity(activity);
                
                // 存入map
                activityMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(newActivity);
            }
            activityScanner.close();
        }catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }

        // 读取完成后构建联系人图
        buildContactGraph();
    }
}

2. 实现无权重图结构

采用邻接表实现无向无权重图:顶点为用户,边代表两人是密切联系人(同一社区/学校/雇主)。用HashSet存储邻接列表,避免重复添加边:

public class ContactGraph {
    // 邻接表:键为用户唯一标识,值为该用户的密切联系人集合
    private Map<String, Set<String>> adjacencyList = new HashMap<>();

    // 添加用户顶点
    public void addVertex(String userId) {
        adjacencyList.putIfAbsent(userId, new HashSet<>());
    }

    // 添加双向边(无向图)
    public void addEdge(String userId1, String userId2) {
        adjacencyList.get(userId1).add(userId2);
        adjacencyList.get(userId2).add(userId1);
    }

    // 获取目标用户的推荐列表(过滤隐私开启的用户)
    public List<Person> getRecommendations(String targetUserId, Map<String, Person> personMap) {
        List<Person> recommendations = new ArrayList<>();
        if (!adjacencyList.containsKey(targetUserId)) {
            return recommendations;
        }

        for (String contactId : adjacencyList.get(targetUserId)) {
            Person contact = personMap.get(contactId);
            // 只推荐未开启隐私的用户
            if ("N".equals(contact.getPrivacy())) {
                recommendations.add(contact);
            }
        }
        return recommendations;
    }
}

3. 构建图的关联关系

通过分组批量添加边提升效率:按社区、学校、雇主分别分组,同一组内的用户互相添加边。在InfoReader中添加如下方法:

private void buildContactGraph() {
    contactGraph = new ContactGraph();
    // 按社区分组构建边
    groupAndAddEdges(personMap, Person::getCommunity);
    // 按学校分组构建边
    groupAndAddEdges(personMap, Person::getSchool);
    // 按雇主分组构建边
    groupAndAddEdges(personMap, Person::getEmployer);
}

// 通用分组添加边方法,避免重复代码
private void groupAndAddEdges(Map<String, Person> personMap, Function<Person, String> groupKeyExtractor) {
    Map<String, List<String>> groups = new HashMap<>();
    // 按指定维度(社区/学校/雇主)分组
    for (Map.Entry<String, Person> entry : personMap.entrySet()) {
        String userId = entry.getKey();
        Person person = entry.getValue();
        String groupKey = groupKeyExtractor.apply(person);
        groups.computeIfAbsent(groupKey, k -> new ArrayList<>()).add(userId);
    }

    // 给同一组内的用户添加边
    for (List<String> group : groups.values()) {
        // 先添加所有顶点
        group.forEach(contactGraph::addVertex);
        // 两两用户之间添加双向边
        for (int i = 0; i < group.size(); i++) {
            for (int j = i + 1; j < group.size(); j++) {
                contactGraph.addEdge(group.get(i), group.get(j));
            }
        }
    }
}

// 对外提供获取推荐列表的方法
public List<Person> getRecommendationsForUser(String userId) {
    if (contactGraph == null || !personMap.containsKey(userId)) {
        return Collections.emptyList();
    }
    Person user = personMap.get(userId);
    // 开启隐私的用户不接收推荐
    if ("Y".equals(user.getPrivacy())) {
        return Collections.emptyList();
    }
    return contactGraph.getRecommendations(userId, personMap);
}

4. 关键注意事项

  • 用户唯一标识:如果数据中有手机号、邮箱等唯一字段,优先用这些字段作为用户ID,避免重名问题
  • 数据清洗:CSV字段可能带有空格,必须用trim()处理,否则会导致分组匹配失败
  • 隐私规则:严格过滤开启隐私的用户,既不让他们出现在推荐列表,也不给他们推送推荐
  • 性能优化:如果用户量极大,两两添加边的O(n²)复杂度会有性能问题,可考虑用更高效的分组关联方式(比如批量插入)

内容的提问来源于stack exchange,提问作者Demali Gregg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 04:30:48