查找数组中任何一对相等元素之间的最小距离

Posted

技术标签:

【中文标题】查找数组中任何一对相等元素之间的最小距离【英文标题】:Finding the minimum distance between any pair of equal elements in an array 【发布时间】:2017-05-21 20:26:58 【问题描述】:

给定一些整数数组 A = [a0, a1, ..., an],求最小距离在 ai 和 aj 之间,使得 ai = aj 和 i != j (或表示不存在这样的索引)。

所以我在 C++ 中实现了一个简单的 O(n2) 方法,包括遍历数组以寻找相等的元素并适当地更新最小距离:

#include <vector>
#include <climits>
#include <algorithm>

int MinPair(const std::vector<int>& nums)

    int ret = std::numeric_limits<int>::max();
    for(int i = 0; i != nums.size(); ++i)
     
        for(int j = 0; j != i; ++j)
         
            if(nums[i] == nums[j])  
                ret = std::min(ret, i - j);
        
    

    if(ret == std::numeric_limits<int>::max()) 
        return -1;

    return ret;

这很好用,但有人告诉我存在一个涉及 std::map 的更“高效”的实现,但没有太多说明什么更有效。即,可以遍历输入数组并将元素的第一次出现存储在映射中,并为每个后续出现找到该出现与映射中该元素的第一个索引之间的距离。如果该距离小于当前最小值,则我们更新该最小值。

但是,我看不出这以何种方式更“有效”。时间复杂度方面,您仍然需要遍历输入数组 (O(n)),并使用 std::map::find 来识别元素是否是第一次出现也是 O(n) ,总复杂度为 O(n2)。空间复杂度方面,除了数组/向量之外,您还必须存储地图。我到底错过了什么?

编辑: 我错误地认为 map::find 是 O(n);插入和查找操作实际上是 O(log n),即使假设使用二进制搜索之类的基本实现,也可以立即看到。

【问题讨论】:

"使用 std::map::find 来识别一个元素是否是第一次出现或不是也是 O(n)" 你确定吗?文档说:“复杂性:大小为对数。” 保持数组排序,您可以在 O(n) 时间内搜索重复项。 您的容器在搜索前是排序的还是未排序的? @arekolek 你是对的,它确实是对数的! 【参考方案1】:

我最初发布了一个与 grigor 提到的解决方案类似的编码解决方案。然后我意识到有一个明显的优化可以让整个事情在 O(N) 时间内完成最佳情况和平均情况。

typedef pair<bool, int> LastPositionFound;

int MinPair(const std::vector<int>& nums)

    unordered_map<int, LastPositionFound> table; // maps value found in array to the last position that value was seen at.
    int best_distance = -1;

    for (size_t index = 0; index < nums.size(); index++)
    
        int value = nums[index];
        LastPositionFound& lpf = table[value];  // returns false,0 if not found
        if (lpf.first)
        
            int distance = index - lpf.second;
            if ((distance < best_distance) || (best_distance == -1))
            
                best_distance = distance;
            
        

        // update reference to hash table entry
        lpf.first = true;
        lpf.second = index;
    
    return best_distance;

【讨论】:

【参考方案2】:

您可以将每个元素映射到它的一组索引。所以你会有类似map&lt;int, set&lt;int&gt;&gt; m 的东西,然后通过你的向量:for(int i = 0, i &lt; nums.size(); ++i) m[nums[i]].insert(i)。之后,您可以遍历地图,如果一个元素有多个索引,则找到索引之间的最小距离。应该是 O(nlog(n))。

【讨论】:

保留所有值有什么意义?将重复元素的索引与先前出现的索引进行比较就足够了。为什么O(n log n)?地图和集合都具有插入 O(1) 的平均运行时间。 @Paul (Ordered) 集合和 C++ 中的映射具有 O(log n) 操作。 C++ 还具有预期 O(1) 复杂度的无序映射。 @Paul 问题被标记为 C++,答案提到了 mapset,它们是 C++ 标准 API 中有序容器类的名称,操作复杂度为 O(log n)。

以上是关于查找数组中任何一对相等元素之间的最小距离的主要内容,如果未能解决你的问题,请参考以下文章

【python】求数组中两个元素的最小距离?

最近的一对点平面情况

最小化最大距离,一维数组

查询区间内距离标尺最近且不大于最大值的元素

查找大矩阵的行之间的最小距离:分配限制错误

查找项目对之间的全局最小距离的算法