?个人博客:www.hellocode.top?
?Java知识导航:Java-Navigate?
?CSDN:HelloCode
?掘金HelloCode
?知乎HelloCode
✨如有问题,欢迎指正,一起学习~~

冒泡排序(Bubble Sort)是一种简单的排序算法,它通过重复地交换相邻元素的位置来将最大(或最小)的元素逐步“冒泡”到正确的位置。它是一种稳定的排序算法,意味着具有相等键值的元素在排序后的序列中相对位置不会发生改变。

基本思想

在这里插入图片描述

  1. 从序列的第一个元素开始,比较相邻的两个元素大小,如果它们的顺序不正确,则交换它们的位置。
  2. 继续向后遍历序列,对每一对相邻元素执行步骤1,直到序列的末尾。
  3. 重复上述过程,但是每次比较的元素个数减一,因为每次遍历都会将最大(或最小)的元素“冒泡”到正确的位置。
  4. 重复执行以上步骤,直到整个序列排序完成。
如上图所示,传统的冒泡排序就是通过对数组内的元素,从前往后,两两进行比较,每一轮都会确定出一个最大值,放在合适的位置。

代码实现

对应在代码层面,就是两轮for循环来进行遍历,这种思想还是比较简单且容易理解的(简单粗暴)

下面展示Java和C两种版本(C语言版本由俺的女朋友小周提供?)

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月07日 20:43
 */
public static void bubbleSort(int[] arr){
    for(int i = 0; i < arr.length; i++){
        for (int j = 0; j < arr.length - i - 1; j++){
            if(arr[j] > arr[j + 1]){
                // 需要交换
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

测试:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月07日 20:43
 */
public class Test {
    public static void main(String[] args) {
        int[] arr = {21, 13, 4, 10, 7, 65, 32, 15, 32, 19};
        System.out.println("排序前:" + Arrays.toString(arr));
        BubbleSort.bubbleSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }
}

在这里插入图片描述

//C语言实现
#include <stdio.h>
void Bubble_Sort(int arr[])
{
    int i, j, temp;
    for (int i = 0; i < 10-1; i++)
    {
        for (int j = 0; j < 10 - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

//测试代码
int main()
{
    int arr[] = { 5,5,4,6,2,1,0,8,11,9 };
    printf("排序前:");
    for (int i = 0; i < 10; i++)
    {
        printf("%d  ", arr[i]);
    }
    Bubble_Sort(arr);
    printf("\n排序后:");
    for (int i = 0; i < 10; i++)
    {
        printf("%d  ", arr[i]);
    }
    return 0;
}

测试结果:

image-20230808225843085

优化

优化法一

在每一轮循环之前,设置一个标志位来标记本轮循环是否进行了元素交换。如果在一轮循环中没有发生任何交换操作,说明整个数组已经有序,就无需继续进行后续的比较,直接退出循环。这个优化可以提前结束排序过程,减少了不必要的比较操作,从而提高算法的效率。然而,当数组内的元素乱序程度较高时,这种优化的效果可能并不明显。具体代码如下:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月07日 21:00
 */
public static void bubbleSortOptimized1(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        boolean isSwap = false;
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 需要交换,设置标记位为true
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                isSwap = true;
            }
        }
        if (!isSwap) {
            // 如果一轮循环结束未发生一次交换,则说明已经有序,提前结束循环
            break;
        }
    }
}

测试:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月07日 21:02
 */
public class Test {
    public static void main(String[] args) {
        int[] arr = {21, 13, 4, 10, 7, 65, 32, 15, 32, 19};
        System.out.println("排序前:" + Arrays.toString(arr));
        BubbleSort.bubbleSortOptimized1(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }
}

在这里插入图片描述

#include <stdio.h>

void Bubble_Sort(int arr[])
{
    int i, j, temp;
    for (int i = 0; i < 10-1; i++)
    {
        int count = 0;
        for (int j = 0; j < 10 - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                count += 1;
            }
        }
        if (count == 0)
        {
            break;
        }
    }
}

//测试代码
int main()
{
    int arr[] = { 5,5,4,6,2,1,0,8,11,9 };
    printf("排序前:");
    for (int i = 0; i < 10; i++)
    {
        printf("%d  ", arr[i]);
    }
    Bubble_Sort(arr);
    printf("\n排序后:");
    for (int i = 0; i < 10; i++)
    {
        printf("%d  ", arr[i]);
    }
    printf("\n");
    return 0;
}
//C++实现
#include <iostream>
#include <string.h>

using namespace std;

void Bubble_Sort(int* arr,int sz)
{
    int i, j,temp;
    for (int i = 0; i < sz - 1; i++)
    {
        int count = 0;
        for (int j = 0; j < sz - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                count += 1;
            }
        }
        if (count == 0)
            break;
    }
}
int main()
{
    int arr[] = { 5,5,4,6,2,1,0,8,11,9 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    cout << "排序前:";
    for (int i = 0; i < sz; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
    Bubble_Sort(arr,sz);
    cout << "排序后:";
    for (int i = 0; i < sz; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
    return 0;
}

优化法二

在外层和内层两个循环中,内层循环进行两次遍历:一次从头到尾,找出本轮循环中最大的元素;另一次从尾到头,找出本轮循环中最小的元素。结合优化法一使用标志位提前退出循环的方式,可以进一步减少每轮循环中的比较次数,从而提高冒泡排序的性能。具体代码如下:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月07日 21:10
 */

public static void bubbleSortOptimized2(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        boolean isSwap = false;
        // 从头到尾找出最大元素并将其冒泡到正确位置
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 需要交换,设置标记位为true
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                isSwap = true;
            }
        }
        if (!isSwap) {
            // 如果一轮循环结束未发生一次交换,则说明已经有序,提前结束循环
            break;
        }
        isSwap = false;
        // 从尾到头找出最小元素并将其冒泡到正确位置
        for (int j = arr.length - i - 2; j >= i + 1; j--) {
            if (arr[j] < arr[j - 1]) {
                // 需要交换,设置标记位为true
                int temp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = temp;
                isSwap = true;
            }
        }
        if (!isSwap) {
            // 如果一轮循环结束未发生一次交换,则说明已经有序,提前结束循环
            break;
        }
    }
}

测试:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月07日 21:18
 */
public class Test {
    public static void main(String[] args) {
        int[] arr = {21, 13, 4, 10, 7, 65, 32, 15, 32, 19};
        System.out.println("排序前:" + Arrays.toString(arr));
        BubbleSort.bubbleSortOptimized2(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }
}

在这里插入图片描述

总结

优点

  1. 冒泡排序算法实现简单,易于理解和实现。
  2. 对于小规模的数据集,冒泡排序可能比其他排序算法性能稍微好一些。
  3. 由于每次只交换相邻元素,冒泡排序可以实现原地排序,不需要额外的内存空间。

缺点

  1. 冒泡排序的时间复杂度较高,特别是对于大规模数据集。它需要进行多次遍历和交换操作,导致性能较差。
  2. 不管数据集是否已经有序,冒泡排序都要执行完所有的遍历和比较操作。

复杂度

  • 时间复杂度:冒泡排序的最坏时间复杂度为O(n^2),平均时间复杂度也为O(n^2),其中n为待排序序列的长度。在最好情况下(待排序序列已经有序),冒泡排序的时间复杂度为O(n)。
  • 空间复杂度:冒泡排序是一种原地排序算法,不需要额外的内存空间,因此空间复杂度为O(1)。
虽然冒泡排序在实际应用中并不常用,但理解它可以帮助我们理解其他更复杂的排序算法,并且在某些特定场景下,它也可能是一种合理的选择。然而,对于大规模数据的排序,更高效的排序算法(如快速排序、归并排序等)通常会更为适用。
END
本文作者: 文章标题:【数据结构与算法】十大经典排序算法-冒泡排序
本文地址:https://www.jiusi.cc/archives/247/
版权说明:若无注明,本文皆九思のJava之路原创,转载请保留文章出处。
最后修改:2023 年 08 月 09 日
如果觉得我的文章对你有用,请随意赞赏