常见排序算法简述

冒泡排序BubbleSort

基本思想

通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻的元素值,  
若发现逆序则交换,使值较大的元素逐渐从前往后移动,就像水底的气泡逐渐往上冒  

优化

若一趟比较下来没有进行过交换,就说明序列有序,通过设置flag判断元素是否进行过交换,减少不必要的比较

过程

  1. 一共进行 数组大小-1 次的大循环
  2. 每一趟排序的次数逐渐减少
  3. 如果发现某趟排序中,没有发生一次交换,则提前结束排序

选择排序SelectSort

基本思想

每次假定需要排序数组第一位为最小值,找到需要排序数组中最小的值及其下标,将min与数组第一位进行交换。  
即每次找到最小值放在数组最前面。总共进行n-1次

过程

  1. 选择排序一共有 数组大小-1 轮排序
  2. 每一轮排序,又是一个循环
    • 假定当前这个数为最小值
    • 然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,并得到下标
    • 当遍历到数组最后,就得到本轮最小数和下标
    • 交换

插入排序InsertSort

基本思想

把n个待排序的元素看成一个有序和一个无序表,开始有序表只包含一个元素,无序表中包含n-1个元素,  
排序过程中每次从无序表中取出第一个元素,把它的排序吗依次与有序表元素的排序码比较,  
将它插入到有序表中的适当位置,使之成为新的有序表

希尔排序ShellSort

基本思想

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

在这里插入图片描述

快排排序QuickSort

基本思想

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分所有数据都要小,  
然后再按照此方法对这两部分数据分别进行快速排序,整个排序过程通过递归实现,依次达到整个数据变成有序序列

在这里插入图片描述

优化

  1. 基准选取可以优化为随机选取待排序的其中一个
  2. 分段可以优化为分成三段,一段比基准小,一段等于基准,一段大于基准,后续分别对小于和大于基准的进行快排即可

归并排序MergeSort

基本思想

利用归并的思想实现的排序方法,该算法采用经典的分治策略,分治法将问题分成一些小问题然后递归求解,  
而治的阶段则将分的阶段得到的各答案修补在一起,即分而治之

在这里插入图片描述

堆排序HeapSort

基本思想

  1. 将待排序序列构造成一个大顶堆
  2. 此时整个序列的最大值就是堆顶的根节点
  3. 将其与末尾元素进行交换,此时末尾就为最大值
  4. 然后将剩余n-1个元素重新构造成一个堆,就会得到n个元素的次小值。反复执行,即可得到一个有序序列

特点

  1. 利用数组模拟完全二叉树
  2. 父节点 i 的两个 子节点位置为2i+1与2i+2

基数排序RadixSort

基本思想

  1. 将所有待比较数值同一为同样的数为长度,数位较短的数前面补零。然后从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序数列
  2. 基数排序属于分配式排序,又称桶子法bucket sort或bin sort,顾名思义,通过键值的各个位的值,将要排序的元素分配至某些桶中,达到排序的作用
  3. 基数排序法属于稳定排序,是效率高的稳定排序法
  4. 是桶排序的扩展
  5. 将整数按位数切割成不同的数字,然后按每个位数分别比较

代码

import java.util.Arrays;

/**
 * @author ZhangXiong
 * @version v12.0.1
 * @date 2020-07-17
 */
public class Sort {
    public static void main(String[] args) {
        int n = 8000000;
        int[] arr = new int[n];
        int[] arr1 = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = (int) (Math.random() * n);
            arr1[i] = arr[i];
        }
        long start = System.currentTimeMillis();
        // 测试sort
        new Sort().heapSort(arr);
        long mid = System.currentTimeMillis();
        System.out.println("mySort cost: " + (mid - start) + "ms");
        Arrays.sort(arr1);
        long end = System.currentTimeMillis();
        // 打印排序后的数组
        //System.out.println(Arrays.toString(arr));
        System.out.println("ArraysSort cost: " + (end - mid) + "ms");
        System.out.println(Arrays.equals(arr, arr1));
    }


    /**
     * 冒泡排序
     * 稳定排序
     *
     * @param arr
     */
    public void bubbleSort(int[] arr) {
        for (int i = arr.length - 1; i > 0; i--) {
            for (int j = 1; j <= i; j++) {
                if (arr[j - 1] > arr[j]) {
                    swap(arr, j - 1, j);
                }
            }
        }
    }

    /**
     * 选择排序
     * 不稳定排序
     *
     * @param arr
     */
    public void selectSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int min = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[min]) {
                    min = j;
                }
            }
            swap(arr, i, min);
        }
    }

    /**
     * 插入排序
     * 稳定
     *
     * @param arr
     */
    public void insertSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int j = i;
            while (j > 0) {
                if (arr[j] < arr[j - 1]) {
                    swap(arr, j - 1, j);
                    j--;
                } else {
                    break;
                }
            }
        }
    }

    /**
     * 希尔排序
     * 不稳定
     * 思想:
     * 把记录按下标的一定增量分组,对每组使用插排
     * 随着增量逐渐减小,每组包含关键词越来越多,当增量减为1的时候,算法终止
     *
     * @param arr
     */
    public void shellSort(int[] arr) {
        //增量gap(当前分组数),并逐步的缩小变量
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            // 从第gap个元素,逐个对其所在的组进行直接插入排序
            // 从第一组的第二个元素开始插排
            for (int i = gap; i < arr.length; i++) {
                int j = i;
                int tmp = arr[j];
                // 对当前分组进行插排
                while (j - gap >= 0 && tmp < arr[j - gap]) {
                    arr[j] = arr[j - gap];
                    j -= gap;
                }
                //当退出while循环,就给temp找到插入的位置
                arr[j] = tmp;
            }
        }
    }

    /**
     * 归并排序
     * 稳定,外部排序
     *
     * @param arr
     */
    public void mergeSort(int[] arr) {
        int[] tmp = new int[arr.length];
        mergeSort(arr, 0, arr.length - 1, tmp);
    }

    /**
     * 归并排序 分治
     *
     * @param arr 待排序数组
     * @param l   待分割的左边界
     * @param r   代分割的右边界
     * @param tmp 外部数组
     */
    public void mergeSort(int[] arr, int l, int r, int[] tmp) {
        if (l < r) {
            int mid = l + (r - l) / 2;
            // 递归左分割
            mergeSort(arr, l, mid, tmp);
            // 递归右分割
            mergeSort(arr, mid + 1, r, tmp);
            // 合并
            merge(arr, l, mid, r, tmp);
        }
    }

    /**
     * 归并排序的merge函数
     *
     * @param arr 原数组
     * @param l   待排序区间 左边界
     * @param mid 待排序区间 中间分隔点
     * @param r   待排序区间 右边界
     * @param tmp 外部区间
     */
    public void merge(int[] arr, int l, int mid, int r, int[] tmp) {
        // 左数组的起始
        int i = l;
        // 右数组的起始
        int j = mid + 1;
        // 外部数据的起始
        int t = 0;
        // 合并两数组到外部数组
        while (i <= mid && j <= r) {
            if (arr[i] <= arr[j]) {
                tmp[t++] = arr[i++];
            } else {
                tmp[t++] = arr[j++];
            }
        }
        // 将未处理的数组放到tmp尾部
        while (i <= mid) {
            tmp[t++] = arr[i++];
        }
        while (j <= r) {
            tmp[t++] = arr[j++];
        }
        // 将排好序的区间从tmp复制到arr的对应区间
        t = 0;
        while (l <= r) {
            arr[l++] = tmp[t++];
        }
    }

    /**
     * 快速排序
     *
     * @param arr
     */
    public void quickSort(int[] arr) {
        quickSort(arr, 0, arr.length - 1);
    }

    /**
     * 快排
     *
     * @param arr 待排序数组
     * @param l   待排序 左边界
     * @param r   待排序 右边界
     */
    public void quickSort(int[] arr, int l, int r) {
        if (l >= r) {
            return;
        }
        int right = r;
        int mid = l;
        int tar = arr[l + (int) (Math.random() * (r - l))];
        int i = l;
        while (i <= right) {
            if (arr[i] < tar) {
                swap(arr, i++, mid++);
            } else if (arr[i] == tar) {
                i++;
            } else {
                swap(arr, i, right--);
            }
        }
        quickSort(arr, l, mid - 1);
        quickSort(arr, i, r);
    }

    /**
     * 堆排序
     *
     * @param arr
     */
    public void heapSort(int[] arr) {
        for (int i = (arr.length - 1) / 2; i >= 0; i--) {
            adjust(arr, i, arr.length - 1);
        }
        for (int i = arr.length-1; i > 0 ; i--) {
            swap(arr,0,i);
            adjust(arr,0,i-1);
        }
    }

    /**
     * 堆排的调整结构
     * @param arr
     * @param i
     * @param len
     */
    public void adjust(int[] arr, int i, int len) {
        int tmp = arr[i];
        for (int k = i * 2 + 1; k < len; k = k * 2 +1) {
            if (k+1 <= len && arr[k] < arr[k+1]){//说明左子节点的值小于右子节点的值
                k++;//k指向右子节点
            }
            if (arr[k] > tmp){//如果子节点大于父节点
                arr[i] = arr[k];//把较大的值赋给当前节点
                i = k;//i指向k,继续循环比较
            }else {
                break;
            }
        }
        arr[i] = tmp;
    }

	/**
     * 基数排序
     * @param arr
     */
    public  void radixSort(int[] arr) {
        //得到数组中最大的数的位数
        int max = arr[0];
        for (int i = 1; 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) {
                    //循环该桶,放入
                    for (int l = 0; l < bucketElementCounts[k]; l++) {
                        //取出元素放到arr
                        arr[index] = bucket[k][l];
                        index++;
                    }
                }
                //第i+1轮处理后需要将每个bucketElementCounts[k] = 0
                bucketElementCounts[k] = 0;
            }
        }
    }

    /**
     * 交换函数
     *
     * @param arr
     * @param i
     * @param j
     */
    private void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

总结

排序比较

L0pjZHVoZHQ=,size_16,color_FFFFFF,t_70)
在这里插入图片描述

相关术语

  1. 稳定:如果a原本在b前面,且a=b,排序后a仍在b前面
  2. 不稳定:如果a原本在b前面,且a=b,排序后a可能会在b后面
  3. 内排序:所有排序操作都在内存中完成
  4. 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行
  5. 时间复杂度:一个算法执行所耗费的时间
  6. 空间复杂度:运行完一个程序所需内存大小
  7. n:数据规模
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值