从排序数组中查找小于O(n)的唯一数字

我接受了采访,有以下问题:

在小于O(n)的时间内从排序的数组中查找唯一的数字。

Ex: 1 1 1 5 5 5 9 10 10 Output: 1 5 9 10 

我给出了解决方案,但那是O(n)。

编辑:排序的数组大小约为200亿,唯一数字约为1000。

分而治之

  • 查看排序序列的第一个和最后一个元素(初始序列是data[0]..data[data.length-1] )。
  • 如果两者相等,则序列中唯一的元素是第一个(无论序列有多长)。
  • 如果不同,则将序列分开并重复每个子序列。

在平均情况下以O(log(n))求解,而在最坏的情况下(当每个元素不同时)求解O(n)。

Java代码:

 public static List findUniqueNumbers(int[] data) { List result = new LinkedList(); findUniqueNumbers(data, 0, data.length - 1, result, false); return result; } private static void findUniqueNumbers(int[] data, int i1, int i2, List result, boolean skipFirst) { int a = data[i1]; int b = data[i2]; // homogenous sequence a...a if (a == b) { if (!skipFirst) { result.add(a); } } else { //divide & conquer int i3 = (i1 + i2) / 2; findUniqueNumbers(data, i1, i3, result, skipFirst); findUniqueNumbers(data, i3 + 1, i2, result, data[i3] == data[i3 + 1]); } } 

我不认为它可以在少于O(n)的情况下完成。 假设数组包含1 2 3 4 5 :为了获得正确的输出,必须查看数组的每个元素,因此O(n)。

如果您排序的大小为n数组有m不同的元素,则可以执行O(mlogn)

注意,当m << n (eg m=2 and n=100)时,这将是有效的

算法:

初始化:当前元素y = first element x[0]

步骤1:二进制搜索x最后一次出现的y (可以在O(log(n))时间内完成。让它的索引为i

步骤2: y = x[i+1]并转到步骤1

编辑:在m = O(n)情况下,该算法将会运行得很糟糕。 为了缓解它,您可以与常规O(n)算法并行运行它。 元算法由我的算法和并行运行的O(n)算法组成。 当这两个算法中的任何一个完成时,元算法停止。

由于数据由整数组成,因此在任意两个值之间可能存在有限数量的唯一值。 因此,首先查看数组中的第一个和最后一个值。 如果a[length-1] - a[0] < length - 1 ,则会有一些重复值。 将a[0]a[length-1]放入一个像哈希集一样的常量访问时容器中。 如果这两个值相等,那么您可以知道数组中只有一个唯一值,您就完成了。 您知道数组已排序。 因此,如果两个值不同,您现在可以查看中间元素。 如果中间元素已经在值集中,您知道可以跳过数组的整个左侧部分,并且只递归地分析右侧部分。 否则,递归地分析左右两部分。

根据数组中的数据,您将能够在不同的操作数中获得所有唯一值的集合。 如果所有值都相同,则在恒定时间O(1)得到它们,因为在仅检查第一个和最后一个元素之后您将知道它。 如果“相对较少”的唯一值,您的复杂性将接近于O(log N)因为在每个分区之后,您将“经常”能够丢弃至少一半分析的子arrays。 如果值都是唯一的并且a[length-1] - a[0] = length - 1 ,您还可以在恒定时间内“定义”该集合,因为它们必须是从a[0]a[length-1] a[0]连续数字a[length-1] 。 但是,为了实际列出它们,您必须输出每个数字,并且它们中有N个。

也许有人可以提供更正式的分析,但我估计这个算法在唯一值的数量上大致是线性的而不是数组的大小。 这意味着如果只有很少的唯一值,即使对于一个庞大的数组,也可以在很少的操作中得到它们(例如,如果只有一个唯一值,则无论数组大小如何都在恒定时间内)。 由于唯一值的数量不比数组的大小大,我声称这使得该算法“优于O(N)”(或严格地说:“不比O(N)差,在许多情况下更好” )。

 import java.util.*; /** * remove duplicate in a sorted array in average O(log(n)), worst O(n) * @author XXX */ public class UniqueValue { public static void main(String[] args) { int[] test = {-1, -1, -1, -1, 0, 0, 0, 0,2,3,4,5,5,6,7,8}; UniqueValue u = new UniqueValue(); System.out.println(u.getUniqueValues(test, 0, test.length - 1)); } // i must be start index, j must be end index public List getUniqueValues(int[] array, int i, int j) { if (array == null || array.length == 0) { return new ArrayList(); } List result = new ArrayList<>(); if (array[i] == array[j]) { result.add(array[i]); } else { int mid = (i + j) / 2; result.addAll(getUniqueValues(array, i, mid)); // avoid duplicate divide while (mid < j && array[mid] == array[++mid]); if (array[(i + j) / 2] != array[mid]) { result.addAll(getUniqueValues(array, mid, j)); } } return result; } }