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

题目要求是将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<listnode*, 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;
       
    }

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值