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总会被覆盖;