排序算法剖析

排序算法浅谈

参考资料

数据结构与算法

评价指标

  • 稳定性:两个相同的关键字排序过后相对位置不发生变化
  • 时间复杂度
  • 空间复杂度
  • 适用性:是否适用于链表

可视化工具

https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

概览

排序算法稳定性平均时间复杂度平均 空间复杂度备注
插入排序稳定O(n^2)O(1)基本有序时时间复杂度接近O(n)
折半插入排序稳定O(n^2)O(1)查找插入位置是使用折半查找
希尔排序不稳定O(n^1.3)O(1)利用基本有序可以降低插入排序时间复杂度的思想,将数据分组进行插入排序
冒泡排序稳定O(n^2)O(1)基本有序时时间复杂度接近O(n),如果一趟冒泡排序过程中没有发生交换,则说明序列已经有序
快速排序不稳定O(nlogn)O(logn)每次至少确定一个元素位置
选择排序不稳定O(n^2)O(1)每—趟在待排序元素中选取关键字最小的元素加入有序子序列;时间复杂度与初始状态无关
堆排序不稳定O(nlogn)O(1)将序列初始化为堆,然后不断取堆顶元素,并不断调整堆使其保持堆的性质
归并排序稳定O(nlogn)O(n)不断递归合并两个有序序列
基数排序稳定O(d(n+r))拆分成多个位,并按位的权重从小到大进行分配与收集

插入排序

  • 算法思想:每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中,直到全部记录插入完成。
  • 稳定性:稳定
  • 时间复杂度:最好O(n),平均O(n^2),最坏O(n^2)
  • 特点: 当数组的元素基本有序,那么插入排序的时间复杂度接近O(n)
  • 适用性:适用链表
/**
 * 插入排序
 * 每次将第i个元素,一次与前i个元素比较,正确地插入到[0, i]的序列中
 * 时间复杂度 O(n²)
 *
 * 插入排序有提前终止的可能,效率比选择排序高一点
 * 当数组的元素基本有序,那么插入排序的时间复杂度接近O(n)
 * @param arr 待排序数组
 */
 public static void insertionSort(int[] arr) {
     for (int i = 1; i < arr.length; i++) {
         int num = arr[i];
         int j;
         // 前面的元素是否比当前元素大
         for (j = i; j > 0 && num < arr[j-1]; j--) {
             arr[j] = arr[j - 1];
         }
         arr[j] = num;
     }
 }

折半插入排序

  • 算法思想:对于插入排序,要将元素插入到前面的有序序列,由于前面的序列已经有序,那么在查找插入位置时可以使用二分查找算法,也就是折半插入排序
  • 稳定性:稳定
  • 时间复杂度:相比于插入排序只能较少元素比较次数,无法减少元素移动的次数,平均时间复杂度依旧是O(n^2)
  • 特点: 当数组的元素基本有序,那么插入排序的时间复杂度接近O(n)
  • 适用性:不适用链表
    在这里插入图片描述

希尔排序

  • 算法思想:先将待排序表分割成若干形如 L[i, i +d, i + 2d…, i + kd]的“特殊”子表,对各个子表分别进行直接插入排序。缩小增量d,重复上述过程,直到d=1为止。(n/2, n/4, n/8, …, 1)
  • 稳定性:不稳定
  • 时间复杂度:最坏O(n^2),平均O(n^1.3)
  • 适用性:不适用链表在这里插入图片描述

在这里插入图片描述

冒泡排序

  • 算法思想:从后往前((或从前往后)两两比较相邻元素的值,若为逆序〈(即A[i-1]>A[i]),则交换它们,直到序列比较完。称这样过程为“一趟”冒泡排序。如果一趟冒泡排序过程中没有发生交换,则说明序列已经有序。
  • 稳定性:稳定
  • 时间复杂度:最好O(n),平均O(n^2)
  • 适用性:适用于链表
  • 特点: 当数组的元素基本有序,那么冒泡排序的时间复杂度接近O(n)

在这里插入图片描述

快速排序

  • 算法思想∶在待排序表L[1…n]中任取一个元素pivot作为枢轴(或基准,通常取首元素),通过一趟排序将待排序表划分为独立的两部分L[1…k-1]和LIk+1…n],使得L[1…k-1]中的所有元素小于pivot,L[k+1…n]中的所有元素大于等于pivot,则pivot放在了其最终位置L(k)上,这个过程称为一次“划分”。然后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。
  • 时间复杂度:平均最好O(nlogn),最坏O(n^2)(取决于每个划分是否均衡)
  • 空间复杂度:最好O(logn),最坏O(n)
  • 适用性:不适用于链表
  • 稳定性:不稳定
    在这里插入图片描述
    在这里插入图片描述

简单选择排序

  • 算法思想:每—趟在待排序元素中选取关键字最小的元素加入有序子序列
  • 时间复杂度:O(n^2)无优化空间
  • 适用性:适用于链表
  • 稳定性:不稳定
/**
 * 每进行一次外层循环找出第i小的元素
 * 选择排序,升序
 * 每次都从 [i, n) 中找出最小的元素
 * 时间复杂度 O(n²)
 * @param arr 待排序数组
 */
public static void selectionSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        swap(arr, i, minIndex);
    }
}

堆排序

  • 大根堆:以完全二叉树顺序存储的方式来看一个数组,每个非终端节点都大于它的左(2i)右(2i+1)节点,非终端节点的编号i <= n/2
  • 算法思想:每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换),并将待排序序列再次调整为大根堆
  • 时间复杂度:建堆O(n),整体O(nlogn)
  • 空间复杂度:O(1)
  • 适用性:不适用于链表
  • 稳定性:不稳定
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

归并排序

  • 算法思想:每次将两个有序子序列合并,递归到每个子序列大小为1
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)
  • 适用性:适用于链表
  • 稳定性:稳定
public static void mergeSort(int[] arr) {
    mergeSort(arr, 0, arr.length - 1);
}

/**
 * 把当前要排序的数组分成两半,层层递归,直至剩余一个元素
 * 将两半排序好的数组进行合并
*/
private static void mergeSort(int[] arr, int l, int r) {
    if (l >= r) {
        return;
    }
    int mid = l + (r-l)/2;
    mergeSort(arr, l, mid);
    mergeSort(arr, mid + 1, r);
    merge(arr, l, mid, r);
}

private static void merge(int[] arr, int l, int mid, int r) {
    int[] aux = new int[r-l+1];
    for (int i = l; i <= r; i++) {
        aux[i-l] = arr[i];
    }
    int left = l;
    int right = mid + 1;
    for (int i = l; i <=r; i++) {
        if (left > mid) {
            arr[i] = aux[right - l];
            right ++;
        } else if (right > r) {
            arr[i] = aux[left - l];
            left ++;
        } else if (aux[left -l] < aux[right - l]) {
            arr[i] = aux[left - l];
            left ++;
        } else {
            arr[i] = aux[right - l];
            right ++;
        }
    }
}

基数排序

  • 算法思想:分别以个位,十位,百位等(关键字权重递增,百位对关键字的大小影响更大)进行排序和收集
  • 空间复杂度:O®
  • 时间复杂度:O(d(n+r))
  • 稳定性:稳定
  • 应用场景
    • 数据元素的关键字可以方便地拆分为d组,且d较小
    • 每组关键字的取值范围不大,即r较小
    • 数据元素个数n较大
      在这里插入图片描述
      在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

it00zyq

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

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

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

打赏作者

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

抵扣说明:

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

余额充值