【算法】合并k个已排序的链表
https://www.nowcoder.com/questionTerminal/65cfde9e5b9b4cf2b6bafa5f3ef33fa6
- 这题的解法一、解法二与归并排序的自底向上、自顶向下的思想是一样的,可参照对比。
1.题目
-
ListNode
public class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } }
2.我的解法
-
代码
package 合并k个已排序的链表; import 合并k个已排序的链表.ListNode; import java.util.*; /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ /* 取链表与rs链表合并: 若rs链表为空,直接插入rs 若该链表的首元素比rs链表的末元素大,直接排在rs后面; 若该链表的末元素比rs链表的首元素小,直接排在rs前面; ,否则正常合并两者 */ public class Solution { public static ListNode mergeKLists(ArrayList<ListNode> lists) { ListNode rsHead = null; //结果链表的头指针 ListNode temp = null; //结果链表的临时节点 ListNode list1 = null; ListNode temp1 = null;//链表1的节点 for (int i = 0; i < lists.size(); i++) { list1 = lists.get(i); if (list1 == null) { continue; } temp1 = list1; while (temp1.next != null) { temp1 = temp1.next; } if (rsHead == null) { rsHead = list1; //找结果链表的末节点 temp = list1; while (temp.next != null) { temp = temp.next; } } else if (list1.val > temp.val) { temp.next = list1; //找结果链表的末节点 while (temp.next != null) { temp = temp.next; } } else if (temp1.val < rsHead.val) { temp1.next = rsHead; rsHead = list1; //找结果链表的末节点 while (temp.next != null) { temp = temp.next; } } else { temp = rsHead; temp1 = list1; ListNode rs2 = null;//头结点 ListNode temp2 = null; //两条链表都没结束 while (temp != null && temp1 != null) { if (temp.val <= temp1.val) { if (rs2 == null) { rs2 = new ListNode(temp.val); temp2 = rs2; } else { temp2.next = new ListNode(temp.val); temp2 = temp2.next; } temp = temp.next; } else { //temp1.val < temp.val if (rs2 == null) { rs2 = new ListNode(temp1.val); temp2 = rs2; } else { temp2.next = new ListNode(temp1.val); temp2 = temp2.next; } temp1 = temp1.next; } } //rs链表先结束 if (temp == null) { while (temp1 != null) { temp2.next = new ListNode(temp1.val); temp2 = temp2.next; temp1 = temp1.next; } } else if (temp1 == null) { //链表1先结束 while (temp != null) { temp2.next = new ListNode(temp.val); temp2 = temp2.next; temp = temp.next; } } rsHead = rs2; temp = temp2; } } return rsHead; } public static void main(String[] args) { ArrayList<ListNode> lists = new ArrayList<>(); ListNode list1 = new ListNode(1); ListNode temp1 = list1; temp1.next = new ListNode(2); temp1 = temp1.next; temp1.next = new ListNode(2); lists.add(list1); ListNode list2 = new ListNode(1); ListNode temp2 = list2; temp2.next = new ListNode(1); temp2 = temp2.next; temp2.next = new ListNode(2); lists.add(list2); ListNode rs = mergeKLists(lists); ListNode rsTemp = rs; while (rsTemp != null) { System.out.print(rsTemp.val + " "); rsTemp = rsTemp.next; } } }
3.解法一
-
代码
package 合并k个已排序的链表; import java.util.ArrayList; /** * @author musecho801 * @title Solution1 * @description 合并k个已排序的链表: * 将数组中链表两两合并,N个链表变成N/2个链表;循环,直到只有一个链表。 * 将问题规模由N变为N/2、N/4、。。1 * 注意:确保有偶数个节点 * @date 2021/3/10 18:41 */ public class Solution1 { /** * @title mergeKLists * @description 合并k个已排序的链表 主算法 * @date 2021/3/10 19:44 * @Param lists: 存放待排序链表的数组 * @return: ListNode */ public static ListNode mergeKLists(ArrayList<ListNode> lists) { if (lists == null || lists.size() == 0) return null; if (lists.size() == 1) return lists.get(0); //确保链表为偶数个 if (lists.size() % 2 != 0) lists.add(null); ArrayList<ListNode> rsList = new ArrayList<>();//结果数组 for (int i = 0; i < lists.size(); i += 2) { rsList.add(mergeTwoLists(lists.get(i), lists.get(i + 1))); } return mergeKLists(rsList); } /** * @title mergeTwoLists * @description 合并两个链表 * @date 2021/3/10 19:45 * @Param list1 * @Param list2 * @return: ListNode */ public static ListNode mergeTwoLists(ListNode list1, ListNode list2) { ListNode list3 = new ListNode(0);//两个链表合并的结果链表 ListNode list4 = list3;//标注结果链表的头节点 while (list1 != null && list2 != null) { if (list1.val <= list2.val) { list3.next = list1; list1 = list1.next; } else { list3.next = list2; list2 = list2.next; } list3 = list3.next; } // while (list1 != null) { // list3.next = new ListNode(list1.val); // list3 = list3.next; // list1 = list1.next; // } // while (list2 != null) { // list3.next = new ListNode(list2.val); // list3 = list3.next; // list2 = list2.next; // } list3.next = list1 != null ? list1 : list2; return list4.next; } public static void main(String[] args) { ArrayList<ListNode> nodeArrayList = new ArrayList<>(); ListNode l1 = new ListNode(1); nodeArrayList.add(l1); l1.next = new ListNode(2); l1 = l1.next; l1.next = new ListNode(3); ListNode l2 = new ListNode(4); nodeArrayList.add(l2); l2.next = new ListNode(5); l2 = l2.next; l2.next = new ListNode(6); l2 = l2.next; l2.next = new ListNode(7); ListNode rs = mergeKLists(nodeArrayList); while (rs != null) { System.out.print(rs.val + " "); rs = rs.next; } } }
亮点
-
合并两条链表时,新建节点0,最后返回合并结果的头结点时返回该节点后面一个结点。
这段代码看起来我的那段合并代码清爽些。
ListNode list3 = new ListNode(0);//两个链表合并的结果链表 ListNode list4 = list3;//标注结果链表的头节点 。。。 return list4.next;
-
list3链接后面节点时,不需要一个一个节点遍历连接。
// while (list1 != null) { // list3.next = new ListNode(list1.val); // list3 = list3.next; // list1 = list1.next; // } // while (list2 != null) { // list3.next = new ListNode(list2.val); // list3 = list3.next; // list2 = list2.next; // } list3.next = list1 != null ? list1 : list2;
4.解法二:分治思想
-
代码
package 合并k个已排序的链表; import java.util.ArrayList; /** * @author musecho801 * @title Solution2 * @description 分治法: * 数组一分为二,左半部分、右半部分的链表继续递归,然后两两合并 * @date 2021/3/10 23:05 */ public class Solution2 { public ListNode mergeKLists(ArrayList<ListNode> lists) { if (lists.size() == 0) return null; return mergeKLists(lists, 0, lists.size() - 1); } public ListNode mergeKLists(ArrayList<ListNode> lists, int low, int high) { if (low == high) return lists.get(low); // int mid = (low + high) / 2; int mid = low + (high - low) / 2; ListNode l1 = mergeKLists(lists, low, mid); ListNode l2 = mergeKLists(lists, mid + 1, high); return mergeTwoLists(l1, l2); } public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if (l1 == null || l2 == null) return l1 == null ? l2 : l1; ListNode l3 = new ListNode(0); ListNode l4 = l3;//标识合并的结果链表的头结点 while (l1 != null && l2 != null) { if (l1.val <= l2.val) { l3.next = l1; l1 = l1.next; } else { l3.next = l2; l2 = l2.next; } l3 = l3.next; } l3.next = l1 != null ? l1 : l2; return l4.next; } public static void main(String[] args) { Solution2 solution2 = new Solution2(); ArrayList<ListNode> nodeArrayList = new ArrayList<>(); ListNode l1 = null; nodeArrayList.add(l1); ListNode l2 = new ListNode(-2); nodeArrayList.add(l2); ListNode l3 = null; nodeArrayList.add(l3); ListNode rs = solution2.mergeKLists(nodeArrayList); while (rs != null) { System.out.print(rs.val + " "); rs = rs.next; } } }
亮点
-
取mid的写法。
下面的写法能防止溢出,参考:https://blog.csdn.net/weixin_44556968/article/details/110288725
// int mid = (low + high) / 2; int mid = low + (high - low) / 2;
5.解法三:优先队列
-
代码
package 合并k个已排序的链表; import java.util.ArrayList; import java.util.Comparator; import java.util.PriorityQueue; /** * @author musecho801 * @title Solution3 * @description 优先队列: * 重写优先队列的排序规则使其升序,将数组中所有节点加入优先队列, * 取优先队列中的首节点加入结果链表rs,将其下一个节点加入优先队列。 * @date 2021/3/11 11:50 */ public class Solution3 { public ListNode mergeKLists(ArrayList<ListNode> lists) { if (lists.size() == 0) return null; if (lists.size() == 1) return lists.get(0); PriorityQueue<ListNode> que = new PriorityQueue<>( new Comparator<ListNode>() { @Override public int compare(ListNode o1, ListNode o2) { return o1.val - o2.val; } } ); for (ListNode list : lists) { if (list == null) continue; que.add(list); } ListNode rs = new ListNode(0); ListNode rsHead = rs; while (que.size() > 0) { ListNode node = que.poll(); rs.next = node; if (node.next != null) { que.add(node.next); } rs = rs.next; } return rsHead.next; } public static void main(String[] args) { Solution3 solution3 = new Solution3(); ArrayList<ListNode> nodeArrayList = new ArrayList<>(); ListNode l1 = new ListNode(1); nodeArrayList.add(l1); l1.next = new ListNode(2); l1 = l1.next; l1.next = new ListNode(3); ListNode l2 = new ListNode(-1); nodeArrayList.add(l2); l2.next = new ListNode(0); l2 = l2.next; l2.next = new ListNode(1); l2 = l2.next; l2.next = new ListNode(2); ListNode rs = solution3.mergeKLists(nodeArrayList); while (rs != null) { System.out.print(rs.val + " "); rs = rs.next; } } }
亮点
- 这种解法代码量少,较易理解。学会使用优先队列。
参考
解法一:https://www.nowcoder.com/profile/853696206/codeBookDetail?submissionId=88248807
解法二:https://www.nowcoder.com/profile/688208353/codeBookDetail?submissionId=89021972
解法三:https://leetcode-cn.com/problems/merge-k-sorted-lists/comments/64343