冒泡排序算法(二)
一、冒泡排序优化分析
上一篇文章我们分析了简单冒泡排序的算法原理,我们知道要想实现简单冒泡排序需要得到array.length-1个最大值即需要循环array.length-1次,而每个最大值的获取需要第二层循环遍历array.length-1-i次,设我们的数组中元素的个数为n,那么第一层循环最后一次循环时i=n-1-1,所以我们可以求得共需要遍历的次数计算如下式:
(n-1)+(n-2)+(n-3)+...+[n-1-(n-1-1-1)]+[n-1-(n-1-1)]
= [(n-1)+1]*(n-2)/2
= n(n-2)/2
Tip: 等差数列 首项为(n-1),末项为1,项数为(n-2)
所以简单冒泡排序的时间复杂度为O(n^2)
然而我们很容易发现简单冒泡排序的一个致命缺点,那就是当我们的数据非常优质时(也就是说在未经过排序时数据内部已经有大量已排序的数据)
在这里举个极端的例子
[88, 2, 4, 34, 55]
很容易我们可以发现对于这一组数据我们只需要走一趟(外层循环遍历一次)就可以得到正确的结果
[2, 4, 34, 55, 88]
之后的第二趟,第三趟,第array.length-1趟都是在浪费时间
所以我们可以根据以上的分析对冒泡排序进行优化
二、优化代码
class MaoPaoSort {
void maoPaoSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
void maoPaoSortOptimized(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
boolean flag = false;
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
flag = true; //说明发生了交换,那么此时将标志位flag设置位true说明发生了交换
}
}
if (!flag) //在每一趟(也就是第一层循环的每一次循环)都判断flag的值,如果flag为false,也就是说这一趟数据无任何交换,那么就说明数据已经被排好了,不需要再进行循环了
break;
}
}
}
三、运行结果
四、小结
可以看出我们在优化后的冒泡排序方法里设置了一个布尔类型的标志位(flag),在第一层循环的每一次循环里都判断标志位的布尔值,在第二层循环里动态改变flag的值(当第二层循环的没一次循环发生了元素之间的交换就置flag为true表示这次循环发生过交换),如果第二层循环结束之后flag为false也就证明第二次循环每组相邻元素没有进行过任何位置交换,也就说明元素之间的位置已经排好了顺序。
如果判断出flag为false的话,那么第一层循环继续下去也就没有了任何意义,所以我们应该break出第一层循环,此时得到的元素依然是已经排好顺序的。