冒泡排序
概念:将一组数据中的每个元素,根据自身大小,逐步向数组的某一侧移动,使其达到有序状态
核心思想:以从小到大的顺序为例
将相邻的元素两两比较,当一个元素的值大于右侧相邻的元素,交换他们的位置;当一个元素小于或等于右侧相邻的元素,位置保持不变
原始实现
两层 for 循环实现
/**
* 每一轮循环都兢兢业业,从头到尾,存在优化空间
* @param arr
*/
public static void sort1(int arr[]) {
// 外循环:控制所有回合
for (int i = 0; i < arr.length - 1; i++) {
// 内循环:实现每一轮冒泡
for (int j = 0; j < arr.length - i - 1; j++) {
// 数据位置交换
int temp = 0;
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
优化方式一
循环过程中,某一轮循环过后数组已经达到有序状态,后续循环就可以省略
因此,关键点就是找到那个数组达到有序的时刻
思路:设置一个标志位用于记录数组是否达到有序
/**
* 当某一轮循环以后数据已经排好序了,就可以跳出循环了
* 设置一个flag作为标志,当flag满足某一条件,就跳出循环
* @param arr
*/
public static void sort2(int arr[]) {
// 外循环:控制所有回合
for (int i = 0; i < arr.length - 1; i++) {
// 增加flag作为标志,每一轮都初始化为true
Boolean flag = true;
// 内循环:实现每一轮冒泡
for (int j = 0; j < arr.length - i - 1; j++) {
// 数据位置交换
int temp = 0;
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
//只要有数据交换,就不是有序的,证明排序还没有完成
flag = false;
}
}
//每一轮内循环结束后,判断是否发生数据交换,如果没发生,证明排序完毕,跳出循环就行
if (flag) {
break;
}
}
}
优化方式二
原始数组的某一部分可能本来就是有序的,在循环过程中,可以将这一部分忽略,仅仅关注未达到有序的那一部分
思路:设置一个边界,边界左边继续进行冒泡,边界右边不进行冒泡,当边界左边也达到有序时,排序结束
/**
* 假设这样一个数组[4,2,5,3,1, 6,7,8,9]
* 右半部分已经有序,但是程序还是会每一轮都执行
* 可以考虑设置一个边界,边界左边仍然无序,边界右边已经有序,不需要继续进行冒泡
* @param arr
*/
public static void sort3(int arr[]) {
// 记录最后发生交换的位置
int lastExchangeIndex = 0;
// 无序数组的边界,每次比到这里就不比了
int border = arr.length - 1;
// 外循环:控制所有回合
for (int i = 0; i < arr.length - 1; i++) {
// 增加flag作为标志,每一轮都初始化为true
Boolean flag = true;
// 内循环:实现每一轮冒泡
for (int j = 0; j < border; j++) {
// 数据位置交换
int temp = 0;
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
//只要有数据交换,就不是有序的,证明排序还没有完成
flag = false;
lastExchangeIndex = j;
}
}
// 将最后发生交换的数组下标赋值给border
border = lastExchangeIndex;
// 每一轮内循环结束后,判断是否发生数据交换,如果没发生,证明排序完毕,跳出循环就行
if (flag) {
break;
}
}
}
测试一下:
public static void main(String[] args) {
int[] elements = new int[]{9, 5, 6, 8, 4, 7, 3, 1, 2};
// Instant start = Instant.now();
sort3(elements);
// Instant end = Instant.now();
// long timeElapsed = Duration.between(start, end).toMillis(); // 单位为毫秒
for (int ele : elements
) {
System.out.println(ele);
}
// System.out.println("时间消耗:" + timeElapsed * 1000);
}
测试结果:
三种冒泡方式都能实现排序,性能方面,由于数据量太小,差异不明显,因此没有统计