Algorithmic Design and Analysis (16)
Topic: Merge k Sorted Lists
Description of the problem: Merge K sorted linked lists and return it as one sorted list. Analyse and describe its complexity.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists)
{
}
};
Algorithmic ideas:
The requirement of the title is to synthesize k ordered lists into one list. In order to minimize the complexity, we need to use some data structures as an aid.
(1) First of all, we already have k ordered lists. So in order to synthesize a list, each time we need to remove the largest node from the k list and put it at the end of the new list. Therefore, in order to avoid multiple comparisons to find the largest node, we should think of using the data structure of the large root heap. The time to generate a large root heap of size n is O(n), and the time to take out and put in each time is O(log2 n).
(2) Using large root heap data structure, we can consider using priority_queue directly in STL. To this end, we need to introduce a comparison function:
struct compare
{
bool operator()(const ListNode* l1, const ListNode* l2)
{
if (l1 == NULL || l2 == NULL)
return true;
return l1->val > l2->val;
}
};
(3) When we put the head of the k-linked list into priority_queue, we can directly use top () operation to remove the largest node. Then the next node of the extracted node is placed in the priority_queue. Because k-linked lists are ordered, this algorithm ensures that each node removed from the priority_queue is the largest node currently. Therefore, we ensure the correctness of using priority_queue.
(4) In specific operations:
(a) First initialize priority_queue, where you need to ensure that the pointer in priority_queue is not NULL:
priority_queue<ListNode*, vector<ListNode*>, compare> pque;
for (int i = 0; i < lists.size(); ++i)
if (lists[i] != NULL)
pque.push(lists[i]);
b) If the priority_queue is empty, return the null pointer directly:
if (pque.empty())
return NULL;
c) We only use three pointers to point to the head and tail of the new list and the nodes taken out of the pque, and initialize them:
ListNode *tail, *head, *node;
head = pque.top();
pque.pop();
tail = head;
node = tail;
d) At last, the algorithm is judged whether the Pque is empty or not. If it is not empty and the next node of node is not empty, it can be put into pque. Then continue to remove the most node from the Pque and put it at the end of the new list, so that the loop:
while (!pque.empty())
{
if (node->next != NULL)
pque.push(node->next);
node = pque.top();
pque.pop();
tail->next = node;
tail = tail->next;
}
e) Finally, return the head of the new list.
Implementation code
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
struct compare
{
bool operator()(const ListNode* l1, const ListNode* l2)
{
return l1->val > l2->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists)
{
priority_queue<ListNode*, vector<ListNode*>, compare> pque;
for (int i = 0; i < lists.size(); ++i)
if (lists[i] != NULL)
pque.push(lists[i]);
ListNode *tail, *head, *node;
if (pque.empty())
return NULL;
head = pque.top();
tail = head;
node = tail;
pque.pop();
while (!pque.empty())
{
if (node->next != NULL)
pque.push(node->next);
node = pque.top();
pque.pop();
tail->next = node;
tail = tail->next;
}
return head;
}
int main()
{
ListNode *l1 = NULL, *l2 = NULL;
vector<ListNode*> lists;
lists.push_back(l1);
lists.push_back(l2);
ListNode* res = mergeKLists(lists);
return 0;
}