首页 > 编程知识 正文

冒泡排序的最优时间复杂度,冒泡排序法的平均时间复杂度

时间:2023-05-05 03:56:53 阅读:242328 作者:3671


在我们写一些简单的程序中,对一组数据进行简单的有序的排列是经常会遇到的,所以我们必须熟悉几种基本的算法。

而冒泡排序就是我们学习排序的基础


冒泡排序:

形象的可以理解为:水中上升的气泡,在上升的过程中,气泡越来越小,不断比较相邻的元素,将小的往后调


我们来解决这样一个问题:

设有一数组,其大小为6个元素(int array[6]),数组内的数据是(4,2,5,3,2,6),此刻数据是无序的,现要求我们编程将其变成一个有序的从大到小的数组,从下标0开始。


思考:按照题目要求,毫无疑问,最终的有序数组应该是这样(6,5,4,3,2,2),要做到这样,最简单的办法就是进行比较交换


1:我们从数组的第一个元素array[0]向后走,如果比相邻的小,那么交换,如此进行下去,可以发现,我们找到了所

      有元素中最小的并且已经将它放在了最后一个位置array[5]

2:然后,我们重新从第一个元素向后走,还是相邻的比较,并且交换,但是我们只需要比较到4,放在array[4]

3:重复第二步,直到比较到1


代码:

#include <stdio.h>#include <iostream>using namespace std;void Bubble_sort(int *array, int length){ //变量i,j用于循环,变量t用于交换中间值  int i, j, t; for(i = 1; i < length; i++) { //比较的次数越来越少,但是每一个都能在相应的范围内确定一个最小值并放在合理的位置 for(j = 0; j < length - i ; j++) { //在满足条件的情况下,交换两个数  if(array[j] < array[j + 1]) { t = array[j]; array[j] = array[j + 1]; array[j + 1] = t; } } } //将6个数输出 for(i = 0; i< length; i++) cout<<array[i]<<endl;}int main(){ int array[] = {4,2,5,3,2,6}; //定义一个数组并简单的初始化 int length = sizeof(array)/sizeof(int);//求定义数组的具体长度 Bubble_sort(array, length); //调用相应的排序函数 return 0; }
关于优化:我们必须从时间复杂度和空间复杂度上面来看

好多人一直都在纠结冒泡排序的时间复杂度

在最坏的情况下是:O(N)

还是在最坏的情况下是比较比较次数是:n(n - 1)/ 2

他们两到底是什么关系???


其实:总而言之,冒泡排序是一种用时间在换空间的排序。

最坏的情况当然是:每一次的比较都会进行交换,也就是说要把逆序的数列变成顺序或者要把顺序变成逆序

5,4,3,2,1以冒泡升序排列

1,从第一个数5开始和后面的数进行比较比较到倒数第二个数2为止,过程为:5跟4比较,5比4大,两者交换,然后

      跟3比较,5比3大,继续进行交换,依次进行比较,比较完成为:4,3,2,1,5

2,从第一个数4开始和后面的数进行比较,过程为:4跟3比较,4比3大,两者进行交换,然后跟2进行比较,4比2大

      ,继续交换,依次比较,比较完成为:3,2,1,4,5

3,同理


所以总的比较次数就是:4 + 3 + 2 + 1

所以,对于n位数的比较次数就是:n + (n - 1)+(n -2)+...+1 = n(n - 1)/ 2


而O(N^2)表示的是复杂度的数量级,举个例子来说,如果n = 10000,那么n(n -1 )/2 = (n^2 - n)/ 2

= (100000000 - 10000)/2,相对于10^8来说,10000可以忽略不计,所以总计算次数约为0.5 * N^2.

所以,就用O(N^2)表示了数量级(忽略了前面的系数0.5)



所以,我们可以从趟数上面处理,因为有可能我们比较一定的倘数之后就已经变成了有序的了,没有必要再去做一些无用的比较了

如:5,1,2,3,4

冒泡一次之后就变成了:1,2,3,4,5已经有序了,我们没有必要再去比较了


程序:

#include <stdio.h>#include <iostream>using namespace std;void Bubble_sort(int *array, int length){ int i, j, t; int flag; //标志量,用于指示,数列已然是有序的 for(i = 1; i < length; i++) { flag = 1; for(j = 0; j < length - i ; j++) { if(array[j] < array[j + 1]) //合理的交换相邻的两个数 { flag = 0; t = array[j]; array[j] = array[j + 1]; array[j + 1] = t; } } if(flag == 1) //标志位 break; } for(i = 0; i< length; i++)//输出排序后的数列 cout<<array[i]<<endl;}int main(){ int array[] = {4,2,5,3,2,6}; int length = sizeof(array)/sizeof(int); Bubble_sort(array, length); return 0; }
这里我们加入了标志,flag,如果有一趟排序中没有产生交换的话,那么说明此刻数列以及变成了有序的数列



从上面时间复杂度看,时间主要花费在交换上面了,如果转汇编的话,比较可能只需要一条指令,而交换可能就需要很多指令,所以我们的目的就是减少某一倘的交换次数即可

如下:为了便于讲解,我们将上面用于排序的那些数列简单的改改

4,2,3,2,6,7,8

主要的就是:4,3,2,2(前面的排列),后面就是比较就是:

array[3]跟array[4]比较,2小于6,那么记录下来,继续比较array[3]和array[5],2小于7,继续比较array[3]和array[6],

2小于8,但是由于8是数列的最后一个所以我们直接交换:

所以,第一次排序的结果就是:4,3,2,8,6,7,2

可以简单的理解:

就变成这样了,,,,(理解如上)


#include <stdio.h> #include <iostream>using namespace std;void Bubble_sort(int *array, int length){ int i, j, t, k; int flag; //flag表示减少的倘数 for(i = 1; i < length; i++) { flag = 1; for(j = 0; j < length - i ; j++) { if(array[j] < array[j + 1]) { flag = 0; k = j+ 1; while(k < length - i + 1)//确定其有效性 { if(array[j] >= array[k]) //这里添加等号,是为了不影响程序的稳定性 { //交换array[j]与array[k - 1] t = array[j]; array[j] = array[k - 1]; array[k - 1] = t; break; } if(k == length - i) //如果走到最后了,那么直接进行交换 { t = array[j]; array[j] = array[k]; array[k] = t; break; } k++; } if(k == length - i) //已经到了末尾了,可以直接退出本次的循环了 break; k = k - 2; j = k; } } if(flag == 1) break; } //输出我们排 for(i = 0; i< length; i++) cout<<array[i]<<endl;} int main(){ //定义一个数组,并给其一个简单的初始化        int array[] = {4,2,3,2,6,7,8}; //求出数组的大小        int length = sizeof(array)/sizeof(int); //调用排序函数        Bubble_sort(array, length);                return 0;       }


如上程序:上面的k值就是类似与一个简单的标志



关于第三步的优化方式,可能一部分人不太认同,因为对于冒泡而言,必须是跟相邻的进行比较,然后进行交换,而我上面这个不完全是这样的,但是,我还是认为,对于程序,重要的是:

时间复杂度,空间复杂度,如果我们能针对当前的数列选择一个合理的排序方式,那就是极好的!!!

因为,不同的排序,块排,插入,基数,堆排都有自己最适合的情况,我们不能要求某一个排序必须用某一个排序,是吧,,,,

鄙人的一点简单看法,,谢谢大家指点,,,小子谢谢啦!!!






版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。