【问题标题】:Finding unique numbers from sorted array in less than O(n)在小于 O(n) 的时间内从排序数组中查找唯一数字
【发布时间】:2015-01-13 12:06:50
【问题描述】:

我有一个面试,有以下问题:

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

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

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

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

【问题讨论】:

  • 你必须至少知道最后一个元素,所以你不必至少遍历所有元素一次。所以最小界限是 O(N)
  • 如果新的“唯一”编号与最后一个索引上的编号相同,则退出循环。因此,如果到达第一个3,就可以停止循环。
  • @Tom 仍然是 O(N)
  • @Tom 它仍然与元素数量成线性关系,因此 O(N)。我希望你知道 O(N) 是什么意思?
  • 你可以根据抽样/二分搜索来做一些事情,尽管很难猜测它会如何工作。

标签: java algorithm time-complexity


【解决方案1】:

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

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

算法:

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

第 1 步:对 yx 中的最后一次出现进行二分搜索(可以在 O(log(n)) 时间完成。让它的索引为 i

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

编辑:在m = O(n) 的情况下,此算法将无法正常工作。为了缓解它,您可以与常规 O(n) 算法并行运行它。元算法由我的算法和并行运行的O(n) 算法组成。当这两种算法中的任何一种完成时,元算法就会停止。

【讨论】:

  • 但是还是不小于O(n)
  • 不是最坏的情况。但是当 m
  • 你说得对,在某些情况下它更快,但渐近复杂度不能仅在某个范围内进行比较。我知道你的意思,但这并不能解决给定的问题。
  • 修复了一点。现在复杂度
【解决方案2】:
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<Integer> getUniqueValues(int[] array, int i, int j) {
        if (array == null || array.length == 0) {
            return new ArrayList<Integer>();
        }
        List<Integer> 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;
    }
}

【讨论】:

  • 在这个问题中使用分而治之是一个有趣的想法。我的代码已准备好运行。分割部分是一个小技巧,你必须避免重复元素出现在两边。
【解决方案3】:

分而治之

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

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

Java 代码:

public static List<Integer> findUniqueNumbers(int[] data) {
    List<Integer> result = new LinkedList<Integer>();
    findUniqueNumbers(data, 0, data.length - 1, result, false);
    return result;
}

private static void findUniqueNumbers(int[] data, int i1, int i2, List<Integer> 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(log n) 不是一般情况。只有当有相对大量的重复时,它才是 O(log n) (或更好)。在一般情况下,它是 O(n)。
  • 你是对的,但是这个解决方案可以在小于 O(n) 的时间内找到唯一数字,因为它不一定需要查看所有数字。没有重复是最坏的情况,而不是平均情况。
  • @PeterWalser 我认为您的解决方案比其他解决方案更好,并且符合我编辑的问题。在接受您的回答之前,让我检查其他输入。谢谢。
  • @PeterWalser:我们对输入的分布知之甚少。您无法断言平均情况是什么或不是什么。
  • 我认为根据 OP 提出的问题(200 亿个数字,只有 1000 个唯一),这种方法效果很好,并且可以在 O(logn)时间内检索唯一元素。在所有唯一元素的情况下,无论如何都没有已知算法可以在少于 O(n) 的时间内求解。
【解决方案4】:

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

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

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

【讨论】:

  • 您似乎给出了顺序条目的解决方案,但它也可以是 1 55 55 1000,就像这样。
  • @DeepakTiwari 原始问题表明数组已排序
  • @DeepakTiwari 是的,在 1、55、55、1000 的情况下确实没有增益,但我们正在研究渐近行为。如果数字“55”在这个序列中重复了 100 次而不是两次,那么完整的值集将需要比序列长度 102 少得多的操作。
【解决方案5】:

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

【讨论】:

  • 我同意你的观点,我也给出了相同的答案,但他告诉我这是可能的。这就是我在这里寻找答案的原因,因为我还不知道它是如何可能的。
  • 可能面试官对 O(n) 的理解不同,或者将库函数计算为常数时间。
  • 您可以将复杂性作为其他事物的函数(例如,数组中的不同元素)。请参阅下面的答案。
猜你喜欢
  • 2020-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-08
  • 1970-01-01
  • 2017-01-26
  • 1970-01-01
  • 2011-11-28
相关资源
最近更新 更多