Java-数据结构-排序(三) |ू・ω・` )

目录

❄️一、归并排序:

[☞ 基本思想:](#☞ 基本思想:)

[☞ 代码:](#☞ 代码:)

[☞ 归并排序的非递归方法:](#☞ 归并排序的非递归方法:)

❄️二、排序算法的分析:

❄️三、非基于比较的排序:

❄️总结:


❄️一、归并排序:

☞ 基本思想:

归并排序是一个建立在归并操作上的一种排序算法,这个算法是采用分治法的典型案例。

就是:将有序的子序列合并,得到一个完全有序的列表。

即是先使每个子序列有序,再使子序列段间有序。

若将两个有序的列表合并成一个列表,称为二路归并。

我们来看看这个排序是如何进行排序的:

就是分成了 分解操作 和 合并操作 ,那么我们的这个 分解操作是如何做的,合并操作又是如何做的?我们呢来一一的看看如何操作的:

分解操作:

1、我们呢对于这个数据 开始位置设置为left ,结束位置设置为 right ,之后求出中间值 mid ,

2、之后把left 到 mid 下标的值分为一组,mid +1 到 right 的值分为一组。

3、对于 left 到 mid这一组,再分的话把 mid 的下标给到 right 之后再求 mid 位置。

4、对于 mid+1 到 right 这一组,再分的话就把 mid+1 的下标给到 left 之后再求 mid 位置。

5、这样直至我们的 left >= right 的时候结束。

OK,我们的分解思路就是这样的,我们呢来看看代码是如何编写的 :

java 复制代码
public static void mergeSort(int[] array) {
        mergeSortTmp(array,0,array.length - 1);
    }

    private static void mergeSortTmp(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        //分解操作
        int mid = (left + right) / 2;
        mergeSortTmp(array,left,mid);
        mergeSortTmp(array,mid+1,right);
        
    }

合并操作:

1、我们在每次分解完一个区间的数据之后呢,就对其进行合并操作,合并之后就是有序的数据了

2、比如我们合并长度为2 的两个有序的数据,就是相当于合并两个有序的数组

3、我们要知道每个数组的开始和结尾

第一个数组:开头是left 用s1 保存,结尾是mid 用e1 保存

第二个数组:开头是mid+1 用s2 保存,结尾是right 用e2 保存

4、我们把 s1 和 s2 下标的值进行比较之后小的值呢放到新的数组中,只要有一个超过了e1或者e2就需要退出循环,进行下一步的判断操作。

5、还有判断哪个数组放到新的数组中,之后再把没放入的放进数组中

6、把排好序的数组放到原先的数组中,但是要注意放的时候我们的原数组的下标要加上一个left

因为我们第二次排序的时候呢下标不是从 0 开始的是从 left 下标开始的。

我们来看看代码是如何编写的:

java 复制代码
    private static void merge(int[] array, int left, int mid, int right) {
        int[] tmp = new int[right - left + 1];
        int k = 0;
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;

        while(s1 <= e1 && s2 <= e2) {

            if (array[s1] <= array[s2]) {
                tmp[k++] = array[s1++];
            }else {
                tmp[k++] = array[s2++];
            }
        }

        while (s1 <= e1) {
            tmp[k++] = array[s1++];
        }
        while (s2 <= e2) {
            tmp[k++] = array[s2++];
        }

        for (int i = 0; i < k; i++) {
            array[i+left] = tmp[i];
        }
    }

这就是我们的合并排序的代码了,我们来看看整体的代码:


☞ 代码:

java 复制代码
 /**
     * 归并排序
     * 时间复杂度:O(N*logN)
     * 空间复杂度:O(N)
     * 稳定性:稳定
     * @param array
     */
    public static void mergeSort(int[] array) {
        mergeSortTmp(array,0,array.length - 1);
    }

    private static void mergeSortTmp(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        //分解操作
        int mid = (left + right) / 2;
        mergeSortTmp(array,left,mid);
        mergeSortTmp(array,mid+1,right);
        //每一个分解完之后呢,都要进行排序合并操作
        merge(array,left,mid,right);
    }

    private static void merge(int[] array, int left, int mid, int right) {
        int[] tmp = new int[right - left + 1];
        int k = 0;
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;

        while(s1 <= e1 && s2 <= e2) {

            if (array[s1] <= array[s2]) {
                tmp[k++] = array[s1++];
            }else {
                tmp[k++] = array[s2++];
            }
        }

        while (s1 <= e1) {
            tmp[k++] = array[s1++];
        }
        while (s2 <= e2) {
            tmp[k++] = array[s2++];
        }

        for (int i = 0; i < k; i++) {
            array[i+left] = tmp[i];
        }
    }

☞ 归并排序的非递归方法:

我们的非递归呢,就是把我们的分解操作变成非递归的操作,那么如何做呢?

对于分解操作,我们一开始是不是可以看成它们是 一个一个有序,之后是两个两个有序,之后是四个四个有序,这样下去就可以把我们的分解操实现了。

1、我们定义一个 gap 用来 判断是几个一组的

我们的 gap 每次变化都是 乘2 的,并且 gap < 数组长度 才可以执行下面的操作

2、我们的定义个 i 从 0 下标开始,我们的 left 就是 i

这里我们的 i 不能是++操作进行往下走,我们的 i 这里是 i = i + gap*2

3、mid = left + gap - 1

在赋值之后呢,我们的mid 要进行判断操作,是否是 >= 数组的长度 ,如果是,就把其进行 - 1

因为这里我们之后要传给 合并有序数组的操作,防止数组越界

4、right = mid + gap

这里同样要进行判断操作, 是否是 >= 数组的长度 ,如果是,就把其进行 - 1

代码:

java 复制代码
public static void mergeSort(int[] array) {
        mergeSortNor(array);
    }

private static void mergeSortTmp(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        //分解操作
        int mid = (left + right) / 2;
        mergeSortTmp(array,left,mid);
        mergeSortTmp(array,mid+1,right);
        //每一个分解完之后呢,都要进行排序合并操作
        merge(array,left,mid,right);
    }
//非递归实现归并
public static void mergeSortNor(int[] array) {
        int gap = 1;
        while (gap < array.length) {
            for (int i = 0; i < array.length; i = i + gap*2) {
                int left = i;
                int mid = left + gap - 1;
                if (mid >= array.length) {
                    mid = array.length - 1;
                }
                int right = mid + gap;
                if (right >= array.length) {
                    right = array.length - 1;
                }
                merge(array,left,mid,right);
            }
            gap *= 2;
        }
    }

❄️二、排序算法的分析:

|----------|----------------|----------------|----------------|---------------------|---------|
| 排序方法 | 最好 | 平均 | 最坏(时间复杂度) | 空间复杂度 | 稳定性 |
| 冒泡排序 | O(N^2) | O(N^2) | O(N^2) | O(1) | 稳定 |
| 插入排序 | O(N) | O(N^2) | O(N^2) | O(1) | 稳定 |
| 选择排序 | O(N^2) | O(N^2) | O(N^2) | O(1) | 不稳定 |
| 希尔排序 | O(N) | O(N^1.3) | O(N^1.5) | O(1) | 不稳定 |
| 堆排序 | O(N*logN) | O(N*logN) | O(N*logN) | O(1) | 不稳定 |
| 快速排序 | O(N*logN) | O(N*logN) | O(N^2) | O(log(N))~O(N) | 不稳定 |
| 归并排序 | O(N*logN) | O(N*logN) | O(N*logN) | O(N) | 稳定 |


❄️三、非基于比较的排序:

我们之前介绍的排序都是需要比较的排序方法,我们还有不需要比较的排序方法:计数排序、基数排序、桶排序。

我们的非基于比较的排序可以通过 "传送门" 来了解:

基数排序:

基数排序

桶排序:

桶排序

当然如果需要 计数排序的 传送门 的话,这里也有:

计数排序


❄️总结:

OK,到这里呢我们的排序呢就结束了,这次关于排序的代码呢还是比较多的,要认真的理解它们,同时要画图的去理解它们的执行流程。 下一篇博客呢我们就要介绍关于Map与Set的知识啦,让我们尽情期待吧!!!拜拜~~~

相关推荐
转世成为计算机大神9 分钟前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
搬砖的小码农_Sky10 分钟前
C语言:数组
c语言·数据结构
宅小海28 分钟前
scala String
大数据·开发语言·scala
qq_3273427330 分钟前
Java实现离线身份证号码OCR识别
java·开发语言
锅包肉的九珍31 分钟前
Scala的Array数组
开发语言·后端·scala
心仪悦悦34 分钟前
Scala的Array(2)
开发语言·后端·scala
yqcoder1 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
Swift社区1 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
baivfhpwxf20231 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
许嵩661 小时前
IC脚本之perl
开发语言·perl