归并排序和基数排序算法介绍

归并排序的思想

1.分而治之,采用递归的思想进行分化治理,这就是归并排序的一个基本思想。
在这里插入图片描述
不停的往下分化
从中间节点开始分化治理,一直到分到只有一个的时候
分化方法很简答,
单数个数据,就/2,得到那个下标前面的全部放一组
偶数也是/2得到的数据下标前面的一组后面的一组。
在这里插入图片描述
一直划分到只有一个为止。
在这里插入图片描述

怎么拆开的再怎么进行合并回去
合并的过程中要进行比较,合并的时候,让合并的数字进行比较,每组数字都要进行比较,将前面的数字放前面。

在这里插入图片描述

怎么拆的就怎么合并回去,回去时候,还要记得进行排序
在这里插入图片描述
每次将两个组合并到一起的时候,都要记得将里面数据进行排序、

说白了,就是先分化,然后归并回去进行排序

package org.example.sort;



public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {1,4,7,8,3,6,9};
        //这是两个数组,从中间开始分割的两个数组
        //合并时候,两个数组需要的是,找到中间分割那个为止
        sort(arr,0,arr.length - 1);

        for(int i = 0;i < arr.length;i++){
            System.out.println(arr[i]);
        }
    }

    public static void sort(int[] arr,int left,int right){
        if(left == right) return;//左右相等直接就返回了
        //直接去找到中点,找到下标的中间点
        int mid = (right - left) / 2 + left;//起始点加偏移量定位到中点坐标

        //分成两个部分后对左右进行排序
        sort(arr,left,mid);//左边界部分排序
        sort(arr,mid + 1,right);//右边排序


        merge(arr,left,right);//直接将左右告诉它就行自己去找中点进行排序

    }
//这里是给递归回来的,已经好了的数组进行排序
    //指定一下,左指针,右指针,右指针其实就是指向中间,还要有右边界,毕竟这些一会都是要发生变化的,我们是要递归的
    private static void merge(int[] arr,int leftPtr,int rightBound) {//边界是指

        //这里我们需要假定前面数据虽然分为两段,但是每一段数据都是被排好序的
        //因为递归返回来的结果就是两段数据本身就是被排好序的
        int mid = (rightBound - leftPtr) / 2 + leftPtr;//起始点加偏移量定位到中点坐标
        //找到中间分割的位置
        int[] temp = new int[rightBound - leftPtr + 1];


        //需要做的事就是将整个数组进行分解,
        //前面一半数据,后面一半数据,然后遍历前后数据,进行排序合并
        int i = leftPtr;
        int j = mid + 1;//将数组从中间分割的位置分成两个数组
        //遍历前后两个数组用的

        int 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++];

        //将数据都存在临时temp数组里了,所以我们要讲temp替换到arr里面去
        for(int value = 0;value < temp.length;value++){
            arr[leftPtr++] = temp[value];
            //将temp的值全部注入到arr中去。这样相当于一段一段修改arr的值
        }

        //
    }
}

正整理一下思路,
先分而治之方法,每次都沿着中间去将数据分成两个部分
每次分成两个,每次分成两个,一直到
数据被分成一个一个
使用递归去分

在这里插入图片描述
不断递归下去,将数组分成两份,每一份都,左边一份右边一份
每一份都要进行排序

第二步是将左右排序好的序列,合并到一起,
然后这两个序列合并前需要再重新拍一下队。这样排完之后,再进行递归。

//这里是给递归回来的,已经好了的数组进行排序
    //指定一下,左指针,右指针,右指针其实就是指向中间,还要有右边界,毕竟这些一会都是要发生变化的,我们是要递归的
    private static void merge(int[] arr,int leftPtr,int rightBound) {//边界是指

        //这里我们需要假定前面数据虽然分为两段,但是每一段数据都是被排好序的
        //因为递归返回来的结果就是两段数据本身就是被排好序的
        int mid = (rightBound - leftPtr) / 2 + leftPtr;//起始点加偏移量定位到中点坐标
        //找到中间分割的位置
        int[] temp = new int[rightBound - leftPtr + 1];


        //需要做的事就是将整个数组进行分解,
        //前面一半数据,后面一半数据,然后遍历前后数据,进行排序合并
        int i = leftPtr;
        int j = mid + 1;//将数组从中间分割的位置分成两个数组
        //遍历前后两个数组用的

        int 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++];

        //将数据都存在临时temp数组里了,所以我们要讲temp替换到arr里面去
        for(int value = 0;value < temp.length;value++){
            arr[leftPtr++] = temp[value];
            //将temp的值全部注入到arr中去。这样相当于一段一段修改arr的值
        }

这里是合并代码部分,递归返回来的其实是一个整个序列,只不过这个序列左右都排好了,我们以中点为分界线,左边是拍好的,右边也是拍好的,但是左右整体是没拍好的,我们需要排好左右

或者我自己写了一个代码也ok

package org.example.sort;



public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {1,4,7,8,3,6,9};
        //这是两个数组,从中间开始分割的两个数组
        //合并时候,两个数组需要的是,找到中间分割那个为止
        sort(arr,0,arr.length - 1);

        for(int i = 0;i < arr.length;i++){
            System.out.println(arr[i]);
        }
    }

    public static int[] sort(int[] arr,int left,int right){
        if(left == right) return arr;//左右相等直接就返回了
        //直接去找到中点,找到下标的中间点
        int mid = (right - left) / 2 + left;//起始点加偏移量定位到中点坐标

        //分成两个部分后对左右进行排序
        sort(arr,left,mid);//左边界部分排序
        sort(arr,mid + 1,right);//右边排序

        //这里是给递归回来的,已经好了的数组进行排序
        //指定一下,左指针,右指针,右指针其实就是指向中间,还要有右边界,毕竟这些一会都是要发生变化的,我们是要递归的

            //这里我们需要假定前面数据虽然分为两段,但是每一段数据都是被排好序的
            //因为递归返回来的结果就是两段数据本身就是被排好序的

            //找到中间分割的位置
            int[] temp = new int[right - left + 1];


            //需要做的事就是将整个数组进行分解,
            //前面一半数据,后面一半数据,然后遍历前后数据,进行排序合并
            int i = left;
            int j = mid + 1;//将数组从中间分割的位置分成两个数组
            //遍历前后两个数组用的

            int k = 0;//将数组整合成新的排好序的数组
            while (i <= mid && j <= right){
                temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
            }

            //跑完了循环然后没完成的部分接上
            while (i <= mid) temp[k++] = arr[i++];
            while (j <= right) temp[k++] = arr[j++];

            //将数据都存在临时temp数组里了,所以我们要讲temp替换到arr里面去
            for(int value = 0;value < temp.length;value++){
                arr[left++] = temp[value];
                //将temp的值全部注入到arr中去。这样相当于一段一段修改arr的值
            }
            //排序好

           return temp;



    }
//这里是给递归回来的,已经好了的数组进行排序
    //指定一下,左指针,右指针,右指针其实就是指向中间,还要有右边界,毕竟这些一会都是要发生变化的,我们是要递归的
    private static void merge(int[] arr,int leftPtr,int rightBound) {//边界是指


        //
    }
}

package org.example.sort;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class MergeSort {


    public static void main(String[] args) {
        int[] nums = new int[]{10,3,5,11,12,45,67,10,4};
        Merge(nums,0,nums.length - 1);
        //左右递归界限放进去
        for (int i:nums
        ) {
            System.out.println(i);

        }


    }

    public static void Merge(int[] nums,int left,int right){
        if(left == right){
            return;
        }
        //归并排序
        //先进行拆分,找到中点进行拆分
        int mid = (left + right) / 2;//找到中间点
        //中间点到左边的向左递归
        Merge(nums,left,mid);
        Merge(nums,mid + 1,right);//中间点到最后向右递归
        //本质就是递归回来后
        //左边从nums数组里从left 到 Mid已经被排好顺序了
        //右边mid + 1到right也已经排好顺序了
        //我们需要将这两个排好顺序的再次进行排序

        int pL = left;
        int pR = mid + 1;//两个指针进行比较排序
        int[] result = new int[nums.length];//result作为临时存储数组,本质存储的是left到right这一段的
        //nums排序的值
        int i = left;
        while (pL <= mid && pR <= right){
            if(nums[pL] <= nums[pR]){
                result[i++] = nums[pL++];


            }
            else {
                result[i++] = nums[pR++];
            }

        }

        while (pL <= mid){
            result[i++] = nums[pL++];
        }
        while (pR <= right){
            result[i++] = nums[pR++];
        }

       //result其实是nums中的一段,这一段的范围是
        //left到right
       for(int j = left;j <= right;j++){
           //nums是一段一段
           //result中包含的值只是nums中的一段
           //这一段起自left,也就是左边界,重点是右边界,
           nums[j] = result[j];

       }
       //或者我们这样处理,让result和nums一样,然后,将result值只存放left到rigth这个区间段的





    }

}








新写的归并代码
归并排序
8个数据会Megre 8 -1 次
在这里插入图片描述
基数排序

桶排序思路中一种
计数排序
在这里插入图片描述
数据量大,但是范围小
数组下标是数据,然后每个下标位置上数据是这个下标重复次数。

简单来说就是将
出现的数据
比如数据是1 - 15,将1到15按照下标做成一个数组
数组中去记录,每个下标出现的次数
比如11出现 3次,那在数组arr[11]这个位置,就是3
比如7出现2次,那在数组arr[7]这位置就是2

有一个统计数组
还有一个返回数组
返回数组根据统计数组数据,恢复成一个完成数组。

package org.example.sort;


import java.util.Arrays;

public class MergeSort {
    private static int[] arr = {1,1,2,3,4,5,6,1,5,14};;
    public static void main(String[] args) {

        //这是两个数组,从中间开始分割的两个数组
        //合并时候,两个数组需要的是,找到中间分割那个为止
        int[] sort = sort();



        System.out.println(" ");
        for (int i = 0;i < arr.length;i++){

            System.out.print(arr[i] + "  ");
        }






    }

    public static int[] sort(){
        int[] counts = new int[15];
        //数据范围是0 - 14,计数数组用来统计每个数据重复多少次
        int[] result = new int[arr.length];//用来返回结果

        for(int i = 0;i < arr.length;i++){
            //遍历数据放进计数数组
            counts[arr[i]]++;//计数数组下标就是要放的数据的位置

        }

        for(int i = 0;i < counts.length;i++){
            System.out.print(counts[i] + "  ");
        }

        //由计数数组恢复出来
        int j = 0;
        for(int i = 0;i < counts.length;i++){
            while(counts[i] > 0){
                //计数数组里,没有的数,都会为0,比如下标为9的数,如果没有,那么a[9] = 0,这种没有的数我们就不便利了
                arr[j++] = i;
                counts[i]--;//每提交一个数据,统计存量时候就会下降一下,一直到这个数据被完全释放完了
                //比如counts[3] = 5,说明3这个数据重复了五次,那么我们就需要将5个3放进arr里
                //这个时候,i,和j不能同事处理,毕竟I是用来遍历counts的,J是用来往arr中加数据的,j遇到那种重复数据要动好几下,i才会动
            }
        }
        return arr;


    }

}

计数排序有一个严重问题,就是当数据是很大范围很大,比如
10 到 100000时候,计数下标太大了。

基数排序
在这里插入图片描述
先按照个位数进行排序

在这里插入图片描述

然后按照十位数进行排序

最后按照百位数进行排序

然后是千位数排序

每次排序都使用一个下标为
0 – 9的一维数组做底层,然后
然后每次设计都是二维数组桶
桶[][]前面那个取值范围是0 – 9代表数据下标排序范围
后面那个括号里面放的是,这个序号下数据有多少个

说白了就是建立10个桶
桶下标就是存放依据
每个桶是每次遍历排序的依据,遍历时候将数据放进对应桶里
放完之后将数据又重新按照桶中位置码放好。

package org.example.sort;


import java.util.Arrays;

public class MergeSort {
    private static int[] arr = {18,48,77,767,6,6,91,1,14};;
    public static void main(String[] args) {

        //这是两个数组,从中间开始分割的两个数组
        //合并时候,两个数组需要的是,找到中间分割那个为止
        int[] sort = sort(3);
        for(int i = 0;i < arr.length;i++){
            System.out.println(arr[i]);
        }


    }

    public static int[] sort(int input){
        int[][] bucket = new int[10][arr.length];//一个二维数组,因为每次分类,你不知道一个桶里会有多少数据
        int[] bucketElementCounts = new int[10];
        //用于统计某一个位置上数据有多少个
        //记录每个桶放了多少了数据


        //排序轮数跟最大值位数有关
        for(int n = 1, l = 1; l <= input;n = n * 10,l++) {//用来控制循环多少轮


            //第一轮
            for (int i = 0; i < arr.length; i++) {
                int digOfElment = arr[i] / n % 10;//循环多少轮拿到那个数据
                bucket[digOfElment][bucketElementCounts[digOfElment]++] = arr[i];
                //将数组中某一个数组位置上数据

            }
            //将桶中数据,放回到原来数组中
            //i,j用来遍历桶,k用来存放arr中数据
            int k = 0;
            for (int i = 0; i < 10; i++) {
                //遍历上面那个统计数组,还原数据排序
                //统计数组中,为数值为0的下标不参与排序,数值为0就说明,这个下标没有数据
                //bucketElementCounts里放的是当某一个位置上数据有多少个
                //比如bucketElementCounts[1]
                if (bucketElementCounts[i] > 0) {//遍历每个桶,找到有数据那个桶
                    //当前遍历的行里,桶里有数据
                    int j = 0;
                    while (j < bucketElementCounts[i]) {
                        //只要这个位置上数据还是大于1,就会让循环继续下去。
                        arr[k] = bucket[i][j];//将下标交给数据,交出一个数据自己值减少一次
                        bucket[i][j] = 0;//交出数据之后自己这个位置的值为0
                        j++;
                        k++;

                    }
                }

                bucketElementCounts[i] = 0;
                //一个列检查完了之后让所有计算每列数据那个也为零
            }

        }
        return  arr;

    }

}

第二种微微改正方式

package org.example.sort;


import java.util.Arrays;

public class MergeSort {
    private static int[] arr = {18,48,77,767,6,6,91,1,14};;
    public static void main(String[] args) {

        //这是两个数组,从中间开始分割的两个数组
        //合并时候,两个数组需要的是,找到中间分割那个为止
        int[] sort = sort(3);
        for(int i = 0;i < arr.length;i++){
            System.out.println(arr[i]);
        }


    }

    public static int[] sort(int input){


        //第二种改法,将所有数据当做临时变量,可以节省空间,这样不用特意去把桶清空

        //排序轮数跟最大值位数有关
        for(int n = 1, l = 1; l <= input;n = n * 10,l++) {//用来控制循环多少轮
            int[][] bucket = new int[10][arr.length];//一个二维数组,因为每次分类,你不知道一个桶里会有多少数据
            int[] bucketElementCounts = new int[10];
            //用于统计某一个位置上数据有多少个
            //记录每个桶放了多少了数据


            //第一轮
            for (int i = 0; i < arr.length; i++) {
                int digOfElment = arr[i] / n % 10;//循环多少轮拿到那个数据
                bucket[digOfElment][bucketElementCounts[digOfElment]++] = arr[i];
                //将数组中某一个数组位置上数据

            }
            //将桶中数据,放回到原来数组中
            //i,j用来遍历桶,k用来存放arr中数据
            int k = 0;
            for (int i = 0; i < 10; i++) {
                //遍历上面那个统计数组,还原数据排序
                //统计数组中,为数值为0的下标不参与排序,数值为0就说明,这个下标没有数据
                //bucketElementCounts里放的是当某一个位置上数据有多少个
                //比如bucketElementCounts[1]
                if (bucketElementCounts[i] > 0) {//遍历每个桶,找到有数据那个桶
                    //当前遍历的行里,桶里有数据
                    int j = 0;
                    while (j < bucketElementCounts[i]) {
                        //只要这个位置上数据还是大于1,就会让循环继续下去。
                        arr[k] = bucket[i][j];//将下标交给数据,交出一个数据自己值减少一次
                        j++;
                        k++;

                    }
                }


                //一个列检查完了之后让所有计算每列数据那个也为零
            }

        }
        return  arr;

    }

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值