常见排序算法的实现与总结(Java)

排序算法

基本介绍

排序算法(Sort Algorithm),是将一组数据,依指定的顺序进行排列的过程。

排序算法的分类

  • 内部排序:指将需要处理的所有数据都加载到内部存储器(内存)中进行排序。
  • 外部排序:数据量过大,无法全部加载到内存中,需要借助外部存储(文件等)进行排序。

图解
图解分析

冒泡排序

基本介绍:冒泡排序(BubbleSorting)的基本思想是通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

优化:因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排序写好后,再进行)

图解
冒泡排序

代码实现

package com.athome.sort;

import java.util.Arrays;
import java.util.Random;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/22 8:39
 */
public class BubbleSort {
    public static void main(String[] args) {
        //测试冒泡排序
        int[] arr = new int[10];
        Random random = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(50);
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        bubbleSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    /**
     * 冒泡排序,从小到大排序
     *
     * @param arr 待排序的数组
     */
    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++) {
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                    flag = true;//如果发生了交换,将flag设置为true
                }
            }
            if (flag) {
                flag = false;
            } else {
                //说明有没发生交换,数组已经有序。
                break;
            }
        }
    }
}

选择排序

基本介绍:选择排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。

选择排序思想:选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]到arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]到arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]到arr[n-1]中选取最小值,与arr[2]交换,…,第i次从arr[i-1]到arr[n-1]中选取最小值,与arr[i-1]交换,…,第n-1次从arr[n-2]到arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

图解
选择排序
代码实现

package com.athome.sort;

import java.util.Arrays;
import java.util.Random;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/22 8:46
 */
public class SelectSort {
    public static void main(String[] args) {
        //测试选择排序
        int[] arr = new int[10];
        Random random = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(50);
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        selectSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    /**
     * 选择排序,从小到大排序
     *
     * @param arr 待排序数组
     */
    public static void selectSort(int[] arr) {
        int temp;//交换时使用
        int index;//记录下标
        for (int i = 0; i < arr.length - 1; i++) {
            temp = arr[i];
            index = i;
            for (int j = i; j < arr.length; j++) {
                if (arr[j] < temp) {
                    temp = arr[j];
                    index = j;
                }
            }
            if (index != i) {
                arr[index] = arr[i];
                arr[i] = temp;
            }
        }
    }
}

插入排序

基本介绍:插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。

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

图解
插入排序

代码实现

package com.athome.sort;

import java.util.Arrays;
import java.util.Random;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/22 10:15
 */
public class InsertSort {
    public static void main(String[] args) {
        //测试插入排序
        int[] arr = new int[10];
        Random random = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(50);
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        insertSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));

    }

    /**
     * 插入排序,从小到大排序
     *
     * @param arr 待排序的数组
     */
    public static void insertSort(int[] arr) {
        int insertValue;//待排序的值
        int insertIndex;//待排序的位置
        for (int i = 1; i < arr.length; i++) {
            insertValue = arr[i];
            insertIndex = i - 1;
            while (insertIndex >= 0 && insertValue < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            if (insertIndex != i - 1) {
                arr[insertIndex + 1] = insertValue;
            }
        }
    }
}

希尔排序

简单插入排序存在的问题:当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响。

希尔排序介绍:希尔排序是希尔(DonaldShell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。

希尔排序基本思想:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

图解
希尔排序

代码实现

package com.athome.sort;

import java.util.Arrays;
import java.util.Random;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/22 10:34
 */
public class ShellSort {
    public static void main(String[] args) {
        //测试插入排序
        int[] arr = new int[10];
        Random random = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(50);
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        shellSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    /**
     * 希尔排序,从小到大排序
     *
     * @param arr 待排序数组
     */
    public static void shellSort(int[] arr) {
        int insertValue;
        int insertIndex;
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                insertValue = arr[i];
                insertIndex = i - gap;
                while (insertIndex >= 0 && insertValue < arr[insertIndex]) {
                    arr[insertIndex + gap] = arr[insertIndex];
                    insertIndex -= gap;
                }
                if (insertIndex != i - gap) {
                    arr[insertIndex + gap] = insertValue;
                }
            }
        }
    }
}

快速排序

快速排序介绍:快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

图解
快速排序

代码实现

package com.athome.sort;

import java.util.Arrays;
import java.util.Random;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/22 12:18
 */
public class QuickSort {
    public static void main(String[] args) {
        //测试快速排序
        int[] arr = new int[10];
        Random random = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(100);
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        quickSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    /**
     * 重载快速排序,从小到大排序
     *
     * @param arr 待排序数组
     */
    public static void quickSort(int[] arr) {
        int left = 0;
        int right = arr.length - 1;
        quickSort(arr, left, right);
    }

    /**
     * @param arr   待排序数组
     * @param left  左角标
     * @param right 右角标
     */
    public static void quickSort(int[] arr, int left, int right) {
        int l = left;//左角标
        int r = right;//右角标
        int mid = arr[(left + right) / 2];//中间值
        int temp;//临时变量
        while (l < r) {
            //寻找左边的值大于中间值的
            while (arr[l] < mid) {
                l++;
            }
            //寻找右边的值小于中间值的
            while (arr[r] > mid) {
                r--;
            }
            //如果左角标右角标相等了,说明找不到满足条件的值
            if (l >= r) {
                break;
            }
            //执行到这里说明有值满足条件,交换位置
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //防止死循环
            if (arr[l] == mid) {
                l++;
            }
            if (arr[r] == mid) {
                r--;
            }
        }
        //防止死循环
        if (l == r) {
            l += 1;
            r -= 1;
        }
        //向左半部分递归
        if (left < r) {
            quickSort(arr, left, r);
        }
        //向右半部分递归
        if (right > l) {
            quickSort(arr, l, right);
        }
    }
}

归并排序

归并排序介绍:归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

归并排序基本思想
在这里插入图片描述

动态图解
归并排序

代码实现

package com.athome.sort;

import java.util.Arrays;
import java.util.Random;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/22 10:44
 */
public class MergeSort {
    public static void main(String[] args) {
        //测试归并排序
        int[] arr = new int[10];
        Random random = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(50);
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        mergeSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    /**
     * 重载归并排序,从小到大排序
     *
     * @param arr 待排序数组
     */
    public static void mergeSort(int[] arr) {
        int[] temp = new int[arr.length];//临时数组
        int left = 0;
        int right = arr.length - 1;
        mergeSort(arr, temp, left, right);
    }

    /**
     * 归并排序
     *
     * @param arr   待排序数组
     * @param temp  临时数组
     * @param left  左角标
     * @param right 右角标
     */
    public static void mergeSort(int[] arr, int[] temp, int left, int right) {
        if (left < right) {
            int mid = (left + right) / 2;
            //向左递归分解
            mergeSort(arr, temp, left, mid);
            //向右递归分解
            mergeSort(arr, temp, mid + 1, right);
            //合并
            merge(arr, temp, left, mid, right);
        }
    }

    /**
     * 合并
     *
     * @param arr   待排序数组
     * @param temp  临时数组
     * @param left  左角标
     * @param mid   左右数据分割点
     * @param right 右角标
     */
    public static void merge(int[] arr, int[] temp, int left, int mid, int right) {
        int l = left;//左边数据的初始坐标
        int r = mid + 1;//右边数据的初始坐标
        int t = 0;//记录temp数组的角标
        //1.将左右两边的数据按照规则添加到临时数组中
        while (l <= mid && r <= right) {
            if (arr[l] <= arr[r]) {
                temp[t++] = arr[l++];
            }
            if (arr[l] > arr[r]) {
                temp[t++] = arr[r++];
            }
        }
        //2.将左右两边剩下的数据填入到temp数组中
        while (l <= mid) {
            temp[t++] = arr[l++];
        }
        while (r <= right) {
            temp[t++] = arr[r++];
        }
        //3.将temp中的元素拷贝到数组中,不是拷贝所有
        t = 0;
        int tempLeft = left;
        while (tempLeft <= right) {
            arr[tempLeft++] = temp[t++];
        }
    }
}

基数排序

基数排序(桶排序)介绍

  • 基数排序(radixsort)属于“分配式排序”(distributionsort),又称“桶子法”(bucketsort)或binsort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用。
  • 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法。

基数排序基本思想:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

图解
基数排序

代码实现

package com.athome.sort;

import java.util.Arrays;
import java.util.Random;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/22 12:41
 */
public class RadixSort {
    public static void main(String[] args) {
        //测试基数排序
        int[] arr = new int[10];
        Random random = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(50);
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        radixSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    /**
     * 基数排序,从小到大排序
     *
     * @param arr 待排序的数组
     */
    public static void radixSort(int[] arr) {
        //1.首先找出数组中的最大值的长度
        int max = arr[0];
        for (int i : arr) {
            if (i > max) {
                max = i;
            }
        }
        //最大值的长度
        int len = String.valueOf(max).length();
        //2.创建桶和记录桶中数据的数组
        //注:这里将每个桶的长度设为arr.length是为了防止角标越界
        int[][] bucket = new int[10][arr.length];//存放数据的桶
        int[] bucketCount = new int[10];//记录每个桶中数据个数
        int bucketNum;//桶的编号
        int index = 0;//记录下标
        //3.开始向桶中放数据
        for (int i = 0, n = 1; i < len; i++, n *= 10) {
            //(1).从低位到高位的顺序按照数字大小依次放入桶中
            for (int value : arr) {
                bucketNum = (value / n) % 10;//确定桶的编号
                bucket[bucketNum][bucketCount[bucketNum]] = value;
                bucketCount[bucketNum]++;
            }
            //(2).从桶中依次取出数据放回原数组
            for (int j = 0; j < bucket.length; j++) {
                if (bucketCount[j] != 0) {
                    for (int k = 0; k < bucketCount[j]; k++) {
                        arr[index++] = bucket[j][k];
                    }
                }
                //取出数据后,将记录桶数据的位置置空
                bucketCount[j] = 0;
            }
            //将记录原数组下标的值置空
            index = 0;
        }
    }
}

基数排序的说明

  • 基数排序是对传统桶排序的扩展,速度很快.
  • 基数排序是经典的空间换时间的方式,占用内存很大,当对海量数据排序时,容易造成OutOfMemoryError。
  • 基数排序时稳定的。[注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的]。
  • 有负数的数组,我们不用基数排序来进行排序,如果要支持负数,可以考虑优化算法。

常用排序算法总结和对比

排序算法比较图
排序算法比较图

相关术语解释

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
内排序:所有排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度:一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小。
n:数据规模
k:“桶”的个数
In-place:不占用额外内存
Out-place:占用额外内存


结语

只要能收获甜蜜,荆棘丛中也有蜜蜂忙碌的身影,未来的你一定会感谢现在努力的自己。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值