[原创]二路归并排序针对单向链表的场景(java版)
二路归并排序最常用的场景是数组,那么如果数据结构采用单向链表呢?如何实现?
基本思想是一样的,对比数组场景,需要注意一下几点:
①在合并阶段:merge(..)合并两个已经有序的链表时,要比数组更简单些;
②在递归调用阶段:mergeSort(...),在该方法里,和数组一样需要确定中间位置,然后分为两半,对这两半分别进行递归调用二路归并排序,
然后得到两个已经排好序的单向链表,最后对这两个单向链表进行合并;
需要注意之处在于,单链表时,需要将一个单链表拆分成两个单链表,中间的next指针联系需要断开,否则在merge的时候会形成环,从而导致无限循环下去;
③该单链表的场景,时间复杂度还是O(N*log2N),但空间复杂度从O(n)减为了O(1);
具体代码如下:
//合并阶段:合并两个已经排好序的单向链表
private ListNode merge(ListNode head1, ListNode head2) {
ListNode head = new ListNode(0);
ListNode p = head;
ListNode p1 = head1;
ListNode p2 = head2;
while (p1 != null && p2 != null) {
if (p1.val < p2.val) {
p.next = p1;
p1 = p1.next;
} else {
p.next = p2;
p2 = p2.next;
}
p = p.next;
}
while (p1 != null) {
p.next = p1;
p1 = p1.next;
p = p.next;
}
while (p2 != null) {
p.next = p2;
p2 = p2.next;
p = p.next;
}
p.next = null;
return head.next;
}
//递归调用阶段
private ListNode mergeSort(ListNode low, ListNode high, int n) {
ListNode head = low;
ListNode leftH = null;
ListNode rightH = null;
if (low != high) {
ListNode mid = getIndexof(low, n / 2);
//注意这里将其拆分为了两个链表,必须将两个链表之间的联系进行断开,否则会无线循环
ListNode midNext = mid.next;
mid.next = null;
leftH = mergeSort(low, mid, n / 2);
rightH = mergeSort(midNext, high, n - n / 2);
head = merge(leftH, rightH);
}
return head;
}
//辅助函数,从low结点出发,返回第n个结点位置(包括low结点)
private ListNode getIndexof(ListNode low, int n) {
ListNode p = low;
while (n-- != 1) {
p = p.next;
}
return p;
}
//通过调用归并排序mergeSort(..)进行排序单链表
//这里为了确定该单向链表最后一个结点以及结点总数,进行了遍历,但这并不影响时间复杂度为O(N*log2N)
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
int n = 1;
ListNode p = head;
while (p.next != null) {
p = p.next;
n++;
}
return mergeSort(head, p, n);
}
浙公网安备 33010602011771号