一道编程题——合并K个有序链表

题目描述与分析

合并 k 个已排序的链表并将其作为一个已排序的链表返回。这与之前遇到的合并两个有序链表为同类型的题目。

这里提供两种方法:

  1. 很显然,合并多个有序链表是合并两个有序链表的拓展情况,因此,可以用合并两个链表的方法,依次两两合并,使得链表最终为1。合并过程类似归并排序的归并过程。
  2. 设置一个大小为K的最小堆,堆中的节点为K个链表的头节点,每次取出最小值(堆顶),然后将这个最小值的下一个节点加入链表(如果有的话),直到堆为空。

一般数据结构堆的实现方式采用优先队列,队列中的元素有一个优先值,每次pop元素的时候,会取出优先值最小的元素。加入新元素的时候也会重新排序。

优先队列中元素排列顺序采用堆的层次遍历,由于堆中的父节点均小于子节点,这在优先队列中的表现形式为第一个元素(堆顶)小于第二个元素(左子)和第三个元素(右子)。第K个元素小于第2K和第2K+1个元素。(这里指的是第几个元素,由于很多编程语言中队列首元素从0开始,左右孩子节点计算起来略有不同)

代码实现

python实现

归并

# 迭代,两两合并
class Solution:
    def mergeKLists(self , lists ):
        # write code here
        if not lists:return None
        # 每次取队首两个链表合并,合并之后加入到队尾
        while(len(lists)>1):
            first_link = lists.pop(0)
            second_link = lists.pop(0)
            merged_link = self.merge(first_link, second_link)
            # 返回值不是None则加入到队尾
            if merged_link:
                lists.append(merged_link)
        if not lists:return None
        return lists[0]
    
    # 两两链表合并
    def merge(self, link1, link2):
        if (not link1) and (not link2):
            return None
        root = ListNode(0)
        tmp = root
        while(link1 and link2):
            if link1.val <=link2.val:
                tmp.next = link1
                link1 = link1.next
            else:
                tmp.next = link2
                link2 = link2.next
            tmp = tmp.next
        tmp.next = link1 if link1 else link2
        return root.next

有序数组

class Solution:
    def mergeKLists(self , lists ):
        # write code here
        queue = [link for link in lists if link]
        if not queue:
            return None
        # 这里用有序数组替代了最小堆
        queue.sort(key=lambda x:x.val)
        # 建立一个虚拟首节点
        root = ListNode(0)
        tmp = root
        while queue:
            # 取最小值,加入到有序链表
            tmp.next = queue.pop(0)
            tmp = tmp.next
            if tmp.next:
                queue.append(tmp.next)
                queue.sort(key=lambda x:x.val)
        return root.next

C++实现

归并

class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        if(lists.size() == 0) return nullptr;
        ListNode* root = lists[0];
        for(int i=1; i < lists.size();i++){
            root = merge(root, lists[i]);
        }
        return root;
    }
    // 合并两个有序链表,返回新链表的头节点 
    ListNode* merge(ListNode* l1, ListNode* l2){
        if(l1==NULL)return l2;
        if(l2==NULL)return l1;
        ListNode* root = new ListNode(0);
        ListNode* tmp = root;
        while(l1!=NULL && l2!=NULL){
            if(l1 -> val <= l2 -> val){
                tmp -> next = l1;
                l1 = l1 -> next;
            }
            else{
                tmp -> next = l2;
                l2 = l2 -> next;
            }
            tmp = tmp->next;
        }
        if(l1!=NULL) tmp -> next = l1;
        else tmp -> next = l2;
        return root -> next;
    }
};

class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        ListNode L(0);
        ListNode *p = &L;
        vector<ListNode *> v;
         
        int n = lists.size();
        for(int i=0;i<n;i++)
            if(lists[i] != NULL)
                v.push_back(lists[i]);
        // 建堆
        make_heap(v.begin(),v.end(),cmp);
        while(v.size())
        {
            // 取堆中最小元素
            p -> next = v.front();
            pop_heap(v.begin(),v.end(),cmp);
            v.pop_back();
            p = p -> next;
            if(p -> next)
            {
                v.push_back(p -> next);
                push_heap(v.begin(),v.end(),cmp);
            }
        }
        return L.next;
    }
    static bool cmp(ListNode *L1,ListNode *L2)
    {
        return L1 -> val > L2 -> val;
    }
};

  上一篇
hexo加速计划 hexo加速计划
github服务器在国外,国内访问速度很不稳定,导致部署在GitHub Page上的hexo页面加载很慢,严重影响了访问体验,因此,本文介绍了一些加速访问GitHub page的方法。
2020-03-30
下一篇 
一道编程题——寻找环的入口节点 一道编程题——寻找环的入口节点
对于一个给定的链表,返回链表中环的入口节点,如果没有环,则返回null,给出一个不利用额外空间的解法。
2020-03-02
   目录