焦点关键词主题配图,直观展示核心要点与实用场景

DoorDash 最近邻城市面试题 2026:同轴最近城市高分解法

DoorDash 最近邻城市面试题 2026:同轴最近城市高分解法

DoorDash 最近邻城市面试题 2026 是近期高频题。
因此,本文给你一套可复述模板。
这是我们学员贡献的最新面经。
同时,这也是 2026年最新 面试经验。

此外,题目描述很长,规则很多。
但是,核心只有筛选与比较。
先筛同 x 或同 y 的城市。
然后,再做距离和字典序判断。

2026 面试流程深度复盘:DoorDash 最近邻城市面试题 2026

具体来说,开场先确认输入格式。
城市名通常唯一。
与此同时,查询次数可能很多。
你要主动问清空值返回规则。

因此,先给暴力解很重要。
每次查询都扫全部城市。
命中同轴候选后计算距离。
最后按 tie-break 更新答案。

此外,DoorDash 最近邻城市面试题 2026 常追问两轮优化。
第一轮是按 x 与 y 分组。
第二轮是组内排序加二分。
换句话说,你要主动指出瓶颈。

但是,面试官也看沟通质量。
你要边写边讲复杂度。
同时,要明确预处理代价。
此外,要解释为何查询是对数级。

总而言之,这题节奏是先稳后快。
先保正确,再做优化。
与此同时,主动补充边界用例。
这会明显提高通过率。

核心题目解析

因此,我们把 DoorDash 最近邻城市面试题 2026 拆成三层。
第一层是规则复述。
第二层是数据结构选择。
第三层是 tie-break 一次写对。

1) 题意与判定规则

具体来说,只比较同 x 或同 y 的点。
距离可写成 |dx| + |dy|
因为同轴,所以是单轴差值。
如果没有候选,返回 NONE

2) 暴力解法(先拿基础分)

因此,暴力法最直接。
对每个查询遍历全部城市。
满足同轴就尝试更新答案。
时间复杂度是 O(n*q)

3) 优化解法(分组 + 排序 + 二分)

此外,优化法要建两个索引。
x -> 有序 y 键 是第一个。
y -> 有序 x 键 是第二个。
每个键下放名字有序集合。

查询时先走 x 索引。
然后,二分定位当前 y。
与此同时,只检查左邻和右邻。
若同键桶有他城,距离就是 0。

但是,最终比较要统一函数。
先比距离,再比名字。
这样可稳定处理平局。
因此,单次查询是 O(log n) 级。

总而言之,复杂度可这样陈述。
- 预处理:O(n log n)
- 单次查询:O(log n)
- 额外空间:O(n)

4) 逻辑流程图(可直接口述)

与此同时,你可以按下图讲流程。

flowchart TD
    A[读取城市与坐标] --> B[按 x 分组并排序 y]
    A --> C[按 y 分组并排序 x]
    B --> D[收到查询城市]
    C --> D
    D --> E[二分定位邻近坐标桶]
    E --> F[生成 x 轴候选与 y 轴候选]
    F --> G[先比较距离]
    G --> H[距离相同比字典序]
    H --> I[输出城市名或 NONE]

5) Java 参考实现(含注释)

此外,下面代码覆盖完整规则。

import java.util.*;

public class NearestCitySolver {
    private static class City {
        String name;
        int x;
        int y;
        City(String name, int x, int y) {
            this.name = name;
            this.x = x;
            this.y = y;
        }
    }

    private static class AxisIndex {
        List<Integer> keys; // 排序后的可变坐标
        Map<Integer, TreeSet<String>> coordToNames; // 坐标 -> 城市名有序集
        AxisIndex(List<Integer> keys, Map<Integer, TreeSet<String>> coordToNames) {
            this.keys = keys;
            this.coordToNames = coordToNames;
        }
    }

    private static class Candidate {
        String name;
        long dist;
        Candidate(String name, long dist) {
            this.name = name;
            this.dist = dist;
        }
    }

    private final Map<String, City> nameToCity = new HashMap<>();
    private final Map<Integer, AxisIndex> byX;
    private final Map<Integer, AxisIndex> byY;

    public NearestCitySolver(String[] names, int[] xs, int[] ys) {
        if (names.length != xs.length || xs.length != ys.length) {
            throw new IllegalArgumentException("Input length mismatch.");
        }

        Map<Integer, Map<Integer, TreeSet<String>>> xTmp = new HashMap<>();
        Map<Integer, Map<Integer, TreeSet<String>>> yTmp = new HashMap<>();

        for (int i = 0; i < names.length; i++) {
            City city = new City(names[i], xs[i], ys[i]);
            if (nameToCity.putIfAbsent(city.name, city) != null) {
                throw new IllegalArgumentException("Duplicate city name: " + city.name);
            }

            xTmp.computeIfAbsent(city.x, k -> new HashMap<>())
                .computeIfAbsent(city.y, k -> new TreeSet<>())
                .add(city.name);

            yTmp.computeIfAbsent(city.y, k -> new HashMap<>())
                .computeIfAbsent(city.x, k -> new TreeSet<>())
                .add(city.name);
        }

        this.byX = buildIndex(xTmp);
        this.byY = buildIndex(yTmp);
    }

    private Map<Integer, AxisIndex> buildIndex(
            Map<Integer, Map<Integer, TreeSet<String>>> raw) {
        Map<Integer, AxisIndex> out = new HashMap<>();
        for (Map.Entry<Integer, Map<Integer, TreeSet<String>>> entry : raw.entrySet()) {
            int fixed = entry.getKey();
            Map<Integer, TreeSet<String>> inner = entry.getValue();

            List<Integer> keys = new ArrayList<>(inner.keySet());
            Collections.sort(keys);

            Map<Integer, TreeSet<String>> buckets = new HashMap<>();
            for (Map.Entry<Integer, TreeSet<String>> e : inner.entrySet()) {
                buckets.put(e.getKey(), new TreeSet<>(e.getValue()));
            }

            out.put(fixed, new AxisIndex(keys, buckets));
        }
        return out;
    }

    public String query(String cityName) {
        City city = nameToCity.get(cityName);
        if (city == null) return "NONE";

        Candidate best = null;
        best = better(best, queryOnAxis(byX.get(city.x), city.y, city.name));
        best = better(best, queryOnAxis(byY.get(city.y), city.x, city.name));

        return best == null ? "NONE" : best.name;
    }

    public List<String> batchQuery(String[] queries) {
        List<String> ans = new ArrayList<>(queries.length);
        for (String q : queries) {
            ans.add(query(q));
        }
        return ans;
    }

    private Candidate queryOnAxis(AxisIndex index, int coord, String selfName) {
        if (index == null || index.keys.isEmpty()) return null;

        List<Integer> keys = index.keys;
        int bs = Collections.binarySearch(keys, coord);

        Candidate best = null;
        int left;
        int right;

        if (bs >= 0) {
            best = better(best, fromSameCoord(index.coordToNames.get(coord), selfName));
            left = bs - 1;
            right = bs + 1;
        } else {
            int insertPos = -bs - 1;
            left = insertPos - 1;
            right = insertPos;
        }

        if (left >= 0) {
            int c = keys.get(left);
            String name = index.coordToNames.get(c).first();
            best = better(best, new Candidate(name, Math.abs((long) coord - c)));
        }

        if (right < keys.size()) {
            int c = keys.get(right);
            String name = index.coordToNames.get(c).first();
            best = better(best, new Candidate(name, Math.abs((long) coord - c)));
        }

        return best;
    }

    private Candidate fromSameCoord(TreeSet<String> names, String selfName) {
        if (names == null) return null;
        if (names.size() == 1 && names.contains(selfName)) return null;

        String pick = names.first();
        if (pick.equals(selfName)) {
            pick = names.higher(selfName);
        }
        if (pick == null) return null;

        return new Candidate(pick, 0L);
    }

    private Candidate better(Candidate a, Candidate b) {
        if (a == null) return b;
        if (b == null) return a;
        if (a.dist != b.dist) return a.dist < b.dist ? a : b;
        return a.name.compareTo(b.name) <= 0 ? a : b;
    }
}

6) 易错边界清单

因此,面试前要把坑位过一遍。
- 查询城市不存在。
- 同轴只有自己一座城。
- 两侧等距且名字需比较。
- 同坐标多城市且要排除自己。
- 坐标可能为负数。
- 输入长度不一致。
- 城市名重复。

专家备考策略与高频考点:DoorDash 最近邻城市面试题 2026

与此同时,DoorDash 最近邻城市面试题 2026 的得分点很固定。
因此,你要按评分维度准备。
此外,先练口述,再练编码。
最后做一次 45 分钟限时模拟。

具体来说,核心考点如下。
- 约束建模:只看同 x 或同 y。
- 方案对比:先暴力,再优化。
- 二分定位:能解释邻近桶逻辑。
- 规则落地:tie-break 一致且稳定。
- 复杂度:预处理与查询分开讲。
- 工程能力:Java 细节与 bug 修复。

此外,BQ 常和技术题连问。
STAR 应对策略
1. Situation:先说题目长且规则多。
2. Task:再说目标是稳准快实现。
3. Action:然后给建模、优化、验证。
4. Result:最后给复杂度和通过结果。

总结与行动号召(CTA)

总而言之,DoorDash 最近邻城市面试题 2026 并不玄学。
但是,细节会直接决定上限。
因此,你需要可复述的讲解模板。
同时,要把代码与表达一起训练。

此外,想做一对一实战复盘,请点击:联系我们的专家进行一对一面试辅导
与此同时,想补基础术语,可参考:权威算法参考