【leetcode】23.合并K个排序链表

https://leetcode-cn.com/problems/merge-k-sorted-lists/

这道题的前置题目是合并两个排序链表 https://leetcode-cn.com/problems/merge-two-sorted-lists/

  1. 暴力法

将所有链表合并后排序
时间复杂度O(NlogN) N= 总节点数量
空间复杂度O(N)

  1. 归并

基于合并两个排序链表,我们可以做到循环依次合并两个链表,将问题转换为合并两个排序链表k-1次;
由于每次合并前后,被合并链表和所得链表的结构(有序性)不变,因此可以采用分治、归并的方式,对每两个链表进行合并,再将合并后的结果继续按此逻辑每两个相合并
时间复杂度:O(NlogK)

举例图示:

0 1 2 3 4 5 6
01 23 45 6
0123 456
0123456

我的代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
      public ListNode mergeKLists(ListNode[] lists) {

        if(lists.length == 1) return lists[0];

        int distance = 1;
        int sumDistance = 0;
        ListNode head = null;
        while (condition(distance, lists.length)) {
            for (int i = 0; i < lists.length; i += (distance + 1 + sumDistance)) {
                if (i + distance < lists.length) {
                    head = mergeTwoLists(lists[i], lists[i + distance]);
                    lists[i] = head;
                }
            }
            sumDistance += distance;
            distance *= 2;
        }

        return head;
    }

    boolean condition(int distance, int length) {
        //distance = 最后一次合并结果 * 2 ,所以临界值判断需要除以2
        //由于两两合并,所以一旦超过一半必然归并完成
        return length % 2 == 0 ? (distance / 2) < length / 2 : (distance / 2) < (length + 1) / 2 ;
    }


    ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(0);
        ListNode preHead = res;
        ListNode cur1 = l1;
        ListNode cur2 = l2;
        while (cur1 != null || cur2 != null) {

            if (cur1 != null && cur2 != null) {

                if (cur1.val > cur2.val) {
                    res.next = new ListNode(cur2.val);
                    cur2 = cur2.next;
                } else {
                    res.next = new ListNode(cur1.val);
                    cur1 = cur1.next;
                }

            } else if (cur1 == null && cur2 != null) {
                res.next = new ListNode(cur2.val);
                cur2 = cur2.next;

            } else if (cur2 == null && cur1 != null) {
                res.next = new ListNode(cur1.val);
                cur1 = cur1.next;
            }
            res = res.next;
        }
        return preHead.next;
    }

}

改进思路:
由于我的实现需要计算每次合并的两个链表的下标,这个过程比较麻烦;看了其他解答后,发现这样一种解决方式

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        int len = lists.length;
        if (len == 0) {
            return null;
        }
        // 将n个链表以中间为对称,合并,即合并
        while(len>1) {
            for (int i=0; i<len/2; i++) {
                lists[i] = mergeTwoListsForK(lists[i], lists[len-1-i]);
            }
            len = (len+1)/2;
        }
        return lists[0];
    }

    private ListNode mergeTwoListsForK(ListNode l1, ListNode l2) {
        ListNode head = new ListNode(-1);
        ListNode p = head;
        while(l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                p.next = l1;
                l1 = l1.next;
            } else {
                p.next = l2;
                l2 = l2.next;
            }
            p = p.next;
        }
        if (l1 != null) {
            p.next = l1;
        } else if (l2 != null) {
            p.next = l2;
        }
        return head.next;
    }
}

这个思路更方便解答,省却了计算偏移的过程
0 1 2 3 4 5 6
06 15 24 3
063 1524
0631524

上一篇:MySQL系列----创建存储函数、游标的使用


下一篇:大型数据库技术-实验五-游标的使用