学会Java十大排序思想,这一篇就够了

排序

八种排序的时间复杂度:

排序法平均时间最差情形稳定度额外空间备注
冒泡O(n2)O(n2)稳定O(1)n小时较好
选择O(n2)O(n2)不稳定O(1)n小时较好
插入O(n2)O(n2)稳定O(1)大部分已排序时较好
基数O(logRB)O(logRB)稳定O(n)B是真数(0-9),R是基数(个十百)
计数O(n+k)O(n+k)稳定O(n+k)n的范围区间小较好
ShellO(n1.3)O(n2)不稳定O(1)s是所选分组
快速O(nlogn)O(n2)不稳定O(nlogn)n大时较好
归并O(nlogn)O(nlogn)稳定O(1)n大时较好
O(nlogn)O(nlogn)不稳定O(1)n大时较好

核心: 插入排序、堆排序、归并排序、快速排序
在这里插入图片描述

1、快速排序(交换)

核心将确定一个中间值,每一次排序都要将数据分割成独立的两部分,其中一部分的所有数据都要比另一部分的所有数据小,然后按照此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行。

基本思想

1、选定Pivot中心轴(默认选择第一个数)

2、将大于Pivot的数字放在Pivot的右边

3、将小于Pivot的数字放在Pivot的左边

4、分别对左右子序列重复前三部操作。

一共就两种情况找到左边 比中心轴小的数,找到就替换,没就right–, 接着找 右边比中心轴大的数,找到就替换,没就left++。

    private static void quick_sort(int[] arr, int L, int R) {
        if(L >= R) return;
        int left =L;
        int right = R;
        int pr = arr[left];
        while (left < right){
            while (left < right && pr <= arr[right]){
                right -- ;
            }
            if (left < right){
                arr[left]  = arr[right];
            }
            while (left < right && pr >= arr[left]){
                left++;
            }
            if (left < right){
                arr[right]  = arr[left];
            }
            if (left == right){
                arr[left] = pr;
            }
        }
        quick_sort(arr,L,left-1);
        quick_sort(arr,left+1,R);
    }

2、冒泡排序(交换)

冒泡排序法:把N个数通过==N-1==轮排序,升序排序中大的数往下沉,小的数往上浮;降序排序中小的数往下沉,大的数往上浮

特点:每次比较会把最大的数下层到最低(最后面),所以每次比较的次数都会比前一次少一次。

核心代码:双重for循环+ swap

    public static void bubb_sort(int[] arr){
        int num = arr.length-1;
        for (int i=0; i<num; i++ ){
            for (int j=0; j<num-i; j++ ){
                if(arr[j] > arr[j+1]){
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }

3、选择排序(选择)

选择排序特点:在排序中每一轮比较会把最小的数移到最前,所以相互比较的次数每一轮都会比前一轮少一次。经过==N-1==轮

与冒泡排序的区别:

​ 1、冒泡排序是比较相邻另个位置的两个数,而选择是顺序比较,找最大值或最小值

​ 2、冒泡排序每一轮比较后,位置不对都需要换位,选择每一轮比较只需要和最终的一次更换位置

​ 3、冒泡排序是通过数去找位置,选择排序是给定位置去找数

    public static void select_sort(int[] arr){
        int num  = arr.length -1;

        for (int i=0; i<num; i++){
            int index = i;
            for (int j=i+1; j<arr.length; j++){
                if (arr[j] <= arr[index]) {
                    index = j;
                }
            }
            int temp = arr[i];
            arr[i] = arr[index];
            arr[index] = temp;

        }
    }

4、插入排序(插入)

插入排序(Insertion sort)是一种简单直观且稳定的排序算法。如果有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。(扑克牌)

插入排序的基本思想是:每步将一个待排序的记录,按其关键码值的大小插入到前面已经排序的数组中的适当位置上,直到全部插入完为止。记得第二次j 为通用变量

//*****方法1
    public static void insert_sort(int[] arr){
        int num = arr.length;
        int j ; // 用来存插入牌子的其实位置
        // 从第二个位置开始
        for (int i=1; i<num; i++){
            // 作为临时边和 前面已经排好的进行比较
            int temp = arr[i];
            for (j = i-1; j>=0; j--){
                if (temp < arr[j]){
                    arr[j+1] = arr[j];
                }else {
                    break;
                }
            }
            arr[j+1] = temp;

        }
    }
//******方法2
    public static void insert_sort(int[] arr){
        for (int i=1; i<arr.length; i++){
            for (int j = i; j > 0 ; j--) {
                if (arr[j] < arr[j-1]){
                    swap(arr,j,j-1);
                }
            }
        }
    }

5、希尔排序(优化插入)

插入排序的优化

思想:
1.给定一个间隔gap,按照这个间隔进行跳排
2.将间隔缩小再排一遍
3.直到间隔为1进行插排
如果一开始的间隔直接就是1, 那其实就是插入排序

public static void shell_sort(int[] arr){
   for (int gap = arr.length/2; gap > 0 ; gap /=2) {
   
       for (int i=gap; i<arr.length; i++){
           for (int j = i; j > gap-1 ; j-=gap) {
               if (arr[j] < arr[j-gap]) {
                   swap(arr, j, j - gap);
               }else {
                    break;
               }
           }
       }
   }
}

6、归并排序(归并)

1、将序列中带排序数字分为若干组,每个数字分为一组

2、将若干个组两两合并,保证合并后的组是有序的

3、重复第二步操作直到只剩下一组,排序完成。

编程思路:分治法

1、先拆后合,每分解一轮便和一轮

​ 2、由于是递归,拆到最小子元素就是有序的,这时开始合并

3、两边元素都是有序序列,比较两边元素放入临时数组

​ 4、注意一边元素都放入临时数组后,记得把剩下的也放入

public static void mergeSort(int[] arr, int left, int right, int[] temp) {
        // 分解
        if (left < right) {
            int mid = (left + right) / 2;// 中间索引
            // 向左递归进行分解
            mergeSort(arr, left, mid, temp);
            // 向右递归进行分解
            mergeSort(arr, mid + 1, right, temp);// mid + 1,中间位置的后一个位置才是右边序列的开始位置
            // 每分解一轮便合并一轮
            merge(arr, left, right, mid, temp);
        }
    }

    /**
     * 合并的方法
     *
     * @param arr   待排序的数组
     * @param left  左边有序序列的初始索引
     * @param right 中间索引
     * @param mid   右边有序序列的初始索引
     * @param temp  做中转的数组
     */
    public static void merge(int[] arr, int left, int right, int mid, int[] temp) {
        int i = left; // 初始化i,左边有序序列的初始索引
        int j = mid + 1;// 初始化j,右边有序序列的初始索引(右边有序序列的初始位置即为中间位置的后一个位置)
        int t = 0;// 指向temp数组的当前索引,初始为0
        // 先把左右两边的数据(已经有序)按规则填充到temp数组
        // 直到左右两边的有序序列,有一边处理完成为止
        while (i <= mid && j <= right) {
            // 如果左边有序序列的当前元素小于或等于右边有序序列的当前元素,就将左边的元素填充到temp数组中
            if (arr[i] <= arr[j]) {
                temp[t] = arr[i];
                t++;// 索引后移
                i++;// i后移
            } else {
                // 反之,将右边有序序列的当前元素填充到temp数组中
                temp[t] = arr[j];
                t++;// 索引后移
                j++;// j后移
            }
        }
        // 把有剩余数据的一边的元素填充到temp中
        while (i <= mid) {
            // 此时说明左边序列还有剩余元素
            // 全部填充到temp数组
            temp[t] = arr[i];
            t++;
            i++;
        }
        while (j <= right) {
            // 此时说明左边序列还有剩余元素
            // 全部填充到temp数组
            temp[t] = arr[j];
            t++;
            j++;
        }
        // 将temp数组的元素复制到原数组
        t = 0;
        int tempLeft = left;
        while (tempLeft <= right) {
            arr[tempLeft] = temp[t];
            t++;
            tempLeft++;
        }
    }

7、计数排序(桶放)

1、计数排序是非比较排序 (空间换时间)
2、适用于特定问题,也就是对数据源有要求, 范围不易过大
3、时间复杂度 O(n+k)

缺陷优化问题,: 1、稳定性(顺序)2、资源浪费

实现思想:用数值下标作为记录数,该小标的对应数组值为存在的个数

    static int[] sort(int[] arr) {

        int[] result = new int[arr.length];  // 最终返回的排好序的数组
        int[] count = new int[10];  // 需排序数组的范围区间  这里假设 0~9 ,所以如果有1万范围,空间就要很大

        for (int i = 0; i < arr.length; i++) {   // 记录arr中 每个需排序数字出现的次数
            count[arr[i]]++;
        }
        System.out.println(Arrays.toString(count));
        /*
        for (int i = 0, j = 0; i < count.length; i++) {  // 回填 (该方法没有考虑稳定性
            while (count[i]-- > 0) result[j++] = i;
        }
        */

        // 累加法 解决稳定性
        for (int i = 1; i < count.length; i++) { // 知道每个需排序的数字,在排序好后,最后一个位置的下标
            count[i] = count[i] + count[i-1];
        }
        System.out.println(Arrays.toString(count));
        for (int i = arr.length-1; i >=0; i--) { // arr和result等长,  arr重后往前的位置, 就是该值 累加后范围的重后往前放的位置
            result[--count[arr[i]]] = arr[i];
        }

        return result;
    }

8、基数排序(桶放)

本质上是一种多关键字排序
有地位优先和高位优先两种

实现思路:将数列按 个位 排序后,放到桶里,再按 十位排序后, 重新放到桶里,以此类推

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值