手撕算法——歸併排序及排序鏈表

歸併排序

對於歸併排序,大家怕是都不陌生,也可以非常熟練的寫出歸併排序的代碼,因此本篇文章的重點並不是向大家介紹歸併排序,而是討論歸併排序的拓展題型——力扣:148.排序鏈表。至於歸併排序的原理可以參考:圖解排序算法(四)之歸併排序,下面也貼出代碼實現

代碼實現

/**
歸併排序
 分而治之
 通過遞歸分,然後merge
 */
public class MergeSort {

    public  void sort(int[] a,int begin,int end,int[] tmp){
        int mid=(begin+end)/2;
        if(begin<end){//if begin<end 則分
            sort(a,begin,mid,tmp);
            sort(a,mid+1,end,tmp);
            merge(a,begin,mid,end,tmp);
        }
    }
    public void merge(int a[] ,int begin,int mid,int end,int[] tmp){
        int i=begin;
        int j=mid+1;
        int t=0;
        while(i<=mid&&j<=end){
            if(a[i]<=a[j]){
                tmp[t++]=a[i++];
            }else{
                tmp[t++]=a[j++];
            }
        }
        while(i<=mid){
            tmp[t++]=a[i++];
        }
        while(j<=end){
            tmp[t++]=a[j++];
        }
        /*
       // System.arraycopy(a,0,tmp,0,a.length);
        for(int index=begin;index<=end;index++){
            a[index]=tmp[index];
        }*/

        t=0;
        while(begin<=end){
            a[begin++]=tmp[t++];
        }
    }

    public static void main(String[] args) {
        MergeSort mergeSort=new MergeSort();
        int[] a= {8,4,5,7,1,3,6,2};
        int[] tmp=new int[a.length];
        mergeSort.sort(a,0,a.length-1,tmp);

        for(int e :tmp){
            System.out.println(e);
        }
    }
}

排序鏈表

題目描述:

O(n log n) 時間複雜度和常數級空間複雜度下,對鏈表進行排序。

示例 1:

輸入: 4->2->1->3
輸出: 1->2->3->4

示例 2:

輸入: -1->5->3->4->0
輸出: -1->0->3->4->5

歸併排序的時間複雜度爲O(nlogn),這個大家都知道,但是數組的歸併排序,我們需要創建一個臨時數組來保存中間結果,空間複雜度爲n。所以這道題的主要思想就是如何利用鏈表的將歸併排序的空間複雜度降爲1。
結合歸併排序的思想,我們主要需要考慮以下幾個問題:

  1. 如何找到中間節點
  2. 如何進行拆分
  3. 如何進行合併
    結合之前做鏈表類題型,我們可以很輕鬆的想到答案,這裏直接貼出關鍵代碼:
    (1) 通過快慢指針找中點。
ListNode fast=head.next,slow=head;
        //快慢指針 fast先置爲 head.next,當它走到末尾時 slow指向的正好是中間節點的下標
        //fast 如果也置爲 head, slow最終指向的是中間節點的先一個的下標  這裏需要明確一下
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }

(2)拆分鏈表

		ListNode tmp=slow.next;
        slow.next=null;//拆分
        ListNode left=sortList(head);
        ListNode right=sortList(tmp);

(3)通過虛假頭結點進行鏈表合併

		ListNode h=new ListNode(0);//構造虛假鏈表頭
        ListNode res=h;
        while(left!=null&&right!=null){
            if(left.val<=right.val){
                h.next=left;
                left=left.next;
            }else{
                h.next=right;
                right=right.next;
            }
            h=h.next;
        }
        h.next=left!=null?left:right;
        return  res.next;

總結

總體來說這是一道非常不錯的題目,鏈表相關的知識點和排序都考察到了,非常值得學習!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章