[排序算法]冒泡排序、插入排序、选择排序

1. 排序算法要关注哪些问题

1) 复杂度

对于算法,我们首先考虑的就是其时间复杂度和空间复杂度。

(1) 时间复杂度

在上一篇文章中,我们说到,复杂度是估计的趋势,所以我们常常忽略常数项。但是对于排序算法,如果数据规模不大,常数项对执行时间的影响就大了,所以我们还要关注常数项、低阶项和系数。

另外,由于序列顺序不一定,不同情况下的时间复杂度也不同,所以我们还要考虑最好情况和最坏情况。

(2) 空间复杂度

这里引入了一个新的概念,我们把O(1)的空间复杂度的排序算法称为原地排序。

2) 稳定性

我们平时说的稳定性,是指波动程度很小。这里举个例子:

假设我们教务处的管理系统,期末排序学生成绩。首先按照成绩进行排名,之后按照学号大小进行二次排名。如果按照学号大小的排名不会打乱前面按照成绩排名的结果,我们就说这个排序算法是稳定的。

简单点说,如果排序 2,3,6,1,8,6,3这个序列,排序之后第二位的3依然在最后一位的3前面,我们就说这个排序是稳定的。

2. 冒泡排序

在这里插入图片描述

public void bubbleSort(int[] a) {
    int n = a.length;
    if (n <= 1) {
        return ;
    }

  	// 这里面,i的作用是标记已经排序好的元素数量
  	// 每次排序,j从0到n-1-i的位置里面找到最大的元素,放到n-1-i的位置
  	// 整个过程就像冒泡一样,每次将剩余元素中最大的选出来,浮到最后
    for (int i = 0; i < n; i++) {
        // 提前退出标志
        boolean flag = false;
        for (int j = 0; j < n - i - 1; j++) {
            if (a[j] > a[j + 1]) {
                int tmp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = tmp;
                // 表示有数据交换
                flag = true;
            }
        }
      	// 如果其中一次过程没有元素交换,则说明整个数组已经是有序的了,这时就可以退出了
        if (!flag) {
            break;
        }
    }
}

1) 时间复杂度

最好情况下,数据本来就是有序的,所以执行一次就退出了,时间复杂度为O(n)

最差情况下,数据要进行n次冒泡,时间复杂度为O(n^2)

2) 冒泡排序是原地算法

没有使用额外的空间,空间复杂度为O(1)

3) 冒泡排序是稳定算法

当两个元素相等的时候,我们不会交换他们的顺序,所以冒泡排序是稳定的

3. 插入排序

在这里插入图片描述

public void insertionSort(int[] a) {
    int n = a.length;
    if (n <= 1) {
        return ;
    }

    // i从1到n-1遍历每一个元素,插入到前面有序的集合中
    for (int i = 1; i < n; i++) {
        int value = a[i];
        int j = i - 1;
      	// 找到第i个元素的正确位置,并将大于第i个元素的元素都向后移动一个位置
        for (; j >= 0; j--) {
            if (a[j] > value) {
                a[j + 1] = a[j];
            } else {
                break;
            }
        }
        // 插入数据
        a[j + 1] = value;
    }
}

1) 时间复杂度

最好情况,序列是有序的,只需要当前元素和他前一个元素进行一次比较操作,时间复杂度为O(n)

最差的情况,序列是倒序的,每一次都要插入到第一个位置,执行i-1次移动操作,时间复杂度为O(n^2)

2) 插入排序是原地算法

没有使用额外的空间,空间复杂度为O(1)

3) 插入排序是稳定算法

插入的时候,相等的情况,我们将后面的元素插入到前面的元素的后面,不影响之前的排序,所以插入排序是稳定的算法。

4. 选择排序

在这里插入图片描述

public void selectSort(int[] a) {
    int n = a.length;
    if (n <= 1) {
        return ;
    }

    for (int i = 0; i < n - 1; i++) {

        // 找到未排序的区域中最小的元素
        int j = i;
        int minIdx = j;
        for (; j < n; j++) {
            if (a[j] < a[minIdx]) {
                minIdx = j;
            }
        }
        // 将其放置到排序区最后一个 (交换到未排序区第一个)
      	if (minIdx != i) {
        	int tmp = a[minIdx];
            a[minIdx] = a[i];
            a[i] = tmp;
        }
    }
}

1) 时间复杂度

最好和最坏情况,每次都要查找未排序区域中最小的元素,时间复杂度都为O(n^2)

2) 插入排序是原地算法

没有使用额外的空间,空间复杂度为O(1)

3) 插入排序不是稳定算法

插入算法每一次都要找未排序区域的最小值和前面元素交换位置,这样破坏了稳定性。假设序列6,9,6,1,7,最小的1和第一个6交换,就破坏了两个6的先后顺序,所以插入排序不是稳定算法。

5. 总结

这三种排序算法,可以说大学都会学过,算是开拓思维类的算法,由于时间复杂度高,在实际开发中用的并不多。

下一篇:[排序算法]归并排序、快速排序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值