合并K个升序链表

156 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情

前言

笔者除了大学时期选修过《算法设计与分析》和《数据结构》还是浑浑噩噩度过的(当时觉得和编程没多大关系),其他时间对算法接触也比较少,但是随着开发时间变长对一些底层代码/处理机制有所接触越发觉得算法的重要性,所以决定开始系统的学习(主要是刷力扣上的题目)和整理,也希望还没开始学习的人尽早开始。

系列文章收录《算法》专栏中。

力扣题目链接

问题描述

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

剖析

笔者的基本思路就是从每个链表的头部开始遍历每个链表,比较拿到最小的那个,拼接在要返回的链表上,然后将最小的值对应的链表向右移动一位,其他链表不移动参与下次遍历,继续比较拿到最小值继续前面的操作,一旦某个链表遍历到达尾部空节点就不参与比较,一旦所有链表都到达尾部空节点就停止比较返回结果。

还是具体描述下步骤:

  1. 新建一个无值头节点head,用于最终返回链表的头节点,最后结果返回head.next即可。
  2. 遍历提供的链表数组,从第一个节点开始看那个节点的值第一次最小,拿到最小的节点作为返回链表的下一个节点。
  3. 对刚刚产生最小值的链表进行重新赋值,比如最小值是lists[i]那么把lists[i]=lists.next。这样下次继续遍历比较最小值的时候就不用跳过了它。
  4. 重复2-3步骤,直到所有的链表都是null了之后就停止遍历。

代码

package com.study.algorithm.linklist;

/**
 * 合并K个升序链表
 * https://leetcode-cn.com/problems/merge-k-sorted-lists/
 */
public class MergeKLists {
    public static class ListNode {
        int val;
        ListNode next;

        ListNode() {
        }

        ListNode(int val) {
            this.val = val;
        }

        ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }
    }

    public static ListNode mergeKLists(ListNode[] lists) {
        ListNode head = new ListNode();
        int length = lists.length;
        //如果链表数组长度等于0直接返回null
        if (length == 0) {
            return null;
        }
        ListNode current = head;

        retry:
        while (true) {
            //赋最大值,根据题目的给定范围那么后面出现的一定比它小
            int currentMin = Integer.MAX_VALUE;
            //当前最小值节点,用于后面结果链表的拼接
            ListNode currentMinNode = null;
            //当前最小值节点的链表在链表数组中的下标
            int currentMinIndex = 0;

            //用于统计为null的链表
            int j = 0;
            for (int i = 0; i < length; i++) {
                ListNode listNode = lists[i];

                //如果出现null需要跳出本次for循环或者跳出外层while循环(当所有链表都是null时)
                if (listNode == null) {
                    j++;
                    if (j == length) {
                        break retry;
                    }
                    continue;
                }

                int listNodeValue = listNode.val;
                if (listNodeValue < currentMin) {
                    currentMin = listNodeValue;
                    currentMinNode = listNode;
                    currentMinIndex = i;
                }
            }

            if (currentMinNode != null) {
                //拼接结果
                current.next = currentMinNode;
                Integer nodeIndex = currentMinIndex;
                //重新赋值,跳过刚新竞的最小值节点
                lists[nodeIndex] = lists[nodeIndex].next;
                //跳到下一个节点,用于后面继续拼接
                current = current.next;
            }
        }
        return head.next;
    }

    public static void main(String[] args) {
        ListNode listNodeOne = new ListNode(1);
        listNodeOne.next = new ListNode(4);
        listNodeOne.next.next = new ListNode(5);

        ListNode listNodeTow = new ListNode(1);
        listNodeTow.next = new ListNode(3);
        listNodeTow.next.next = new ListNode(4);

        mergeKLists(new ListNode[]{listNodeOne, listNodeTow});
    }
}