leetcode
leetcode 201 ~ 250
最短单词距离 II

最短单词距离 II

难度:

标签:

题目描述

代码结果

运行时间: 39 ms, 内存: 23.8 MB


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
 
/**
 * 设计一个类,该类包含以下两个函数:
 * 1. WordDistance(String[] words) 初始化对象,传入单词列表。
 * 2. int shortest(String word1, String word2) 返回两个单词之间的最短距离。
 */
public class WordDistance {
    private Map<String, List<Integer>> wordIndices;
 
    /**
     * 初始化对象,存储每个单词的所有出现位置
     */
    public WordDistance(String[] words) {
        wordIndices = new HashMap<>();
        for (int i = 0; i < words.length; i++) {
            wordIndices.computeIfAbsent(words[i], k -> new ArrayList<>()).add(i);
        }
    }
 
    /**
     * 返回两个单词之间的最短距离
     */
    public int shortest(String word1, String word2) {
        List<Integer> indices1 = wordIndices.get(word1);
        List<Integer> indices2 = wordIndices.get(word2);
        return indices1.stream()
            .flatMapToInt(index1 -> indices2.stream().mapToInt(index2 -> Math.abs(index1 - index2)))
            .min()
            .orElse(Integer.MAX_VALUE);
    }
}

解释

方法:

该题解采用了预处理+双指针的思路。在初始化时,先用哈希表 wordIdx 将单词映射到对应的序号,然后用列表 idxs 存储每个单词在原数组中出现的所有下标。在计算最短距离时,对于给定的两个单词 word1 和 word2,分别取出它们在 idxs 中存储的下标列表 idxs1 和 idxs2,然后用双指针 i 和 j 同时遍历这两个列表,更新最短距离 ans。

时间复杂度:

O(n+m1+m2)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在计算最短单词距离时选择使用双指针方法而不是其他可能的算法?
双指针方法适用于处理两个已排序的列表来寻找最小差值的问题,这种方法可以高效地通过逐步逼近最小差值来减少不必要的比较。对于本题,每个单词的索引列表是有序的,使用双指针可以在线性时间内找到两个列表中索引的最小差距,相比于暴力方法的平方时间复杂度要高效得多。
🦆
在双指针遍历中,当`idxs2[j] < idxs1[i]`时,为什么选择仅增加j的值而不是同时比较增加i的情况是否能得到更小的距离?
当`idxs2[j] < idxs1[i]`时,意味着当前`idxs2[j]`的值小于`idxs1[i]`的值,此时增加`j`可以使`idxs2[j]`向`idxs1[i]`靠近,可能得到更小的差值。如果增加`i`,则`idxs1[i]`将远离`idxs2[j]`,从而增大两者之间的差距。因此,为了寻找最小距离,我们应优先考虑增加`j`。
🦆
当一个单词比另一个单词出现次数多很多时,这种双指针遍历是否还是效率最高的方法?
即使当一个单词出现次数远多于另一个单词时,双指针算法仍然是效率较高的方法。这是因为双指针方法只需遍历两个列表一次,总的时间复杂度依然是O(m+n),其中m和n是两个单词的出现次数。双指针算法能够有效地跳过不必要的比较,直接逼近最小距离。
🦆
在初始化哈希表`wordIdx`时,为什么选择`DefaultDict(int)`而不是普通的字典?
使用`DefaultDict(int)`可以自动处理新单词的索引分配问题。当访问一个尚未存储在字典中的键时,`DefaultDict(int)`会自动为该键创建一个默认值,这里的默认值是从0开始的整数,这样可以自动为新单词分配序号。如果使用普通字典,我们则需要手动检查键是否存在并适当地添加新键和值,这会使代码更加复杂和容易出错。

相关问题

合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

 

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

 

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列

最短单词距离

最短单词距离

最短单词距离 III

最短单词距离 III