排序算法(上):冒泡排序,插入排序,选择排序

一:排序算法的执行效率

  1.  最好情况、最坏情况、平均情况时间复杂度

           第一,有些排序算法会区分,为了好对比,所以我们最好都做一下区分。第二,对于要排序的数据,有的接近有序,有的完全无序。有序度不同的数据,对于排序的执行时间肯定是有影响的,我们要知道排序算法在不同数据下的性能表现。

      2.时间复杂度的系数、常数、低阶

           时间复杂度反应的是数据规模 n 很大的时候的一个增长趋势,所以它表示的时候会忽略系数、常数、低阶。但是实际的软件开发中,我们排序的可能是 10 个、100 个、1000 个这样规模很小的数据,所以,在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来。

       3.比较次数和交换(或移动)次数

           基于比较的排序算法的执行过程,会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动。所以,如果我们在分析排序算法的执行效率的时候,应该把比较次数和交换(或移动)次数也考虑进去。

二:排序算法的内存消耗

      算法的内存消耗可以通过空间复杂度来衡量,排序算法也不例外。不过,针对排序算法的空间复杂度,我们还引入了一个新的概念,原地排序(Sorted in place)。原地排序算法,就是特指空间复杂度是 O(1) 的排序算法。冒泡排序、插入排序、选择排序这三种排序算法,都是原地排序算法。

三:排序算法的稳定性

     仅仅用执行效率和内存消耗来衡量排序算法的好坏是不够的。针对排序算法,我们还有一个重要的度量指标,稳定性。这个概念是说,如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。那我们就把这种排序算法叫作稳定的排序算法;如果前后顺序发生变化,那对应的排序算法就叫作不稳定的排序算法

四:冒泡排序(Bubble Sort)

       冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否 满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。实际上,冒泡过程还可以优化。当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用再继续执行后续的冒泡操作。

public void bubbleSort(int[] a,int n){                   //a:数组  n:数组个数
    if (n <= 1) return;
    for(i = 0; i < n; ++i){
        boolean flag = false;                            //退出循环的标志
        for(int j = 0; j < n - i -1; ++j){
            if(a[j] > a[j+1]){                           //交换
            int temp = a[j];
            a[j] = a[j+1];
            a[j+1] = temp;
            flag = true;                                 //有数据交换
            }
        }
        if(!flag) break;                                 //没有数据交换,退出
    }
}

       第一,冒泡排序是原地排序算法吗?

       冒泡的过程只涉及相邻数据的交换操作,只需要常量级的临时空间,所以它的空间复杂度为 O(1),是一个原地排序算法。

       第二,冒泡排序是稳定的排序算法吗?

      在冒泡排序中,只有交换才可以改变两个元素的前后顺序。为了保证冒泡排序算法的稳定 性,当有相邻的两个元素大小相等的时候,我们不做交换,相同大小的数据在排序前后不会 改变序,所以冒泡排序是稳定的排序算法。

        第三,冒泡排序的时间复杂度是多少?
   
       最好情况下,要排序的数据已经是有序的了,我们只需要进行一次冒泡操作,就可以结束了,所以最好情况时间复杂度是 O(n)。而最坏的情况是,要排序的数据刚好是倒序排列的,我们需要进行 n 次冒泡操作,所以最坏情况时间复杂度为 O(n2 )。 平均情况下,需要 n*(n-1)/4 次交换操作,比较操作肯定要比交换操作多,而复 杂度的上限是 O(n 2 ),所以平均情况下的时间复杂度就是 O(n 2 )。

五:插入排序

        将数组中的数据分为两个区间,已排序区间未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。

        插入排序也包含两种操作,一种是 元素的比较 ,一种是 元素的移动 。当我们需要将一个数据
a 插入到已排序区间时,需要拿 a 与已排序区间的元素依次比较大小,找到合适的插入位置。找到插入点之后,我们还需要将插入点之后的元素顺序往后移动一位,这样才能腾出位置给元素 a 插入。对于不同的查找插入点方法(从头到尾、从尾到头),元素的比较次数是有区别的。但对于
一个给定的初始序列,移动操作的次数总是固定的,就等于逆序度。
public void insertionSort(int[] a,int n){      //a数组,n数组的大小
    if(n <= 1) return;
    for(int i = 1; i < n; ++i){
        int value = a[i];
        int j = i - 1;
        for(; j >= 0; --j){                     //查找要插入的位置
            if(a[j] > value){
                a[j + 1] = a[j];                //数据移动
            }else{
                break;
            }
        }
        a[j+1] = value;                          //插入数据
    }
}
        第一,插入排序是原地排序算法吗?
        从实现过程可以很明显地看出,插入排序算法的运行并不需要额外的存储空间,所以空间复
杂度是 O(1),也就是说,这是一个原地排序算法。
        第二,插入排序是稳定的排序算法吗?
        在插入排序中,对于值相同的元素,我们可以选择将后面出现的元素,插入到前面出现元素
的后面,这样就可以保持原有的前后顺序不变,所以插入排序是稳定的排序算法。
        第三,插入排序的时间复杂度是多少?
        如果要排序的数据已经是有序的,我们并不需要搬移任何数据。如果我们从尾到头在有序数 据组里面查找插入位置,每次只需要比较一个数据就能确定插入的位置。所以这种情况下,最好是时间复杂度为 O(n)。注意,这里是从尾到头遍历已经有序的数据 。如果数组是倒序的,每次插入都相当于在数组的第一个位置插入新的数据,所以需要移动大量的数据,所以最坏情况时间复杂度为 O(n2 )。还记得我们在数组中插入一个数据的平均时间复杂度是多少吗?没错,是 O(n)。所以,对于插入排序来说,每次插入操作都相当于在数组中插入一个数据,循环执行 n 次插入操作,所以平均时间复杂度为 O(n2 )。

六:选择排序

        选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序 每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

  public static void selectionSort(int[] a, int n) {   // 选择排序,a表示数组,n表示数组大小
      if (n <= 1) return;
      for (int i = 0; i < n - 1; ++i) {                // 查找最小值
          int minIndex = i;
          for (int j = i + 1; j < n; ++j) {
              if (a[j] < a[minIndex]) {
                  minIndex = j;
              }
           }
           int tmp = a[i];                            // 交换
           a[i] = a[minIndex];
           a[minIndex] = tmp;
        }
   }
选择排序空间复杂度为 O(1),是一种原地排序算法。选择排序的最好情况时间复杂度、最坏情况和平均情况时间复杂度都为 O(n2 ), 选择排序是一种不稳定的排序算法。 选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏 了稳定性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值