【问题标题】:Given an array of integers, find the first integer that is unique给定一个整数数组,找到第一个唯一的整数
【发布时间】:2011-10-26 18:42:50
【问题描述】:

给定一个整数数组,找出第一个唯一的整数。

我的解决方案:使用std::map

将整数(数字作为键,其索引作为值)一一放入(O(n^2 lgn)),如果有重复,则从映射中删除条目(O(lg n)),将所有数字放入映射后,迭代映射并找到具有最小索引 O(n) 的键。

O(n^2 lgn) 因为map需要排序。

效率不高。

其他更好的解决方案?

【问题讨论】:

  • 当您说“唯一的第一个整数”时,您是指遇到的第一个整数还是最小的整数或随机数?
  • 将所有整数添加到映射是O(n log(n)),而不是O(n^2 log(n))。每个插入都是O(log(n)),其中有n
  • 新解决方案是否符合您的问题,还是我错过了什么?

标签: c++ c algorithm sorting map


【解决方案1】:

我相信以下是最佳解决方案,至少基于时间/空间复杂度:

第 1 步: 将整数存储在哈希映射中,该映射将整数作为键并将其出现的次数作为值。这通常是一个 O(n) 操作,平均而言,哈希表中元素的插入/更新应该是恒定时间。如果发现一个整数出现两次以上,您实际上不必进一步增加使用次数(如果您不想这样做的话)。

第 2 步: 对整数执行第二次遍历。在哈希图中查找每个,第一个出现计数为 1 的就是您要查找的那个(即第一个出现的整数)。这也是O(n),使得整个过程O(n)

针对特殊情况的一些可能的优化:

优化 A:可以使用简单的数组代替哈希表。即使在最坏的情况下,这也保证了 O(1) 用于计算特定整数的出现次数以及查找其出现计数。此外,这提高了实时性能,因为不需要执行散列算法。由于可能较差的参考局部性(即较大的稀疏表与具有合理负载因子的哈希表实现),可能会受到影响。但是,这将适用于非常特殊的整数排序情况,并且可以通过哈希表的哈希函数根据传入的整数产生伪随机存储桶放置来缓解(即,开始时引用的局部性较差)。

数组中的每个字节都表示该字节索引所表示的整数的计数(最多 255)。这只有在最低整数和最高整数之间的差异(即有效整数域的基数)足够小以至于该数组可以放入内存时才有可能。特定整数数组中的索引将是它的值减去数据集中存在的最小整数。

例如,在具有 64 位操作系统的现代硬件上,可以分配一个 4GB 的数组来处理整个 32 位整数域是完全可以想象的。如果有足够的内存,甚至更大的数组也是可以想象的。

在处理之前必须知道最小和最大整数,否则需要使用 minmax 算法对数据进行另一次线性传递以找出此信息。

优化 B:您可以进一步优化 优化 A,每个整数最多使用 2 位(一位表示存在,另一位表示多重性)。这将允许每个字节表示四个整数,扩展数组实现以处理给定数量的可用内存的更大的整数域。可以在这里玩更多的位游戏以进一步压缩表示,但它们只支持特殊的数据输入情况,因此不推荐用于仍然主要是一般情况的情况。

【讨论】:

  • 哈希值平均为 O(1),但前提是您正确设置。
  • @yi_H,由于根据问题定义为您提供了一个整数数组,这意味着您知道问题集的大小,您可以适当地调整哈希表的大小以获得良好的最终负载因子。
  • @Michael,如何确保您找到的数字是给定数组中的“第一个”单整数?您的解决方案忽略索引。谢谢
  • @user1002288,您缺少识别“第一个”此类数字的第 2 步!
  • @user1002288:因此,当您插入时,您存储一个元组(插入的元素索引,计数:= 0),然后您更新元组的计数部分。然后迭代哈希。这不会改变算法的复杂性。或者,正如迈克尔建议的那样,您可以再次迭代整个数组。
【解决方案2】:

这一切都是无缘无故的。只需使用 2 for-loops 和一个变量即可为您提供一个简单的 O(n^2) 算法。

如果您不厌其烦地使用哈希映射,那么@Micheal Goldshteyn 所建议的可能也是如此

更新:我知道这个问题已经存在 1 年了。但是正在查看我回答的问题并遇到了这个问题。认为有比使用哈希表更好的解决方案。

当我们说独特时,我们将有一个模式。例如:[5, 5, 66, 66, 7, 1, 1, 77]。在此让移动窗口为 3。首先考虑 (5,5,66)。我们可以很容易地建立。这里有重复。所以将窗口移动 1 个元素,得到 (5,66,66)。同样在这里。移动到下一个(66,66,7)。再次在这里重复。下一个(66,7,1)。这里没有重复!取中间元素,因为它必须是集合中的第一个唯一元素。左边的元素属于 dup,所以 1 也可以。因此 7 是第一个唯一元素。

空间:O(1) 时间:O(n) * O(m^2) = O(n) * 9 ≈ O(n)

【讨论】:

  • 我喜欢这个,很简单,而且很可能会非常快。
  • O(N^2) 比 O(N) 快吗?
  • 不,但是增加局部性和低系数的好处可能超过复杂性的成本。特别是如果将内部循环替换为memmem(),这个简单的解决方案有可能很快。
  • 它也已经到位,特别是不需要任何堆分配。除非我知道我将使用非常大的数据集,否则我倾向于从这里开始。
  • 是的,这是我的第一个想法。从易于实施的具有下降性能的解决方案开始,然后在整个程序完成后疯狂优化它......
【解决方案3】:

插入地图是 O(log n) 而不是 O(n log n) 所以插入 n 键将是 n log n。也最好使用set

【讨论】:

  • O(N log N) 什么时候比 O(N) 好?
  • @MichaelGoldshteyn:它比 OP 认为的 O(n^2 log n) 好,而不是 O(n)
  • @Dani,每次插入map,key都会排序O(n lg n),所以如果插入m次,就是O(m n lgn),n是当前大小map,m是需要插入的总数。
  • @user1002288:不,它们不会被排序。该映射实际上是一个自平衡 RB 树,它对插入进行二分搜索,它不只是将其放入并排序。
【解决方案4】:

虽然是O(n^2),但是下面的系数小,缓存也不算太差,而且使用memmem(),速度很快。

 for(int x=0;x<len-1;x++)
     if(memmem(&array[x+1], sizeof(int)*(len-(x+1)), array[x], sizeof(int))==NULL &&
        memmem(&array[x+1], sizeof(int)*(x-1), array[x], sizeof(int))==NULL)
            return array[x];

【讨论】:

  • 我相信 memmem 是 POSIX 标准的一部分,但是 C 和 C++ 都没有正式采用它。
  • 这也有一个小错误。考虑输入 [0 0 2]... 您的第一步检查 [0 2] 中的 0,它找到了。第二步将检查 [2] 是否为 0,但没有找到,使 array[1] 看起来像答案
  • 第二步是什么?当 memmem 在 [0 2] 中看到 0 时,它返回非 null,我返回 array[0]
  • 哦,原来如此。然后它是一个主要错误......你想要第一个唯一元素,并且 array[0] 显然是非唯一的。
  • 啊!我看到了错误。 *修复修复*
【解决方案5】:
public static string firstUnique(int[] input)  
{
    int size = input.Length;
    bool[] dupIndex = new bool[size];
    for (int i = 0; i < size; ++i) 
    {
        if (dupIndex[i]) 
        {
            continue;
        } 
        else if (i == size - 1) 
        {
            return input[i].ToString();
        }
        for (int j = i + 1; j < size; ++j) 
        {
            if (input[i]==input[j]) 
            {
                dupIndex[j] = true;
                break;
            } 
            else if (j == size - 1)
            {
                return input[i].ToString();
            }
        }
    }
    return "No unique element";
}

【讨论】:

    【解决方案6】:

    @user3612419

      Solution given you is good with some what close to O(N*N2) but further optimization in same code is possible I just added two-3 lines that you missed.
    
    
    
    public static string firstUnique(int[] input)  
    {
      int size = input.Length;
      bool[] dupIndex = new bool[size];
      for (int i = 0; i < size; ++i) 
      {
         if (dupIndex[i]) 
        {
            continue;
        } 
        else if (i == size - 1) 
        {
            return input[i].ToString();
        }
        for (int j = i + 1; j < size; ++j) 
        {
      if(dupIndex[j]==true)
      {
     continue;
     }
            if (input[i]==input[j]) 
            {
                dupIndex[j] = true;
        dupIndex[i] = true;
                break;
            } 
            else if (j == size - 1)
            {
                return input[i].ToString();
             }
         } 
      }
    return "No unique element";
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-10
      • 2012-04-02
      • 2021-07-17
      • 2018-12-23
      • 2012-04-29
      • 1970-01-01
      • 2018-09-10
      • 1970-01-01
      相关资源
      最近更新 更多