LeetCode 23. 合并 K 个升序链表

23. 合并 K 个升序链表

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。

方法一:顺序合并

记得前面实现过的两个升序链表的合并,我们能否利用之前实现的两个有序链表的合并呢?当然可以,但是性价比不高而已。

顺序合并执行流程
时间复杂度:假设每个链表长度为n,当i=0,ans长度为n,i=1时ans长度为2n,因此当i时ans长度为in,求和公式得到O(k+1)k/2n),约等于O(NK^2)
空间复杂度:O(1)

时间复杂度太高了,但是先实现再优化吧…

Swift

//朴素解法,利用之前已经实现的两个有序链表的合并算法
    func mergeKLists(_ lists: [ListNode?]) -> ListNode? {
        let cnt = lists.count
        guard cnt > 0 else {return nil}
     
        //合并两个有序链表函数
        func mergeTwoListNode(_ list1: ListNode?, _ list2:ListNode?) -> ListNode? {
            guard list1 != nil else { return list2 }
            guard list2 != nil else { return list1 }
            
            //构造哑巴节点
            let dummyNode = ListNode(-1);
            var pre:ListNode? = dummyNode;
            
            var list1 = list1
            var list2 = list2
            while list1 != nil && list2 != nil {
                if list1!.value < list2!.value {
                    pre?.next = list1!;
                    list1 = list1?.next;
                }else {
                    pre?.next = list2
                    list2 = list2?.next
                }
                pre = pre?.next
            }
            
            pre?.next = list1 != nil ? list1 : list2
            
            return dummyNode.next
        }
        
        
        
        var ans:ListNode?
        for i in 0..<cnt {
            ans = mergeTwoListNode(ans, lists[i])
        }
        
        return ans
    }

OC

- (ListNodeOC * _Nullable) mergeKLists:(NSArray <ListNodeOC *>*)lists {
    if (lists.count <= 0) {
        return nil;
    }
    
    ListNodeOC *ans = nil;
    for (NSInteger i=0; i<lists.count; i++) {
        ans = [self mergeTwoList:ans list2:lists[i]];
    }
    return ans;
}

- (ListNodeOC * _Nullable)mergeTwoList:(ListNodeOC * _Nullable)list list2:(ListNodeOC * _Nullable)list2 {
    if (!list || !list2) {
        return list ? list : list2;
    }
    
    //构造哑巴节点
    ListNodeOC *dummyNode = [[ListNodeOC alloc] initWithVal:-1];
    ListNodeOC *prev = dummyNode;
    while (list && list2) {
        if (list.val < list2.val) {
            prev.next = list;
            list = list.next;
        }else {
            prev.next = list2;
            list2 = list2.next;
        }
        
        prev = prev.next;
    }
    
    //将任意一个剩余的部分拼接起来
    prev.next = list ? list : list2;
    
    return dummyNode.next;
}

方法二、分治法 (推荐)

思路
考虑优化方法一,用分治的方法进行合并。

  • 将 k 个链表配对并将同一对中的链表合并;
  • 第一轮合并以后, k个链表被合并成了 k/2个链表,平均长度为 2n/k,然后是 k/4个链表, k/8个链表等等;
  • 重复这一过程,直到我们得到了最终的有序链表。

分治法程序执行流程
时间复杂度:渐进时间复杂度为 O(kn×log⁡k)
空间复杂度:O(logk)

Swift

//分治合并
    func mergeKLists(_ lists: [ListNode?]) -> ListNode? {
        
        func merge(_ lists: [ListNode?], _ left: Int, _ right:Int) -> ListNode? {
            if left == right {
                return lists[left]
            }
            
            if left > right { return nil }
            
            let mid = (left + right) >> 1
            //mergeTwoListNode 同方法一中
            return mergeTwoListNode(merge(lists, left, mid), merge(lists, mid+1, right))
        }
        
        return merge(lists, 0, lists.count-1)
    }

OC

- (ListNodeOC * _Nullable) merge:(NSArray <ListNodeOC *>*)lists left:(NSInteger)left right:(NSInteger)right {
    if (left == right) {
        return lists[left];
    }
    
    if (left > right) {
        return nil;
    }
    
    NSInteger mid = (left+right) / 2;
    //mergeTwoList 方法用上面👆🏻
    return [self mergeTwoList:[self merge:lists left:left right:mid] list2:[self merge:lists left:mid+1 right:right]];
}

//分治合并
- (ListNodeOC * _Nullable) mergeKLists:(NSArray <ListNodeOC *>*)lists {
    return [self merge:lists left:0 right:lists.count-1];
}
  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarlen John

谢谢你给我一杯咖啡的温暖

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值