Algorithm 查找第一个唯一元素

Algorithm 查找第一个唯一元素,algorithm,sorting,search,data-structures,time-complexity,Algorithm,Sorting,Search,Data Structures,Time Complexity,我在面试中遇到了一个无法回答的问题。 必须找到数组中的第一个唯一元素(整数)。 例如: 3,2,1,4,4,5,6,6,7,3,2,3 然后唯一元素是1,5,7和1中的第一个唯一元素 所需的解决方案: O(n)时间复杂度 O(1)空间复杂性 我试着说: 使用Hashmaps、Bitvector…但它们都没有空间复杂度O(1) 有谁能告诉我O(1)空间的解决方案吗?我认为这是不可能的。这不是一个证据,而是一个猜想的证据。我的理由如下 首先,您说过元素的值并没有界限(它们可以是负数、0或正数)。其

我在面试中遇到了一个无法回答的问题。 必须找到数组中的第一个唯一元素(整数)。 例如:

3,2,1,4,4,5,6,6,7,3,2,3
然后唯一元素是
1,5,7
1
中的第一个唯一元素

所需的解决方案:

O(n)时间复杂度

O(1)空间复杂性

我试着说:

使用Hashmaps、Bitvector…但它们都没有空间复杂度O(1)


有谁能告诉我O(1)空间的解决方案吗?

我认为这是不可能的。这不是一个证据,而是一个猜想的证据。我的理由如下

首先,您说过元素的值并没有界限(它们可以是负数、0或正数)。其次,只有
O(1)
空间,所以我们不能存储超过固定数量的值。因此,这意味着我们只能通过比较来解决这个问题。此外,我们无法对数组中的值进行排序或交换,因为我们将丢失唯一值的原始顺序(并且无法存储原始顺序)

考虑一个所有整数都是唯一的数组:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10
为了在此数组上返回正确的输出
1
,而无需对数组重新排序,我们需要将每个元素与所有其他元素进行比较,以确保其唯一性,并按相反顺序执行此操作,以便最后检查第一个唯一的元素。这需要
O(n^2)
O(1)
空间进行比较


如果有人找到了解决方案,我会删除这个答案,我欢迎有人指点我如何把它变成更严格的证明。

注意:这在一般情况下是行不通的。参见下面的推理

原创创意

也许在O(n)时间和O(1)额外空间中有一个解

可以在O(n)时间内构建堆。看

因此,您向后构建了堆,从数组中的最后一个元素开始,并将最后一个位置作为根。在构建堆时,跟踪不是重复项的最新项


这假设在堆中插入项时,您将遇到堆中已经存在的任何相同项。我不知道我是否能证明

假设上述情况属实,那么在构建完堆后,您就知道哪个项是第一个非重复项

为什么它不起作用

就地构建堆的算法从数组的中点开始,并假设该点以外的所有节点都是叶节点。然后,它向后工作(朝向项0),将项筛选到堆中。该算法不会以任何特定的顺序检查最后的n/2项,并且随着项被筛选到堆中,顺序会发生变化


因此,我们所能做的最好的事情(即使这样,我也不确定我们是否能可靠地做到)是,只有在第一个非重复项出现在数组的前半部分时,才能找到它。

这里有一个非严格的证据证明这是不可能的: 众所周知,当使用O(1)空间时,重复检测不可能比O(n*logn)更好。假设当前问题在O(n)时间和O(1)内存中是可解的。如果我们将第一个非重复数字的索引“k”作为0以外的任何值,我们就知道k-1是一个重复数,因此在数组中再扫描一次,我们就可以得到它的重复数,从而使重复检测成为O(n)练习

同样,它并不严格,我们可以进行最坏情况分析,其中k始终为0。但这有助于你思考并说服面试官这是不可能的

说:
在大小为n的多个集合中出现n/k次以上的元素可以在时间O(n log k)中找到。这里k=n,因为我们希望元素出现不止一次。

OP的原始问题没有提到数字的限制(尽管后一个加法数字可以是负/正/零)。在此,我假设另一个条件:

数组中的数字都小于数组长度和 非负

然后,给出一个O(n)时间,O(1)空间解是可能的,并且看起来像一个访谈问题,并且测试用例OP在问题中给出的结果符合上述假设

解决方案:

for (int i = 0; i < nums.length; i++) {
  if (nums[i] != i) {
    if (nums[i] == -1) continue;
      if (nums[nums[i]] == nums[i]) {
        nums[nums[i]] = -1;
      } else {
        swap(nums, nums[i], i);
        i--;
      }
    }
  }
}

for (int i = 0; i < nums.length; i++) {
  if (nums[i] == i) {
    return i;
  }
} 
for(int i=0;i

这里的算法将原始数组视为bucket-in-bucket排序。将数字放入其桶中,如果超过两次,则将其标记为-1。使用另一个循环查找具有nums[i]==i的第一个数字

元素是否总是同时出现?@AleksandarToplek next或prev;同样要小心OOBNo…@smk元素可能不会同时出现…@Jan元素可能是正/负/零…如果两者都不是真的,我会伸出我的头说O(n)时间和O(1)空间不可能我想OP可能忘记了原始问题的一些条件。如果可能的话,这个问题现在已经解决了。现在正在考虑一个不可能的证明…“我们不能…交换数组中的值,因为我们会丢失唯一值的原始顺序。”--我相信这是有争议的。你只需要
O(n)
比较来显示第一个元素是唯一的是,但是你可能需要O(n)乘以O(n)比较来显示O(n)前面的值不是唯一的。@G.Bach
O(n^2)
比较就足够了。你仍然需要一个证明它们对于任何算法都是必要的,而不需要摆弄数组中存储值的顺序,也不需要存储关于最坏情况的O(n)条信息O(n)数组中的不同值,O(n^2)比较的界限是紧密的;只需要取一组两两不同的值,将它们放在第一个flo中