常见排序算法学习笔记(一)


学习网站: b站视频
JavaGuide

冒泡排序

就是比如有1,4,3,5,2五个数组成的一个数组arr。冒泡排序就是索引0开始,arr[0]和arr[1]比较,1小于3,不动(1,4,3,5,2),然后arr[1]和arr[2]比较,4大于3,交换位置(1,3,4,5,2),此时arr[2]为4,然后arr[2]和arr[3]比较,4小于5,不动(1,3,4,5,2),然后arr[3]和arr[4]比较,5大于2,交换位置,此时arr[4]为5(1,3,4,2,5)。这样,一次冒泡就结束了

第一次冒泡能确定数组最后一个元素为最大的元素,以此类推,第二次冒泡能确定数组倒数第二个元素为倒数第二大的元素。(注意,第二次冒泡时,数值比较到arr.length-1就可以,因为最后一个元素已经最大,没有比较意义,后面也一样,每次冒泡少比较一个数)因此,需要进行arr.length-1次冒泡,就能排好序。

/**
 * Description 冒泡排序学习演示
 * date 2023/10/16 10:40
 *
 * @author zqh
 * @since JDK 1.8
 */
public class BubbleSort {
    public static void main(String[] args) {
        int[] arr = new int[]{100,54,-324,2,1,1,98,1};
        bubbleSort(arr);
        for (int i : arr) {
            System.out.print(" "+i);
        }
    }

    /**
     * Description 冒泡排序
     * date 2023/10/16 10:40
     *
     * @param arr 需要排序的数组
     * @author zqh
     * @since JDK 1.8
     */
    public static void bubbleSort(int[] arr){
        // 冒泡次数
        for (int i = 0;i<arr.length-1;i++){
            // 冒泡比较次数,j代表每个索引
            for (int j = 0;j<arr.length-1-i;j++){
                // 比较过程
                // 当前一个数大于后一个数的时候,交换位置
                if (arr[j] > arr[j+1]){
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
}

冒泡次数如何确定?
看数组有几个数,假如有五个,第一次确定最后一个元素,第二次确定倒数第二个元素…以此类推,确定了arr.length-1次(因为确定到第二个元素的时候就可以结束了,第一个元素一定最小)。
所以是i=0;i<arr.length-1

优化:如果数组一开始就有序,还需要排序吗?

当然不需要,所以添加一个boolean的标志标志数组是否有序

/**
 * Description 冒泡排序学习演示
 * date 2023/10/16 10:40
 *
 * @author zqh
 * @since JDK 1.8
 */
public class BubbleSort {
    public static void main(String[] args) {
        int[] arr = new int[]{100,54,-324,2,60,1,98,1};
        bubbleSort(arr);
        for (int i : arr) {
            System.out.print(" "+i);
        }
    }

    /**
     * Description 冒泡排序
     * date 2023/10/16 10:40
     *
     * @param arr 需要排序的数组
     * @author zqh
     * @since JDK 1.8
     */
    public static void bubbleSort(int[] arr){
        // 优化:如果没进if,证明没交换,数组已经有序
        // 定义一个标志,默认为true,代表有序
        boolean flag = true;
        // 冒泡次数
        for (int i = 0;i<arr.length-1;i++){
            // 冒泡比较次数,j代表每个索引
            for (int j = 0;j<arr.length-1-i;j++){
                // 比较过程
                // 当前一个数大于后一个数的时候,交换位置
                // 进了if证明数组不有序,改标志为false
                flag = false;
                if (arr[j] > arr[j+1]){
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
            if (flag) {
                break;
            }
        }
    }
}

选择排序

依旧是准备一个数组arr:1,3,-2,4,5
设置一个int变量min,由数组下标0开始初始化,每选择排序一次,就加一。
同时需要定义一个int变量min的index,在后面交换时用
那么启始的min为arr[0]=1。
然后用后面的数和这个min比较,3比1大,不变,-2比1小,那么他们俩交换(-2,3,1,4,5),此时min就等于-2了,然后后面的数就和-2比较,4大于-2,不变,5大于-2,不变。这一趟选择排序就比较完了(-2,3,1,4,5)。
第二次快排,初始化min的下标就为**(上一次min数组下标+1)**的0+1=1。此时min=arr[1]=3,min为3,后面的数就和3比较,步骤同上。
这样比较需要比较几次呢?当min=arr.length-1时,证明比较到了倒数第二个数,这次比较结束后,(数组下标0~倒数第二个数的下标)这个之间的数都是有序的了,剩下最后一个数就不用排了。所以选择排序次数为arr.length-1

selection_sort.gif

/**
 * Description 选择排序学习
 * date 2023/10/16 12:15
 *
 * @author zqh
 * @since JDK 1.8
 */
public class SelectSort {
    public static void main(String[] args) {
        int[] arr = new int[]{100, 54, 60, 0, -324, 2, 60, 1, 98, 1};
        selectSort(arr);
        for (int i : arr) {
            System.out.print(" " + i);
        }
    }

    /**
     * Description 选择排序
     * date 2023/10/16 12:15
     *
     * @param arr 接收的数组
     * @author zqh
     * @since JDK 1.8
     */
    public static void selectSort(int[] arr){
        // 选择排序次数
        for (int i = 0;i<arr.length-1;i++){
            // 排序
            // 定义min
            int min = arr[i];
            // 定义minindex
            int minIndex = i;
            // 开始比较,后面的数跟min比较,所以j+1
            // 这个for是用来找出每次选择排序时的最小值和最小值的索引的
            for (int j = i+1; j < arr.length; j++) {
                if (min > arr[j]){
                    // 得到最小值
                    min = arr[j];
                    // 得到最小值的索引
                    minIndex = j;
                }
            }
            // 实际交换数值的逻辑
            // 能走到这里,证明一开始初始化的min不是最小值,然后通过上面的for循环,得到了min和minIndex
            if (i!=minIndex){
                // 互相交换
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }
}

代码其实没太懂。。用debug看的才稍微明白点

看了眼JavaGuide上的代码,比这个好一点,贴一个

/**
 * 选择排序
 * @param arr
 * @return arr
 */
public static int[] selectionSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {
            int tmp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = tmp;
        }
    }
    return arr;
}

JavaGuide:https://javaguide.cn/cs-basics/algorithms/10-classical-sorting-algorithms.html#%E5%9B%BE%E8%A7%A3%E7%AE%97%E6%B3%95-1

插入排序

准备数组int[] arr = new int[]{1,3,2,4,5}
第一次插入排序,从arr[1]开始,arr[1]和arr[0]比较,如果arr[0]>arr[1],就交换位置。这样一次插入排序就完成了(1,3,2,4,5)。
第二次插入排序,关注arr[2],用arr[2]和arr[1]比较,2<3,那么2应该插在3前面,此时先别急着插,再用arr[2]和更前面的arr[0]比较,2>1,证明arr[2]不在arr[0]前面,这样就比较完了,注意要比较到arr[0]位置。此时(1,2,3,4,5)。
人话:从索引1开始,每次取一个索引+1的数a和前面的数排大小,插在合适的位置(左边小于a,右边大于a)
insertion_sort.gif
插入排序次数依旧是arr.length-1。

public class InsertSort {
    public static void main(String[] args) {
        int[] arr = new int[]{5,4,9,6,5,3,6,7,4,1,2,8};
        insertSort(arr);
        for (int i : arr) {
            System.out.print(" " + i);
        }
    }
    public static void insertSort(int[] arr){
        // 外层循环,循环次数
        // 从第一个元素开始的话是i<arr.length-1,但是这是从第二个元素开始的(索引为1),所以是i<arr.length
        for (int i = 1; i < arr.length; i++) {
            // 比较,注意这个比较不一定只和前面一个数比较,要和前面所有数比较
            // 前一个元素的索引
            int preIndex = i-1;
            // 当前元素
            int current = arr[i];
            // 比较,将当前元素和前面的进行比较
            // 用while不用if,因为while才是循环,if的话,不满足条件就执行下面代码了
            while (preIndex>=0 && current<arr[preIndex]){
                // 把大的放后面(右边)
                arr[preIndex+1] = arr[preIndex];
                // 接着比较前面(左边)的数
                preIndex = preIndex - 1;
            }
            // 比较完了,插入,把当前数current插到找到的perIndex右边
            arr[preIndex+1] = current;
        }
    }
}

代码参考的JavaGuide。

希尔排序

首先有一个增量的概念gap=length/2,缩小增量gap=gap/2…一直除以2
这个增量有啥用?
假如有数组arr:1,5,6,7,9,2
gap=6/2=3。意思就是原数组要被分为3个数组,怎么分呢?根据gap,现在gap=3。就是每间隔3(gap)个元素成一组,arr[0] 和 arr[3] 一组,arr[1] 和 arr[4] 一组。
这样分好组之后,在不改变位置、索引的前提下组内排序,排好后如下:
1,5,2,7,9,6。然后gap=gap/2=1
每隔一个成一组,这时当做普通插入排序即可。

gap也可以理解成步长,普通插入排序步长为1,希尔排序步长为gap,假如gap=3。按照上面的例子就是,arr[0] 和arr[3] 插入排序

如果gap为2,就是arr[0] 和 arr[2] 和arr[4]插入排序
shell_sort.png

/**
 * Description 希尔排序 JavaGuide 学习笔记
 * date 2023/10/16 18:31
 *
 * @author zqh
 * @since JDK 1.8
 */
public class ShellSort {
    public static void main(String[] args) {
        int[] arr = new int[]{100, 54, 60, 0, -324, 2, 60, 1, 98, 1};
        shellSort(arr);
        for (int i : arr) {
            System.out.print(" " + i);
        }
    }

    /**
     * Description 希尔排序
     * date 2023/10/16 18:39
     *
     * @param arr 待排序数组
     * @author zqh
     * @since JDK 1.8
     */
    public static void shellSort(int[] arr){
        // 定义初始步长
        int gap = arr.length/2;
        // 当步长大于0时,希尔排序
        while (gap>0){
            // 从gap位置比较,相当于插入排序中从第二个数开始
            for (int i = gap; i < arr.length; i++) {
                // 当前元素
                int current = arr[i];
                // 前一个元素的索引
                int preIndex = i-gap;
                // 插入排序
                while (preIndex>=0 && current<arr[preIndex]){
                    // 交换之把大的放右边
                    arr[preIndex+gap] = arr[preIndex];
                    // 接着比较前面的数
                    preIndex = preIndex - gap;
                }
                // 交换之把小的放左边
                arr[preIndex+gap] = current;
            }
            // gap/2
            gap = gap/2;
        }
    }
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值