排序算法(一)-冒泡排序
程序员文章站
2022-03-24 13:53:22
...
0. 原理
- 临近的数字两两比较,按照从小到大或者从大到小的顺序进行交换。
- 每一趟会将最大或者最小的数字放到最终结果的位置
- 重复操作直到倒数第二位
对[2, 8, 1, 5, 3]这个数组排序,完整流程如下
round 1 start
2 8 1 5 3 (exchange data: 3 <-> 5) 2 8 1 3 5
2 8 1 3 5 (exchange data: 1 <-> 8) 2 1 8 3 5
2 1 8 3 5 (exchange data: 1 <-> 2) 1 2 8 3 5
round 1 finish
round 2 start
1 2 8 3 5 (exchange data: 3 <-> 8) 1 2 3 8 5
round 2 finish
round 3 start
1 2 3 8 5 (exchange data: 5 <-> 8) 1 2 3 5 8
round 3 finish
round 4 start
round 4 finish
可见第一趟将最小的1放到了第一位
为什么叫冒泡排序呢?因为上面两两交换的过程每一趟都将最小的值放到了最顶端,很像气泡上升到顶部的过程。
1. 实现
关键代码实现如下:
@Override
public int[] sort(int[] data) {
if (data == null || data.length <= 1) {
return data;
}
for (int i = 0; i < data.length - 1; i++) {
for (int j = data.length - 1; j > i; j--) {
if (data[j] < data[j - 1]) {
swap(data, j, j - 1);
}
}
}
return data;
}
具体实现可查看:
2. 复杂度分析
从代码和原理可以看出,外层循环需要n-1次,内层循环需要n-i次,比较一次,数据操作三次。因此最坏的情况应该是整个序列时逆向的,时间复杂度为O(n²),最好的情况是序列时正序的一次扫描即可,时间复杂度为O(n)。平均时间复杂度为O(n²)。由于不需要额外空间,空间复杂度为O(1)
3. 优化
3.1 优化点一
如果某一趟没有发生过一次交换,则说明该数组已经有序,可以直接停止循环。只要加一个标识变量就可以实现。
3.2 优化点二
使用一个标识变量记录下最后一个发生交换的位置,则可以说明前面没有发生交换的序列已经有序,所以内部循环的终结点可以用这个值来作为结束点,也就是上面代码中的i值。在内部循环结束后i=changFlag
就是这个作用。
两个优化点写入代码后如下:
@Override
public int[] sort(int[] data) {
if (data == null || data.length <= 1) {
return data;
}
//标识是否已经有序
boolean isChanged;
//标识最后一个发生变化的位置
int changFlag = 0;
for (int i = 0; i < data.length - 1; i++) {
isChanged = false;
System.out.println("round " + (i + 1) + " start");
for (int j = data.length - 1; j > i; j--) {
if (data[j] < data[j - 1]) {
swap(data, j, j - 1);
isChanged = true;
changFlag = j - 1;
}
}
//i直接设置为最后一次的交换
i = changFlag;
//如果没有改变,则证明序列已经有序
if (!isChanged) {
System.out.println("no exchange, break out loop");
break;
}
System.out.println("round " + (i + 1) + " finish");
}
return data;
}
具体实现可查看:
上一篇: C#插入排序原理讲解及代码块
下一篇: c++归并排序代码及原理