Leetcode: 23. Merge k Sorted Lists (week1 --- hard)
阿新 • • 發佈:2018-11-10
Merge k Sorted Lists:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
題意:
把k個已經排好序的佇列重新排成一個完整的佇列
題解:
這一道題的含義就跟歸併排序的合併步驟一樣,也就是有序佇列的合併。
- 一開始想直接進行k的佇列的每一次尋找最小值然後進行合併,但是看到了這一道題的難度是hard,所以上面的演算法的效率是非常低的:設元素的數目為N個,所以大概的比較次數為k*N,也就是每個元素的都需要進行k次比較,時間消耗一定會比較巨大,所以這種方法是行不通的。
- 這一道題是在搜尋分治演算法的時候找到的,但是覺得這一道題大概跟分治的關係也就是前面所提到的?所以可能沒有分治思維的方法。
- 然後就開始考慮進行排序演算法,排序演算法最快的速度也就是nlogn,效率還不如尋找最大值。
- 在思考排序演算法的時候剛好想到了堆排序,想起了之前資料結構老師說的堆排序是很重要的,然後想到了建堆的步驟,以及插入和刪除,而這個合併有序序列只需要提供最小值也就可以了,所以用小頂堆是一個不錯的演算法,因為一開始建堆需要的比較次數為klogk,然後每一次刪除以及插入所需要的比較次數是logk,所以總的複雜度是Nlogk,比起k*N算是一個不錯的進步了,特別是k很多的時候體現會更好。
所以解題的程式碼如下:
class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { //獲取向量的大小 length = lists.size(); //利用迭代器刪除向量中的空元素,從測例中發現的[[]]等樣例 vector<ListNode*>::iterator it = lists.begin(); for(;it != lists.end();) { if(*it == nullptr) { it = lists.erase(it); length--; } else //迭代器指向下一個元素位置 ++it; } //不存在元素時返回空值 if(length == 0){ return nullptr; } //只有一個非空有序序列時返回該序列的指標 if(length == 1){ return lists[0]; } //建堆 for (int i = length / 2 - 1; i >= 0; i--) MinHeapDown(lists, i, length); //宣告結果頭節點 ListNode * a = new ListNode(lists[0]->val); //宣告輔助節點 ListNode * temp = a; while(length != 1){ MinHeapDelete(lists); if(delete_->next != nullptr){ MinHeapAdd(lists, length, delete_->next); } temp->next = new ListNode(lists[0]->val); temp = temp->next; } //用於只剩一個或者兩個有序序列時的元素的補充(因為有可能另一個被刪除了,但是還剩餘元素) if(length == 1){ lists[0] = lists[0]->next; while(delete_->next != NULL && lists[0] != NULL){ if(delete_->next->val > lists[0]->val){ temp->next = new ListNode(lists[0]->val); temp = temp->next; lists[0] = lists[0]->next; } else{ temp->next = new ListNode(delete_->next->val); temp = temp->next; delete_ = delete_->next; } } while(lists[0] != NULL){ temp->next = new ListNode(lists[0]->val); temp = temp->next; lists[0] = lists[0]->next; } while(delete_->next != NULL){ temp->next = new ListNode(delete_->next->val); temp = temp->next; delete_ = delete_->next; } } return a; }; //新增新元素 void MinHeapAdd(vector<ListNode*>& lists, int n, ListNode * new_){ length++; lists[length - 1] = new_; MinHeapUp(lists, length - 1); } //刪除元素 void MinHeapDelete(vector<ListNode*>& lists){ length--; swap(lists[0], lists[length]); delete_ = lists[length]; MinHeapDown(lists, 0, length); } //新增新的元素(新增到尾部後再進行重新排序) void MinHeapUp(vector<ListNode*>& lists, int n){ int j; ListNode* temp = lists[n]; j = (n - 1) / 2; while(j >= 0 && n != 0){ if (lists[j]->val <= temp->val) break; lists[n] = lists[j]; n = j; j = (n - 1) / 2; } lists[n] = temp; } //堆排序的下沉演算法(用於堆的建立以及元素的刪除) void MinHeapDown(vector<ListNode*>& lists, int i, int n){ int j; ListNode* temp = lists[i]; j = 2 * i + 1; while(j < n){ if(j + 1 < n && lists[j + 1]->val < lists[j]->val) j++; if(lists[j]->val > temp->val) break; lists[i] = lists[j]; i = j; j = 2 * i + 1; } lists[i] = temp; } //用於本地的程式碼測試(輸出結果) void print(ListNode *result){ ListNode * temp = result; while(temp != NULL && temp->next!=NULL){ cout << temp->val << " -> "; temp = temp->next; } if(temp == NULL) return ; cout << temp->val << endl; } private: //用於儲存向量的大小 int length; //用於儲存最近的被刪除的節點(為了記錄需要重新加入的元素的上一個結點) ListNode * delete_; };
在寫這一部分程式碼的時候還是有點手生的,然後參考了一下https://blog.csdn.net/hrn1216/article/details/51465270(最小堆 構建、插入、刪除的過程圖解)這一篇部落格,因為有圖解看起來會比較好懂些。
後記:
在disscuss中查找了一下min heap C++之後看到了一個
Beats 100% method, using min heap by make_heap, pop_heap, push_heap
的答案,看了一下原來STL還自帶提供了min heap的有關的函式方法,而且還優化的這麼好,所以就學習了一波。
- make_heap(實際上是用 priority_queue 來實現的)
default (1) template <class RandomAccessIterator> void make_heap (RandomAccessIterator first, RandomAccessIterator last);
custom (2) template <class RandomAccessIterator, class Compare> void make_heap (RandomAccessIterator first, RandomAccessIterator last, Compare comp );
Rearranges the elements in the range[first,last)
in such a way that they form a heap.
A heap is a way to organize the elements of a range that allows for fast retrieval of the element with the highest value at any moment (with pop_heap), even repeatedly, while allowing for fast insertion of new elements (with push_heap).
The element with the highest value is always pointed by first. The order of the other elements depends on the particular implementation, but it is consistent throughout all heap-related functions of this header.
- push_heap 以及 pop_heap 詳見 http://www.cplusplus.com/reference/algorithm/