K是数组中最小的元素

时间:2016-04-02 07:44:07

标签: arrays algorithm

给定一个大小为n的数组给出一个确定性算法(不是快速排序),它使用O(1)空间(不是中位数的中位数)来找到K'的最小项。

有明显的解决方案,例如在nlogn上对数组进行排序或在nk中找到最小k时间,但我确信有更好的方法可以做到这一点。它不必是衬垫(我怀疑它是否可以)。

感谢帮助者。

2 个答案:

答案 0 :(得分:4)

安排(以便不会浪费额外的空间)将数组转换为min-heapcan 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)。