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