Problem:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Iterative Solution:
/**
* 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) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
vector<ListNode *> sol;
if (lists.size() == 0)
return NULL;
while (lists.size() - 1) {
mergeTwo(lists[0], lists[1]);
lists.erase(lists.begin() + 1);
}
return lists[0];
}
void mergeTwo(ListNode* &first, ListNode* &second) {
ListNode *temp=NULL, *temp2=NULL, *prev=NULL, *iter = first;
if (first == NULL) {
first = second;
return;
}
while(second!=NULL) {
if (iter->val > second->val) {
if (prev != NULL) {
// Insert into first, but not in beginning
temp2=second->next;
prev->next = second;
second->next = iter;
iter = second;
second = temp2;
} else {
// Insert into first at the beginning
temp = first;
temp2 = second->next;
first = second;
second->next = temp;
second = temp2;
}
} else if (iter->next != NULL) {
prev = iter;
iter = iter->next;
} else if (iter->next == NULL) {
iter->next = second;
second = NULL;
}
}
}
};
Recursive Solution by Runhe Tian:
{align = "center"}
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Iterative Solution:
/**
* 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) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
vector<ListNode *> sol;
if (lists.size() == 0)
return NULL;
while (lists.size() - 1) {
mergeTwo(lists[0], lists[1]);
lists.erase(lists.begin() + 1);
}
return lists[0];
}
void mergeTwo(ListNode* &first, ListNode* &second) {
ListNode *temp=NULL, *temp2=NULL, *prev=NULL, *iter = first;
if (first == NULL) {
first = second;
return;
}
while(second!=NULL) {
if (iter->val > second->val) {
if (prev != NULL) {
// Insert into first, but not in beginning
temp2=second->next;
prev->next = second;
second->next = iter;
iter = second;
second = temp2;
} else {
// Insert into first at the beginning
temp = first;
temp2 = second->next;
first = second;
second->next = temp;
second = temp2;
}
} else if (iter->next != NULL) {
prev = iter;
iter = iter->next;
} else if (iter->next == NULL) {
iter->next = second;
second = NULL;
}
}
}
};
Recursive Solution by Runhe Tian:
class Solution { public: ListNode *mergeKLists(vector<ListNode *> &lists) { ListNode *head = NULL; for(int i = 0; i < lists.size(); ++i) head = mergeTwoLists(head, lists[i]); return head; } private: ListNode *mergeTwoLists(ListNode *head1, ListNode *head2) { if (head1 == NULL || head2 == NULL) return head1 == NULL ? head2 : head1; if (head1 -> val < head2 -> val) { head1 -> next = mergeTwoLists(head1 -> next, head2); return head1; } else { head2 -> next = mergeTwoLists(head2 -> next, head1); return head2; } } };======== Using min heap (make_heap) =====class Solution { public: struct comp { bool operator() (ListNode *a, ListNode *b) { int first = (a != NULL ? a -> val: -1); int second = (b != NULL ? b -> val: -1); return first > second; } }; ListNode* mergeKLists(vector<ListNode*>& lists) { ListNode *ans = NULL, *head = NULL; make_heap(lists.begin(), lists.end(), comp()); while (!lists.empty()) { ListNode *node = lists.front(); pop_heap(lists.begin(), lists.end(), comp()); lists.pop_back(); if (node == NULL) { continue; } if (ans == NULL) { ans = new ListNode(node -> val); head = ans; } else { ans -> next = new ListNode(node -> val); ans = ans -> next; } if (node -> next) { lists.push_back(node -> next); push_heap(lists.begin(), lists.end(), comp()); } } return head; } };======== Priority queue by bangbingsyb ====class Solution { public: struct compNode { bool operator()(ListNode *p, ListNode *q) const { return p->val>q->val; } }; ListNode *mergeKLists(vector<ListNode *> &lists) { priority_queue<ListNode*, vector<ListNode*>, compNode> pq; ListNode *dummy = new ListNode(0), *tail = dummy; for(int i=0; i<lists.size(); i++) if(lists[i]) pq.push(lists[i]); while(!pq.empty()) { tail->next = pq.top(); tail = tail->next; pq.pop(); if(tail->next) pq.push(tail->next); } return dummy->next; } };============== Another attempt ==========/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class comp { public: bool operator()(const ListNode *lhs, const ListNode *rhs) { int first = (lhs != NULL ? lhs -> val: INT_MIN); int second = (rhs != NULL ? rhs -> val: INT_MIN); return first > second; } }; class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue<ListNode*, vector<ListNode*>, comp> pq; ListNode *dummy = new ListNode(INT_MIN); ListNode *prev = dummy; for (auto node : lists) { pq.push(node); } while (!pq.empty()) { ListNode *node = pq.top();pq.pop(); if (node == NULL) { continue; } dummy -> next = node; if (node -> next != NULL) { pq.push(node -> next); } dummy = dummy -> next; } return prev -> next; } };============ Another ========/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ struct comp { bool operator()(ListNode *a, ListNode *b) { return a -> val > b -> val; } }; class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { ListNode *head = NULL, *trav = NULL; if (lists.size() == 0) { return head; } priority_queue<ListNode *, vector<ListNode *>, comp> pq; for (auto list : lists) { if (list != NULL) { pq.push(list); } } while (!pq.empty()) { ListNode *node = pq.top(); pq.pop(); if (node -> next != NULL) { pq.push(node -> next); } if (trav == NULL) { head = node; trav = head; } else { trav -> next = node; trav = trav -> next; } } return head; } };======== Leetcode explanation =======Solution
Approach 1: Brute Force
Intuition & Algorithm
- Traverse all the linked lists and collect the values of the nodes into an array.
- Sort and iterate over this array to get the proper value of nodes.
- Create a new sorted linked list and extend it with the new nodes.
As for sorting, you can refer here for more about sorting algorithms.
Complexity Analysis
- Time complexity : where is the total number of nodes.
- Collecting all the values costs time.
- A stable sorting algorithm costs time.
- Iterating for creating the linked list costs time.
- Space complexity : .
- Sorting cost space (depends on the algorithm you choose).
- Creating a new linked list costs space.
Approach 2: Compare one by one
Algorithm
- Compare every nodes (head of every linked list) and get the node with the smallest value.
- Extend the final sorted linked list with the selected nodes.
1 / 11
Complexity Analysis
- Time complexity : where is the number of linked lists.
- Almost every selection of node in final linked costs ( times comparison).
- There are nodes in the final linked list.
- Space complexity :
- Creating a new linked list costs space.
- It's not hard to apply in-place method - connect selected nodes instead of creating new nodes to fill the new linked list.
Approach 3: Optimize Approach 2 by Priority Queue
Algorithm
Almost the same as the one above but optimize the comparison process by priority queue. You can refer here for more information about it.
Complexity Analysis
- Time complexity : where is the number of linked lists.
- The comparison cost will be reduced to for every pop and insertion to priority queue. But finding the node with the smallest value just costs time.
- There are nodes in the final linked list.
- Space complexity :
- Creating a new linked list costs space.
- The code above present applies in-place method which cost space. And the priority queue (often implemented with heaps) costs space (it's far less than in most situations).
Approach 4: Merge lists one by one
Algorithm
Complexity Analysis
- Time complexity : where is the number of linked lists.
- We can merge two sorted linked list in time where is the total number of nodes in two lists.
- Sum up the merge process and we can get: .
- Space complexity :
- We can merge two sorted linked list in space.
Approach 5: Merge with Divide And Conquer
Intuition & Algorithm
This approach walks alongside the one above but is improved a lot. We don't need to traverse most nodes many times repeatedly
- Pair up lists and merge each pair.
- After the first pairing, lists are merged into lists with average length, then , and so on.
- Repeat this procedure until we get the final sorted linked list.
Thus, we'll traverse almost nodes per pairing and merging, and repeat this procedure about times.

Complexity Analysis
- Time complexity : where is the number of linked lists.
- We can merge two sorted linked list in time where is the total number of nodes in two lists.
- Sum up the merge process and we can get:
- Space complexity :
- We can merge two sorted linked lists in space.
No comments:
Post a Comment