一起养成写作习惯!这是我参与「掘金日新计划 · 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
剖析
笔者的基本思路就是从每个链表的头部开始遍历每个链表,比较拿到最小的那个,拼接在要返回的链表上,然后将最小的值对应的链表向右移动一位,其他链表不移动参与下次遍历,继续比较拿到最小值继续前面的操作,一旦某个链表遍历到达尾部空节点就不参与比较,一旦所有链表都到达尾部空节点就停止比较返回结果。
还是具体描述下步骤:
- 新建一个无值头节点head,用于最终返回链表的头节点,最后结果返回head.next即可。
- 遍历提供的链表数组,从第一个节点开始看那个节点的值第一次最小,拿到最小的节点作为返回链表的下一个节点。
- 对刚刚产生最小值的链表进行重新赋值,比如最小值是lists[i]那么把lists[i]=lists.next。这样下次继续遍历比较最小值的时候就不用跳过了它。
- 重复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});
}
}