Featured image of post 计算机基础 数据结构与算法 10大排序算法实践

计算机基础 数据结构与算法 10大排序算法实践

🌏排序算法实践 🎯 这篇文章用于记录有关排序算法的内容 包括各种排序算法的分析和讲解视频

🍭十大排序算法概述

十大排序算法 测试数据量:1000 0000(1千万) 数组 数据范围 100 0000(1百万)
以下是我在自己的笔记本上的运行时间统计:
(1)冒泡排序  排序用时:105626毫秒~  (20万)    复杂度高 不选择
(2)选择排序  排序用时:56199毫秒~  (20万)    复杂度高 不选择
(3)插入排序  排序用时:6060毫秒~  (20万)    适用于数据量小 且已接近有序的数据集
(4)希尔排序  排序用时:4144毫秒~   (1000万)  插入排序的改进 适用于大量数据集
(5)归并排序  排序用时:3862毫秒~   (1000万)  适用于大量数据集 但相对于快速排序会多占用内存空间
(6)堆排序    排序用时:3824毫秒~   (1000万)  可用于对大量数据集的排序 但性能不如快速排序
(7)快速排序  排序用时:2033毫秒~   (1000万)  综合性能优秀 但当数据集数据量大且基本有序的时候复杂度会降低到O(n2)
(8)计数排序  排序用时:132毫~      (1000万)  适用于数据量小且分布范围较小 分布密秒度高的数据集
(9)桶排序    排序用时:6272毫秒~ 100桶 (1000万)
             排序用时:4232毫秒~ 10000桶  (1000万)
  适用于数据集分布均匀的数据集 为何说桶排序的时间复杂度可以趋近O(n)?
  因为当桶的数量足够多 每个桶的数据量就会非常小 对其进行插入排序所用的时间也趋于常数级别
(10)基数排序 适用于需要对数据的不同模块按照不同的优先级进行排序的场景 例如 依次按照学生的语文 数学 英语成绩进行排序

🍭快速排序

⚡算法实现

排序函数

private static void quickSort(int[] a, int l, int r) { 
    // 子数组当中只有一个元素的时候 会直接返回
    if (l >= r) return; // [l, r]

    // 获取基准点(这个分区函数在后面实现)
    int p = partition(a, l, r); 
    // 递归对基准数左右两侧的子数组进行快排
    quickSort(a, l, p - 1);
    quickSort(a, p + 1, r);
}

分区函数

public static int partition(Integer[] a, int l, int r) {
    // 选取基准元素base
    int base = a[l]; 
    // ij主键靠近 完成元素以base为中间元素 分布在左右两边
    int i = l, j = r;

    // 要求从小到大排序
    while(i < j) {
        // j从右到左寻找一个比base小的数
        while(i < j && a[j] >= base) --j;
        // 找到后与i所在的位置进行替换
        if(i < j) { a[i] = a[j]; ++i; }

        // i从左到右寻找一个比base大的数
        while(i < j && a[i] <= base) ++i;
        // 找到后与j所在的位置进行替换
        if(i < j) { a[j] = a[i]; --j; }
    }

    // while循环结束 这时ij指向同一个位置 把base赋值给a[i/j]
    a[i] = base;
    // 返回中间元素下标
    return i;
}

⚡视频讲解

🍭堆排序

⚡算法实现

/**
 * 堆排序
 * @param a        要排序的数组
 * @param minOrMAX 最大堆还是最小堆 0 最小堆 1 最大堆
 */
private static void heapSort(int[] a, int minOrMAX) {
    // 首先还是建堆 从最后一个为叶子结点执行堆调整函数 
    // 直到整个堆呈最大堆或最小堆的形式
    for(int i = a.length >>> 1 - 1; i >= 0; --i) {
        adjustHeap(a, i, a.length, minOrMAX);
    }

    // 执行排序
    int size = a.length;
    while(size > 0) {
        // 每次从堆顶元素取数 放置到数组末尾 这样就完成了一个数的排序
        int t = a[0]; a[0] = a[--size]; a[size] = t;
        // 调整堆以确定下一个堆顶元素
        adjustHeap(a, 0, size, minOrMAX);
        // 直到所有的数完成排序 函数结束
    }
}

/**
 * 堆调整
 * @param a         要调整的堆
 * @param p         当前调整的元素的下标 parent
 * @param size      数组当前逻辑上的大小
 * @param minOrMAX  标识是大根堆还是小根堆
 */
private static void adjustHeap(int[] a, int p, int size, int minOrMAX) {
    int l = p * 2 + 1; // 获取当前结点的左孩子下标
    int r = p * 2 + 2; // 获取当前结点的右孩子下标
    int m = p;         // 指向父节点和左右孩子节点中最大值的下标

    // 根据堆是否是大根堆还是小根堆进行动态处理 更新m下标
    if(l < size && (minOrMAX == MIN ? a[l] < a[m] : a[l] > a[m])) m = l;
    if(r < size && (minOrMAX == MIN ? a[r] < a[m] : a[r] > a[m])) m = r;

    // 如果m被更新 说明找到了比父节点更大或更小的数
    if(m != p) {
        // 执行交换调整
        int t = a[m]; a[m] = a[p]; a[p] = t;
        // 继续调整 直到m下标没有孩子节点 或者m指向的元素满足堆顶特性(最大或最小)
        adjustHeap(a, m, size, minOrMAX);
    }
 }

🍭其他排序算法链接

🌈冒泡排序

🌈选择排序

🌈插入排序

🌈希尔排序

🌈归并排序

🌈计数排序

🌈基数排序

🌈桶排序