经典内部排序算法学习总结(算法思想、可视化、Java代码实现、改进、复杂度分析、稳定性分析)

本文详细介绍了经典内部排序算法,包括冒泡排序、选择排序、插入排序、希尔排序、堆排序、归并排序、快速排序、桶排序/基数排序/计数排序。阐述了每种排序算法的基本思想、Java实现、改进方法、时间复杂度、空间复杂度和稳定性分析。此外,讨论了不同排序算法在不同场景下的适用性和选择准则,强调了理解排序目的对于优化算法的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是排序算法?

排序,顾名思义,就是按照一定的规则排列事物,使之彼此间有序
而排序算法所要做的工作,就是将数据按照人为制定的比较规则排列好,使数据处于彼此间有序的状态。


二、为什么要进行排序?

那为什么要将数据排序呢?计算机处理速度这么快,会不会有点多此一举。现在考虑手上有一本目录乱序的词典,假设有1w个单词,如果想要查apple这个单词,每次都要从头开始找,一个个的确定是不是apple,忽略心力交瘁和砸字典的冲动,那么假设每次查找都需要12个小时。好,现在手上有一本有序的牛津词典,就是当今经常看到的这种,每次我们查apple这个词时,就可以根据字母的顺序,不到一分钟就可以找出apple的释义了。这样一看,有序与无序就相差了12个小时,而且是每次查找都节省12个小时,则让每个人都可以节省更多的时间去做其他的事情。类比到计算机也是一样的道理,节省下来的时间资源是巨大的,这种规模效益无疑值得我们去排序。

有时候,告诉一个人怎样去做一件事不如告诉他为什么要这么做。例如,告诉一个新手程序员怎样去优化一段代码,他有可能会拖延,但是告诉他说,一旦完成优化,每个用户浏览所花费的时间都会节省5秒。这样出来的效果是不一样的。——观点来源于网络

因此,觉得网络上很多文章一上来就直接说排序算法的思想以及如何实现是不够的。先要弄清楚为什么要排序,再去了解排序算法的细枝末节,毕竟所有排序算法,都是为了一个目的服务的——节约时间。


三、经典内部排序算法思想、可视化、Java代码实现、改进方法、时间复杂度、空间复杂度、稳定性

结论先行,接下来会有大量篇幅去讲述排序算法的细节。为了方便,数组元素都使用整数。
结论
博客参考:
八大排序算法
十种排序算法总结
视觉直观感受7种常用的排序算法
常用排序算法稳定性、时间复杂度分析(转,有改动)
常用排序算法时间复杂度分析
十大经典排序算法|JavaScript描述

算法可视化网站:
VisuAlgo
书籍:Data Structures and Algorithm Analysis in Java (Thrid Edition)


1.冒泡排序BubbleSort

介绍:
冒泡排序是一种较为容易理解的排序算法,因为它就是相邻数两两比较,符合条件就交换位置而已,如果从小到大排的话,就像水中升起的泡泡一样越来越大

算法步骤:基于交换
假设数组a[n]有N个整数
第一趟,第一个数与第二个数比较,符合条件就交换位置,然后第二个数和第三个数比较,符合条件就交换位置,以此类推。如此,最后一个数字为最大数;
第二趟,除去第一趟最后一个数字,第一个数与第二个数比较,符合条件就交换位置,如此类推,此时最后一个数字为本趟最大数
第三趟,如上类推
……
第N-1趟,如上类推,所有交换完成后数组元素有序

可视化:
冒泡排序

Java代码实现:

public class BubbleSort {
    public static void sort(int[] a){
        int temp;//定义用于交换的临时变量
        int length = a.length;//定义递减长度变量

        //外循环,冒泡排序进行的趟数,取a.length-1是因为最后一趟只有一个数字,没有必要排序
        for(int i=0;i<a.length-1;i++){
            //内循环,实际进行两两比较
            for(int j=0;j<length-1;j++){
                if(a[j]>a[j+1]){
                    temp = a[j+1];
                    a[j+1] = a[j];
                    a[j] = temp;
                }
            }
            length--;//每趟结束后,最后一个数字有序且为该趟最大,下一趟排序不必进行比较

            //用于在控制台输出每一趟的结果
            System.out.println();
            System.out.print("第"+(i+1)+"趟排序");
            for(int k = 0;k<a.length;k++){
                System.out.print(a[k]+" ");
            }
        }
    }

    public static void main(String[] args) {
        int[] a = {
  
  99,89,76,66,54,47,32,20,18,5};
        System.out.print("原数组:");
        for(int i = 0;i<a.length;i++){
            System.out.print(a[i]+" ");
        }
        sort(a);
        System.out.println();
        System.out.print("冒泡排序结束后:");
        for(int i = 0;i<a.length;i++){
            System.out.print(a[i]+" ");
        }
    }
}

运行结果:

改进方法:
(1)设置标志位,每一趟循环开始默认有序,当发生交换时,则数组无序,仍需继续循环
参考博客白话经典算法系列之一 冒泡排序的三种实现

public static void improveBubbleSort(int[] a){
        int temp;//定义用于交换的临时变量
        int length = a.length;//length存储数组长度,可以避免每次循环中调用a.length方法的消耗
        boolean flag = true;//设置标志位为ture,假设初始状态无序

        //若无序,则进行循环
        while(flag){
            flag = false;//假设元素正序
            for(int i = 0;i<length-1;i++){
                if(a[i]>a[i+1]){
                    temp = a[i+1];
                    a[i+1] = a[i];
                    a[i] = temp;
                    //若发生交换操作,则为无序状态,仍需进行下一次循环
                    //若不发生交换操作,则此步不会执行,则为正序,只需执行一趟循环
                    flag=true;
                }
            }
        }
    }

(2)轮流从左到右以及从右到左进行冒泡排序
参考博客:排序算法系列:冒泡排序与与双向冒泡排序

(3)同样是设置标志位,不过这个标志位用于记录最后一次发生交换的位置,则下一次循环只需要执行到该位置即可,因为后面的元素已经有序。

时间复杂度:与比较次数、逆序数有关,一次交换减少一个逆序
最好情况为O( N )。按照最开始的思路,假设元素一开始全部有序,但即使不需要交换,都需要不断循环比较,N个元素两两比较共需要 N(N1)2 次,再加上其他赋值等操作,所以时间复杂度为O( N2 );改进方法一,若元素一开始全部有序,则一次循环即可比较次数为N-1,再加上其他赋值等操作,所以时间复杂度为O( N )。

最坏情况为O( N2 )。元素一开始全部逆序,则逆序数有 N(N1)2 个,按照最开始的思路,则需要经历N-1趟,共 N(N1)2 次比较与交换,再加上其他赋值等操作,所以时间复杂度为O( N2 );采用改进一的算法,也需要进行N-1次循环,所以时间复杂度为O( N2 )。

平均情况为O( N2 )。书上定理:N个互异数的数组的平均逆序数是 N(N

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值