[原创]二路归并排序针对单向链表的场景(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); }