我的算法类中有人要求我做一个K-way合并算法O(nlogk)
在搜索之后,我发现它可以通过制作k长度优先级队列并将其与每个列表的第一个元素一起排队来完成。提取最小值,将其附加到结果并从已提取元素的列表中排队。
我很困惑:
如何知道特定列表何时耗尽,假设a list的元素比其他列表中的其他元素小?
如何知道元素是哪个列表(如果结构不用于定义)?
时间复杂度如何O(nlogk)
?
修改
如果有人可以逐步记下算法,那将会更有帮助,因为我已经阅读过的所有内容都在句子中并且很难理解它的方式,如果有人能够记下算法可能会有所帮助了解。
答案 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=676和http://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;
}