java算法系列:第二篇——冒泡排序

为了方便理解,就先从较为容易理解的循环加递归的方式入手吧:

@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()方法才是本次算法的主体;

逻辑如下:

  1. 从右往左进行比较,如果左边的大于右边的数,就交换位置,当 j 也就是最·右边的索引缩小到0的位置,也就是第一个数的时候,排序完成,退出程序。
  2. 创建一个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)方法:

  1. sort方法用于对传入的整数数组arr进行冒泡排序。
  2. 外层循环for (int j = arr.length - 1; j > 0; j--)表示排序的轮次,初始轮次为数组长度减1,每次循环都会将未排序区间的最大元素“冒泡”到正确的位置。
  3. 在每一轮循环开始时,我们假设本轮循环数组已经排序好(boolean isSorted = true;)。
  4. 内层循环for (int i = 0; i < j; i++)用于比较相邻元素并进行交换。如果当前元素大于后一个元素,则交换它们的位置,并将isSorted标记设为false,表示本轮循环发生了交换,说明数组未排序好。
  5. 在内层循环结束后,我们会检查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;
                }

            }

逻辑分析:

  1. 外层for循环for (int i = 0; i < arr.length - 1; i++)表示排序的轮次,初始轮次为0,每一轮循环都会将未排序区间的最大元素“冒泡”到正确的位置。由于每一轮循环都会将最大的数放到最后,因此可以将循环次数逐渐减少。

  2. 在每一轮循环开始时,内层for循环for (int j = 0; j < arr.length - 1 - i; j++)用于比较相邻元素并进行交换。由于每一轮循环后,已经确定的最大元素会被放置到正确的位置,所以在后续的循环中就不再需要对已排序的元素进行比较,因此可以逐渐缩小内层循环的范围。

  3. j 表示内层循环的当前索引,范围是从0到 arr.length - 1 - i
  4. arr.length 表示数组的长度,而 i 表示外层循环已经进行过的轮次。
  5. arr.length - 1 表示数组的最后一个元素的索引,arr.length - 1 - i 表示已经确定的最大元素的索引。
  6. 在内层循环中,通过比较arr[j]arr[j + 1]的大小,如果前面的数比后面的数大,就交换它们的位置,将较大的数往后移动。

  7. 在外层循环和内层循环结束后,整个数组会按照升序进行排序。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值