小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
说到算法,很多人都会害怕。但是想升职加薪怎么办呢?
总是逃避是改变不了什么,也解决不了什么问题。我不希望自己以后一直被这些算法堵住,但同时也不强求一下子掌握优雅的实现算法。
一点一点地吃透它。
冒泡排序的概念:
不断的比较相邻的元素,如果顺序不对,就交换。 一轮又一轮,直到所有相邻的元素都比较完,是一种非常朴素的排序方法。
摆平心态放大招
import java.util.Arrays;
public class 冒泡算法 {
// 优化点:挨个比较了一圈,没有交换,说明已经排好了;已经有序的不要去交换了
public static void main(String[] args) {
int[] values = {3, 1, 6, 2, 9, 0, 7, 4, 5, 8};
bubbleSort(values);
System.out.println(Arrays.toString(values));
}
private static void bubbleSort(int[] values) {
int temp;
for (int i=0; i<values.length; i++) {
for (int j = 0; j<values.length-1-i; j++) {
if (values[j] > values[j+1]) { // 这一行决定了是从小到大,还是从大到小
temp = values[j];
values[j] = values[j+1];
values[j+1] = temp;
}
System.out.println(i + ": " + Arrays.toString(values));
}
System.out.println();
}
}
}
复制代码
我手敲了基础实现版后,还加了很多 print,把过程复现,然后理解程序的每一句话实现了什么,改变了什么,就会更容易理解一些。
- 核心实现部分,嵌套循环,挨个比较:
这一行可以看到 values[j]
和 values[j+1]
分别是前一个元素和后一个元素。我文章里用的是前一个大于后一个的话,就交换,也就是把前一个元素往后堆,循环这个过程,就很像泡泡。一个一个这样冒到最后。
最后的排序结果:
- 这里 j 的上界没有取 values.length, 就是因为每次冒过去,最大值已经排到最后了。
- 我们来仔细看看,每个循环内做了什么样的事情:
- 第一个循环:
第一个循环,一共 0-9,10个元素,两两比较,比较 9 次。
- 第二个循环,最后两个肯定比较好了,少一次比较:
- 第三个循环:
- 第四个循环:
- 第五 ~ 第九个循环:
也就是这个时候,你是不是明白了,为什么那么多调优文章都说,有重复的了吧?
到这里看起来已经非常棒了,上面我们已经对冒泡的次数,以及每次冒泡需要比较的次数都做了优化,但这就够了嘛,不 我们还可以对比较的次数做进一步的优化,每轮冒泡时,最后一次交换的索引可以作为下一轮冒泡比较的次数,如果这个值是 0, 就证明整个数组有序,直接退出外层循环即可。「非正经程序员 juejin.cn/post/701632… 」
优化了!
上面的截图已经很清楚地表现了代码可以优化的地方:
让这个循环在有序的情况下别比较了。
private static void bubbleSort(int[] values) {
int temp;
for (int i=0; i<values.length; i++) {
boolean flag = true;
for (int j = 0; j<values.length-1-i; j++) {
if (values[j] > values[j+1]) {
temp = values[j];
values[j] = values[j+1];
values[j+1] = temp;
// 发生交换了,就改变,数组在这一趟是无序的,还要接着比较
flag = false;
}
System.out.println(i + ": " + Arrays.toString(values));
}
// 内循环都跑完了,flag 还是 true,说明已经有序了,后面不用再比较了
if (flag) {
break;
}
System.out.println();
}
}
复制代码
我们是通过新增了一个布尔变量实现的:
重新跑一次以后发现,前面的循环和之前都一样,不同的是排好序以后,就不再进行多余的比较了: