给定一个大小为n的数组给出一个确定性算法(不是快速排序),它使用O(1)空间(不是中位数的中位数)来找到K'的最小项。
有明显的解决方案,例如在nlogn上对数组进行排序或在nk中找到最小k时间,但我确信有更好的方法可以做到这一点。它不必是衬垫(我怀疑它是否可以)。
感谢帮助者。
答案 0 :(得分:4)
安排(以便不会浪费额外的空间)将数组转换为min-heap(can be built in O(n)
time),然后执行k
extract-min操作whicih需要O(log n)
时间。所以你完全有O(n + k*log n)
次。 (自k <= n
以来最糟糕的情况是O(n log n)
。)
空间复杂度为O(1)
,或者,如果算上我们修改过的数组,O(n)
;但任何算法都需要数组,因此需要数组提供的O(n)
空间。堆产生的额外空间成本为O(1)
。
很明显,这种方法是正确的:第一个extract-min返回(并从堆中删除)最小的元素;第二个extract-min返回(并从堆中删除)第二个最小的元素; ...;并且k
- extract-min返回(并从堆中删除)k
个最小元素。
如果k
是&#34;更大&#34;比n/2
,你可以通过使用max-heap和使用类似方法搜索(n-k)
- 最大元素来加快速度。
答案 1 :(得分:0)
我发现BitVector是解决此问题的另一种方法。
填充BitVector需要O(n)时间,找到第k个最小元素需要大约O(k)时间。因此,总复杂度将为O(n)。
根据每种情况,空间复杂度可能有所不同。在我的情况下,列表可以包含从1到2147483647(java.lang.Integer.MAX_VALUE)(2147483647/32 * 4/1024/1024 =〜255 MB)之间的一组未排序的正整数。
这是我在Java中的实现:
int findKthSmallestInUnsortedList(List<Integer> list, int k) {
// Step 1: Populate (read) data from list to the bit-vector in O(n) time
int[] bitVector = new int[Integer.MAX_VALUE/32]; //4 bytes chunks (int): 32 bits
for(Integer num : list) {
int chunkNum = num / 32;
int indexInChunk = num % 32;
bitVector[chunkNum] |= (1 << indexInChunk);
}
// Step 2: Find the k'th '1' starting from the first chunk in O(k) time
int count = 0;
for (int i = 0; i < bitVector.length; i++) {
int bitCount = 0;
while(bitVector[i] != 0) {
if ((bitVector[i] & 1) != 0) {
count++;
}
if (count == k) {
return i * 32 + bitCount;
}
bitCount++;
bitVector[i] >>= 1;
}
}
return -1; // not found
}
对于那些不熟悉BitVector的人来说,这是一个示例:
想象一下数字4在列表中。因此,我们将 first 块中的 4th 位设置为1:
00000000 00000000 00000000 00001000
如果读取了33,则根据上述实现,我们进入 second 块并将 first 位设置为1,依此类推。
最后,我们从BitVector的开始一直计数k'1'。当找到第k个1时,我们将那个1的块号乘以32,然后将该1的位置加到那个块中(i * 32 + bitCount)。