每天一道Code 23. 合并K个升序链表

在这里插入图片描述
在这里插入图片描述

代码思路:
最傻的方法应该就是设一个ans,每次用第i个链表和ans合并,并存储在ans中,每个链表都合并一次。
时间复杂度:设最长链表长度为n,所以第一次合并,ans也就是第一个链表,长度 n n n,第二次合并,ans长度为 2 n 2n 2n,依次合并,第i个链表合并完ans长度为 i n in in,每次合并都是遍历一次,求和为 ∑ i = 1 k i ⋅ n = k ( k − 1 ) 2 n \sum_{i=1}^{k}i·n=\cfrac{k(k-1)}{2}n i=1kin=2k(k1)n 故时间复杂度 O ( k 2 n ) O(k^2n) O(k2n)

代码就不写了,无非就是循环。

分治思想
一个一个往最终结果上合并时间太长,于是两两合并,链表数量就会从 k k k k 2 \cfrac{k}{2} 2k,再到 k 4 \cfrac{k}{4} 4k…直到最后合并为一个。
时间复杂度:第一次合并时,有 k 2 \cfrac{k}{2} 2k组链表参与两两合并,合并后每组长度为 2 n 2n 2n,因此第一次合并时间 k 2 2 n = k n \cfrac{k}{2}2n=kn 2k2n=kn。第二次合并有 k 4 \cfrac{k}{4} 4k组链表参与合并,合并后每组长度 4 n 4n 4n,第二次合并时间 k 4 4 n = k n \cfrac{k}{4}4n=kn 4k4n=kn
事实上,还有落单的链表,但是因为代码中可以直接返回也就意味着落单链表不会过多影响时间。
求和得:
∑ i = 1 ⌈ l o g 2 k ⌉ k 2 i 2 i n = k ⋅ n ⋅ l o g 2 k \sum_{i=1}^{\lceil log_2k\rceil}\cfrac{k}{2^i}2^in=k·n·log_2k i=1log2k2ik2in=knlog2k
即时间复杂度 O ( k ⋅ n ⋅ l o g 2 k ) O(k·n·log_2k) O(knlog2k)

优先队列
也可以使用优先队列实现,只不过有几个注意的点。
优先队列的时间复杂度:优先队列中元素个数不超过k个,则插入和删除元素的时间复杂度为 O ( l o g k ) O(log k) O(logk),每个节点都会进队出队一次,因此总时间复杂度 O ( k ⋅ n ⋅ l o g k ) O(k·n·logk) O(knlogk)
注意事项:

  1. 优先队列是由堆实现的,内部自含排序,因此优先队列的元素不能使用题中的节点指针,因为没法通过该指针实现排序。
  2. 优先队列默认是由大根堆实现的。
  3. 尽量不要用迭代器去访问vector,因为本来vector中存放的就是指针,再使用iterator访问,双指针,混乱无比,反正我是没绕过来弯。可以使用vector本身的性质也就是v[i]这种方式或者通过auto自动设置变量类型来遍历。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* pairMerge(ListNode *first,ListNode *second){
        //合并两个链表,懒得注释,看不懂建议先做简单题
        if(!first) return second;
        if(!second) return first;
        ListNode *dummy = new ListNode(-1);
        ListNode *head = dummy;
        while(first && second){
            if(first->val <= second->val){
                head->next=first;
                first=first->next;
            }
            else{
                head->next=second;
                second=second->next;
            }
            head=head->next;
        }
        head->next=(first?first:second);
        return dummy->next;
    }
    ListNode* merge(vector<ListNode*>& lists,int l,int r){
        //递归合并
        if(l == r) return lists[l];
        if(l > r) return nullptr;
        int mid = (l + r) >> 1;
        return pairMerge( merge(lists,l,mid), merge(lists,mid+1,r));
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //分治 主程序
        return merge(lists,0,lists.size()-1);
    }


    
};
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    struct Point{
        int val;
        ListNode *ptr;
        Point(int _val,ListNode *_ptr) : val(_val),ptr(_ptr) {}
        bool operator < (const Point &a)const{     //重载队列运算符
            return val > a.val;
        }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //优先队列 
        ListNode *deummy = new ListNode(-1);
        ListNode *head = deummy;
        priority_queue<Point> q;
        for(int i = 0; i < lists.size(); ++i){      //将每个链表的第一个节点加入队列
            if(lists[i]) q.push(Point(lists[i]->val,lists[i])); //用Point存节点的值以及指向节点的指针
        }
        while(!q.empty()){                          //队列不为空,意味着所有链表未处理完成
            Point node = q.top();
            q.pop();                               
            head->next = node.ptr;                  //指向队首节点
            head = head->next;
            if(node.ptr->next) q.push(Point(node.ptr->next->val,node.ptr->next));   //若队首节点的所在链表不为空,则当前链表的下一节点入队。
        }
        return deummy->next;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值