LeetCode第二十三题(合并K个升序链表)

193 阅读3分钟

K路归并-简单选择(Java)

核心思想: 对于K个升序链表中的结点,利用简单选择排序算法的思想,即从还有结点尚未合并的链表中顺序比较头结点的val并标记最小值结点,每次最多从K个链表中选择一个最小值结点并将其合并到结果链表resList中,直至K个链表的所有结点合并完成。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
       ListNode resList = new ListNode(0,null);//结果链表
       ListNode rear = resList;//尾插法建表
       while(true){
           ListNode min = null;//用于指向最小值结点
           int minPos = -1;//用于标记最小值结点下标
           //从还有结点尚未合并的链表中顺序比较头结点的val并标记最小值结点
           for(int i = 0; i < lists.length; i++){
                if(lists[i] == null)//跳过已合并完成的链表
                    continue;
                if(min == null || min.val > lists[i].val){//更新最小值结点
                    min = lists[i];
                    minPos = i;
                }
           }
            if(minPos == -1)//K个链表的所有结点合并完成
                break;
            lists[minPos] = lists[minPos].next;//在原链表中删除最小值结点
            min.next = rear.next;
            rear.next = min;//最小值结点合并到结果链表
            rear = min;
       }
       return resList.next;
    }
}

K路归并-小根堆(Java)

核心思想: 对上述采用简单选择排序算法的思想进行优化,改用堆(小根堆)排序算法思想,Java优先队列的底层实现正是基于小根堆。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode resList = new ListNode(0, null);//结果链表
        ListNode rear = resList;//尾插法建表
        PriorityQueue<ListNode> pq = new PriorityQueue<>((node1, node2) -> node1.val - node2.val);//优先队列
        //将链表数组中所有链表的非空头结点加入优先队列
        for(int i = 0; i < lists.length; i++){
            if(lists[i] != null)
                pq.offer(lists[i]);
        }
        while(pq.size() != 0){//优先队列不空,即还有结点尚未合并的链表
           ListNode min = pq.poll();//指向最小值结点
           if(min.next != null)
                pq.offer(min.next);
            min.next = rear.next;
            rear.next = min;//最小值结点合并到结果链表
            rear = min;
       }
        return resList.next;
    }
}

两两归并-顺序

核心思想: 将链表数组中的前两个链表进行归并,之后再将归并后的结果链表和链表数组中的第三个链表进行归并,依次归并直至K个链表归并完成。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode resList = null;//结果链表
        //顺序归并链表数组的每个链表
        for(int i = 0; i < lists.length; i++)
            resList = mergeTwoLists(resList, lists[i]);
        return resList;
    }
    //合并两个有序链表
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null)//list1合并完成
            return list2;
        else if(list2 == null)//list2合并完成
            return list1;
        else if(list1.val < list2.val){//list1的结点值比list2的结点值小
            //较小值的结点和较小值结点所在链表list1的剩余结点与另一链表list2的所有结点合并的结果进行合并
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        }
        else{
            //较小值的结点和较小值结点所在链表list2的剩余结点与另一链表list1的所有结点合并的结果进行合并
            list2.next = mergeTwoLists(list2.next, list1);
            return list2;
        }
    }
}

两两归并-分治

核心思想:

image.png

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0)//特判
            return null;
        return merge(lists, 0, lists.length - 1);
    }
    //分治归并链表数组的每个链表
    public ListNode merge(ListNode[] lists, int left, int right){
        if(left == right)
            return lists[left];
        int mid = (left + right) / 2;
        ListNode leftList = merge(lists, left, mid);
        ListNode rightList = merge(lists, mid + 1, right);
        return mergeTwoLists(leftList, rightList);
    }
    //合并两个有序链表
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null)//list1合并完成
            return list2;
        else if(list2 == null)//list2合并完成
            return list1;
        else if(list1.val < list2.val){//list1的结点值比list2的结点值小
            //较小值的结点和较小值结点所在链表list1的剩余结点与另一链表list2的所有结点合并的结果进行合并
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        }
        else{
            //较小值的结点和较小值结点所在链表list2的剩余结点与另一链表list1的所有结点合并的结果进行合并
            list2.next = mergeTwoLists(list2.next, list1);
            return list2;
        }
    }
}