确定数组是不是包含重复值的最快方法是啥?

Posted

技术标签:

【中文标题】确定数组是不是包含重复值的最快方法是啥?【英文标题】:What is the fastest way to determine if an array contains a repeated value?确定数组是否包含重复值的最快方法是什么? 【发布时间】:2020-08-09 06:54:27 【问题描述】:

数组只能有一个重复项或根本没有。

我需要该算法通过一些单元测试,并有不同的版本通过不同的测试。

如果您发现这两种解决方案有任何问题或知道更快的解决方案,我将不胜感激。

散列:

对于大小为 UInt16.MaxValue 的数组(包含和不包含重复值)的持续时间测试失败。

通过 - 空数组不包含重复 通过 - 没有重复的小数组 通过 - 带有重复的小数组(重复) 通过 - 带有重复的小数组(重复) 通过 - 没有重复的大型数组(重复) 失败 - 没有重复的大型数组(持续时间) 通过 - 带有重复的大型数组(重复) 通过 - 带有重复(重复)的大型数组 失败 - 具有重复(持续时间)的大型数组 失败 - 合并

public bool ContainsRepeat(UInt16[] values, out UInt16 repeat)
        
            //HASH SET//
            var set = new HashSet<UInt16>();
            repeat = 0;
            foreach (UInt16 value in values)
            
                if (!set.Add(value))
                
                    repeat = value;
                    return true;
                
            
            return false;
         

排序然后二分查找重复项:

对于相同大小的 UInt16.MaxValue 数组的持续时间测试失败,但只有在没有重复时才会失败,而且在有重复值时也无法返回正确的重复值,即使它适用于较小的数组。

通过 - 空数组不包含重复 通过 - 没有重复的小数组 通过 - 带有重复的小数组(重复) 通过 - 带有重复的小数组(重复) 通过 - 没有重复的大型数组(重复) 失败 - 没有重复的大型数组(持续时间) 通过 - 带有重复的大型数组(重复) 失败 - 带有重复(重复)的大型数组 通过 - 具有重复(持续时间)的大型数组 失败 - 合并

public bool ContainsRepeat(UInt16[] values, out UInt16 repeat)
        
            int findRepeatingElement(UInt16[] arr, int low, int high)
            
                if (low > high)
                    return -1;

                int mid = (low + high) / 2;

                if (arr[mid] != mid + 1)
                
                    if (mid > 0 && arr[mid] == arr[mid - 1])
                        return mid;

                    return findRepeatingElement(arr, low, mid - 1);
                

                return findRepeatingElement(arr, mid + 1, high);
            

            repeat = 0;
            if (values.Length <= 1)
            
                return false;
            

            Array.Sort(values);

            int index = findRepeatingElement(values, 0, values.Length - 1);

            if (index != -1)
            
                repeat = values[index];
                return true;
            
            else
            
                return false;
            


        

这是我的第一篇文章,因此也欢迎任何有关格式化未来问题的意见:)

【问题讨论】:

这些失败的测试是否会抛出异常? 否,但持续时间需要低于 2 毫秒 【参考方案1】:

创建一个新的 UInt16.MaxValue 元素的 bool 数组。使用此数组(而不是 HashSet)作为探针来标记已看到的值并检测后续重复项。

public bool ContainsRepeat(UInt16[] values, out UInt16 repeat)

  var seen = new bool[UInt16.MaxValue]; // O(k) space/time; fixed with very small C
  foreach (UInt16 value in values)      // O(n) time; n <= k, with small C
  
    if (seen[value]) 
      repeat = value;
      return true;
    
    seen[value] = true;
  
  repeat = 0;
  return false;

这具有 O(n+k) 时间和 O(k) 空间(k = 范围)的特性,固定。在这种情况下,k = 2^16 ~ 65k 并且 n

虽然两个探针实现都是 O(n),但由于常数 (C) 较小,这应该比使用 HashSet 更好地执行很多。但是,对于具有 UInt32 范围值(k = 范围,其中 k >> n)的数据集,不建议使用这种方法,因为这样会付出恒定的初始化和内存成本。

此特征类似于Radix sort,并且相关的空间与时间权衡是一般排序。

也可以应用微优化(确保在实际条件下进行基准测试)。清除现有数组与创建新数组;或使用 int 和 increment+check vs. boolean check+set;或者通过使用 unsafe 来避免索引范围保护。

如果在“大”数组情况下失败.. 祝“最快”好运。

【讨论】:

为什么,除了创建一个包含 40 亿成员的数组之外,还必须有其他选择? 具体问题是针对 UInt16 范围的“最快”:因此该方法同样专业化,就像选择基数排序而不是一般合并排序时一样。 那么这与一个巨大的数组有什么关系呢?请在你的回答中解释一下。 更新说明。 非常感谢,我之前尝试过使用 int 的探针数组,但总是超出边界异常,而且愚蠢的是,从未想过将大小增加到原始数组的长度之外

以上是关于确定数组是不是包含重复值的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何初始化只有-1值的数组[重复]

判断一个数组是不是至少有一个重复项的最快算法

获取数组第一项的最快方法是啥? [复制]

Swift:确定自定义对象数组是不是包含特定字符串[重复]

从数据框中的所有列中获取值的最简单方法是啥[重复]

检查输入字段中的空值的正确方法是啥[重复]