冒泡排序,选择排序,插入排序

冒泡排序

  • 理解:在水里小泡泡往上面浮,大泡泡往水下沉,所以冒泡排序其实就是前一个和后一个比较,小的放前面,大的放后面.
  • 原理:以int[] arr = {2,3,4,1,9,7,5}为例.
    • 第一圈:
      • 先数组中第一个元素(下标为0)和数组中第二个元素(下标为1)比较大小.
        • arr[0] > arr[1],两者交换.
        • arr[0] <= arr[1],两者不交换
      • 然后比较数组第一个元素(下标为1)和数组第二个元素(下标为2)的大小.
        • arr[1] > arr[2],两者交换.
        • arr[1] <= arr[2],两者不交换
      • …以此类推
      • 第一圈结束后,数组中最后一个元素为当前数组最大值.
    • 第二圈(不管第N-1个数):
      • 先数组中第一个元素(下标为0)和数组中第二个元素(下标为1)比较大小.
        • arr[0] > arr[1],两者交换.
        • arr[0] <= arr[1],两者不交换
      • 然后比较数组第一个元素(下标为1)和数组第二个元素(下标为2)的大小.
        • arr[1] > arr[2],两者交换.
        • arr[1] <= arr[2],两者不交换
      • …以此类推
      • 第二圈结束后,数组中倒数第二个为当前数组第二个最大值.
    • …以此类推,共执行N次

代码

public class BubbleSort {
    public static void main(String[] args) {
        int[] array = new int[]{2,1,6,9,13,3,4,2};
        System.out.println(array.length);

        //排序前
        System.out.println(Arrays.toString(array));

        bubbleSort(array);

        //排序后
        System.out.println(Arrays.toString(array));
    }

    private static void bubbleSort(int[] array) {
        //首先判断array对象是否为空
        //然后判断array是否是单个元素数组
        if (array == null || array.length < 2) {
            //是就直接退出
            return;
        }
        //控制次数(第一个~最后一个--第一个~倒数第二个--以此类推)
        //array.length - 1才是最后一个元素的下标,因为下标是从0开始的
        for (int end = array.length - 1; end > 0; end--) {
            //j只能指向倒数第二个元素,防止越界
            for (int j = 0; j < end; j++) {
                //前一个比后一个元素值大,交换位置
                if (array[j] > array[j + 1]) {
                    swap(j, j + 1, array);
                }
            }
        }
    }

    //交换位置
    private static void swap(int i, int j, int[] array) {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
    }
}

  • 分析:
    • 数组中两两交换和数组寻址都是常数时间O(1)的.
    • 第一个for一共执行N次,第二个for一共执行N-1次,所以一共要执行N^2 + N次.
    • 不要高阶项系数,不要低阶项,所以我们就可以评估冒泡排序的时间复杂度为O(N^2).
    • 其空间复杂度为O(1),因为只用了一个变量没有开辟多余空间.

选择排序

  • 理解:和冒泡排序相反,选择排序是第一个元素依次和后面的每一个元素相比,比后面的小就交换,所以结束第一圈后最小的数就放在了最前面…以此类推,就可以将数组从小到大排序好.

代码

public class SelectionSort {
    public static void main(String[] args) {
        int[] array = new int[]{7, 6, 8, 9, 1, 2, 7, 6, 4};

        //排序前
        System.out.println(Arrays.toString(array));

        selectionSort(array);

        //排序后
        System.out.println(Arrays.toString(array));
    }

    //选择排序代码
    private static void selectionSort(int[] array) {

        //首先判断数组对象是否为空以及是否为单个元素数组
        if (array == null || array.length < 2){
            return;
        }
        //控制循环次数
        for (int start = 0; start < array.length; start++) {
            //注意这里的i只能取到倒数第二个数,防止i+1越界
            for (int i = star t; i < array.length - 1; i++) {
                //如果前一个比后面的大,就交换
                if (array[start] > array[i + 1]) {
                    swap(start,i + 1, array);
                }
            }
        }
    }

    private static void swap(int start, int i, int[] array) {
        int temp = array[i];
        array[i] = array[start];
        array[start] = temp;
    }
}

  • 分析:
    • 数组中两两交换和数组寻址都是常数时间O(1)的.
    • 第一个for一共执行N次,第二个for一共执行N-1次,所以一共要执行N^2 + N次.
    • 不要高阶项系数,不要低阶项,所以我们就可以评估选择排序的时间复杂度为O(N^2).
    • 其空间复杂度为O(1),因为只用了一个变量没有开辟多余空间.

插入排序(重点)

  • 理解:以int[] arr = {3,1,4,1,6}为例,可以理解为斗地主嘛,你手上的牌都从小到大排好序了,拿到一张新的牌可不就得和前面已经拿了的牌一个一个对比嘛,小的放前面,大的放后面,对吧?
    • 首先,第一个位置的数(下标为0)不动,去和第二个位置的数(下标为1)比较(即拿31比较).
      • 此时发现3>1,所以arr[0]和arr[1]的值交换.
      • 此时数组为[1,3,4,1,6].
    • 然后用第二个位置的数(下标为1)去和第三个位置的数(下标为2)比较,(即拿34比较)
      • 此时发现3 < 4,所以不交换.
      • 此时数组为[1,3,4,1,6].
    • 然后用第三个位置的数(下标为2)去和第四个位置的数(下标为3)比较,(即拿41比较)
      • 此时发现4 > 1,所以41交换位置,得到[1,3,1,4,6].
        • 再将31比较,发现3 > 1,两者交换,得到[1,1,3,4,6].
        • 再将11比较,发现1 == 1,所以不交换.
    • …以此类推
    • 当到数组末尾时结束,得到排序好的数组[1,1,3,4,6].

代码


public static void main(String[] args) {
        int[] array = new int[]{3, 1, 5, 2, 7, 6, 1};

        // 排序前
        System.out.println(Arrays.toString(array));

        insertionSort(array);

        // 排序后
        System.out.println(Arrays.toString(array));
    }

    //插入排序
    private static void insertionSort(int[] array) {

        //判断数组对象是否为空和是否为单个元素数组
        if (array == null || array.length < 2) {
            return;
        }

        //控制开始和结束,到数组末尾就停,i表示右边第一个未排序的元素下标
        for (int i = 1; i < array.length; i++) {
            // 让左边已经排好序的和右边未排序的依次比较,同时和已排序的进行比较,j表示左边已排序的最后一个元素下标
            for (int j = i - 1; j >= 0; j--) {
                //大就交换
                if (array[j] > array[j + 1]) {
                    swap(array, j);
                }
            }
        }
    }

    //交换
    private static void swap(int[] array, int j) {
        int temp = array[j];
        array[j] = array[j + 1];
        array[j + 1] = temp;
    }
}

  • 分析:
    • 首先数组寻址,交换位置这些时间复杂度都是O(1)这是没有问题的.
    • 和选择排序和冒泡排序不同的点是:前两个时间复杂度一定是O(N^2),因为不管你数组有序或者无序,你都是要按照流程一个一个比较的,跟数据状况没关系的,但是插入排序不一样,插入排序和数据状况有关系,要分情况.
      • 最好情况下的时间复杂度:
        • 当数组本来就有序时,时间复杂度为O(N),因为没有交换,就和遍历一次数组一样.
      • 最差情况下的时间复杂度
        • 当数组是逆序,而我要的是正序时(比如我要的是[1,2,3,4,5],但是你给的是[5,4,3,2,1]),时间复杂度为O(N^2),每一次都要和前面所有的交换,(例如5要交换4次,4要交换3次这样子),这就形成了一个等差数列(联想到冒泡排序怎么形成等差数列的),所以评估其时间复杂度为O(N^2).
      • 一般情况下的时间复杂度:
        • 一般情况我们一律按照最差情况估计,所以我们一般说插入排序的时间复杂度为O(N^2).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值