十大排序算法总结(Java和Python实现)

4 篇文章 0 订阅
2 篇文章 0 订阅


前言

  学过算法设计与分析或者算法导论的同学都知道在面试中面试官为了考察我们的基础能力,经常会让我们手写一些排序算法。考察比较多的如:快速排序、归并排序、堆排序等较为复杂的算法。而简较为单的排序算法:冒泡排序、选择排序等这些考察的相对较少。
  排序算法也可以划分为外部排序和内部排序两大类。其中外部排序可以参考维基百科上的解释:

  外排序(External sorting)是指能够处理极大量数据的排序算法。通常来说,外排序处理的数据不能一次装入内存,只能放在读写较慢的外存储器(通常是硬盘)上。外排序通常采用的是一种“排序-归并”的策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件。而后在归并阶段将这些临时文件组合为一个大的有序文件,也即排序结果。
  简而言之,可以直接在内存中完成的排序算法称为内部排序,需要用到外存的算法称为外部排序。

接下来我会按照从简单到复杂的这样的一个过程分别介绍这些算法。每一种算法介绍大致分为三个阶段:算法分析、动画演示以及代码构建。

一、排序算法概览

首先我们从整体的角度了解一下目前常用的排序算法,包括他们的时间复杂度以及稳定性。下表排版参考博主一像素的博文。

排序方法平均时间复杂度最坏时间复杂度最好时间复杂度空间复杂度稳定性
插入排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)稳定
希尔排序 O ( n 1.3 ) O(n^{1.3}) O(n1.3) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)不稳定
选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)不稳定
堆排序 O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n) O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n) O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n) O ( 1 ) O(1) O(1)不稳定
冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)稳定
快速排序 O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n) O ( n 2 ) O(n^2) O(n2) O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n) O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n)不稳定
归并排序 O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n) O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n) O ( n log ⁡ 2 n ) O(n\log_{2}{n}) O(nlog2n) O ( n ) O(n) O(n)稳定
......
计数排序 O ( n + k ) O(n + k) O(n+k) O ( n + k ) O(n + k) O(n+k) O ( n + k ) O(n + k) O(n+k) O ( n + k ) O(n + k) O(n+k)稳定
桶排序 O ( n + k ) O(n + k) O(n+k) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n + k ) O(n + k) O(n+k)稳定
基数排序 O ( n ∗ k ) O(n * k) O(nk) O ( n ∗ k ) O(n * k) O(nk) O ( n ∗ k ) O(n * k) O(nk) O ( n + k ) O(n + k) O(n+k)稳定

各项指标解释如下:

最坏时间复杂度: 指的是在最糟糕的情况下所需的时间复杂度。

最好时间复杂度: 指的是在最理想的情况下所需的时间复杂度。

平均时间复杂度: 由于上述两种情况均属于极端事件,发生的概率很小,因此通常会采用平均时间复杂度指标作为代码的时间复杂度。

空间复杂度: 指算法在执行过程中所需要的空间消耗,它也是与输入规模n相关的函数。

稳定性: 在一组待排序数组中,如果存在任意两个相等的元素 A 和 B,并且 元素A 在 元素B 前,如果在排序后元素 A 依然在元素 B 前,即它们的前后位置在排序前后不发生改变,则称为排序算法为稳定的。

堆排序、快速排序、希尔排序、直接选择排序是不稳定的排序算法,而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序等是稳定的排序算法。

二、算法

1. 冒泡排序(Bubble Sort)

算法分析:冒泡排序通常是我们接触的第一个排序算法,它的原理非常简单,它通过元素之间的比较,将元素后移,直到找到合适的位置。通过对每一个元素做比较和移位操作后,可以最终获得有序列表。

注:文中涉及动画演示参考博文链接如下,在此感谢各位博主的贡献。
https://segmentfault.com/a/1190000022080909

在这里插入图片描述
Java版代码如下:

// 1.bubbleSort
    public static void bubbleSort(int[] arrays){
        boolean flag = true;
        for (int i = 1; i <= arrays.length - 1 && flag; i++) {
            flag = false;
            for (int j = 0; j < arrays.length - i; j++) {
                if (arrays[j] > arrays[j + 1]){
                    swap(arrays, j);
                    flag = true;
                }
            }
        }
    }

5. 快速排序(Quick Sort)

算法描述
Quick Sort是一种「把大问题分成小问题处理」的Divide and Conquer方法。

快速排序不稳定,这是因为快排的算法思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小(如果元素相同的话,可以在基准值的左边也可以在右边),则可分别对这两部分记录继续进行排序,以达到整个序列有序。

算法流程
快排采用了分治法的思想,因此采用递归来解决问题是一个很好的方案。初始选中一个元素作为基准(pivot)值(可以任意选择,通常选取最左或最右侧值),通过一次调整后使得基准值左边的元素值小于pivot,而基准值右边的元素值都大于pivot。

1⃣️ 从左侧开始寻找第一个大于pivot的元素下标,记为 i
2⃣️ 从右侧寻找第一个小于pivot的元素下标,记为 j
3⃣️ 如果 i < j i < j i<j,那么交换元素
4⃣️ 如果3⃣️不满足,那么此时将下标为 i 的元素与pivot交换
5⃣️ 对pivot左侧数据和右侧数据分别执行上述4个步骤,直到整个列表有序

算法动画演示如下:
快速排序
此处参考一下掘金关于快速排序算法的动画演示,博主选择使用最右侧作为pivot值。
在这里插入图片描述
算法大同小异,实现方式可能有差异,我选择最左侧数据作为pivot值,实现升序排序。

Java版代码如下:

private static void qSort(int[] arrays, int L, int R){

        // 对L到R之间的区间元素进行快速排序
        if (L >= R){
            return;
        }
        // 以最左边的元素作为基准点
        int pivot = arrays[L];
        int i = L, j = R;
        // 循环中做的是 如何令小于pivot的元素排在其左边,大于pivot的元素排在右边
        while (i < j){
            while (arrays[j] >= pivot && i < j){
                j--;
            }
            while (arrays[i] <= pivot && i < j){
                i++;
            }
            if (i < j){
                int temp = arrays[j];
                arrays[j] = arrays[i];
                arrays[i] = temp;
            }
        }
        // 将基准点与i位置元素进行交换
        arrays[L] = arrays[i];
        arrays[i] = pivot;
        qSort(arrays, L, i - 1);
        qSort(arrays, i + 1, R);
    }

Python版代码如下:

def qSort(arrays, L, R):

        # 对L到R之间的区间元素进行快速排序
        if L >= R:
            return
        
        # 以最左边的元素作为基准点
        pivot = arrays[L]
        i = L; j = R

        # 循环中做的是 如何令小于pivot的元素排在其左边,大于pivot的元素排在右边
        while i < j:
            while arrays[j] >= pivot and i < j:
                j -= 1
            while arrays[i] <= pivot and i < j:
                i += 1
            if i < j: 
                arrays[i], arrays[j] = arrays[j], arrays[i]
                
        # 将基准点与i位置元素进行交换
        arrays[L] = arrays[i]
        arrays[i] = pivot

        qSort(arrays, L, i - 1)
        qSort(arrays, i + 1, R)

# 终端输入数据,以空格作为区分
dataArr = [int(e) for e in input().split(' ')]
qSort(dataArr, 0, len(dataArr) - 1)
print("dataArr sorted: ", dataArr)

总结

未完待续 … …

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值