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

如何高效实现基于字段前缀匹配的两个Map合并?

嘿,这个问题确实有很好的优化空间,暴力遍历的O(n*m)复杂度在数据量上去之后肯定会拖慢性能,咱们可以通过预处理把时间复杂度降到接近线性的水平,下面给你两种可行的优化方案:

方案一:前缀树(Trie)预处理

前缀树是处理前缀匹配问题的经典数据结构,它可以让我们把所有fullName的预处理时间控制在O(m)(m是mapPersonsWithFullName的条目数),之后每个firstName的查询时间只和其长度以及匹配到的fullName数量有关,整体时间复杂度为O(m + n + k)(n是mapPersonsWithFirstName的条目数,k是匹配到的总条目数)。

1. 实现简单的前缀树节点

class TrieNode {
    private final Map<Character, TrieNode> children = new HashMap<>();
    private final List<String> matchingFullNames = new ArrayList<>();

    // 插入fullName时,更新所有前缀节点的匹配列表
    public void insert(String fullName) {
        TrieNode current = this;
        for (char c : fullName.toCharArray()) {
            current.matchingFullNames.add(fullName);
            current = current.children.computeIfAbsent(c, k -> new TrieNode());
        }
        // 最后一个字符节点也要添加当前fullName(自身也是自己的前缀)
        current.matchingFullNames.add(fullName);
    }

    // 查询所有以指定前缀开头的fullName
    public List<String> findMatches(String prefix) {
        TrieNode current = this;
        for (char c : prefix.toCharArray()) {
            current = current.children.get(c);
            if (current == null) {
                return Collections.emptyList();
            }
        }
        return new ArrayList<>(current.matchingFullNames);
    }
}

2. 编写优化后的核心函数

import java.util.*;

public class PersonMatcher {
    public static Map<Integer, PersonWithFirstNameAndFullName> myFunction(
            Map<Integer, PersonWithFirstName> mapPersonsWithFirstName,
            Map<Integer, PersonWithFullName> mapPersonsWithFullName) {

        // 第一步:构建前缀树
        TrieNode root = new TrieNode();
        for (PersonWithFullName person : mapPersonsWithFullName.values()) {
            root.insert(person.fullName);
        }

        // 第二步:遍历每个PersonWithFirstName,查询匹配结果
        Map<Integer, PersonWithFirstNameAndFullName> result = new HashMap<>();
        for (Map.Entry<Integer, PersonWithFirstName> entry : mapPersonsWithFirstName.entrySet()) {
            int id = entry.getKey();
            PersonWithFirstName firstNamePerson = entry.getValue();
            List<String> matchedFullNames = root.findMatches(firstNamePerson.firstName);

            PersonWithFirstNameAndFullName combinedPerson = new PersonWithFirstNameAndFullName();
            combinedPerson.firstName = firstNamePerson.firstName;
            combinedPerson.fullNames = matchedFullNames;

            result.put(id, combinedPerson);
        }

        return result;
    }

    // 题目中的类定义(调整为可测试的格式)
    static class PersonWithFirstName {
        String firstName;
        public PersonWithFirstName(String firstName) {
            this.firstName = firstName;
        }
    }

    static class PersonWithFullName {
        String fullName;
        public PersonWithFullName(String fullName) {
            this.fullName = fullName;
        }
    }

    static class PersonWithFirstNameAndFullName {
        String firstName;
        List<String> fullNames;
    }

    // 测试示例
    public static void main(String[] args) {
        Map<Integer, PersonWithFirstName> mapPersonsWithFirstName = new HashMap<>();
        Map<Integer, PersonWithFullName> mapPersonsWithFullName = new HashMap<>();
        mapPersonsWithFirstName.put(1, new PersonWithFirstName("bob"));
        mapPersonsWithFirstName.put(2, new PersonWithFirstName("alice"));
        mapPersonsWithFullName.put(1, new PersonWithFullName("bobjohnson"));
        mapPersonsWithFullName.put(2, new PersonWithFullName("bobjames"));

        Map<Integer, PersonWithFirstNameAndFullName> result = myFunction(mapPersonsWithFirstName, mapPersonsWithFullName);

        // 打印验证结果
        for (Map.Entry<Integer, PersonWithFirstNameAndFullName> entry : result.entrySet()) {
            System.out.printf("%d: { firstName: \"%s\", fullNames: %s }%n",
                    entry.getKey(),
                    entry.getValue().firstName,
                    entry.getValue().fullNames);
        }
    }
}
方案二:排序 + 二分查找

如果不想实现自定义的数据结构,这种方案更简单,利用排序后的列表结合二分查找来定位匹配范围,时间复杂度为O(m log m + n log m),比暴力法高效很多。

import java.util.*;

public class PersonMatcher {
    public static Map<Integer, PersonWithFirstNameAndFullName> myFunctionWithSort(
            Map<Integer, PersonWithFirstName> mapPersonsWithFirstName,
            Map<Integer, PersonWithFullName> mapPersonsWithFullName) {

        // 提取所有fullName并排序
        List<String> sortedFullNames = new ArrayList<>();
        for (PersonWithFullName person : mapPersonsWithFullName.values()) {
            sortedFullNames.add(person.fullName);
        }
        Collections.sort(sortedFullNames);

        Map<Integer, PersonWithFirstNameAndFullName> result = new HashMap<>();
        for (Map.Entry<Integer, PersonWithFirstName> entry : mapPersonsWithFirstName.entrySet()) {
            int id = entry.getKey();
            String firstName = entry.getValue().firstName;

            // 找到第一个匹配的起始位置
            int start = Collections.binarySearch(sortedFullNames, firstName);
            start = start < 0 ? -start - 1 : start;

            // 用"firstName + 最大字符"作为上界,找到第一个不匹配的位置
            String upperBound = firstName + Character.MAX_VALUE;
            int end = Collections.binarySearch(sortedFullNames, upperBound);
            end = end < 0 ? -end - 1 : end;

            // 截取匹配的子列表
            List<String> matchedFullNames = new ArrayList<>(sortedFullNames.subList(start, end));

            PersonWithFirstNameAndFullName combinedPerson = new PersonWithFirstNameAndFullName();
            combinedPerson.firstName = firstName;
            combinedPerson.fullNames = matchedFullNames;

            result.put(id, combinedPerson);
        }

        return result;
    }

    // 同样包含题目中的类定义和测试代码(和方案一一致,这里省略)
}
方案对比
  • 前缀树:适合需要频繁查询的场景,预处理一次后查询效率极高,空间复杂度取决于所有fullName的总字符数。
  • 排序+二分:实现简单,无需自定义数据结构,适合一次性查询的场景,空间复杂度为O(m)。

内容的提问来源于stack exchange,提问作者Matt C.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 18:42:52