leetcode23 合并K个排序链表(频繁考)

题目

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

 

思路1:二分递归

经典k路归并,这里采用二分递归

时间复杂度:

归并两个有序数组时间复杂度为O(n),将K个有序数组分治归并时间复杂度为O(logk),算法整体时间复杂度为O(nlogk)

/**
 * 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) {
        if(lists.empty()) return NULL;
        return mergeK(lists,0,lists.size()-1);
    }

    ListNode* mergeK(vector<ListNode*>& lists,int l,int r){
        if(l > r) return NULL;
        if(l == r) return lists[l];
        if(l+1 == r) return merge(lists[l],lists[r]);
        int mid = (l+r)/2;
        ListNode* left = mergeK(lists,l,mid);
        ListNode* right = mergeK(lists,mid+1,r);
        return merge(left,right);
    }

    ListNode* merge(ListNode* l,ListNode* r){
        ListNode *dummy_head = new ListNode(-1),*cur = dummy_head;
        while(l && r){
            if(l->val <= r->val){
                cur->next = l;
                l = l->next;
            }else{
                cur->next = r;
                r = r->next;
            }
            cur = cur->next;
        }
        if(l) cur->next = l;
        else if(r) cur->next = r;
        return dummy_head->next;
    }
};

 

思路2:堆

k大小的堆,时间复杂度也是nlogk

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
struct cmp{
    bool operator()(ListNode* h1,ListNode* h2){
        return h1->val > h2->val;
    }
};
 
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.empty()) return NULL;
        if(lists.size() < 2) return lists[0];
 
        priority_queue<ListNode*,vector<ListNode*>,cmp> q;
        ListNode* dummy_head = new ListNode(-1);
        ListNode* pre = dummy_head,*cur;
 
        for(auto &node:lists){
            if(node) q.push(node);
        }
 
        while(!q.empty()){
            cur = q.top(); q.pop();
            pre->next = cur;
            pre = pre->next;
            if(cur->next) q.push(cur->next);
        }
        return dummy_head->next;
    }
};
 

 

相似题目:632. 最小区间

你有 k 个升序排列的整数数组。找到一个最小区间,使得 k 个列表中的每个列表至少有一个数包含在其中。

我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小。

示例 1:

输入:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
输出: [20,24]
解释: 
列表 1:[4, 10, 15, 24, 26],24 在区间 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在区间 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在区间 [20,24] 中。
注意:

给定的列表可能包含重复元素,所以在这里升序表示 >= 。
1 <= k <= 3500
-105 <= 元素的值 <= 105

思路

使用k路归并,堆来做,事实上每一次归并前的k个就是一个区间

这题实现起来比较难,因为是数组,不是链表,所以用堆的时候,堆内放置

(数值(用来排序),来自哪个数值)的二元组

同时利用index数组,记录每个数组下一次需要访问的下标

// 小顶堆
struct cmp{
    bool operator()(const pair<int,int> &a,const pair<int,int> &b){
        return a.first > b.first;
    }
};

class Solution {
public:
    vector<int> smallestRange(vector<vector<int>>& nums) {
        int n = nums.size(),cur_Max = INT_MIN;
        // 记录每个数组下一次需要访问的下标
        vector<int> index(n,1);
        // 小顶堆
        priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> q;
        for(int i = 0;i < n;i++){
            q.push({nums[i][0],i});
            cur_Max = max(cur_Max,nums[i][0]);
        }

        vector<int> res{q.top().first,cur_Max};
        // 没有一个数组走到尾
        while(index[q.top().second] < nums[q.top().second].size()){
            // 加入新的数,更新最大值
            int cur = q.top().second;q.pop();
            cur_Max = max(cur_Max,nums[cur][index[cur]]);
            q.push({nums[cur][index[cur]],cur});
            // 更新下一次需要访问的下标
            index[cur]++;
            // 获得此次堆中的最大最小区间
            if((cur_Max-q.top().first) < res[1]-res[0]){
                res[0] = q.top().first;res[1] = cur_Max;
            }
        }
        return res;
    }
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值