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),因此对于大规模数据集来说,并不是最优的排序算法选择。在实际应用中,通常更倾向于使用其他更高效的排序算法,如快速排序、归并排序等。

相关推荐
喵叔哟8 分钟前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生14 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
不是二师兄的八戒37 分钟前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
爱编程的小生1 小时前
Easyexcel(2-文件读取)
java·excel
带多刺的玫瑰1 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
爱敲代码的憨仔1 小时前
《线性代数的本质》
线性代数·算法·决策树
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
阿史大杯茶2 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
计算机毕设指导62 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
Gu Gu Study2 小时前
枚举与lambda表达式,枚举实现单例模式为什么是安全的,lambda表达式与函数式接口的小九九~
java·开发语言