多个维度解析常见的排序算法及其稳定性

本文详细介绍了排序算法中的稳定性概念,以及多种常见排序算法的原理和实现,包括直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序和归并排序。重点讨论了快速排序和归并排序的优化策略,如快速排序的三数取中法和归并排序在海量数据中的应用。
摘要由CSDN通过智能技术生成

1.基本概念

1.1排序的稳定性(重要)

两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算法是具备稳定性的排序算法。
在这里插入图片描述(经验)如果当前这个排序,在排序的过程中没有发生跳跃式的交换,那么我们认为这个排序是稳定的排序,比如堆排,就是不稳定的。稳定的排序也可被实现为不稳定的排序,但不稳定的则不可以变成稳定的排序。

现实生活中的应用

在这里插入图片描述

2.常用排序总览

在这里插入图片描述

3.插入排序

3.1直接插入排序-原理

整个区间被分为

  1. 有序区间
  2. 无序区间
    每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入
    在这里插入图片描述
    确定下标i,来遍历数组的元素,那么下标i从几开始呢,从数组的·1一位置开始,因为第一个数它肯定是有序的,再来一个下标j,j=i-1,再定义一个tmp,把当前下标的值(i下标)放到tmp中,如果j下标的值大于tmp中的值,将j下标的值放到i位置,j–,此时j<0,0位置没有元素,就将tmp放到0位置(这就成为了一个有序区间)这是第一次直接插入排序的过程,接下来我们上代码。

3.2实现

//稳定性:稳定
//时间复杂度:最坏:数据逆序o(n^2)   最好:数据有序可以达到o(n)
//所以对于直接插入排序,数据越有序越快
import java.util.Arrays;
public class dSort {
   
    public static void insertSort(int[]array){
   
        for (int i = 1; i <array.length ; i++) {
   
            int tmp=array[i];
            int j=i-1;
            for(;j>=0;j--){
   
                if(array[j]>tmp){
   //如果这里是>=,此时这个排序就是不稳定的
                    array[j+1]=array[j];
                }else {
   
                    break;
                }
            }
            array[j+1]=tmp;
        }
    }
    public static void main(String[] args) {
   
        int[] arr={
   2,5,6,8,5};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

在这里插入图片描述

对于直接插入排序我们上面总结的结论,会经常遇见这样的问题:当前有一组待排序序列,但是这组待排序序列大部分是有序的请问下面那个排序最适合,对于这样的问题,不要犹豫,选直接插入排序。
另外:直接插入排序一般也会用在一些排序的优化上,例如快排,当数据越来越有序时,就可以进行更好的优化。

性能分析

在这里插入图片描述

4.希尔排序(了解)

说一句:如果是面试,你可以跳过希尔排序

4.1希尔排序-原理

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数gap,把待排序文件中所有记录分成个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后,取增量,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

  1. 希尔排序是对直接插入排序的优化
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
    在这里插入图片描述

4.2实现

//稳定性:不稳定
//时间复杂度:最坏:o(n^2)  平均:o(n^1.3) 最坏:o(n^2)
//是对直接插入排序的优化
import java.util.Arrays;
public class shellSort {
   
    public static void shell(int[]array,int gap){
   
        for(int i=gap;i<array.length;i++){
   
            int tmp=array[i];
            int j=i-gap;
            for (;j>=0;j=j-gap){
   
                if(array[j]>tmp){
   
                    array[j+gap]=array[j];
                }else {
   
                    break;
                }
            }
            array[j+gap]=tmp;
        }
    }
    public static void shellSort(int[]array){
   
        int []drr={
   5,3,1};//增量数组,取值要求,没有除1之外的公因子,且最后一个增量值必须为1
        for (int i = 0; i < drr.length; i++) {
   
            shell(array,drr[i]);
        }
    }
    public static void main(String[] args) {
   
        int[] arr={
   2,5,6,8,7,23,21,32,43,54,43,2,13,4,34,65,7,6,90};
       shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

4.3性能分析

在这里插入图片描述

5.选择排序

5.1直接选择排序-原理

每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素排完 。
在这里插入图片描述

5.2实现

import java.util.Arrays;
public class selectSort {
   
public static void selectSort(int []array){
   
    for (int i = 0; i < array.length-1; i++) {
   
        int max=0;
        for (int j = i+1; j <array.length;j++) {
   
            if(array[i]>array[j]){
   //交换
                max=array[j];
                array[j]=array[i];
                array[i]=max;
            }
        }
     }
}
    public static void main(String[] args) {
   
        int []arr={
   2,3,5,7,8,9,13};
        selectSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

在这里插入图片描述

5.3性能分析

在这里插入图片描述

其实关于选择排序还有双向选择排序,但由于少用到,在此我们不再描写,感兴趣的朋友可以自行了解。

6.堆排序

6.1堆排序-原理

基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意: 排升序要建大堆;排降序要建小堆。
关于具体实现过程以及原理请看此篇文章

6.2实现

6.21建大堆
/**
 * 调整为大顶堆
 * @param arr   待调整的数组
 * @param parent   当前父节点的下标
 * @param length   需要对多少个元素进行调整
 */
import java.util.Arrays;
public class heapSort {
   
//首先我们先完成大堆的构建
    private static void adjustHeap(int[] arr, int parent, int length){
   
        //临时保存父节点
        int temp = arr[parent];
        //左子节点的下标
        int child = 2 * parent + 1;
        //如果子节点的下标大于等于当前需要比较的元素个数,则结束循环
        while(child < length){
   
            //判断左子节点和右子节点的大小,若右边大,则把child定位到右边
            if(child + 1 < length && arr[child] < arr[child + 1]){
   
                child ++;
            }
            //若child大于父节点,则交换位置,否则退出循环
            if(arr[child] > temp){
   
                //父子节点交换位置
                arr[parent] = arr[child];
                //因为交换位置之后,不能保证当前的子节点是它子树的最大值,所以需要继续向下比较,
                //把当前子节点设置为
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little-peter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值