什么是冒泡排序?
一种简单直观的排序算法,它重复地走访过要排序的元素列,依次比较相邻的两个元素,如果顺序错误则交换它们,直到没有再需要交换的元素,排序完成。
冒泡排序的步骤
-
比较相邻元素:
首先,比较数组中相邻的两个元素,依次向后移动。 -
交换元素位置:
如果发现前一个元素比后一个元素大,就交换它们的位置。 -
重复步骤:
重复步骤 1 和步骤 2,直到没有再需要交换的元素。这意味着数组已经按照升序排列完成。
代码案例:
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
// 外层循环控制排序轮数,最多进行 n-1 轮
for (int i = 0; i < n - 1; i++) {
// 内层循环控制每轮比较次数
for (int j = 0; j < n - 1 - i; j++) {
// 如果前面的元素大于后面的元素,交换它们
if (arr[j] > arr[j + 1]) {
// 交换 arr[j] 和 arr[j + 1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
public static void main(String[] args) {
// 测试用例
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组:");
printArray(arr); // 打印原始数组
// 调用冒泡排序算法
bubbleSort(arr);
System.out.println("排序后的数组:");
printArray(arr); // 打印排序后的数组
}
// 打印数组的方法
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println(); // 换行
}
}
详细步骤解释
初始化数组:
在 main
方法中,首先定义了一个整数数组 arr
,包含了一些无序的整数。
打印原始数组:
调用 printArray
方法,将原始数组打印出来,展示出未排序的状态。
调用冒泡排序算法:
bubbleSort(arr)
方法被调用,开始对数组 arr
进行冒泡排序。
冒泡排序核心:
-
外层循环
for (int i = 0; i < n - 1; i++)
控制排序的轮数,其中n
是数组长度。每轮排序会将当前未排序部分的最大元素冒泡到正确的位置。 -
内层循环
for (int j = 0; j < n - 1 - i; j++)
对当前未排序部分的相邻元素进行比较和可能的交换,确保每轮结束后最大的元素位于正确的位置。
交换操作:
如果发现 arr[j] > arr[j + 1]
,就交换 arr[j]
和 arr[j + 1]
的值,保证较大的值“冒泡”到数组的右侧。
打印排序后的数组:
冒泡排序完成后,再次调用 printArray
方法打印已排序的数组,展示出升序排列的结果。
优化冒泡排序
public static void bubbleSort(int[] arr) {
int n = arr.length;
boolean flag = false; // 结束冒泡的条件
// 外层循环控制排序轮数,最多进行 n-1 轮
for (int i = 0; i < n - 1; i++) {
// 内层循环控制每轮比较次数
for (int j = 0; j < n - 1 - i; j++) {
// 如果前面的元素大于后面的元素,交换它们
if (arr[j] > arr[j + 1]) {
// 交换 arr[j] 和 arr[j + 1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = true; // 如果执行了交换,则修改flag值
}
}
// 判断flag变量有没有变动
// 如果没有变动,表示这一轮循环都没有进入交换操作,说明数组已经有序,可以提前结束排序过程
if (!flag) {
break;
} else {
// 每次内层循环完毕修改flag的值
flag = false;
}
}
}
代码解释
初始化变量
-
int n = arr.length;
获取数组的长度,确定要排序的元素个数。 -
boolean flag = false;
这个标志变量用来指示是否进行了交换操作。初始为false
,表示本轮排序开始时还没有进行交换。
外层循环:
-
for (int i = 0; i < n - 1; i++)
控制排序的轮数,最多进行n-1
轮。每完成一轮,最大的元素就会被冒泡到正确的位置。
内层循环:
-
for (int j = 0; j < n - 1 - i; j++)
在每一轮中,对相邻的元素进行比较和可能的交换操作。
交换操作和标志变量的使用:
-
if (arr[j] > arr[j + 1])
如果前面的元素比后面的元素大,则执行交换,并将flag
设置为true
表示进行了交换操作。 -
如果在一轮内部循环中没有进行任何交换操作(即
flag
仍为false
),则说明数组已经有序,可以提前结束排序过程,以节省不必要的比较。
优化效果:
-
这种优化可以显著减少冒泡排序的比较次数,特别是对于近乎有序的数组或者数据集,可以大幅度提升算法的效率。
维护标志变量:
-
每次内层循环完成后,不管是否进行了交换,都会将
flag
重新设置为false
,以便下一轮内层循环开始时重新判断是否有交换操作。
总结
这段代码在经典的冒泡排序基础上增加了一个 flag
变量,通过检测是否进行了交换来判断是否可以提前结束排序。这种优化对于某些特定情况下的数组可以显著提升性能,使得排序算法更加高效。
欧了,到这里我应该解释的差不多啦,我是南极,大胆做自己,活出精彩的人生👊👊👊