(纯白话算法系列)归并排序,时间复杂度分析、代码演示,堆是什么?

归并排序(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),比较难,所以本篇博客不作记载,如有需要请自行百度。

原文地址:https://www.cnblogs.com/taobean/p/12364264.html