在O(n)时间和O(1)额外内存

时间:2017-04-15 22:19:11

标签: algorithm sorting runtime time-complexity space-complexity

我试图找到一个我最近得到的面试问题的答案但是无法解决。我被要求使用O(1)空间和O(n)时间,通过10位整数对1000万个对象(每个包含10位整数和唯一字符串值)进行排序。对于所有密集的目的,字符串值只会使问题变得更加困难,但是您不能通过它对对象进行排序。

我正在考虑使用计数排序,但这只有在我只是对10位整数进行排序时才有效。对对象进行排序会使事情变得更复杂,因为我需要跟踪它们唯一的字符串值。我查看了基数排序,但似乎使用O(n)作为最坏情况。

我缺少哪种类型的那种?

2 个答案:

答案 0 :(得分:3)

There's an in-place (MSB) radix sort

它是这样的:

  • 从第一位开始。
  • 向左移动该位等于0的所有元素 以及所有那些在右边等于1的人。

    你这样做类似于快速排序,从两侧移动到中间,在左边交换1,右边是0。

  • 考虑到下一位,在左侧和右侧递归。

这个过程是这样的:

        ↓                ↓
      0...             1...
---------------  ---------------
   ↓       ↓        ↓       ↓
 00...   01...    10...   11...
------- -------  ------- -------
 ↓   ↓   ↓   ↓    ↓   ↓   ↓   ↓
000 001 010 011  101 110 110 111
--- --- --- ---  --- --- --- ---
...

时间复杂度为O(bits*n),空间复杂度为O(bits)

因此,对于恒定的位数,时间复杂度为O(n),空间复杂度为O(1)

也可以使用计数排序(具有相同的时间和空间复杂度)来执行此操作。

  • 计算每个元素的数量。
  • 使用此功能,您可以确定数组中应显示哪些元素。
  • 创建一个包含所有上述起点的查找表(将索引映射到偏移量,可以是一个数组),这些起始点将用于确定下一个元素的去向。

  • 现在循环遍历数组并将每个不合适的元素交换到它可以去的第一个点,然后对我们交换的每个元素执行相同的操作,直到当前元素属于它所在的位置。 / p>

    在每个步骤中增加查找表中相关元素的偏移量。

    请注意,两次交换相同的元素是不可能的,因此这将是线性时间。

这一切听起来很混乱,所以这是一个例子:

4 5 3 1 2 3 4 4 1 5 4 3 2 3

// counts:
1: 2, 2: 2, 3: 4, 4: 4, 5: 2

// the starting points (offsets) based on the counts:
1 at position 0
2 at position (offset of 1)+(count of 1) = 0+2 = 2
3 at position (offset of 2)+(count of 2) = 2+2 = 4
4 at position (offset of 3)+(count of 3) = 4+4 = 8
5 at position (offset of 4)+(count of 4) = 8+4 = 12

// sorting:
4 5 3 1 2 3 4 4 1 5 4 3 2 3   // out of place, swap with offsets[4] = 8 (offsets[4]++)
^

1 5 3 1 2 3 4 4 4 5 4 3 2 3   // in correct position, moving on         (offsets[1]++)
^

1 5 3 1 2 3 4 4 4 5 4 3 2 3   // swap with offsets[5] = 12              (offsets[5]++)
  ^

1 2 3 1 2 3 4 4 4 5 4 3 5 3   // swap with offsets[2] = 2               (offsets[2]++)
  ^

1 3 2 1 2 3 4 4 4 5 4 3 5 3   // swap with offsets[3] = 4               (offsets[3]++)
  ^

1 2 2 1 3 3 4 4 4 5 4 3 5 3   // swap with offsets[2] = 3               (offsets[2]++)
  ^

1 1 2 2 3 3 4 4 4 5 4 3 5 3   // in correct position, moving on         (offsets[1]++)
  ^

1 1 2 2 3 3 4 4 4 5 4 3 5 3   // in correct position, moving on         (offsets[2]++)
    ^

你明白了......

请注意,计数表和查找表的大小为O(max value),如果每个数字包含固定的位数,则为O(1)

你为每个元素做了一定量的工作,所以这是O(n)时间。

答案 1 :(得分:1)

可以使用标准的快速排序透视代码将所有带整数0的项目移动到数组的开头。在其余元素上继续使用1,2,3等,在1023上旋转后停止。

这遍历数组1024次,每个数据透视表花费O(n)时间,因此是O(n)。

在实践中更有效的一种非常类似的方法就是使用标准的三向快速排序算法。通常认为这需要O(n log n)时间,并且在最坏的情况下使用O(log n)空间。但是在这里,整数的域限制为1024个值,所以保证在O(n)时间内完成并使用O(1)空间,因为在每次递归调用quicksort时,一个pivot值永远不会被使用两次。

[这个答案做了一个假设 - 那个" 1000万"在问题是n,那" 10位"仍然固定]。