一、题目

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。

点击查看原题
难度级别: 困难

二、思路

1)暴力法,找出k个链表最小值并组合

确定一个函数findMin,该函数遍历寻找k个链表的最小值,因为链表内部升序排列,只需要比较每条链表头部元素即可。找到后挨个组合成合并的链表

2)手写优先队列合并

k个链表的头节点放置于一个List中,并根据值进行从小到大的排序
每次取出第一个链表头节点加入合并链表中,并往后遍历,直到它值大于List中的第二个链表头节点
然后将该合并链表的end后一个节点,找到合适位置插入(维持List中从小到大的排序),反复上述过程

3)分治法

先写出两个链表合并的函数mergeTwo
再根据分治法的思想,对k个链表,使用函数mergeTwo进行合并

三、代码

1)暴力法,找出k个链表最小值并组合

  1. class Solution {
  2. private int k = 0;
  3. public ListNode mergeKLists(ListNode[] lists) {
  4. ListNode ans = new ListNode();
  5. ListNode end = ans;
  6. if (lists == null || lists.length == 0) {
  7. return ans.next;
  8. }
  9. k = lists.length; // 跳出条件,当lists中没有元素就跳出
  10. while (k > 0) {
  11. ListNode node = findMin(lists);
  12. end.next = node;
  13. end = node;
  14. }
  15. return ans.next;
  16. }
  17. private ListNode findMin(ListNode[] lists) {
  18. int loc = 0;
  19. while (loc < lists.length && lists[loc] == null) { // 找到第一个不为null的元素
  20. loc++;
  21. }
  22. if (loc == lists.length) { // 遍历了一圈,发现lists中没有元素,如[[null]]的情况
  23. k = 0;
  24. return null;
  25. }
  26. for (int i = loc + 1; i < lists.length; i++) { // 找最小值
  27. if (lists[i] != null && lists[i].val < lists[loc].val) {
  28. loc = i;
  29. }
  30. }
  31. ListNode ans = lists[loc];
  32. lists[loc] = ans.next;
  33. if (ans.next == null) { // 发现一条链没有元素了,让k自减
  34. k--;
  35. }
  36. return ans;
  37. }
  38. }

k为链表个数,n为链表平均长度,时间复杂度为O(k*k*n),空间复杂度为O(k)

2)手写优先队列合并

  1. class Solution {
  2. public ListNode mergeKLists(ListNode[] lists) {
  3. ListNode ans = new ListNode();
  4. ListNode end = ans;
  5. List<ListNode> headers = new LinkedList();
  6. if (lists == null || lists.length == 0) {
  7. return ans.next;
  8. }
  9. for (ListNode node : lists) { // 将链表放置如头节点链表中
  10. if (node != null) {
  11. headers.add(node);
  12. }
  13. }
  14. Collections.sort(headers, (ListNode n1, ListNode n2)-> { // 排序
  15. if (n1.val == n2.val) {
  16. return 0;
  17. }
  18. return n1.val > n2.val ? 1 : -1;
  19. });
  20. while (headers.size() != 0) {
  21. end.next = headers.get(0);
  22. end = end.next; // 当插入此节点,节点所在链表的后续节点也可以通过end遍历访问
  23. headers.remove(0);
  24. if (headers.size() != 0) {
  25. // 找到end所在链上节点大于header中第一个节点值
  26. while (end.next != null && headers.get(0).val > end.next.val) {
  27. end = end.next;
  28. }
  29. if (end.next == null) { // end链上节点遍历完也没有找到大于header中第一个节点值
  30. continue;
  31. }
  32. int insertLoc = 0;
  33. // 将找到的节点插入它应该在的位置,寻找该位置
  34. while (insertLoc < headers.size() && headers.get(insertLoc).val < end.next.val) {
  35. insertLoc++;
  36. }
  37. headers.add(insertLoc, end.next);
  38. }
  39. }
  40. return ans.next;
  41. }
  42. }

k为链表个数,n为链表平均长度,时间复杂度为O(k*k*n),空间复杂度为O(k),这里算法比上一个好,是因为上一个每添加一个节点固定需要访问k次进行对比,这里不需要固定访问k次,较好的情况只需比较1一次,就可以确定,最差情况才是k次比较

3)分治法

  1. class Solution {
  2. public ListNode mergeKLists(ListNode[] lists) {
  3. return mergeAll(lists, 0, lists.length - 1);
  4. }
  5. private ListNode mergeAll(ListNode[] lists, int left, int right) {
  6. if (left > right) {
  7. return null;
  8. }
  9. if (left == right) {
  10. return left < lists.length ? lists[left] : null;
  11. }
  12. int mid = (left + right)/2;
  13. return mergeTwo(mergeAll(lists, left, mid), mergeAll(lists, mid + 1, right));
  14. }
  15. private ListNode mergeTwo(ListNode n1, ListNode n2) {
  16. ListNode ans = new ListNode();
  17. ListNode end = ans;
  18. while (n1 != null && n2 != null) {
  19. if (n1.val < n2.val) {
  20. end.next = n1;
  21. n1 = n1.next;
  22. } else {
  23. end.next = n2;
  24. n2 = n2.next;
  25. }
  26. end = end.next;
  27. }
  28. end.next = (n1 != null) ? n1 : n2;
  29. ListNode t = ans.next;
  30. return ans.next;
  31. }
  32. }

k为链表个数,n为链表平均长度,时间复杂度为O(k*n*logk),空间复杂度为O(logk)