当前位置:   article > 正文

阿里面试题: 将两个升序链表合并成一个升序链表_合并链表为一个升序链表

合并链表为一个升序链表

题目

        将两个升序链表合并,合并后还是一个升序列表,尽量用最小的时间复杂度。

思路一

        两个链表合成一个链表,可以将两个链表打散,然后按照大小顺序放入到一个数组里,冒泡排序后,将节点串起来,这样就可以实现一个升序链表。

思路二

        思路一方法虽然可行,但是时间复杂度大于O(N2), 还重新创建了一个新数组去装这些元素, 空间复杂度也是比较大,因此方案一不可取。

        我们可以利用两个升序链表的特点: 可以按照顺序进行遍历,具体实现步骤如下:

        1)  定义一个新链表的头节点为两个链表中头节点的最小值。

 Node root=Max(l1.head.data,l2.head.data)

        2) 将剩下的两个链表,按照头头比较的规则,将值小的节点接入到新链表的尾节点, 因为每次我们就是取两个值,这两个值中肯定较小值肯定比新链表的尾节点的值要大,因此可以采用递归的思想来实现。

什么是头头比较?

        我们每次拿出两个链表的头节点,值小的节点插入到新链表尾,然后在旧链表中删除该节点。

        3) 递归结束的条件。 当任意一个链表结束时,也就意味着遍历结束和递归结束。如果是刚好比较完,那么就返回NULL插入到新链表的尾部,如果有一个链表遍历完了,另外一个链表还多余了很多节点,那么就直接将剩余的节点插入到新链表的尾部即可,因为每次递归(比较完毕后)旧链表中剩下的值总是大于或等于新链表的最大值。
 

  1. import chain.ReverseList;
  2. /**
  3. * @decription:
  4. * @date: 2021/7/9 10:50
  5. * 将两个升序链表合并,合并后还是一个升序列表,尽量用最小的时间复杂度。
  6. * 1->3-5>7
  7. * 2->4->5->8
  8. * 2 3 3 4
  9. * 1 1 2 3
  10. */
  11. public class LinkedListMerge {
  12. static Node one;
  13. static Node two;
  14. static {
  15. one = new Node(1, new Node(3, new Node(5, new Node(7, null))));
  16. two = new Node(2, new Node(4, new Node(5, new Node(8, new Node(9, null)))));
  17. }
  18. static class Node {
  19. Integer data;
  20. Node next;
  21. public Node(Integer data, Node next) {
  22. this.data = data;
  23. this.next = next;
  24. }
  25. public Integer getData() {
  26. return data;
  27. }
  28. public void setData(Integer data) {
  29. this.data = data;
  30. }
  31. // 使用递归的思想遍历链表
  32. public String traverse() {
  33. return this.getData() + "->" + (this.next == null ? "NULL" : this.next.traverse());
  34. }
  35. }
  36. /**
  37. * 定义好头节点后,因为两个链表都是升序的,我们可以把每次的操作分解为三步: 比较两个链表的头节点 -> 将小的节点插入到新链表的尾节点-> 旧链表的节点移除。
  38. * 递归结束的条件: 任意一个链表为空链表。
  39. * @param l1
  40. * @param l2
  41. * @param node
  42. * @return
  43. */
  44. public static Node mergeLists(Node l1, Node l2, Node node) {
  45. // 如果有链表先为空,那么就将另外一个链表接入到新的链表上
  46. if (l1 == null) {
  47. node.next = l2;
  48. return node;
  49. }
  50. if (l2 == null) {
  51. node.next = l1;
  52. return node;
  53. }
  54. // 定义好头节点后,分别比较两个链表中的元素,哪个小哪个就先插入到新链表的后面,每次插完后,删除链表节点(移到下一个节点)
  55. if (l1.getData() > l2.getData()) {
  56. node.next = new Node(l2.getData(), null);
  57. l2 = l2.next;
  58. } else {
  59. node.next = new Node(l1.getData(), null);
  60. l1 = l1.next;
  61. }
  62. return mergeLists(l1, l2, node.next);
  63. }
  64. public static void main(String[] args) {
  65. Node current;
  66. // 定义新链表的头节点
  67. if (one.getData() > two.getData()) {
  68. current = new Node(two.getData(), null);
  69. two = two.next;
  70. } else {
  71. current = new Node(one.getData(), null);
  72. one = one.next;
  73. }
  74. mergeLists(one, two, current);
  75. System.out.println(current.traverse());
  76. }
  77. }

打印结果:

此方法的最坏时间复杂度为o(n),如果链表的长度差距很大的话,最后可以少比较很多次。

类似问题

        归并排序。

        上述的合并方法有点类似归并排序的思想,只不过是给的链表已经是排序好了的,归并排序的思想如下:

        1)  将数列均分成2个数列,然后分别对2个数列进行排序。

        2)  创建一个新的数列,然后使用2个指针分别指向2个数列,将相对较小的值放入到新的数列里。

        3) 将剩余的数列放入到新数列后面,然后重复上述步骤,直到排序完毕。

  1. public static int[] mergeSort(int[] nums, int l, int h) {
  2. if (l == h) {
  3. return new int[]{nums[l]};
  4. }
  5. int mid = l + (h - l) / 2;
  6. //左有序数组
  7. int[] leftArr = mergeSort(nums, l, mid);
  8. //右有序数组
  9. int[] rightArr = mergeSort(nums, mid + 1, h);
  10. //新有序数组
  11. int[] newNum = new int[leftArr.length + rightArr.length];
  12. int m = 0, i = 0, j = 0;
  13. while (i < leftArr.length && j < rightArr.length) {
  14. newNum[m++] = leftArr[i] < rightArr[j] ? leftArr[i++] : rightArr[j++];
  15. }
  16. while (i < leftArr.length) {
  17. newNum[m++] = leftArr[i++];
  18. }
  19. while (j < rightArr.length) {
  20. newNum[m++] = rightArr[j++];
  21. }
  22. return newNum;
  23. }

扩展

        求K个升序链表的合并结果。

思路一

        暴力破解法。 采用一个List装下所有的升序链表,然后使用排序,再将排序后的链表放入到一个链表里。

  1. /**
  2. * 扩展: k 个有序链表怎么合并
  3. * extension: Merge k sorted lists and return it as one sorted list, analyze and describe its complexity.
  4. * use brute force method 暴力破解法
  5. */
  6. private static Node mergeManyList(List<Node> nodeList) {
  7. List<Integer> numLists = new ArrayList<>();
  8. for (Node node : nodeList) {
  9. while (node != null) {
  10. numLists.add(node.getData());
  11. node = node.next;
  12. }
  13. }
  14. // 默认采用插入排序
  15. Collections.sort(numLists);
  16. Node node = new Node(numLists.get(0));
  17. Node head = node;
  18. for (int i = 1; i < numLists.size() - 1; i++) {
  19. head.setNext(new Node(numLists.get(i)));
  20. head = head.next;
  21. }
  22. return node;
  23. }

 思路二

        采用纵向查找的方法,定位第一个值为最小值,将后续的最小值不断的接入到链表后面即可。

        每次比较出K个链表的头节点的最小值,下标为index, 每找到一个最小的值,需要将该节点删除掉, 方法如下:

 nodeList.set(index, nodeList.get(index).next);

         最后当nodeList列表的所有链表都为空时,暂停循环。

  1. /**
  2. * 纵向查找, 每次找出最小的将head的next指针执行最小的元素即可。
  3. * @param nodeList
  4. * @return
  5. */
  6. private static Node mergeListNode(List<Node> nodeList) {
  7. Node head = new Node(Integer.MAX_VALUE);
  8. Node current = head;
  9. while (true) {
  10. boolean isBreak = true;
  11. Node min = new Node(Integer.MAX_VALUE);
  12. int index = -1;
  13. for (int j = 0; j < nodeList.size(); j++) {
  14. if (nodeList.get(j) != null && nodeList.get(j).getData() < min.getData()) {
  15. min = nodeList.get(j);
  16. // to find the minimum index
  17. index = j;
  18. }
  19. // stop the loop until nodeList become empty
  20. if (nodeList.get(j) != null) {
  21. isBreak = false;
  22. }
  23. }
  24. if (isBreak) {
  25. break;
  26. }
  27. // remove the head node in time
  28. nodeList.set(index, nodeList.get(index).next);
  29. // add node which is minimum after the node of current.
  30. head.next = min;
  31. head = head.next;
  32. }
  33. return current.next;
  34. }

打印结果:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码维护者/article/detail/62933
推荐阅读