确定数组是不是包含重复值的最快方法是啥?
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 的探针数组,但总是超出边界异常,而且愚蠢的是,从未想过将大小增加到原始数组的长度之外以上是关于确定数组是不是包含重复值的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章