Ex2_2_17链表归并排序

  1. public class List<Item> {
  2. private class Node{
  3. Item item;
  4. Node next;
  5. }
  6. private Node head;
  7. private Node tail;
  8. private boolean less(Comparable v, Comparable w){ return v.compareTo(w)<0 ;}
  9. private boolean isEmpty(){ return head == null;}
  10. private void add(Item item){
  11. Node current = tail;
  12. tail = new Node();
  13. tail.item = item;
  14. if(isEmpty()) head = tail;
  15. else current.next = tail;
  16. }
  17. private Node ListMerge(Node head){
  18. if(head.next == null) return head;
  19. Node fast = head.next;
  20. Node slow = head;
  21. while (fast != null && fast.next != null){
  22. fast = fast.next.next;
  23. slow = slow.next;
  24. }//slow-fast方法找到中间节点,fast走两步,slow走一步,当fast到末尾,slow即在中间节点
  25. Node leftHead = head;
  26. Node rightHead = slow.next;
  27. slow.next = null;//左边去尾
  28. Node newLeft = ListMerge(leftHead);
  29. Node newRight = ListMerge(rightHead);
  30. Node newList;//newList用来指向归并后数组头结点
  31. Node tail;//tail指向归并中的尾节点
  32. if(less((String)newLeft.item,(String)newRight.item)){
  33. newList = newLeft;
  34. newLeft = newLeft.next;
  35. }else{
  36. newList = newRight;
  37. newRight = newRight.next;
  38. }//确定头结点
  39. tail = newList;
  40. tail.next = null;
  41. //升序归并尾节点
  42. while (newLeft != null || newRight !=null){
  43. if(newLeft == null){
  44. tail.next = newRight;
  45. newRight = null;
  46. }
  47. else if(newRight == null){
  48. tail.next = newLeft;
  49. newLeft = null;
  50. }
  51. else if(less((String)newLeft.item,(String)newRight.item)){
  52. tail.next = newLeft;
  53. newLeft = newLeft.next;
  54. tail = tail.next;
  55. tail.next = null;
  56. }
  57. else{
  58. tail.next = newRight;
  59. newRight = newRight.next;
  60. tail = tail.next;
  61. tail.next = null;
  62. }
  63. }
  64. //返回归并后数组头结点
  65. return newList;
  66. }
  67. public static void main(String[] args){
  68. List<String> list = new List<>();
  69. for(int i = 5; i >= 0; i--){
  70. list.add(Integer.toString(i));
  71. }
  72. List.Node temp = list.ListMerge(list.head);
  73. }
  74. }

要点:

  1. 关于链表如何找到中间节点:使用fast-slow方法,fast节点初始为head.next,slow节点初始为head,每次fast后移两节点,slow后移一节点,当fast到末尾时,slow就在中间节点.比使用一个int变量标记循环要简单的多
  1. Node fast = head.next;
  2. Node slow = head;
  3. while (fast != null && fast.next != null){
  4. fast = fast.next.next;
  5. slow = slow.next;
  6. }//slow-fast方法找到中间节点,fast走两步,slow走一步,当fast到末尾,slow即在中间节点
  1. 记得分成左右部分后,左边链表的尾节点.next要赋值为null,否则最终的链表会无限循环
  1. slow.next = null;//左边去尾
  1. newList节点和tail节点分别标识排序后链表的头和尾
  1. Node newList;//newList用来指向归并后数组头结点
  2. Node tail;//tail指向归并中的尾节点

特点:

  1. 这是链表排序的最佳方法,因为不需要额外空间且运行时间是线性的.

Review:

  1. 链表归并排序只是List类下的方法,不要和其他排序算法实现静态方法搞混,此处都为非静态方法;
  2. 对于链表的归并,一个ListMerge函数既做到排序有完成归并,无需sort()函数;
  3. 对于(newLeft == null)和(newRight == null),直接tail.next = newRight和tail.next = newLeft整个半条链表就会连接,不用一个个节点连接;
  4. 关于newList和tail指针的必要性:之所以分设两个指针,newList为了始终执行归并后链表的头结点,如果只有一个指针,最终无法返回头结点,所以tail起到时刻跟踪尾部节点功能;
  1. tail.next = newRight;//1
  2. newRight = newRight.next;//2
  3. tail = tail.next;//3
  4. tail.next = null;//4
  1. tail语句顺序问题:如上为最后的while循环,2一定要在3,4前,tail相当于一个遥控器,3,4先执行会使其和newRight遥控器对应一个,这样tail.next也就是newRight.next = null,内存中后面的Node直接变成无引用,语句4可有可无,tail.next总会被覆盖;