算法学习第一记——冒泡排序

    今天重新温习了一下关于排序的算法,也有了不少新的体会,相对于第一次学习排序只认识思路,现在更专注于如何分析算法以及如何优化算法。

   首先第一个要讲的是我大学第一个学的排序算法 - 冒泡排序

    冒泡排序算法的思路很简单,首先它是一个交换排序,它只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复ñ次,就完成了ñ个元素的排序工作。

 直接给上代码: 

   void BubbleSort(int arr[],int length)

{
   if(length<=1)
     return;
    for(int i=0;i<length;i++)
      for(int j=0;j<length-i;j++)
        if(arr[j]<arr[j-1])
       {
         swap(arr[j],arr[j-1]);
       }
}

 冒泡排序由于没有使用额外的空间,所以它的空间复杂度为O(1),而且由于相邻数据如果相等的话并不会发生交换操作,所以冒泡排序是稳定的。冒泡排序的最优最坏时间复杂度在未优化的状况下都是O(N²),针对冒泡排序优化的状况下面我会给出,其实也简单,针对一趟冒泡如果未发生交换的话,则排序直接结束。所以我们只需要一个标志位来标识。那么优化后的冒泡排序时间复杂度可以达到多少呢?答案依然是O(N²)!那么它的优化体现在哪里呢?当我们的数据本身就是有序的,或者经过少数几趟排序后就已经有序的时候,优化过的冒泡排序可以直接结束。

  下面给出改良后的冒泡排序算法:

void Bubble_Sort(int arr[],int length)
{
   if(length<=1)
     return;
    bool is_sorted=false;
   for(int i=0;i<length&&!is_sorted;i++)
  {
      is_sorted=true;
      for(int j=0;j<length-i;j++)
         if(arr[j]<arr[j-1])
         {
            swap(arr[j],arr[j-1]);
            is_sorted=false;
         }
   }
}

     直观上来看冒泡排序的时间复杂度是可以一眼看出来的O(N²)但是我最近看网课,看到了一种新的分析方式涉及到两个新的概念“有序度”,“无序度“,针对这两个新的概念我们可以直接来分析冒泡的效率。

。有序度是数组中具有有序关系的元素对的个数有序元素对数字表达式表示就是这样:

       逆序元素对a [i]> a [j],如果我<j。关于这三个概念,我们还可以得到一个新的公式:逆序度=满有序度 - 有序度。我们排序的过程就是一种增加有序度,减少逆序度的过程,最后达到满有序度,就说明排序完成。

     拿排序的数组的初始状态是4,5,6,3,2,1其中有序元素对(4,5),(4,6),(5,6)所以有序度是3.N = 6,所以排序完成之后终态的满有序度为N *(N-1)/ 2 = 15

冒泡排序包含两个操作原子,比较和交换。每交换一次,有序度就加1 ,.不管算法怎么改进,交换次数总是确定的,即为逆序度,也就是N *(N-1 )/ 2-初始有序度。此例中就是15-3 = 12,要进行12次交换。

对于包含Ñ个数据的数组进行冒泡排序,平均交换次数是多少呢?最坏情况下,初始状态的有序度是0,所以要进行N *(N-1)/ 2次交换。最好情况下,初始状态的有序度是N *(N-1)/ 2,就不需要交换。我们可以取个中间值N *(N-1)/ 4,来表示初始有序度既不是很高也不是很低的平均情况。

换句话说,平均情况下,需要N *(N-1)/ 4次交换操作,比较操作肯定要比交换操作多,而复杂度的上限是O(N²),所以平均情况下的时间复杂度就是O(N²),这种论证方法其实并不严格,但是还算实用。

猜你喜欢

转载自blog.csdn.net/qq_31984717/article/details/83903467