题干:对一个链表进行归并排序。
对于数组,归并排序的思想是先将数据分成两部份,分别排序。然后再对已经排好序的两个数组归并。其中在对两部份数组排序时进行递归,最后得到的即是排好序的数组。
对于链表,归并的思想也是如此。需要解决的问题有两个,第一个是如何将链表平均分成两部份。 第二个是将两个有序链表合并成一个有序链表。 解决了这两个问题,归并排序链表就迎仞而解了。
对于第一个问题,可以采用快慢指针的方式,找到链表的中点,然后将节点断开,形成两个链表。
代码如下:
/**
* 返回中间节点
* @param head
* @return
*/
public ListNode split(ListNode head) {
if (head == null) {
return head;
}
ListNode prev = null;
ListNode slow = head;
ListNode fast = head;
// 注意这里了要判断fast.next 是否为空
while (fast != null && fast.next != null) {
fast = fast.next.next;
prev = slow;
slow = slow.next;
}
prev.next = null; // 断开链表,返回后面一段的第一个节点
return slow;
}
对于第二个问题,即用两个指针分别指向两个链表,然后比较两个指针指向的链表节点大小,将小的节点添加到结果中去,此时将指向节点值小的指针向后移动,同时保持另一个指针不动,直到其中一个链表遍历完成。最后,将还没有遍历完成的链表链接到结果中。
代码如下:
/**
* 合并两个有序链表
* @param left
* @param right
* @return
*/
public ListNode merge(ListNode left, ListNode right) {
ListNode head = new ListNode(-1);
ListNode cur = head;
while (left != null && right != null) {
if (left.val > right.val) {
cur.next = right;
right = right.next;
} else {
cur.next = left;
left = left.next;
}
cur = cur.next;
}
while (left != null) {
cur.next = left;
cur = cur.next;
left = left.next;
}
while (right != null) {
cur.next = right;
cur = cur.next;
right = right.next;
}
return head.next;
}
有了这两个问题的基础,就可以很容易的进行归并排序了。
代码如下:
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
// 为空 或 只有一个链表节点均不用排序
return head;
}
ListNode mid = split(head);
ListNode left = sortList(head);
ListNode right = sortList(mid);
return merge(left, right);
}
初看这个问题的时候,感觉会无从下手,但是一步步拆解问题后都是可以通过已有的知识来解决。