如何在O(n * log K)时间内对平均长度为K的排序列表进行排序?

时间:2010-03-20 03:43:01

标签: algorithm

如何在O(n * log K)时间内对平均长度为K的排序列表进行排序?

4 个答案:

答案 0 :(得分:6)

正如您对问题的评论中所提到的,O(nlog(k))是不可能的,但是this page上的一些算法可以有效地完成您的任务;这是一个:

  

获取每个列表的第一个元素   并创建一个堆(大小为k)。弹出   最小的元素。从中找到数组   元素来了(让我们说它来了   从列表编号i)。拿下一个   列表i中的元素并将其推入   堆。对于进入的每个元素   合并列表,我们花了log(k)时间。所以   时间复杂度是O(N * logk)在哪里   N是元素的总数   所有的K列表。

     

- 写作:Abhishek Goyal

答案 1 :(得分:0)

合并排序是关键。让我们假设N是要统一的元素总数,K是包含它们的容器数:

  • 将所有已排序的序列追加到单个向量中,但要记住添加它们的位置。更好的是,如果你按照第一个元素的值对它们进行排序,会加快下一步的速度。

  • 然后合并已排序的序列对(如果使用C ++,则为std :: inplace_merge)。每个合并都是Na + Nb,所以每一步都是N.你必须执行logK步骤。

因此NlogK。

答案 2 :(得分:0)

我相信有可能达到O(N * log(K)),但在最坏的情况下则不行。

考虑对这些列表进行排序:

{0,1,2,3,4,5,6,7,8,9,10},
{10,11,12,13,14,15,16,17,18,19,20},
{20,21,22,23,24,25,26,27,28,29,30}  

我的人脑可以轻松地对这些列表进行排序,而无需读取每个值,因此应该有一个算法可以做同样的事情。我们需要在使用修改的二进制搜索时进行合并以查找值的范围。

在最坏的情况下,你得到O(N * K),因为必须比较每个值。例如:

{0,2,4,6,8},
{1,3,5,7,9}

这是我在Go中的解决方案,如果我知道排序列表通常具有相对于K的小重叠区域,我将只使用它:

// variation of binary search that finds largest
// value up to and including max
func findNext(a []int, imin int, vmax int) int {
    imax := len(a) - 1
    best := -1
    for imin <= imax {
        imid := imin + ((imax - imin) / 2)
        if a[imid] == vmax {
            return imid
        } else if a[imid] < vmax {
            best = imid
            imin = imid + 1
        } else {
            imax = imid - 1
        }
    }
    return best
}

func sortNSortedLists(in [][]int) []int {
    var out []int
    cursors := make([]int, len(in))
    for {
        // Find the array indices that have the smallest
        // and next to smallest value (may be same) at
        // their current cursor.
        minIdx1 := -1
        minIdx2 := -1
        minVal1 := math.MaxInt32
        minVal2 := math.MaxInt32
        for i, cursor := range cursors {
            if cursor >= len(in[i]) {
                continue
            }
            if in[i][cursor] < minVal1 {
                minIdx2 = minIdx1
                minVal2 = minVal1
                minIdx1 = i
                minVal1 = in[i][cursor]
            } else if in[i][cursor] < minVal2 {
                minIdx2 = i
                minVal2 = in[i][cursor]
            }
        }
        if minIdx1 == -1 {
            // no values
            break
        }
        if minIdx2 == -1 {
            // only one array has values, so append the
            // remainder of it to output
            out = append(out, in[minIdx1][cursors[minIdx1]:]...)
            break
        }

        // If inVal1 is smaller than inVal2,
        // append to output all values from minVal1 to minVal2 found in
        // the minIdx1 array, and update the cursor for the minIdx1 array.
        if minVal1 < minVal2 {
            firstCursor := cursors[minIdx1]
            lastCursor := findNext(in[minIdx1], firstCursor, minVal2)
            if lastCursor != -1 {
                out = append(out, in[minIdx1][firstCursor:lastCursor+1]...)
                cursors[minIdx1] = lastCursor+1
                continue
            }
        }
        // Append the single value to output
        out = append(out, minVal1)
        cursors[minIdx1]++
    }
    return out
}

答案 3 :(得分:-2)

您可以调整合并排序来完成工作。合并排序利用将已排序列表合并到新排序列表中的简便性。