【标题】八大经典排序算法的简介以及测试对比(java实现)

目录

一 、冒泡排序

二、选择排序

三、插入排序

四、希尔排序(分为移位法和交换法)

五、归并排序

六、快速排序

七、基数排序(桶排序的扩展)

八、堆排序


   

一 、冒泡排序

冒泡排序(Bubble Sorting)的基本思想是:通过对待 排序序列从前向后(从下标较小的元素开始),依次比较 相邻元素的值,若发现逆序则交换,使值较大 的元素逐渐从前移向后部,像水底下的气泡一样逐渐 向上冒。冒泡排序属于稳定排序算法的范畴,其实较为简单基础的排序算法 ,时间复杂度为O(n²),空间复杂度为O(1)。

动图演示

以下是具体的java代码实现:

public static void bubbleSort(int[] arr){
    int temp = 0;
    boolean flag = false; // 标识
    for (int i = 0; i < arr.length - 1 ; i++) {
        for (int j = 0; j < arr.length - 1 - i; j++) {
            // 如果前面的数比后面的数大就交换
            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 = false; // 重置flag,进行下次判断
        }
    }
}

用冒泡排序算法测试80000个随机数


二、选择排序

选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中找出最小(最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。选择排序属于不稳定排序算法的范畴。其时间复杂度为O(n²),空间复杂度为O(1)。

动图演示

以下是具体的java代码实现:

public static void selectSort(int[] arr){
    for (int i = 0;  i < arr.length-1 ; i++){
        int minIndex = i;
        int min = arr[i];
        for (int j = i + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 说明我们假定的最小值,并不是最小
                min = arr[j]; // 重置min
                minIndex = j; // 重置minIndex
            }
        }
        // 将最小值,放在arr[0],即交换
        if (minIndex != i) {
            arr[minIndex] = arr[i];
            arr[i] = min;
        }
    }
}

用选择排序算法测试80000个随机数

三、插入排序

插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。插入排序属于稳定排序的范畴,其时间复杂度为O(n²),空间复杂度为O(1)。

动图演示

以下是具体的java代码实现:

public static void insertSort1(int[] arr) {
    int insertVal = 0;
    int insertIndex = 0;
    for (int i = 0; i < arr.length - 1; i++) {
        // 定义带插入的数
        insertVal = arr[i + 1];
        insertIndex = i;
        // i 即arr[i+1] 前面这个数的下标
        // 给insertVal 找到插入的位置
        while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex--;
        }
            arr[insertIndex + 1] = insertVal;
    }
}

用插入排序算法测试80000个随机数

在进行插入排序算法测试的时候发现一个奇妙的事情,如果将insertIndex用i代替,它的测试时间会消耗更多,有兴趣的小伙伴可以去试一试看看。


四、希尔排序(分为移位法和交换法)

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。 希尔排序法基本思想 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个数据恰被分成一组,算法便终止。其属于不稳定排序算法的范畴,其时间复杂度为O(n log n),空间复杂度为O(1)。

动图演示

 具体例子:

 以下是具体的java代码实现:

// 希尔排序 (交换法)
public static void shellSort(int[] arr) {
    int temp = 0;
    int count = 0;
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < arr.length; i++) {
            // 遍历各组所有数据(共gap组,每组个元素),步长为gap
            for (int j = i-gap; j >= 0; j-=gap) {
                // 如果当前这个元素大于加上步长后的那个元素,说明交换
                if (arr[j] > arr[j+gap]){
                    temp = arr[j];
                    arr[j] = arr[j+gap];
                    arr[j+gap] = temp;
                }
            }
        }
    }
}

移位法的希尔排序算法测试80000个随机数


另一种是移位法,其改进使排序时间得到了大大的缩短 ,极大的提升了排序速度。

// 希尔排序(移位法)
public static void shellSort1(int[] arr){
    int temp = 0;
    // 增量gap,并逐步的缩小增量
    for (int gap = arr.length/2 ; gap > 0 ; gap /= 2) {
        //从第gap个元素,逐个对其所在的组进行直接插入排序
        for (int i = gap; i < arr.length; i++) {
            int j = i;
            temp = arr[j];
            if (arr[j] < arr[j - gap]){ // 优化
                while (j - gap >= 0 && temp < arr[j - gap]) {
                    // 移动
                    arr[j] = arr[j - gap];
                    j -= gap;
                }
            }
            // 当退出while循环后,就给temp找到了插入的位置
            arr[j] = temp;
        }
    }
}
交换法的希尔排序算法测试80000个随机数


五、归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案连接在一起)。其属于稳定排序算法的范畴,其时间复杂度为O(n log n),空间复杂度为O(n),亦是空间换时间的一种。

动图演示

// 分+合并方法(归并排序)
public static void mergedSort(int[] arr,int left,int right,int[] temp){
    if (left < right){
        int mid = (left + right) / 2; // 中间索引
        // 向左递归
        mergedSort(arr,left,mid,temp);
        // 向右递归
        mergedSort(arr,mid+1,right,temp);
        // 到合并
        merge(arr,left,mid,right,temp);
    }
}
// 合并的方法
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
    int i = left; // 初始化i, 左边有序序列的初始索引
    int j = mid+1; // 初始化j, 右边有序序列的初始索引
    int t = 0; // 指向temp数组的当前索引
    // 先把左右两边的数据按照规则填充到temp数组
    // 直到左右两边的有序序列,有一边处理完毕为止
    while(i <= mid && j <= right){ // 继续
        // 如果左边的有序序列的当前元素,小于右边有序序列的当前元素
        // 即将左边的当前元素拷贝到temp数组
        // 然后 t++ , i++
        if (arr[i] <= arr[j]){
            temp[t++] = arr[i++];
        }else{
            temp[t++] = arr[j++]; 
        }
    }
    // 把有剩余数据的一边的数据依次全部填充到temp
    while(i <= mid){ // 说明左边的有序序列还有剩余元素,就全部填充到temp中
        temp[t++] = arr[i++];
    }
    while(j <= right){ // 说明右边的有序序列还有剩余元素,就全部填充到temp中
        temp[t++] = arr[j++];
    }
    // 将temp数组的元=元素拷贝的arr
    // 注意,并不是每次都拷贝所有
    t = 0;
    int tempLeft = left; //
    while(tempLeft <= right){ // 第一次合并 tempLeft = 0; right = 1;
        // 最后一次 tempLeft = 0 right = 7;
        arr[tempLeft] = temp[t++];
    }
}

用归并排序算法测试80000个随机数

六、快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。其属于不稳定排序算法的范畴,其时间复杂度为O(n log n),空间复杂度为O(log n)。

 动图演示

以下是具体的java代码实现:

public static void quickSort(int[] arr, int left, int right) {
    int l = left;
    int r = right;
    int mid = arr[(left + right) / 2];
    int temp = 0; // temp是临时变量,作为交换时使用
    // while循环的目的是让比mid值小的放到左边
    // 让比mid值大的放到右边
    while (l < r) {
        // 在mid的左边一直找,找到大于等于mid值,才退出
        while (arr[l] < mid) {
            l += 1;
        }
        // 在mid的右边一直找,找到小于等于mid值,才退出
        while (arr[r] > mid) {
            r -= 1;
        }
        // 如果l >= r成立,说明mid的左右两边的值,已经按照左边全部是小于等于mid的值,右边是大于等于mid的值
        if (l >= r) {
            break;
        }
        temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
        // 如果交换完后,发现这个arr[l] =mid值  相当于 r-- 前移一下
        if (arr[l] == mid) {
            r -= 1;
        }
        // 如果交换完后,发现这个arr[l] = mid值 相当于 l++ 后移一下
        if (arr[r] == mid) {
            l += 1;
        }
    }
    // 如果 l == r,必须l++,r--,否则会出现栈溢出
    if (l == r) {
        l += 1;
        r -= 1;
    }
    // 向左递归
    if (left < r){
        quickSort(arr,left,r);
    }
    // 向右递归
    if (right > l){
        quickSort(arr,l,right);
    }
}

用快速排序算法测试80000个随机数

七、基数排序(桶排序的扩展)

基数排序是对传统桶排序的扩展,速度很快. 基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成 OutOfMemoryError(内存溢出)。 其属于稳定排序算法的范畴,其时间复杂度为O(n k),空间复杂度为O(n+k)。

动图演示 

 以下是具体的java代码实现:

public static void radixSort(int[] arr){
    int max = arr[0]; // 假设第一个个数就是最大数
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] > max){
            max = arr[i];
        }
    }
    // 得到最大数是几位数
    int maxLength = (max + "").length();
    // 定义一个二维数组,表示10个桶,每个桶就是一个一维数组
    // 1 二维数组包含10个一维数组
    // 2 为了防止在放入数的时候,数据溢出,则每一个一维数组(桶),大小定为arr.length
    // 3 很明确,基数排序是空间换时间的经典算法
    int[][] bucket = new int[10][arr.length];

    // 为了记录每个桶中,实际存放多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
    int[] bucketElementCounts = new int[10];
    // 这里我们使用循环将这个代码处理

    for (int i = 0, n = 1 ; i < maxLength; i++, n *= 10) {
        // 每一轮(针对每个元素的对应位排序处理,第一次个位,第二个是十位,第三个是百位。。。)
        for (int j = 0; j < arr.length; j++) {
            // 取出每个元素的对应位的值
            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++) {
            // 如果桶中有数据,我们才放入到原数组
            if (bucketElementCounts[k] != 0 ){
                // 循环该桶即第k个桶(即第k个一维数组),放入
                for (int l = 0; l < bucketElementCounts[k]; l++) {
                    // 取出元素放入到arr中
                    arr[index++] = bucket[k][l];
                }
            }
            // 第i+1轮处理后,需要将每个bucketElementCounts[k] = 0;
            bucketElementCounts[k] = 0;
        }
    }
}

用基数排序算法测试80000个随机数

八、堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆,反之称为小顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。其属于不稳定排序算法的范畴,其时间复杂度为O(n log n),空间复杂度为O(1)。

动图演示 

以下是具体的java代码实现: 

public static void  heapSort(int[] arr) {
    int temp = 0;
    System.out.println("堆排序");
    for (int i = arr.length/2 - 1; i >= 0; i--) {
        adjustHeap(arr,i,arr.length);
    }
    for (int j = arr.length-1; j > 0 ; j--) {
        // 交换
        temp = arr[j];
        arr[j] = arr[0];
        arr[0] = temp;
        adjustHeap(arr,0,j);
    }
}
public  static void  adjustHeap(int[] arr, int i , int length){
    int temp = arr[i];
    // 开始调整
    // k = i * 2 +1  k是i节点的左子节点
    for (int k = i * 2 +1; k < length ; k = k * 2 + 1) {
        if (k+1 < length && arr[k] < arr[k+1]){ // 说明左子节点的值小于右子节点的值
            k++; // k 指向右子节点
        }
        if (arr[k] > temp){ // 如果子节点大于父节点
            arr[i] = arr[k]; //  把较大的值赋给当前节点
            i = k; // !!! i指向k, 继续循环比较
        }else{
            break;
        }
    }
    // 当for循环结束后,我们已经将i为父节点的树的最大值,放在了最顶(局部)
    arr[i] = temp; // 将temp值放到调整后的位置
}
用堆排序算法测试测试80000个随机数

 谢谢您的浏览,小白一个,如有不足,请多包涵    ^_^     ^_^     ^_^     ^_^ 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LovingCurry30

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

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

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

打赏作者

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

抵扣说明:

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

余额充值