在数组中找到缺少的数字,时间复杂度O(N),空间复杂度O(1)

Pin*_*ead 3 arrays sorting algorithm

给出一个n个唯一整数0 <= x_i <2*n的数组.打印此数组中不存在的所有整数0 <= x <2*n.

例:

find_missing([0])= [1]

find_missing([0,2,4])= [1,3,5]#因为所有数字都是[0,1,2,3,4,5]

find_missing([])= []

find_missing([0,1,4,5])= [2,3,6,7]#因为所有数字都是[0,1,2,3,4,5,6,7]

怪癖是关于要求:

时间复杂度O(n) - 但是应该有一些与输入大小无关的固定常数C,这样数组的每个元素都被写入/读取<C次,所以对数组进行基数排序是不行的.

空间复杂度O(1) - 你可以修改初始数组,BUT sorted(ini​​tial_array)必须等于sort(array_after_executing_program)并且你不能在这个数组中存储范围[0,2n]之外的整数(想象它是uint32_t的数组) ).

我看到了很多复杂的解决方案,但后来我发现了这个:

public void printNotInArr(int[] arr) {
    if(arr == null)
        return null;

    int len = arr.length;
    int max = 2 * len;

    for(int i = 0; i < len; i++) {
        System.out.println(max - arr[i] - 1);
    }
}
Run Code Online (Sandbox Code Playgroud)

我相信这是最好的解决方案,但我不确定.我想知道为什么那不起作用.

Vau*_*ato 5

正如@ LasseV.Karlsen指出的那样,[0,3]是一个简单的反例,展示了该解决方案是如何工作的.然而,这是一个非常简单的解决方案(在Python中):

def show_missing(l):
  n = len(l)

  # put numbers less than n into the proper slot
  for i in range(0,n):
    while l[i]<n and l[i]!=i:
      j = l[i]
      l[i] = l[j]
      l[j] = j
  for i in range(0,n):
    if l[i]!=i:
      print('Missing %s'%i)

  # put numbers greater than n into the proper slot
  for i in range(0,n):
    while l[i]>=n and l[i]!=i+n:
      j = l[i]
      l[i] = l[j-n]
      l[j-n] = j
  for i in range(0,n):
    if l[i]!=i+n:
      print('Missing %s'%(i+n))
Run Code Online (Sandbox Code Playgroud)

这个想法很简单.我们首先重新排列元素,以便每个小于n的值j存储在索引j处.然后我们可以通过数组并轻松挑选出缺少的n以下的数组.

然后,我们重新排列元素,使得大于或等于n的每个值j都存储在索引jn处.同样,我们可以通过数组并轻松挑选出大于或等于n的数组.

由于仅使用几个局部变量,因此满足O(1)空间复杂度.

由于嵌套循环,O(n)时间复杂度有点难以看出,但是要表明我们从不交换n个元素并不太难,因为一个新元素被放入适当的位置交换.

由于我们只交换了数组的元素,因此也满足了所有原始元素仍在数组中的要求.