使用优先级队列合并K-排序列表

时间:2013-10-20 07:23:47

标签: arrays algorithm merge

我的算法类中有人要求我做一个K-way合并算法O(nlogk) 在搜索之后,我发现它可以通过制作k长度优先级队列并将其与每个列表的第一个元素一起排队来完成。提取最小值,将其附加到结果并从已提取元素的列表中排队。 我很困惑:

  1. 如何知道特定列表何时耗尽,假设a list的元素比其他列表中的其他元素小?

  2. 如何知道元素是哪个列表(如果结构不用于定义)?

  3. 时间复杂度如何O(nlogk)

  4. 修改

    如果有人可以逐步记下算法,那将会更有帮助,因为我已经阅读过的所有内容都在句子中并且很难理解它的方式,如果有人能够记下算法可能会有所帮助了解。

7 个答案:

答案 0 :(得分:7)

这是一些进行合并的Python 2代码。

import heapq

def addtoheap(h, i, it):
    try:
        heapq.heappush(h, (next(it), i))
    except StopIteration:
        pass

def mergek(*lists):
    its = map(iter, lists)
    h = []
    for i, it in enumerate(its):
        addtoheap(h, i, it)
    while h:
        v, i = heapq.heappop(h)
        addtoheap(h, i, its[i])
        yield v

for x in mergek([1, 3, 5], [2, 4, 6], [7, 8, 9], [10]):
    print x

为什么是O(n log k)?好吧,对于每个删除的值,都有一个堆弹出和可能的堆推送(两者都是O(log k))。由于我们删除了n个元素,因此它是O(n log k)。

答案 1 :(得分:2)

不是简单地将每个列表的第一个元素存储在优先级队列中,而是将其包装在这样的结构中;

struct wrapper
{
    int list_number;
    int element;
}

然后,当您将元素推送到优先级队列时,只需添加列表编号表单。这样,当弹出最小元素时,您将通过检查popped_element.list_number来了解应该从哪个列表推送下一个元素以推送队列。

为了查找您的列表是否为空,您应该向其添加一个函数empty,如果列表中没有任何其他元素则返回true,否则返回false。该功能非常容易实现。只需检查大小是否为零,然后列表为空,否则它有一个或多个元素。

从您的问题我假设二进制堆用于实现优先级队列。二进制堆中的插入操作需要O(lg k)时间,而extract-min操作也需要O(lg k)次,其中k是堆的大小(在您的情况下为列表数)。现在,如果您拥有的元素总数为n,则处理所有元素的总时间将为O(n lg k)

答案 2 :(得分:2)

几年前,我在讨论对大文本文件进行排序时写了​​一系列文章。这个想法是你放在堆上的项目不仅包含值,还包含值来自的列表。或者,您可以将列表引用放在堆上,并将比较函数与特定列表中的第一项进行比较。

有关使用顺序列表代替堆的基本算法的说明,请参阅http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=676http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=677。有关使用堆的改进版本,请参阅http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=680

正如我在评论中所说,你也可以在没有堆的情况下进行合并。有关说明,请参阅https://stackoverflow.com/a/18984961/56778

答案 3 :(得分:0)

1个列表在没有更多元素时耗尽

2你需要跟踪min元素com的列表

每个元素的

3,你把它放入一个大小为k的最小堆中,这需要logk,所以你有n次logk

答案 4 :(得分:0)

你不应该在这里" n"所有列表中的节点总数不仅仅是一个列表。在这种情况下,解决方案是O(n logk)。如果我们的意思是我们在每个列表中平均有n个节点(总共k个列表)那么它将是O(nk logk)

Here是对某些代码的深入解释

答案 5 :(得分:0)

Paul Hankin的解决方案是正确的,但它有点难以阅读,特别是你想用c ++或java实现。我的解决方案类似于Paul的解决方案。如果使用c ++或java编写,则可能需要额外的数据结构来存储元素的值,第k个数组中元素的索引以及列表中数组的索引。

Element{
    int value;
    int idInArray,
    int idInList
}

但是在python中,我只存储在一个元组(value,idInArray,idInList)中

def mergeKArray(*lists):
    # implemented by min heap
    h = []
    r = []
    for k, arr in enumerate(lists):
        heapq.heappush(h, (arr[0], 0, k))
    while h:
        # min is the minimum element
        # i is the index of the min in the k-th array
        # k is the index of array in the list
        min, i, k = heapq.heappop(h)
        r.append(min)
        if i < len(lists[k]) - 1:
            i += 1
            heapq.heappush(h, (lists[k][i], i, k))
    return r

因为我只需要维护一个包含k个元素的最小堆,所以弹出或注入堆的时间是O(log k)。我还需要扫描所有n个元素,每个元素花费2 * log(k)时间来注入和弹出。因此,big-O是O(n * log k)。

答案 6 :(得分:0)

这是我使用c ++ stl的代码

#include<iostream>
#include<vector>
#include<climits>
#include<queue>
#define ROWS 4
#define COLS 8
using namespace std;

struct node
{
    int ele;
    int arr_no;
    int next_index; 
};

void printVector(vector<int> v)
{
    for(unsigned int i=0;i<v.size();i++)
        cout<<v[i]<<" ";
}

// THIS IS THE BASIS ON WHICH THE ELEMENTS OF A PRIORITY QUEUE ARE SORTED AND 
// KEPT IN THE QUEUE, HERE THE CRITERIA IS THAT THE NODE WITH SMALLER ELEMENT SHOULD
// COME ABOVE THE ONE WITH LARGER ELEMENT

class compare
{
    public:
        bool operator()(node& n1, node& n2)
        {
           if (n1.ele > n2.ele) 
                return true;
           else
                return false;
        }
};

vector<int> mergeKArrays(vector< vector<int> > v)
{
    int k = v.size();       // NUMBER OF LISTS
    int n = v.at(0).size(); //SIZE OF EACH LIST

    vector<int> result;
    //result.resize( n*k );

    priority_queue<node, vector<node>, compare> minHeap;
    for (int i = 0; i < k; i++)
    {
        node temp;
        temp.ele = v[i][0]; //STORE THE FIRST ELEMENT
        temp.arr_no = i;        //INDEX OF ARRAY
        temp.next_index = 1;    //INDEX OF NEXT ELEMENT TO BE STORED FROM ARRAY
        minHeap.push(temp);
    }

    // NOW ONE BY ONE GET THE MINIMUM ELEMENT FROM MIN
    // HEAP AND REPLACE IT WITH NEXT ELEMENT OF ITS ARRAY
    for (int count = 0; count < n*k; count++)
    {
        // GET THE MINIMUM ELEMENT AND STORE IT IN OUTPUT
        node min_ele_node = minHeap.top();
        minHeap.pop();      
        result.push_back(min_ele_node.ele);

        // FIND THE NEXT ELELEMENT THAT WILL REPLACE CURRENT
        // ROOT OF HEAP. THE NEXT ELEMENT BELONGS TO SAME
        // ARRAY AS THE CURRENT ROOT.
        node new_node;
        new_node.arr_no = min_ele_node.arr_no;
        if (min_ele_node.next_index < n)
        {
            new_node.ele = v.at(min_ele_node.arr_no)[min_ele_node.next_index];
            new_node.next_index = min_ele_node.next_index + 1;
        }
        // IF ROOT WAS THE LAST ELEMENT OF ITS ARRAY
        else 
        {
            new_node.ele =  INT_MAX; //INT_MAX IS FOR INFINITE
        }

        // REPLACE ROOT WITH NEXT ELEMENT OF ARRAY
        minHeap.push(new_node);
    }
    return result;
}


int main()
{
    int arr[ROWS][COLS] = 
                    { 
                        {10, 20, 30, 40, 50, 60, 71, 86},
                        {15, 25, 35, 45, 60, 69, 77, 78},
                        {27, 29, 37, 48, 50, 65, 75, 78},
                        {32, 33, 39, 50, 80, 133, 139, 150},
                    }; 

    vector< vector<int> > matrix ;

    for( int i=0 ; i < ROWS; i++)
    {
        vector<int> vec;
        for(int j=0; j < COLS; j++)
            vec.push_back(arr[i][j]);
        matrix.push_back(vec);
    }

    vector<int> result = mergeKArrays(matrix);
    printVector(result);
    return 0;
}