Leetcode23答案與解析 - Merge k Sorted Lists(cpp)

原題

Leetcode原題傳送門

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

Example:

Input:
[
  1->4->5,
  1->3->4,
  2->6
]
Output: 1->1->2->3->4->4->5->6

解析

大體上有兩種思路:

思路一:divide and conquer

根據divide-and-conquer思想,將k路合併的歸併排序問題,化歸爲2路合併兩個子串(分別再對其k路合併)的問題,迭代解決。主要思想:

k路和並(n個list)=2路合併(k路合併(n/2個list), k路合併(n/2個list))

有些細節問題:1個列就直接輸出了

假設list平均長度爲n,總共有k個數列,則:

時間複雜度 O(knlog(k))
空間複雜度 O(1)

 解釋:

時間複雜度:8個子串要分成4, 2, 1個字串才能解決問題,再重新向上merge。所以,對於每個列表中的元素來說,我最多要對其做\lceil log_2(n) \rceil次merge的過程,並且總共kn個元素。將兩項相乘,算法複雜度由此可得。

空間複雜度:若不要求不能修改輸入的鏈表,實際上可以在原來的鏈表上直接操作(或複製一份,這個複雜度就不計入算法啦)。2路歸併排序中,將指針重新安排就行,不需要額外空間。這裏,不考慮遞歸過程中一些瑣碎的變量所佔的空間。

思路二: priority queue

直接硬槓k路歸併,維護一個大小爲k的優先隊列。開始時,存入所有隊列第一個元素。然後不斷取出Listnode(優先隊列保證是當前隊列中最小的元素),加入輸出的列表。並且:

  1. 若該Listnode不爲原數列中最後一個元素,將原數列的下一個Listnode加入優先隊列中
  2. 若未最後一個,啥都不做

不斷循環直到優先隊列爲空。這個過程很好理解,總結爲一句話:不斷拿出所有隊列頭上元素中最小的。因爲我隊列是遞增的,那麼我一個隊列頭上的元素保證是隊列中最小的,那頭上元素最小的則爲剩下元素中最小的。

假設list平均長度爲n,總共有k個數列,則:

時間複雜度 O(knlog(k))
空間複雜度 O(1)

還是一樣,優先隊列取一個元素和放一個元素複雜度都爲O(log(k)),總共有kn個元素。所以算法複雜度=O(kn*log(k)* 2)=O(knlog(k))。空間複雜度和思路一同理。

思考

這道題難關主要在於手動實現一個Priority Queue。其實最小堆的思想很簡單,上手很快,不復雜。這道題被分爲hard可能就是因爲這個原因吧(然而我覺得並不hard)。

運行速度

我只實現了思路一:

運行速度:16ms | 超過99.98%的cpp程序

 

答案

1.思路一的代碼:discussion

Note: discussion裏的思路一的代碼好像不太高效,用了vector再去存新的List。然而完全沒必要,有興趣的讀者可以自己實現一個更加高效的版本。

2.思路二的代碼(博主實現的):

class PriorityQueue {
public:
vector<ListNode*> nodes;
int size;
PriorityQueue(int listNum);
void push(ListNode* objNode);
ListNode* pop();
int isEmpty();
};

PriorityQueue::PriorityQueue(int listNum) : nodes(vector<ListNode*>(listNum + 1)), size(0) {}


void PriorityQueue::push(ListNode* objNode) {
    int nowNode = this->size + 1;
    this->size += 1;
    nodes[this->size] = objNode;
    while ((nowNode / 2 != 0) && (this->nodes[nowNode / 2]->val
     > this->nodes[nowNode]->val)) {
        ListNode* tmp;
        tmp = this->nodes[nowNode];
        this->nodes[nowNode] = this->nodes[nowNode / 2];
        this->nodes[nowNode / 2] = tmp;
        nowNode = nowNode / 2;
    }
}

ListNode* PriorityQueue::pop() {
    ListNode* retNode = this->nodes[1];
    int nowIndex = 1;
    this->nodes[1] = this->nodes[this->size];
    this->size -= 1;

    for (;;) {
        int leftIndex = nowIndex * 2;
        int rightIndex = nowIndex * 2 + 1;
        int toIndex = 0;
        int toSwap = 0;
        int nowVal = this->nodes[nowIndex]->val;
        if ((leftIndex <= this->size) && (rightIndex <= this->size)) {
            int leftVal = this->nodes[leftIndex]->val;
            int rightVal = this->nodes[rightIndex]->val; 
            if (leftVal < rightVal) {
                if (leftVal < nowVal) {
                    toSwap = 1;
                    toIndex = leftIndex;
                }
            } else {
                if (rightVal < nowVal) {
                    toSwap = 1;
                    toIndex = rightIndex;
                }
            }
        } else if ((leftIndex <= this->size) && (this->nodes[leftIndex]->val < 
            this->nodes[nowIndex]->val)) {
            toSwap = 1;
            toIndex = leftIndex;
        } else if ((rightIndex <= this->size) && (this->nodes[rightIndex]->val < 
            this->nodes[nowIndex]->val)) {
            toSwap = 1;
            toIndex = rightIndex;
        }
        if (toSwap == 1) {
            ListNode* tmp;
            tmp = this->nodes[nowIndex];
            this->nodes[nowIndex] = this->nodes[toIndex];
            this->nodes[toIndex] = tmp;
            nowIndex = toIndex;
        } else break;
    }
    return retNode;
}

int PriorityQueue::isEmpty() {
	if (this->size == 0) {
		return 0;
	} else {
		return 1;
	}
}

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* retList = NULL;
        ListNode* nowListPointer = NULL;
        PriorityQueue* pQueue = new PriorityQueue(lists.size());
        if (lists.size() == 0) return NULL;
        for (int i = 0; i < lists.size(); ++i) {
        	if (lists[i] != NULL) pQueue->push(lists[i]);
        }
        while (pQueue->isEmpty() == 1) {
            ListNode* currentNode = pQueue->pop();
            if (retList == NULL) {
                retList = currentNode;
                nowListPointer = currentNode;
            } else {
                nowListPointer->next = currentNode;
                nowListPointer = nowListPointer->next;
            }
            if (currentNode->next) pQueue->push(currentNode->next);
        }
        return retList;
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章