您的位置:首页 > 编程语言 > C语言/C++

算法导论第三版第六章 合并K个有序链表的三种解法(最小堆法和分治递归法)

2017-12-05 16:40 447 查看
题目要求是将k个有序链表合并为一个链表,时间复杂度限定为O(nlogk)。下面给出应用最小堆方法的两个程序,最后再贴上利用分治递归法的代码,虽然时间复杂度不及堆方法,但思路相对简单好理解。

(1)最小堆方法1

用一个大小为K的最小堆(用优先队列+自定义降序实现)(优先队列就是大顶堆,队头元素最大,自定义为降序后,就变成小顶堆,队头元素最小),先把K个链表的头结点放入堆中,每次取堆顶元素,然后将堆顶元素所在链表的下一个结点加入堆中。代码如下:

// kMeargetest.cpp : 定义控制台应用程序的入口点。
//
#include<vector>
#include<iostream>
#include<queue>
#include<set>
#include<functional>
using namespace std;

struct listnode {
int val;
listnode* next;
};

struct cmp {
bool operator()(listnode* a, listnode* b)
{
return a->val > b->val;
}
};
//利用最小堆方法
//用一个大小为K的最小堆(用优先队列+自定义降序实现)(优先队列就是大顶堆,队头元素最大,自定义为降序后,就变成小顶堆,队头元素最小),
//先把K个链表的头结点放入堆中,每次取堆顶元素,然后将堆顶元素所在链表的下一个结点加入堆中。
listnode* mergeklist(vector<listnode*> lists)
{
if (lists.size() == 0) return NULL;
priority_queue<int, vector<listnode*>, cmp> heap;
for (int i = 0; i < lists.size(); ++i)
{
heap.push(lists[i]);
}

listnode* newhead = NULL;
listnode* p = NULL;
listnode* q = NULL;
while (!heap.empty())
{
q = heap.top();
heap.pop();
if (q->next != NULL) heap.push(q->next);
if (newhead == NULL)
{
newhead = q;
p = q;
}
else {
p->next = q;
p = p->next;
}

}
return newhead;
}
listnode* createlistnode(int value)
{
listnode* pnode = new listnode();
pnode->val = value;
pnode->next = NULL;
return pnode;
}

void destroylist(listnode* phead)
{
listnode* pnode = phead;
while (pnode != NULL)
{
phead = phead->next;
delete pnode;
pnode = phead;
}
}

void connectlistnode(listnode* pcurrent, listnode* pnext)
{
if (pcurrent == NULL)
{
cout << "error to connect two nodes" << endl;
exit(1);
}
pcurrent->next = pnext;
}

int main()
{
vector<listnode*> lists;
listnode* pNode1 = createlistnode(1);
listnode* pNode2 = createlistnode(2);
listnode* pNode3 = createlistnode(3);
listnode* pNode4 = createlistnode(4);

listnode* pNode5 = createlistnode(2);
listnode* pNode6 = createlistnode(3);
listnode* pNode7 = createlistnode(4);
listnode* pNode8 = createlistnode(5);

listnode* pNode9 = createlistnode(6);
listnode* pNode10 = createlistnode(7);
listnode* pNode11 = createlistnode(8);
listnode* pNode12 = createlistnode(9);

connectlistnode(pNode1, pNode2);
connectlistnode(pNode2, pNode3);
connectlistnode(pNode3, pNode4);

connectlistnode(pNode5, pNode6);
connectlistnode(pNode6, pNode7);
connectlistnode(pNode7, pNode8);

connectlistnode(pNode9, pNode10);
connectlistnode(pNode10, pNode11);
connectlistnode(pNode11, pNode12);

listnode* L1 = pNode1;
listnode* L2 = pNode5;
listnode* L3 = pNode9;
cout << "链表l1:";
while (L1)
{
cout << L1->val << " ";
L1 = L1->next;
}
cout << endl;
cout << "链表l2: ";
while (L2)
{
cout << L2->val << " ";
L2 = L2->next;
}
cout << endl;

cout << "链表l3: ";
while (L3)
{
cout << L3->val << " ";
L3 = L3->next;
}
cout << endl;
lists.push_back(pNode1);
lists.push_back(pNode5);
lists.push_back(pNode9);
listnode* res = mergeklist(lists);

cout << "合并后链表:";
while (res)
{
cout << res->val << " ";
res = res->next;
}
cout << endl;
system("pause");
destroylist(res);
return 0;
}
执行结果:



(2)最小堆方法2

LeetCode讨论上的一种方法,感觉代码比较简洁巧妙。思路同上,代码如下:

struct cmp {
bool operator()(ListNode *a, ListNode *b) {
return a->val > b->val;
}
};

ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode *head = nullptr;
ListNode **ppcur = &head;

priority_queue<ListNode *, vector<ListNode *>, cmp> pq;
for (auto list : lists) {
if (list)
pq.emplace(list);
}

while (!pq.empty()) {
auto cur = pq.top(); pq.pop();
*ppcur = cur;
if (cur->next) {
pq.emplace(cur->next);
}
ppcur = &(*ppcur)->next;
}
return head;
}
(3)分治递归法

用vector存放k个链表,然后每次都通过合并vector中前两个链表再push_back到容器,然后将合并过的两个链表erase。重复进行,直到vector中只剩下最后合并完的大链表。代码如下:

ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.empty()){
return nullptr;
}
while(lists.size()>1)
{
lists.push_back(mergeTwoLists(lists[0],lists[1]));
lists.erase(lists.begin());
lists.erase(lists.begin());
}
return lists.front();

}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {//两两合并,每次取最大的头元素放入合并链表中

ListNode* head1=new ListNode(0);
ListNode* pre=head1;
int val1,val2;
while(l1!=NULL||l2!=NULL)
{
val1=INT_MAX;
val2=INT_MAX;
if(l1!=NULL)
{ val1=l1->val; }
else{pre->next=l2;break;}
if(l2!=NULL)
{ val2=l2->val; }
else{pre->next=l1;break;}
if(val1>val2)
{
pre->next=l2;
l2=l2->next;
}
else{ pre->next=l1;l1=l1->next;}
pre=pre->next;
}
return head1->next;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息