为了方便理解,就先从较为容易理解的循环加递归的方式入手吧:
@Test
@DisplayName("冒泡排序--递归")
public void test2() {
int[] arr = {1, 3, 2, 5, 4};
sort(arr, arr.length - 1);
//遍历打印
for (int i : arr) {
System.out.println(i);
}
}
private void sort(int[] arr, int j) {
if(j==0){//如果j=0,说明已经排好序了
return;
}
int x=0;//创建一个x用于记录最后一次交换的位置
for(int i=0;i<j;i++){//每次循环都会把最大的数放到最后
if(arr[i]>arr[i+1]){//如果前面的数比后面的数大,就交换
int temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
x=i;//记录最后一次交换的位置
}
}
sort(arr,x);//x是最后一次交换的位置,也就是下次循环的最后一次比较的位置,因为其右边的数都是排好序的了
}
上面那个带 @Test 注解的方法是一个测试方法,相当一个入口函数,后面那个sort()方法才是本次算法的主体;
逻辑如下:
- 从右往左进行比较,如果左边的大于右边的数,就交换位置,当 j 也就是最·右边的索引缩小到0的位置,也就是第一个数的时候,排序完成,退出程序。
- 创建一个x用于记录本次比较的两个数中的左边那个数,用于当作下次递归调用时的右边界。也就是排除掉已经排序好右边范围。
二、非递归方式
下面是修改后的写法:
public void test2() {
int[] arr = {1, 3, 2, 5, 4};
sort(arr);
// 遍历打印
for (int i : arr) {
System.out.println(i);
}
}
private void sort(int[] arr) {
for (int j = arr.length - 1; j > 0; j--) {
boolean isSorted = true; // 假设本轮循环数组已经排序好
for (int i = 0; i < j; i++) {
if (arr[i] > arr[i + 1]) {
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
isSorted = false; // 发生交换说明数组未排序好
}
}
if (isSorted) {
break; // 如果本轮循环未发生交换,说明数组已经排序好,无需再继续循环
}
}
}
逻辑分析:
sort(int[] arr)
方法:
sort
方法用于对传入的整数数组arr
进行冒泡排序。- 外层循环
for (int j = arr.length - 1; j > 0; j--)
表示排序的轮次,初始轮次为数组长度减1,每次循环都会将未排序区间的最大元素“冒泡”到正确的位置。 - 在每一轮循环开始时,我们假设本轮循环数组已经排序好(
boolean isSorted = true;
)。 - 内层循环
for (int i = 0; i < j; i++)
用于比较相邻元素并进行交换。如果当前元素大于后一个元素,则交换它们的位置,并将isSorted
标记设为false
,表示本轮循环发生了交换,说明数组未排序好。 - 在内层循环结束后,我们会检查
isSorted
的值。如果本轮循环未发生任何交换,说明数组已经排序好,我们可以提前结束排序,因为数组已经有序了,不需要继续后续的冒泡操作。
最常见的写法:
int[] arr = {1, 3, 2, 5, 4};
for (int i = 0; i < arr.length - 1; i++) {
//每次循环都会把最大的数放到最后
for (int j = 0; j < arr.length - 1 - i; j++) {//-i是因为每次循环都会把最大的数放到最后,所以最后的数不用再比较了
//如果前面的数比后面的数大,就交换
if (arr[j] > arr[j + 1]) {
//交换
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
逻辑分析:
-
外层for循环
for (int i = 0; i < arr.length - 1; i++)
表示排序的轮次,初始轮次为0,每一轮循环都会将未排序区间的最大元素“冒泡”到正确的位置。由于每一轮循环都会将最大的数放到最后,因此可以将循环次数逐渐减少。 -
在每一轮循环开始时,内层for循环
for (int j = 0; j < arr.length - 1 - i; j++)
用于比较相邻元素并进行交换。由于每一轮循环后,已经确定的最大元素会被放置到正确的位置,所以在后续的循环中就不再需要对已排序的元素进行比较,因此可以逐渐缩小内层循环的范围。 j
表示内层循环的当前索引,范围是从0到arr.length - 1 - i
。arr.length
表示数组的长度,而i
表示外层循环已经进行过的轮次。arr.length - 1
表示数组的最后一个元素的索引,arr.length - 1 - i
表示已经确定的最大元素的索引。-
在内层循环中,通过比较
arr[j]
和arr[j + 1]
的大小,如果前面的数比后面的数大,就交换它们的位置,将较大的数往后移动。 -
在外层循环和内层循环结束后,整个数组会按照升序进行排序。