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

相关推荐
武子康9 分钟前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
Captain823Jack44 分钟前
nlp新词发现——浅析 TF·IDF
人工智能·python·深度学习·神经网络·算法·自然语言处理
豪宇刘1 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意1 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
Captain823Jack1 小时前
w04_nlp大模型训练·中文分词
人工智能·python·深度学习·神经网络·算法·自然语言处理·中文分词
是小胡嘛2 小时前
数据结构之旅:红黑树如何驱动 Set 和 Map
数据结构·算法
m0_748255022 小时前
前端常用算法集合
前端·算法
FF在路上2 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
呆呆的猫2 小时前
【LeetCode】227、基本计算器 II
算法·leetcode·职场和发展