经典排序算法之冒泡排序

来源:互联网 发布:谷歌访问助手mac版 编辑:程序博客网 时间:2024/05/18 03:06

冒泡排序,顾名思义,就是想冒泡一样,较大的泡泡或者较小的泡泡会往某个方向移动,直到所有的泡泡有序为止。


一、简单排序实现


在给出正宗的冒泡排序之前,我们来看一种简单的冒泡排序(以升序为例)。假定一定一个无序序列,从第一个位置开始,将此位置的元素与后面所有无序的元素逐个进行比较,只要是比这个位置的元素值小的元素,就与这个位置的元素互换值,直到序列末尾。因此,我们可以确定这个位置的元素相较于剩余无序序列中是最小的,即,每一轮比较下来,我们都是能确定一个最小的元素。以下面的图为例:


原始无序序列如下,一共9个元素,下标从0——8:




进入迭代,第一轮,将下标为0的元素与之后的无序序列逐个进行比较,并将每次比较较小的元素存放在此。




可以知道后面的无序元素个数为8,因此需要比较8次才能确定最小元素。8次的结果如下:


1 < 9,互换

5 > 1,不变

8 > 1,不变

3 > 1不变

7 > 1,不变

4 > 1,不变

6 > 1,不变

2 > 1,不变


好了,现在已经确定下标为0的位置的元素了,接下来确定下标为1的位置的元素。因为此下标之前的所有元素已经有序,因此只需要与后面的无序的

序列逐个进行比较得出最小元素。可知下标为1的后面有7个元素,因此需要比较7次才能得出最小值。7次比较结果如下:


5 < 9,互换

8 > 5,不变

3 < 5,互换

7 > 3,不变

4 > 3,不变

6 > 3,不变

2 < 3,互换


好了,经过第二轮,我们可以确定下标为1的位置的元素了。接下来第三轮将确定下标为2的位置元素了,依此类推,最后便能够使整个序列有序。

我们将这整个过程量化:假设序列的元素个数为n,那么我们将要逐个确认下标为0到n-1的位置的元素个数(当只剩最后一个元素的时候就不需要再比较了)。假设我们现在需要确认下标位置为i的位置的元素,将需要将下标为i的位置的元素逐个跟后面n-1-i个无序元素进行比较从而得到最小元素。这个过程需要两层循环便能完成,代码如下:

void BubbleSort0(int array[], int arrayCount){for(int i = 0; i < arrayCount - 1; ++i)  //下标从0到arrayCount-2,依次确定每个位置的元素{for(int j = i + 1; j < arrayCount; ++j)  //每次都与i后面的无序序列比较,得到最小的元素存放在此{if(array[j] < array[i])  //如果发现比当前确定位置的元素要小,则互换,以保证当前确定位置总是存放最小元素{int temp = array[j];array[j] = array[i];array[i] = temp;}}}}


二、冒泡排序算法


上面的排序算法最后总是能够得到有序序列。它每轮比较都能确定一个位置的元素值,但是对于剩下的元素的排序没有任何帮助,甚至在有时候互换
时,还将比较小的元素换到了最后面,像是前面第二轮比较的最后一次比较中,2 < 3,便将应该比较考前的3换到了最后。

我们分析一下产生这种结果的原因,因为每次比较都是与后面所有的无序序列进行比较,因此会使得序列的顺序发生很大的改变,应该说是一种不稳
定的排序了,这并不符合冒泡的规则。

冒泡是一路“浮上来”的,即一路与所遇到的泡泡进行比较,比我小的就把“浮”的权利交给你了,你去与继续往上浮吧;比我大的我则越过你继续
网上浮。因此,最后浮到水面的肯定是最小的泡泡了。因此,每一轮“浮”操作也能够确定一个位置的元素。(以升序为例的)与前面的排序算法相
比较,冒泡排序也将进行大量的比较,但是每次都是相邻元素的比较,并且每次比较都是有益与排序的,即总是将较小的往前移了,即使没有移动最
后。还是以上面的例子为例,图解一下这个过程。

第一轮,最后一个元素往前浮。逐个与相邻元素进行比较,如果遇到更小的元素则让更小的元素接棒继续往前浮;否则自己往前移;

6 > 2,2继续前浮
4 > 2,2继续前浮
7 > 2,2继续前浮
3 > 2,2继续前浮
8 > 2,2继续前浮
5 > 3,2继续前浮
1 < 2,1接棒前浮
9 > 1,1继续前浮

好了,到达终点了,确定了下标为0的位置元素。下一轮将确定下标为1的位置元素,依然是最后一个元素开始“往上浮”,终点将变成下标为1的位置

。可以看出,这一轮不仅确定了一个元素位置,同时还将较小的2也往前移了,并且没有太大改变整个序列的顺序,这将是很稳定的排序。


我们将这个过程量化:每次确认一个,我们将依次确认下标为0到n-1的元素,最后一个元素无序再比较。假设我们将确定下标为i的位置元素,需要从

最后一个位置n-1上浮到位置i,依然是两次循环。代码如下:


void BubbleSort1(int array[], int arrayCount){for(int i = 0; i < arrayCount - 1; ++i){for(int j = arrayCount - 1; j > i; --j){if(array[j] < array[j - 1])  //如果比当前浮动元素大,则互换一下{int temp = array[j];array[j] = array[j - 1];array[j - 1] = temp;}}}}


三、改进的冒泡排序


对于上面的冒泡排序算法,有一种情况我们需要考虑到,假设当序列提前有序。比如像是如下序列:




在准备进行第一轮冒泡的时候就已经是有序的序列了,因此第二轮的冒泡以及后面所有轮的冒泡都只是徒劳的比较,很浪费时间。因此我们在想,如

何能够让我们的冒泡排序算法能够感知到在某次冒泡后整个序列已经有序了呢?我们可以思考,对于有序序列来说,一次冒泡将不会发生任何的交换

;而对于无序的序列来说,一次冒泡肯定会发生交换;因此我们可以设置一个标识flag,初始化为0,0表示没有发生交换,1表示发生了交换。我们可

以在交换的代码段设置flag = 1,那么只要发生了一次交换,flag便为1了。然后我们可以在每轮的冒泡排序之前判断flag是否为0,如果为0则已经得

到有序序列了。代码如下:


void BubbleSort2(int array[], int arrayCount){int flag = 1;for(int i = 0; i < arrayCount - 1; ++i){if(flag == 0) return;for(int j = arrayCount - 1; j > i; --j){flag = 0;if(array[j] < array[j - 1])  //如果比当前浮动元素大,则互换一下{flag = 1;int temp = array[j];array[j] = array[j - 1];array[j - 1] = temp;}}}}


我们可以使用一段程序来测试一下,设置了flag一般比没有设置flag的冒泡算法要优。


代码如下:


const int MAXN = 50000;int array[MAXN];void main(){for(int i = 0; i < MAXN; ++i)array[i] = rand() * rand();clock_t ibegin = clock();BubbleSort1(array, MAXN);clock_t iend = clock();cout<<"没有设置flag的时间为: "<<iend - ibegin<<" 毫秒\n";ibegin = clock();BubbleSort2(array, MAXN);iend = clock();cout<<"设置flag的时间为: "<<iend - ibegin<<" 毫秒\n";system("pause");}

运行结果如下:




0 0
原创粉丝点击