java算法:冒泡排序

目录

基本使用

冒泡排序(Bubble Sort)是一种简单的排序算法,它重复地遍历待排序的元素比较相邻的两个元素并按照规定的顺序交换它们,直到整个序列排序完成为止。冒泡排序的基本思想是将较大(或较小)的元素逐渐"浮"到序列的末端。

以下是冒泡排序的详细步骤:

  • 从待排序的数组中选择第一个元素作为当前元素。
  • 将当前元素与它的下一个元素进行比较。如果当前元素大于(或小于,取决于排序顺序)下一个元素,则交换它们的位置。
  • 移动到下一个元素,重复步骤2。继续比较相邻元素并交换它们的位置,直到到达数组末尾。
  • 重复步骤1至步骤3,直到没有任何元素需要交换,即数组已经排序完成。 排序完成后,数组中的元素就按照指定的顺序排列。

以下是使用Java编写的冒泡排序示例:

java 复制代码
public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - i - 1; j++) {
                if (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[] array = {5, 3, 8, 4, 2};
        bubbleSort(array);
        
        System.out.print("排序结果: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
        // 输出: 排序结果: 2 3 4 5 8
    }
}

bubbleSort方法接受一个整数数组作为输入,并使用冒泡排序算法对数组进行排序。内部的两个嵌套循环用于比较相邻元素并根据需要进行交换,从而实现排序。main方法中的示例用法展示了如何使用bubbleSort方法对一个整数数组进行排序并输出结果。

冒泡排序的时间复杂度为O(n^2),其中n是数组的长度。尽管冒泡排序在大规模数据集上的性能较差,但它是一种简单直观的排序算法,适用于小规模数据或部分有序的数据。

冒泡排序优化

当冒泡排序的待排序数组已经完全有序时,仍然会进行多余的比较和交换操作,这是冒泡排序效率较低的原因之一。

引入标志变量

为了优化冒泡排序,可以引入一个标志变量来记录是否发生了交换操作,如果某一趟遍历中没有发生交换,就说明数组已经有序,可以提前结束排序。

以下是冒泡排序优化的示例代码:

java 复制代码
public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        boolean swapped; // 标志变量,记录是否发生了交换操作
        
        for (int i = 0; i < n - 1; i++) {
            swapped = false; // 每趟遍历开始时,将标志变量设为false
            
            for (int j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // 交换位置
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    swapped = true; // 设置标志变量为true
                }
            }
            
            if (!swapped) {
                // 如果某一趟遍历中没有发生交换,说明数组已经有序,提前结束排序
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] array = {5, 3, 8, 4, 2};
        bubbleSort(array);
        
        System.out.print("排序结果: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
        // 输出: 排序结果: 2 3 4 5 8
    }
}

我们引入了一个名为swapped的布尔型标志变量。每次开始新的一趟遍历时,将标志变量初始化为false。在内层循环中,如果发生了交换操作,就将标志变量设为true。如果某一趟遍历结束后,标志变量仍然是false,说明在该趟遍历中没有发生交换,即数组已经有序,可以提前结束排序。

通过这种优化,当输入数组已经有序时,冒泡排序的最好情况时间复杂度可以降低到O(n),其中n是数组的长度。这是因为在最好情况下,只需要进行一趟遍历就可以确定数组已经有序,不再需要执行多余的比较和交换操作。

边界优化

在每一趟遍历中,通过记录最后一次交换的位置,将该位置作为下一趟遍历的边界。这样可以减少内层循环的遍历次数,因为在边界之后的元素已经有序,无需再进行比较。

java 复制代码
public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        int lastSwap; // 记录最后一次交换的位置
        
        for (int i = 0; i < n - 1; i++) {
            lastSwap = 0; // 每趟遍历开始时,将最后一次交换的位置初始化为0
            
            for (int j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // 交换位置
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    lastSwap = j + 1; // 更新最后一次交换的位置
                }
            }
            
            if (lastSwap == 0) {
                // 如果最后一次交换的位置仍然是0,说明在该趟遍历中没有发生交换,即数组已经有序,提前结束排序
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] array = {5, 3, 8, 4, 2};
        bubbleSort(array);
        
        System.out.print("排序结果: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
        // 输出: 排序结果: 2 3 4 5 8
    }
}

鸡尾酒排序(双向冒泡排序)

传统的冒泡排序是从左到右逐个比较并交换相邻元素的位置,而鸡尾酒排序是交替进行从左到右和从右到左的遍历,将较大的元素从末端冒泡到首端,再将较小的元素从首端冒泡到末端。这样可以提高排序的效率,尤其对于部分有序的数组。

java 复制代码
public class BubbleSort {
    public static void cocktailSort(int[] arr) {
        int n = arr.length;
        boolean swapped;
        
        for (int i = 0; i < n / 2; i++) {
            swapped = false;
            
            // 从左到右遍历,将较大的元素冒泡到末端
            for (int j = i; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // 交换位置
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    swapped = true;
                }
            }
            
            if (!swapped) {
                // 如果没有发生交换,说明数组已经有序,提前结束排序
                break;
            }
            
            swapped = false;
            
            // 从右到左遍历,将较小的元素冒泡到首端
            for (int j = n - i - 2; j > i; j--) {
                if (arr[j] < arr[j - 1]) {
                    // 交换位置
                    int temp = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = temp;
                    swapped = true;
                }
            }
            
            if (!swapped) {
                // 如果没有发生交换,说明数组已经有序,提前结束排序
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] array = {5, 3, 8, 4, 2};
        cocktailSort(array);
        
        System.out.print("排序结果: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
        // 输出: 排序结果: 2 3 4 5 8
    }
}

检测有序区域优化

在每一趟遍历中,可以记录上一次发生交换的位置作为有序区域的边界。如果在某一趟遍历中没有发生交换,可以将该边界作为下一次遍历的边界,以减少比较和交换的次数。

java 复制代码
public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        int lastSwapIndex = n - 1; // 记录最后一次交换的位置
        
        while (lastSwapIndex > 0) {
            int k = lastSwapIndex;
            lastSwapIndex = 0;
            
            for (int i = 0; i < k; i++) {
                if (arr[i] > arr[i + 1]) {
                    // 交换位置
                    int temp = arr[i];
                    arr[i] = arr[i + 1];
                    arr[i + 1] = temp;
                    lastSwapIndex = i; // 更新最后一次交换的位置
                }
            }
            
            if (lastSwapIndex == 0) {
                // 如果最后一次交换的位置仍然是0,说明在该趟遍历中没有发生交换,即数组已经有序,提前结束排序
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] array = {5, 3, 8, 4, 2};
        bubbleSort(array);
        
        System.out.print("排序结果: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
        // 输出: 排序结果: 2 3 4 5 8
    }
}

使用递归

可以使用递归方式实现冒泡排序。将排序的范围缩小为当前元素之后的子数组,并在子数组上进行递归排序。递归的终止条件是数组长度为1,即已经有序。

java 复制代码
public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        bubbleSortRecursive(arr, arr.length);
    }

    public static void bubbleSortRecursive(int[] arr, int n) {
        if (n == 1) {
            return;
        }
        
        for (int i = 0; i < n - 1; i++) {
            if (arr[i] > arr[i + 1]) {
                // 交换位置
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
            }
        }
        
        bubbleSortRecursive(arr, n - 1);
    }

    public static void main(String[] args) {
        int[] array = {5, 3, 8, 4, 2};
        bubbleSort(array);
        
        System.out.print("排序结果: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
        // 输出: 排序结果: 2 3 4 5 8
    }
}

这些优化方法可以根据具体情况选择使用,以提高冒泡排序的效率。然而,尽管这些优化方法可以减少比较和交换的次数,冒泡排序的时间复杂度仍然是O(n^2),因此对于大规模数据集来说,并不是最优的排序算法选择。在实际应用中,通常更倾向于使用其他更高效的排序算法,如快速排序、归并排序等。

相关推荐
哎呦没15 分钟前
SpringBoot框架下的资产管理自动化
java·spring boot·后端
m0_571957582 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
魔道不误砍柴功4 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2344 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
pianmian14 小时前
python数据结构基础(7)
数据结构·算法
闲晨4 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟6 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
好奇龙猫6 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
P.H. Infinity7 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天7 小时前
java的threadlocal为何内存泄漏
java