2021-10-12

归并排序(略过定义,把重点放在介绍如何一步步写出归并排序的思路)

第一步思考:

1、一开始:假设有一个自中间节点mid,在mid左右两边分别都已经有序的数组arr。如何将这个无序的数组变成有序呢?

思路

  1. 左半边的范围:0 – mid; 右半边的范围mid+1 – arr.length;

  2. 开始分别从左右两边的起始位置做比较,将其中较小的元素放在另外一个数组arrNew中(与原数组等长)

  3. 循环进行上一步操作,直到其中一个半边已经没有数据了,那么将另外一个半边的数据直接复制给新的数组arrNew

2、上述思路的实现
static void merge(int[] arr){
        //作为左半边的起始标记
        int i =0;
        //中间节点mid
        int mid = (arr.length-1)/2;
        //作为右半边的起始节点
        int j = mid+1;
        //作为新数组的起始节点
        int k = 0;
        //新的数组arrNew
        int[] arr2 = new int[arr.length];
        //循环 - 比较 -复制
        while(i<=mid&&j<arr.length){
            if(arr[i]<=arr[j]){
                arr2[k++] = arr[i++];
            }else{
                arr2[k++]=arr[j++];
            }
        }
        //当其中一个半边没有数据,而另一个半边还有数据时,直接将余下的数据复制给arrNew
        while(i<=mid) arr2[k++]=arr[i++];
        while(j<arr.length) arr2[k++] = arr[j++];
    }

第二步思考:

3、对于上述的改进,让上述变得更加灵活,不再规定是左半边和右半边,而是数组的某一部分,从下标left到下标为rightBound的部分数组元素,同时我们也需要一个mid标记作为右半边数据的起始下标**
  //归并排序 排好的两半的升级版
   //归并排序 排好的两半的升级版
    static void merge(int[] arr,int left,int right,int rightBound){
        //创建一个新的数组arrNew用来存储排好序的新数组
        //这里要注意数组长度已经不是arr.length,而是数组中一部分的长度
        int[] arrNew = new int[rightBound-left+1];
        int i = left;
        int j = right;
        int mid = right-1;
        int k = 0;
        while(i<=mid && j<=rightBound){
            if(arr[i]<=arr[j]) {
                arrNew[k++]=arr[i++];
            }else{
                arrNew[k++]=arr[j++];
            }
        }
        while(i<=mid) arrNew[k++] = arr[i++];
        while(j<=rightBound) arrNew[k++] = arr[j++];
        //将改变好的值赋值给原来的数组
        for(int x = 0;x<rightBound-left+1;x++){
            arr[left+x] = arrNew[x];
        }
    }
4、最终的归并排序算法

递归调用自己,并调用合并左右两边的算法即可

//归并排序
    public static void sort(int[] arr,int left,int right){
    //如果左边等于右边说明这个数组只有一个元素,则直接返回
        if(left == right) return;
        //分成两半
        int mid = left + (right-left)/2; //防止int类型溢出
        //左边排序
        sort(arr,left,mid);
        //右边排序
        sort(arr,mid+1,right);
        //合并左右两边
        merge(arr,left,mid+1,right);

    }

    //归并(合并左右两边)
    static void merge(int[] arr,int leftPtr,int rightPtr,int rightBound){
        int mid = rightPtr-1;
        int[] temp = new int[rightBound-leftPtr+1];

        int i,j,k;
        i = leftPtr;
        j = rightPtr;
        k = 0;

        while(i<=mid && j<=rightBound){
            temp[k++] = arr[i]<=arr[j]?arr[i++]:arr[j++];
        }
        while(i<=mid) temp[k++] = arr[i++];
        while(j<=rightBound) temp[k++] = arr[j++];

        for(int m = 0;m<temp.length;m++){
            arr[leftPtr + m] = temp[m];
        }
    }
5、时间复杂度、空间复杂度分析

时间复杂度 :O(nlogn)
最好情况下:O(n
logn)
最坏情况下:O(n*logn)
空间复杂度:O(n)

稳定性:稳定

6、验证

因为排序次数少或者原始数据较少可能会产生偶然性,应该编写程序验证排序算法的正确性。



public class QuickSort {
    public static void main(String[] args) {
//        int[] arr = {1,5,9,8,7,4,6,2,3,10,1,2,6};
//        quickSort(arr,0,arr.length-1);
//        printArr(arr);
        check();

    }

    //快速排序
    static void sort(int[] arr,int leftBound,int rightBound) {
        if (leftBound >= rightBound) return;

        int mid = partition(arr, leftBound, rightBound);
        sort(arr,leftBound,mid-1);
        sort(arr,mid+1,rightBound);

    }

    //最开始的分成两半
     static int partition(int[] arr, int leftBound, int rightBound) {
        //设置一个轴 作为比较的初始值
        int pivot = arr[rightBound];
        int left = leftBound;
        int right = rightBound-1;

        while(left<=right){
            while(left<=right && arr[left]<=pivot) left++;
            while( left<=right && arr[right]>pivot) right--;

            if(left<right) swap(arr,left,right);
        }
        swap(arr,left,rightBound);
        //返回轴的位置
        return left;
    }


    //交换两个数
    static void swap(int[] arr ,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    //打印数组
    static void printArr(int[] array){
        for(int i:array)
        {
            System.out.print(" "+i);
        }
    }

    //验证排序算法的正确性
    static void check(){
        int[] arr = generateRandomArray();
        //复制一个数组,一个用Java自带的Array.sort()方法,另外一个用自己写的选择排序
        int[] arr2 = new int[arr.length];
        //系统带的排序算法
        Arrays.sort(arr);
        //自己写的排序算法
        sort(arr2,0,arr.length-1);
        System.arraycopy(arr,0,arr2,0,arr.length);
        boolean same = true;
        for(int i = 0;i<arr2.length;i++){
            if(arr[i]!=arr2[i])
                same=false;
        }
        System.out.println( same == true ? "right":"wrong");
    }

    //产生一个随机数组
    static int[] generateRandomArray(){

        int[] array = new int[10000];
        Random r = new Random();
        for(int i=0;i<array.length;i++)
        {
            array[i]=r.nextInt(10000);
        }
        return array;

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值