概述:
在上一篇中,讲解了如何对单链表进行快速排序,这一篇将介绍如何对单链表进行归并排序,对归并排序不熟悉的小伙伴可以先去了解归并排序的实现。那怎样对一单链表进行归并排序呢?下面开始逐步介绍。
实现:
首先定义单链表的节点,
private static class Node {
public int val;
public Node next;
public Node(int val) {
this.val = val;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
Node cur = this;
while (cur != null) {
sb.append(cur.val);
if (cur.next != null) {
sb.append(",");
}
cur = cur.next;
}
sb.append("]");
return sb.toString();
}
在测试的时候为了方便打印链表,特意重写了toString方法。
回顾基于数组的归并排序的实现逻辑:先找中点,之后对左右两边递归调用,最后待左右两边都有序时就合并,方法如下,
public Node mergeSort(Node head) {
// 只有一个节点类似序列为一时,显然有序,直接返回
if (head == null || head.next == null) {
return head;
}
Node mid = findMid(head), next = mid.next;
// 这一步很关键,将链表断开,防止死循环
mid.next = null;
Node left = mergeSort(head);
Node right = mergeSort(next);
// 左右两边都排好序了就合并
return merge(left, right);
}
下面来讲解如何定位一单链表的中点,实现原理:快慢指针,定义慢指针slow,快指针fast,慢指针走一步,快指针走两步,等快指针到末尾时,慢指针所指的位置就是链表的中点。
通俗点说就是甲、乙两人要走同样远的路程S,已知甲的速度V,乙的速度为2倍的V,即2*V,两人同时从起点出发,问当乙到达终点(走完全程)时,甲在哪个位置(走了多远)?
速度等于路程除以时间,因为时间是相同的,可以列出方程:S甲/V甲=S乙/V乙
所以S甲=S乙 * V乙 / V甲=S * V / (2 * V) = S/2
即甲走了一半的路程,所在位置即中点。
来看代码实现,
public Node findMid(Node head) {
if (head == null) {
return head;
}
Node slow = head;
Node fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
代码比较简单,若链表节点数量为奇数,毫无疑问中点只有一个,若为偶数,中点就有两个,要前面的还是后面的,稍微改动下代码就能实现,具体怎么做,自己可以在纸上画画
然后就是合并两条有序链表的方法,
private Node merge(Node left, Node right) {
Node head = new Node(-1), cur = head;
while (left != null && right != null) {
if (left.val < right.val) {
cur.next = left;
left = left.next;
} else {
cur.next = right;
right = right.next;
}
cur = cur.next;
}
if (left != null) {
cur.next = left;
}
if (right != null) {
cur.next = right;
}
return head.next;
}
至此,对单链表进行归并排序介绍结束,测试类同上一篇如何对单链表进行快速排序
对链表排序也不过如此嘛,如果想更深入的掌握链表结构可以尝试用十大排序对单链表进行排序,篇幅有限,就不一一介绍了