在我们写一些简单的程序中,对一组数据进行简单的有序的排列是经常会遇到的,所以我们必须熟悉几种基本的算法。
而冒泡排序就是我们学习排序的基础
冒泡排序:
形象的可以理解为:水中上升的气泡,在上升的过程中,气泡越来越小,不断比较相邻的元素,将小的往后调
我们来解决这样一个问题:
设有一数组,其大小为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
可以简单的理解:
就变成这样了,,,,(理解如上)
如上程序:上面的k值就是类似与一个简单的标志
关于第三步的优化方式,可能一部分人不太认同,因为对于冒泡而言,必须是跟相邻的进行比较,然后进行交换,而我上面这个不完全是这样的,但是,我还是认为,对于程序,重要的是:
时间复杂度,空间复杂度,如果我们能针对当前的数列选择一个合理的排序方式,那就是极好的!!!
因为,不同的排序,块排,插入,基数,堆排都有自己最适合的情况,我们不能要求某一个排序必须用某一个排序,是吧,,,,
鄙人的一点简单看法,,谢谢大家指点,,,小子谢谢啦!!!