(純白話算法系列)歸併排序,時間複雜度分析、代碼演示,堆是什麼?

歸併排序(MERGE-SORT)是建立在歸併操作上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併。

白話說:所謂歸併排序,望文生義,就是把兩個東西合併在一起了,那麼是什麼東西合併在一起了呢?是兩個已經排好序的數組進行合併到一個數組中,這兩個排好序的數組分別有一個指針指向第0個元素,誰小誰放進大數組中。

大致排序過程是這樣的:
在這裏插入圖片描述
圖摘自–https://www.cnblogs.com/qingmingsang/articles/5283870.html
先把數組中的元素兩兩比較,創建一個輔助數組,將排好序的數字放進輔助數組中,然後再把輔助數組中的數放回原數組,然後兩個合併好的數組再合併,再創建一個輔助數組,再把兩個數組中排好序的數字放進輔助數組中,再把輔助數組放回原數組中,直到所有的數組全部排好序。

看一下代碼:

package com.bean.com.bean.sortAlg;

import java.util.Arrays;

public class MargeSort {
    public static void MergeSort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }
        MergeSort(arr, 0, arr.length - 1);
    }

    public static void MergeSort(int[] arr, int left, int right) {
        if(left == right) {
            return;
        }
        int mid = left + ((right - left)>>1);//等於(left+right)/2,位運算比較快
        MergeSort(arr, left, mid);//把左邊的元素全拆開
        MergeSort(arr, mid+1, right);//把右邊的元素全拆開
        MergeSort(arr, left, mid, right);//left~right之間排序
    }

    public static void MergeSort(int[] arr, int left, int mid, int right) {
        int[] help = new int[right - left + 1];//輔助數組,暫存排好序的元素
        int index = 0;
        int p1 = left;//左指針
        int p2 = mid + 1;//右指針
        while(p1 <= mid && p2 <= right) {//只要左右指針沒超出範圍,就排序
            help[index++] = arr[p1] > arr[p2] ? arr[p2++] : arr[p1++];
            //誰小誰先入help數組
        }
        while(p2 <= right) {//p1排完了,p2還有沒排完的元素,則依次入數組
            help[index++] = arr[p2++];
        }
        while(p1 <= mid) {//p2排完了,p1繼續依次扔進help數組
            help[index++] = arr[p1++];
        }
        for(int i = 0; i < help.length; i++) {
            arr[left + i] = help[i];//把排好序的元素放回原數組中!
        }
    }
    //打印數組
    public static void printArray(int[] arr) {
        if(arr == null) {
            return;
        }
        for(int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    //形成隨機數組
    public static int[] generateRandomArray(int maxSize, int maxNum) {
        if(maxSize <= 0) {
            return null;
        }
        int[] ranArray = new int[(int) (Math.random() * (maxSize + 1) )];
        for(int i = 0; i < ranArray.length; i++) {
            ranArray[i] = (int) (Math.random() * (maxNum + 1)) - (int) (Math.random() * maxNum);
        }
        return ranArray;
    }
    //判斷兩個數組是否相等
    public static boolean isEquals(int[] arr1, int[] arr2) {
        if(arr1.length != arr2.length) {
            return false;
        }
        if(arr1 == null && arr2 != null || arr1 != null && arr2 == null) {
            return false;
        }
        if(arr1 == null && arr2 == null) {
            return true;
        }
        for(int i = 0; i < arr1.length; i++) {
            if(arr1[i] != arr2[i]) {
                return false;
            }
        }
        return true;
    }
    //完全正確的排序方法
    public static void comparator(int[] arr) {
        Arrays.sort(arr);
    }
    //複製數組
    public static int[] copyArray(int[] arr) {
        int[] newArr = new int[arr.length];
        for(int i = 0; i < arr.length; i++) {
            newArr[i] = arr[i];
        }
        return newArr;
    }
    public static void main(String[] args) {
        int testTimes = 100000;
        int maxNum = 100;
        int maxSize = 2000;
        boolean flag = true;
        for(int i = 0; i < testTimes; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxNum);
            int[] arr2 = copyArray(arr1);
            comparator(arr1);
            MergeSort(arr2);
            if(!isEquals(arr1, arr2)){
                flag = false;
                break;
            }
        }
        System.out.println(flag ? "Nice! Nothing is wrong~" : "Ooops! Your method is wrong..");
        int[] arr1 = generateRandomArray(maxSize, maxNum);
        System.out.print("before:");printArray(arr1);
        long beforetime = System.currentTimeMillis();
        MergeSort(arr1);
        long aftertime = System.currentTimeMillis();
        System.out.println("execute time is:"+(aftertime - beforetime)+"ms");
        System.out.print("after");printArray(arr1);
    }
}


其實就是把一個數組進行拆塊,每一小塊都排序,然後再把兩個排好序的小塊整合,再進行排序,直到所有小塊都排好序,整體排序結束。

時間複雜度

假如有N個元素,那麼每次二分,它的時間複雜度是logN,但是每次二分完了還需要進行比較,比較的次數跟有多少個元素N有關,所以整體時間複雜度是O(n*logn),額外空間複雜度說的就是那個輔助數組,它每次創建完又被銷燬了,所以額外的空間複雜度是O(n),但是根據內部緩存法,可以使額外空間複雜度變成O(1),比較難,所以本篇博客不作記載,如有需要請自行百度。

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