Ex2_2_17链表归并排序
public class List<Item> { private class Node{ Item item; Node next; } private Node head; private Node tail; private boolean less(Comparable v, Comparable w){ return v.compareTo(w)<0 ;} private boolean isEmpty(){ return head == null;} private void add(Item item){ Node current = tail; tail = new Node(); tail.item = item; if(isEmpty()) head = tail; else current.next = tail; } private Node ListMerge(Node head){ if(head.next == null) return head; Node fast = head.next; Node slow = head; while (fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; }//slow-fast方法找到中间节点,fast走两步,slow走一步,当fast到末尾,slow即在中间节点 Node leftHead = head; Node rightHead = slow.next; slow.next = null;//左边去尾 Node newLeft = ListMerge(leftHead); Node newRight = ListMerge(rightHead); Node newList;//newList用来指向归并后数组头结点 Node tail;//tail指向归并中的尾节点 if(less((String)newLeft.item,(String)newRight.item)){ newList = newLeft; newLeft = newLeft.next; }else{ newList = newRight; newRight = newRight.next; }//确定头结点 tail = newList; tail.next = null; //升序归并尾节点 while (newLeft != null || newRight !=null){ if(newLeft == null){ tail.next = newRight; newRight = null; } else if(newRight == null){ tail.next = newLeft; newLeft = null; } else if(less((String)newLeft.item,(String)newRight.item)){ tail.next = newLeft; newLeft = newLeft.next; tail = tail.next; tail.next = null; } else{ tail.next = newRight; newRight = newRight.next; tail = tail.next; tail.next = null; } } //返回归并后数组头结点 return newList; } public static void main(String[] args){ List<String> list = new List<>(); for(int i = 5; i >= 0; i--){ list.add(Integer.toString(i)); } List.Node temp = list.ListMerge(list.head); }}
要点:
- 关于链表如何找到中间节点:使用fast-slow方法,fast节点初始为head.next,slow节点初始为head,每次fast后移两节点,slow后移一节点,当fast到末尾时,slow就在中间节点.比使用一个int变量标记循环要简单的多
Node fast = head.next; Node slow = head; while (fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; }//slow-fast方法找到中间节点,fast走两步,slow走一步,当fast到末尾,slow即在中间节点
- 记得分成左右部分后,左边链表的尾节点.next要赋值为null,否则最终的链表会无限循环
slow.next = null;//左边去尾
- newList节点和tail节点分别标识排序后链表的头和尾
Node newList;//newList用来指向归并后数组头结点 Node tail;//tail指向归并中的尾节点
特点:
- 这是链表排序的最佳方法,因为不需要额外空间且运行时间是线性的.
Review:
- 链表归并排序只是List类下的方法,不要和其他排序算法实现静态方法搞混,此处都为非静态方法;
- 对于链表的归并,一个ListMerge函数既做到排序有完成归并,无需sort()函数;
- 对于
(newLeft == null)和(newRight == null),直接tail.next = newRight和tail.next = newLeft整个半条链表就会连接,不用一个个节点连接; - 关于newList和tail指针的必要性:之所以分设两个指针,newList为了始终执行归并后链表的头结点,如果只有一个指针,最终无法返回头结点,所以tail起到时刻跟踪尾部节点功能;
tail.next = newRight;//1 newRight = newRight.next;//2 tail = tail.next;//3 tail.next = null;//4
- tail语句顺序问题:如上为最后的while循环,2一定要在3,4前,tail相当于一个遥控器,3,4先执行会使其和newRight遥控器对应一个,这样tail.next也就是newRight.next = null,内存中后面的Node直接变成无引用,语句4可有可无,tail.next总会被覆盖;