冒泡排序、选择排序、插入排序、希尔排序(推导过程~!)

常见的排序算法:

在这里插入图片描述

一、冒泡排序:演示冒泡过程的例子

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

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

图解:

在这里插入图片描述

总结:

(1) 一共进行 数组的大小-1 次 大的循环
(2)每一趟排序的次数在逐渐的减少
(3) 如果我们发现在某趟排序中,没有发生一次交换, 可以提前结束冒泡排序。这个就是优化


/**
* 推倒思路:
*/
int[] arr = {5,4,3,2,1};
// 第1次排序
int index = 0;
for(int i = 0; i < arr.length - 1 - index; i++) {
    // 找到逆序的,交换位置
    if(arr[i] > arr[i + 1]) {
        int cur = arr[i];
        arr[i] = arr[i + 1];
        arr[i + 1] = cur;
    }
}
//  [4, 3, 2, 1, 5]  把5排序到最后面
System.out.println("第" + (index + 1) + "次排序: " + Arrays.toString(arr));


// 第2次排序:
index = 1;
for(int i = 0; i < arr.length - 1 - index; i++) {
    // 找到逆序的,交换位置
    if(arr[i] > arr[i + 1]) {
        int cur = arr[i];
        arr[i] = arr[i + 1];
        arr[i + 1] = cur;
    }
}

// [3, 2, 1, 4, 5]把4排序到最后面
System.out.println("第" + (index + 1) + "次排序: " + Arrays.toString(arr));

// 第3次排序:
index = 2;
for(int i = 0; i < arr.length - 1 - index; i++) {
    // 找到逆序的,交换位置
    if(arr[i] > arr[i + 1]) {
        int cur = arr[i];
        arr[i] = arr[i + 1];
        arr[i + 1] = cur;
    }
}

// [2, 1, 3, 4, 5] 把3排序到最后面
System.out.println("第" + (index + 1) + "次排序: " + Arrays.toString(arr));


/** 华丽分割线=====================================================*/


/**
* 冒泡排序  8万数据  20 - 30 s
*/
private static void BubbleSort() {

    // 思路: 由上面推倒我们可以用双重循环代替
    //  内层循环:遍历的是每一次的从0位置到最后位置的,然后比较大小排序
    //  外层循环:一共循环几次能排序出来
    int[] arr = {2,1,5,4,11,6,22,7,9};
    // 优化 一旦发现没有变化位置,直接退出即可
    boolean isSort = 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]) {
                int cur =arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = cur;
                isSort=true;
            }
        }
        System.out.println("第" + (i + 1) + "排序: " + Arrays.toString(arr));
        if(!isSort) {
            break;
        }else {
            // 重置状态
            isSort = false;
        }
    }

    System.out.print(Arrays.toString(arr));
    /**
     * 结果:
     * 第1排序: [1, 2, 4, 5, 6, 11, 7, 9, 22]
     * 第2排序: [1, 2, 4, 5, 6, 7, 9, 11, 22]
     * 第3排序: [1, 2, 4, 5, 6, 7, 9, 11, 22] // 排了三次就不用拍了,这就是优化。
     * [1, 2, 4, 5, 6, 7, 9, 11, 22]
     */
}

二、选择排序:

在这里插入图片描述
总结: 默认每次比较的第一位置为最小值,然后遍历比较,如果有比此数还小的值,交换位置。

代码实现:

/** 选择排序*/
private static void selecterSort() {
    int[] arr = {6,5,4,3,2,1};
    
    /** ====== 推倒过程 =========*/
    // 第1次排序
    int index = 0;
    // 默认最小值为数组第一个
    int min = arr[index];
    // 最小值位置
    int minPos = index;
    for(int i = 0 + 1; i < arr.length; i++) {
        // 找到最小值的最小位置
        if(min > arr[i]) {
            min = arr[i];
            minPos  = i;
        }
    }

    // 保证不是原位置
    if(minPos != index) {
        // 交换位置
        arr[minPos] = arr[index];
        arr[index] = min;
    }
    // [1, 5, 4, 3, 2, 6]  把1放到第一个位置
    System.out.println("排序第" + (index + 1) + "次" + Arrays.toString(arr));


    // 第2次排序
    index = 1;
    // 默认最小值为数组第一个
    min = arr[index];
    // 最小值位置
    minPos = index;
    for(int i = 1 + 1; i < arr.length; i++) {
        // 找到最小值的最小位置
        if(min > arr[i]) {
            min = arr[i];
            minPos  = i;
        }
    }
    // 保证不是原位置
    if(minPos != index) {
        // 交换位置
        arr[minPos] = arr[index];
        arr[index] = min;
    }
    // [1, 5, 4, 3, 2, 6] =======>[1, 2, 4, 3, 5, 6]   把2 和5掉换位置
    System.out.println("排序第" + (index + 1) + "次" + Arrays.toString(arr));

    // 第3次排序
    index = 2;
    // 默认最小值为数组第一个
    min = arr[index];
    // 最小值位置
    minPos = index;
    for(int i = 2 + 1; i < arr.length; i++) {
        // 找到最小值的最小位置
        if(min > arr[i]) {
            min = arr[i];
            minPos  = i;
        }
    }

    // 保证不是原位置
    if(minPos != index) {
        // 交换位置
        arr[minPos] = arr[index];
        arr[index] = min;
    }
    // [1, 2, 4, 3, 5, 6]  =======> [1, 2, 3, 4, 5, 6] 把3 和4掉换位置
    System.out.println("排序第" + (index + 1) + "次" + Arrays.toString(arr));

    /**============================ 华丽分割线===============================*/

    /**
     * 根据上面推理:总结两个循环搞定
     * 8万数据 时间 2 - 3 s
     * 时间复杂度  0n²
     * 思路:1、内存循环找到每次循环最小值,和最小值位置
     *       2、外层循环排序一共循环多少次比较。
     */
    // 一共比较几次?  n-1次
    for(int i = 0; i < arr.length - 1; i++) {
        // 假定第一个数是最小值
        min = arr[i];
        // 最小值位置
        index = i;
        // 每一次比较,逻辑
        for(int j = i + 1; j < arr.length; j++) {
            if(min > arr[j]) {
                // 找到最小的值 和 位置
                min = arr[j];
                index = j;
            }
        }
        if(i != index) {
            // 交换位置
            int cur = arr[i];
            arr[i] = min;
            arr[index] = cur;
            System.out.println("排序第" + (i + 1) + "次" + Arrays.toString(arr));
        }else {
            System.out.println("排序第" + (i + 1) + "次" + "没有发生交换");
        }
    }
    System.out.println("结果:" + Arrays.toString(arr));
    /**
     * 排序第1次[1, 5, 4, 3, 2, 6]
     * 排序第2次[1, 2, 4, 3, 5, 6]
     * 排序第3次[1, 2, 3, 4, 5, 6]
     * 排序第4次没有发生交换
     * 排序第5次没有发生交换
     * 结果:[1, 2, 3, 4, 5, 6]
     */
}

三、插入排序:

插入排序法思想:

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

在这里插入图片描述

 private static void insertSort() {
        int[] arr = {101,34,119,1};
        /**
         * 推倒过程
         * 原 [101, 34, 119, 1]
         * 1、看成两个数组,一个有序一个无序。  第一次排序[101]为有序   [34,119,1]为无序
         * 2、比较有序数据小的放在前面
         */
//
//        /** 第一次插入比较*/
//        // 要插入的数
//        int insertNum = arr[1];
//        // 要插入的数前一个位置(比较)
//        int insertPre = 1 - 1;
//
//        /**
//         *  insertPre >= 0   ===========》 比较位置不能越界
//         *  insertNum < arr[insertPre] ==========》 插入的数小于有序数组的数, 数有序的数往后措一位
//         */
//        while (insertPre >= 0 && insertNum < arr[insertPre]){
//            arr[insertPre + 1] = arr[insertPre];
//            insertPre--;
//        }
//
//        // 插入的位置替换
//        arr[insertPre + 1] = insertNum;
//
//        System.out.println("第1次排序结果: " + Arrays.toString(arr));
//
//        /** 第二次插入比较*/
//        insertNum = arr[2];
//        insertPre = 2 - 1;
//
//        while (insertPre >= 0 && insertNum < arr[insertPre]){
//            arr[insertPre + 1] = arr[insertPre];
//            insertPre--;
//        }
//
//        arr[insertPre + 1] = insertNum;
//        System.out.println("第2次排序结果: " + Arrays.toString(arr));
//
//        /** 第二次插入比较*/
//        insertNum = arr[3];
//        insertPre = 3 - 1;
//
//        while (insertPre >= 0 && insertNum < arr[insertPre]){
//            arr[insertPre + 1] = arr[insertPre];
//            insertPre--;
//        }
//
//        arr[insertPre + 1] = insertNum;
//        System.out.println("第3次排序结果: " + Arrays.toString(arr));


        /**
         *  1、一共排序数组长度 - 1 次
         *  第一层循环: i == 》 第几次循环
         */
        for(int i = 1; i < arr.length; i++) {

            // 要插入的数
            int insertNum = arr[i];
            // 要插入的数的前一个位置
            int insertPre = i - 1;

            while (insertPre >= 0 && insertNum < arr[insertPre]){
                arr[insertPre + 1] = arr[insertPre];
                insertPre--;
            }

            arr[insertPre + 1] = insertNum;
        }
        // 排序结果: [1, 34, 101, 119]
        System.out.println("排序结果: " + Arrays.toString(arr));
    }

四、希尔排序:插入算法的优化

希尔排序法的示意图:

在这里插入图片描述

有一群小牛, 考试成绩分别是 {8,9,1,7,2,3,5,4,6,0} 请从小到大排序.请分别使用

  1. 希尔排序时, 对有序序列在插入时采用交换法, 并测试排序速度.
  2. 希尔排序时, 对有序序列在插入时采用移动法, 并测试排序速度
  3. 代码实现
交换法: 8万数据17s
/**
* 希尔排序: 插入算法优化。
* // 交换算法 8万数据处理  17s
*/
private static void shellSort() {
    int[] arr = {8,9,1,7,2,3,5,4,6,0};
    /**
     * 推导过程
     * 思路: 数组的长度开始  /= 2; 直到长度为1
     * 每次循环: 比较小的交换到前面
     */
    // 第一次 arr.length / 2
    for(int i = 5; i < arr.length; i++) {
        // 跟前五步比较交换位置
        for(int j = i - 5; j >= 0 ; j -= 5) {
            if(arr[j + 5] < arr[j]) {
                int cur = arr[j];
                arr[j] = arr[j + 5];
                arr[j + 5]= cur;
            }
        }
    }
    // [3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
    System.out.println(Arrays.toString(arr));
    // 第二次 (arr.length / 2 ) / 2 ======》 5 / 2 = 2
    //
    for(int i = 2; i < arr.length; i++) {
        // 跟前2步比较交换位置
        for(int j = i - 2; j >= 0 ; j -= 2) {
            if(arr[j + 2] < arr[j]) {
                int cur = arr[j];
                arr[j] = arr[j + 2];
                arr[j + 2]= cur;
            }
        }
    }
    // [0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
    System.out.println(Arrays.toString(arr));
    // 第三次  ((arr.length / 2 ) / 2) / 2 ======》 5 / 2 = 2  / 2 = 1
    // 最后一次比较
    for(int i = 1; i < arr.length; i++) {
        // 跟前1步比较交换位置
        for(int j = i - 1; j >= 0 ; j -= 1) {
            if(arr[j + 1] < arr[j]) {
                int cur = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1]= cur;
            }
        }
    }
    // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    System.out.println(Arrays.toString(arr));
    /**  ===========================华丽的分割线==========================*/
    // 每次均分,直到为1。 (一共分这么次)
    for(int gap = arr.length / 2; gap > 0; gap /= 2) {
        // 从均分位置开始遍历比较
        for(int i = gap; i < arr.length; i++){
            // 每一次怎么比
            for(int j = i - gap; j >= 0; j -= gap) {
                if(arr[j + gap] < arr[j]) {
                    int cur = arr[j];
                    arr[j] = arr[j + gap];
                    arr[j + gap]= cur;
                }
            }
        }
    }
    System.out.println(Arrays.toString(arr));
}
移动法:8万数据不到1s(分组 + 插入算法结合)
/**
* 希尔排序 移动:  分组 + 插入算法结合
* 8万数据 不到1s
*/
private static void shellMove() {
    int[] arr = {8,9,1,7,2,3,5,4,6,0};
    //希尔交换排序  8万数据 17s 因为每次都要交换数据
    //在做一步优化,我们直接结合插入,移动位置
    for(int gap = arr.length / 2; gap > 0; gap /= 2)  {
        // 从第 gap 个元素,逐个对其所在的组进行直接插入排序
        // 其实就是比较分组的大小,跟插入排序一样
        for(int i = gap; i < arr.length; i++) {
            int position = i; //要插入的位置
            int insterNum = arr[position];//要插入的数
            if(arr[position] < arr[position - gap]) {
                while ((position - gap) >= 0 && insterNum < arr[position - gap]) {
                    arr[position] = arr[position - gap];
                    position -= gap;
                }
                //当退出 while 后,就给 temp 找到插入的位置
                arr[position] = insterNum;
            }
        }
    }
    System.out.println(Arrays.toString(arr));
//        for(int gap = arr.length / 2; gap > 0 ; gap /= 2) {
//
//            for(int i = gap; i < arr.length; i++) {
//
//                int j = i - gap;
//                int tmp = arr[i];
//
//                if(tmp < arr[j]) {
//                    while (j >= 0 && tmp < arr[j]){
//                        arr[j + gap] = arr[j];
//                        j -= gap;
//                    }
//
//                    arr[j + gap] = tmp;
//                }
//            }
//        }
}
发布了51 篇原创文章 · 获赞 78 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_39079048/article/details/103172523