浅析八大排序算法

一.希尔排序

  • 主要思想通过在每一轮设置索引的增量间隔(随轮数增加不断有规律地缩小)来对每一组间隔的两个元素进行比较排序,直到增量间隔为1。
  • 步骤概述:
    1.设置增量间隔gap的for循环;
    2.设置处理每一组gap间隔的元素的for循环;
    3.通过移位的方式比较排序这两个gap间隔的元素。
  • 模板代码
public static void shellSort(int arr[]){
        for(int gap = arr.length / 2;gap > 0;gap /= 2){
            for(int i = gap; i < arr.length;i++){
                int j = i;
                int temp = arr[j];
                if(arr[j] < arr[j - gap]){
                    while (j - gap >= 0 && arr[j] < arr[j - gap]){
                        arr[j] = arr[j - gap];
                        j -= gap;
                    }
                    arr[j] = temp;
                }
            }
        }
    }
  • 示例理解
    在这里插入图片描述

二.插入排序(从小到大为例)

  • 主要思想把一组元素看成两组,左边为已经有序的,右边为待排序的,然后通过依次让右边的每一个元素与左边从右到左的有序元素作比较,直到找到那个比这个右边的元素大的元素的位置,即为插入位置。

  • 步骤说明
    1.把0索引元素作为左边的有序组,从1索引开始遍历右边待排序组的for循环;
    2.for循环中注意暂存当前的那个右边组的元素值;
    3.while循环条件,如果右边的元素要插入左边,先从左边组的最后一个元素开始比较,由于是从小到大排序,而且左边组已经是有序的,即这个元素在左边组最大。既然想插入到左边组,要么比这个左边元素小,一直找,直到找到插入位置;要么比这个左边元素大,不动。
    4.由于while的递减操作会最终多减去1,则要在该循环外的插入位置索引加1再插入暂存的元素值。

  • 示例图解
    在这里插入图片描述

  • 代码模板

 public static void insertSort(int arr[]){
        for(int i = 1;i < arr.length;i++){
            int inserVal = arr[i];
            int inserIndex = i - 1;
            while(inserIndex >= 0 && arr[inserIndex] > inserVal){
                arr[inserIndex + 1] = arr[inserIndex];
                inserIndex--;
            }
            arr[inserIndex + 1] = inserVal;
        }
    }

三.堆排序(以大顶堆为例)

  • 基础知识回顾
    1.堆:即完全二叉树,可由数组按编号存储。左子节点对应2i+1节点,右子节点为2i+2,父节点为(i-1)/2。例如数组[5, 1, 7, 2, 8, 6, 3, 9, 4],其堆图如下:
    在这里插入图片描述
    2.大顶堆:指每个根节点都比其子节点大,即第一个根节点就是最大的完全二叉树(每一层从左往右编号不中断的二叉树)。大顶堆图示如下:
    在这里插入图片描述
  • 主要思想
    1.大顶堆调节方法:先暂存指定的非叶子节点,然后让该节点与其子节点进行比较并排序,使其构成大顶堆结构。当左右的任一个子节点被与这个父节点交换,则以这个被交换下来的父节点为新的父节点,继续变成大顶堆的操作,最终形成以最初的暂存非叶子节点位置为父节点的局部大顶堆结构。
    2.堆排序方法:基于大顶堆的结构,知道根节点是最大值,那就可以不断缩小遍历范围,让每次缩小范围内的最后一个节点与当前根节点进行交换,即让每次缩小范围的最后一个节点都变成最大值,并对每次缩小范围内的所有节点,从根节点开始,再次重构成大顶堆结构。
  • 代码模板
    1.调节大顶堆:
public static void adjustHeap(int[] arr,int i,int length){
        int temp = arr[i];
        for(int index = i * 2 + 1;index < length;index = index * 2 + 1){
            if(index + 1 < length && arr[index] < arr[index + 1]){
                index++;
            }
            if(arr[index] > temp){
                arr[i] = arr[index];
                i = index;
            }
        }
        arr[i] = temp;
    }

2.堆排序:

public static void heapSort(int[] array){
        int temp;
        for(int i = array.length/2 - 1;i >= 0;i--){
            adjustHeap(array,i,array.length);
        }
        for(int j = array.length - 1;j > 0;j--){//每次减一说明最大值已在最后排好序,接下来不需动它,只对剩余元素进行操作。
            temp = array[j];
            array[j] = array[0];
            array[0] = temp;
            adjustHeap(array,0,j);
        }//可以发现j既表示了每次缩小范围的尾节点索引,也表示了缩小到范围区间。
    }

四.基数排序

  • 主要思想:准备10个桶,每个桶是一个一维数组。首先比较待排序数组的每一个元素的个位数与这10个桶中的哪一个桶的索引对应相等,然后将这个元素放入该桶中,遍历完所有元素后,又将放入了桶中的元素按桶索引依次放回原来的数组。之后依次每一轮比较的是元素的十位,百位…,重复以上操作。(对于位数不够的补0,例如数组[4,10],当比较十位数时,4就看成04,即其十位数为0)。
  • 图解示例第一轮(数组[53,3,542,748,14,214]):
    在这里插入图片描述
  • 代码模板:
    public static void radisSort(int[] arr){
        int max = arr[0];
        for(int i = 0;i < arr.length;i++){
            if(arr[i] > max){
                max = arr[i];
            }
        }//得到最大值max
        //得到最大值位数
        int maxLength = (max + "").length();
        //初始化10个桶,考虑到全部元素可能会放入一个桶的极端情况,故每个桶长度为全部元素个数,即数组长度。
        int[][] bucket = new int[10][arr.length];
        //初始化一个数组来对应记录每个桶中的元素个数。
        int[] bucketElementCounts = new int[10];

        for(int i = 0, n = 1;i < maxLength;i++, n *= 10){//这第一层for是表示从个位开始依次轮每一位数
            for(int j = 0;j < arr.length;j++){
            //这第二层for表示放入桶中
                int digitOfElement = arr[j] / n % 10;
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }
            int index = 0;
            for(int k = 0;k < bucketElementCounts.length;k++){
            //这第三层for表示放回数组
                if(bucketElementCounts[k] != 0){
                    for(int l = 0;l < bucketElementCounts[k];l++){
                        arr[index++] = bucket[k][l];
                    }
                }
                //此处表示放回数组后,对应桶中的元素个数要清空
                bucketElementCounts[k] = 0;
            }
        }
    }

五.选择排序(升序为例)

  • 主要思想:首先将数组中一个元素自定义为最小的元素(一般从左到右选,故第一次选的就是首元素),然后让该元素与剩余元素进行比较,遇到比该元素小的就更换一开始选定的最小元素为这个较小的元素,一轮比较完所有元素后,就将这个最小元素与首元素交换。之后,依次选定剩余的元素为最小元素,每一轮进行同样的比较操作并与选定首元素进行交换。
  • 图解示例及说明:
    在这里插入图片描述
    1.第一趟排序:选定第一个元素7为最小元素,将其与剩余元素进行比较完后,知剩余元素中最小元素为-2,则交换第一个元素7(主要思想提到的选定的首元素)与最小元素-2。
    2.第二趟排序:选定第二个元素4为最小元素,将其与剩余元素进行比较完后,知剩余元素中最小元素为4,此时的最小元素就是选定的元素,则不进行交换操作。
    余下每趟排序分析同理,不赘述。
  • 代码模板:
public static void chooseSort(int[] arr){
        for(int i = 0;i < arr.length;i++){//依次选定每一个元素作为首元素
            int minIndex = i;
            int min = arr[i];
            for(int j = i + 1;j < arr.length;j++) {//让选定首元素与后面的元素比较,从而确定真正的最小元素
                if (min > arr[j]) {
                    min = arr[j];
                    minIndex = j;
                }
            }
            if(minIndex != i){//交换选定首元素和最小元素
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }

六.归并排序(以升序为例)

  • 主要思想:分而治之
    1.分:将所有元素不断按对半拆分成多组,直到分成每一个单元素为一组。
    2.治:分的逆操作,即合并。同时,在不断合并每一组元素时,每合并一次都对合并的元素进行排序操作。
  • 图解示例及说明:
    在这里插入图片描述
    在这里插入图片描述
    图二分析如下(最后一次合并):
    1.合并之后,分成左右两半看待,由于左边的4大于右边的1,则将较小的1放入暂存数组,然后右边索引右移。(可以理解为那个被放到暂存数组中的元素,被从原数组中取出,则要考虑下一个元素,即移动的地方是被放入暂存数组中的元素的位置)。
    2.第一步是右边移动的情况,这第二步是左边移动的情况。由于左边的4小于右边的6,故分析同第一步,较小的4放入暂存数组,左边索引右移。
    3.考虑到可能左边或右边部分会出现剩余未比较的元素,则直接将全部按顺序放入暂存数组。
    注意点:左半部分和右半部分都是已经有序的,因为合并时,从两个单元素开始合并,它们各自本身就是一个有序数组,接着按同样的规律操作而形成的更长的左右半数组也就是有序的。
  • 代码模板:
public static void merge(int[] arr,int left,int right,int mid,int[] temp){
        int t = 0;//暂存数组的索引
        int i = left;//左半部的索引
        int j = mid + 1;//右半部的索引
        while (i <= mid && j <= right){//左右半比较并存放到暂存数组
            if(arr[i] < arr[j]){
                temp[t] = arr[i];
                t++;
                i++;
            }else{
                temp[t] = arr[j];
                t++;
                j++;
            }
        }
        while(i <= mid){//将左半部剩余元素放到暂存数组
            temp[t] = arr[i];
            t++;
            i++;
        }
        while(j <= right){//将右半部剩余元素放到暂存数组
            temp[t] = arr[j];
            t++;
            j++;
        }

        t = 0;
        int tempLeft = left;
        while (tempLeft <= right){//将暂存数组元素拷贝到原数组
            arr[tempLeft] = temp[t];
            tempLeft++;
            t++;
        }
    }

    public static void mergeSort(int[] arr,int left,int right,int[] temp){
        if(left < right){
            int mid = (left + right) / 2;
            mergeSort(arr,left,mid,temp);//对左半部拆分
            mergeSort(arr,mid + 1,right,temp);//对右半部拆分
            merge(arr,left,right,mid,temp);//合并
        }
    }

七.快速排序(以升序为例)

  • 主要思想:首先选定一个基准元素a,然后将这个元素前面的元素依次与它进行比较,直到找到比它大的元素b。再将这个元素后面的元素依次与它进行比较,直到找到比它小的元素c。此时,将b与c交换位置。
  • 图解示例及说明:
    在这里插入图片描述
    以首元素6为基准值,从尾元素开始向左遍历并比较。
    在这里插入图片描述
    j向左遍历到比6小的5就停住,i向右遍历到比6大的7就停住,5和7交换位置。此时序列为6 1 2 5 9 3 4 7 10 8。
    同理,之后要交换左边的9和右边的4,然后i和j遍历到相同位置3处,此时第一轮遍历完成,将基准值6与3交换位置,即6已经到达正确位置。
    在这里插入图片描述
    然后在6的左边序列和右边序列分别选定基准值,再进行同样遍历比较操作。
    在这里插入图片描述
  • 代码模板:
public static void quickSort(int[] arr,int left,int right){
        int l = left;
        int r = right;
        int temp;
        int pivot = arr[(left + right)/2];//基准值
        while(l < r){
            while (arr[l] < pivot){//找基准值左边比它大的元素
                l++;
            }
            while(arr[r] > pivot){//找基准值右边比它小的元素
                r--;
            }
            if(l >= r){
                break;
            }
            temp = arr[l];//交换基准值左右两边的元素
            arr[l] = arr[r];
            arr[r] = temp;

            if(arr[l] == pivot){//保证pivot处用[l,r]这个闭区间内,防止陷入死循环
                r--;
            }
            if(arr[r] == pivot){//保证pivot处用[l,r]这个闭区间内,防止陷入死循环
                l++;
            }
        }

        if(r == l){
            r--;
            l++;
        }
        if(r > left){
            quickSort(arr,left,r);
        }
        if(l < right){
            quickSort(arr,l,right);
        }

    }

理解死循环加一和减一:基准值是要始终保持在[r,l]这个闭区间的,这一点应该能理解,因为交换的都是它左右两边的值,r和l最多也就刚好处在基准值的位置上。如果arr[r]等于pivot,然后通过r++这样移动来突破死循环,会使pivot处用[r,l]之外,同理,在arr[l]等于pivot,l–也一样,所以这样不对,应该是代码模板中的样子。

八.冒泡排序(以升序为例)

  • 主要思想:按下标顺序依次让待排序序列的每一个元素与其相邻元素(下一个)比较,若发现逆序则交换。
  • 图解示例
    在这里插入图片描述
  • 代码模板:
public static void bubbleSort(int[] arr){
        int temp;
        boolean flag = false;
        for(int i = 0;i < arr.length - 1;i++){
            for(int j = 0;j < arr.length - 1 - i;j++{//多间了一个i,说明后面的较大值已经排好,不需要再排了。
                if(arr[j] > arr[j + 1]){
                    flag = true;
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            if(!flag){//一趟排序中没有发生过一次交换
                break;
            }else{
                flag = true;//为下次判断重置flag
            }
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值