首页 > 编程语言 >冒泡排序算法(Bubble Sort)—经典排序算法

冒泡排序算法(Bubble Sort)—经典排序算法

时间:2023-10-16 23:32:21浏览次数:45  
标签:Sort arr 元素 冒泡排序 99 算法 排序

导言

冒泡排序是最基本、最简单的排序算法之一,它通过多次遍历待排序的数组或列表,依次比较相邻的元素并交换位置,使得较大(或较小)的元素逐渐“浮”到数组的一端。

冒泡排序算法(Bubble Sort)—经典排序算法_数据结构与算法


原理分析

冒泡排序算法通过多次遍历待排序的数组或列表,依次比较相邻的元素并交换位置,使得较大(或较小)的元素逐渐“浮”到数组的一端。具体来说,算法会从数组的第一个元素开始,比较它和它后面的元素,如果发现顺序有误则交换它们的位置。通过这样的操作,每次遍历都能将当前最大(或最小)的元素放到合适的位置上。


实现步骤

a. 首先,我们需要两层循环。外层循环控制遍历的次数,内层循环用于比较和交换相邻的元素。

b. 外层循环执行的次数是待排序数组的长度减去1,因为每一轮遍历都会把当前最大(或最小)的元素放到了正确的位置上,所以最后一个元素不需要再进行排序。

c. 内层循环在每一轮遍历中从数组的第一个元素开始,通过比较相邻的两个元素来判断是否需要进行交换操作。如果前一个元素大于后一个元素,则它们的位置需要互换。

d. 通过这样的遍历,每一轮都可以将当前最大(或最小)的元素放到合适的位置上。经过多轮的遍历,整个数组逐渐完成了排序。

注意,看完了这里的操作步骤,我们可以想一下,如果从头到尾进行操作是否可以?当然不可以,不过这样可以完成从小到大的排序。

假如我们要把 12、35、99、18、76 这 5 个数从大到小进行排序,那么数越大,越需要把它放在前面。冒泡排序的思想就是在每次遍历一遍未排序的数列之后,将一个数据元素浮上去(也就是排好了一个数据)。

我们从后开始遍历,首先比较 18 和 76,发现 76 比 18 大,就把两个数交换顺序,得到 12、35、99、76、18;接着比较 76 和 99,发现 76 比 99 小,所以不用交换顺序;接着比较 99 和 35,发现 99 比 35 大,交换顺序;接着比较 99 和 12,发现 99 比 12 大,交换顺序。最终第 1 趟排序的结果变成了 99、12、35、76、18,排序的过程如 1 所示。

冒泡排序算法(Bubble Sort)—经典排序算法_冒泡排序_02


图 1 第 1 趟冒泡排序的过程示例

经过第 1 趟排序,我们已经找到了最大的元素,接下来的第 2 趟排序就只对剩下的 4 个元素排序。第 2 趟排序的过程示例如图 2 所示。

冒泡排序算法(Bubble Sort)—经典排序算法_数组_03


图 2 第 2 趟冒泡排序的过程示例

经过第 2 趟排序,结果为 99、76、12、35、18。接下来应该进行第 3 趟排序了,剩下的元素不多,比较次数也在减少。

第3趟排序的结果应该是 99、76、35、12、18,接下来第 4 趟排序的结果是 99、76、35、18、12,经过 4 趟排序之后,只剩一个 12 需要排序了,这时已经没有可比较的元素了,所以排序完成。

这个算法让我想起了小时候在操场排队跑步,老师总是说:“高的站前面,低的站后面”。我们一开始并不一定会站到准确的位置上,接着老师又说:“你比前面的高,和前面的换换,还高,再和前面换换”,就这样找到了自己的位置。


实现代码

下面是使用 C 语言实现的冒泡排序算法代码示例:

#include <stdio.h>

void bubbleSort(int arr[], int n) {
    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;
            }
        }
    }
}

int main() {
    int arr[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    bubbleSort(arr, n);
    
    printf("排序后的数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    
    return 0;
}

在上述代码中,bubbleSort 函数用于实现冒泡排序算法,main 函数展示了如何使用该函数对数组进行排序。最后,通过迭代输出数组中的元素,展示排序结果。


特点及性能

通过冒泡排序的算法思想,我们发现冒泡排序算法在每轮排序中会使一个元素排到一端,也就是最终需要 n-1 轮这样的排序(n 为待排序的数列的长度),而在每轮排序中都需要对相邻的两个元素进行比较,在最坏的情况下,每次比较之后都需要交换位置,所以这里的时间复杂度是 O(n2)。其实冒泡排序在最好的情况下,时间复杂度可以达到 O(n),这当然是在待排序的数列有序的情况下。在待排序的数列本身就是我们想要的排序结果时,时间复杂度的确是 O(n),因为只需要一轮排序并且不用交换。但是实际上这种情况很少,所以冒泡排序的平均时间复杂度是 O(n2)

对于空间复杂度来说,冒泡排序用到的额外的存储空间只有一个,那就是用于交换位置的临时变量,其他所有操作都是在原有待排序列上处理的,所以空间复杂度为 O(1)

冒泡排序是稳定的,因为在比较过程中,只有后一个元素比前面的元素大时才会对它们交换位置并向上冒出,对于同样大小的元素,是不需要交换位置的,所以对于同样大小的元素来说,相对位置是不会改变的。

冒泡排序算法的时间复杂度其实比较高。从 1956 年开始就有人研究冒泡排序算法,后续也有很多人对这个算法进行改进,但结果都很一般,正如 1974 年的图灵奖获得者所说的:“冒泡排序除了它迷人的名字和引起的某些有趣的理论问题,似乎没有什么值得推荐的。”


优化策略

尽管冒泡排序算法简单易懂,但它的时间复杂度较高,为O(n^2),在大规模数据集上排序效率低下。因此,有几个优化策略可以提高冒泡排序的性能:

  1. 设置标志位:如果在一轮遍历中没有进行任何元素交换,说明数组已经完全有序,可以提前结束排序过程。
  2. 减少比较次数:每一轮遍历都将最大(或最小)的元素“浮”到了末尾。因此,下一轮遍历时无需再比较已经排好序的部分。
  3. 针对特定数据集进行优化:例如,对于大部分已经有序或基本有序的数据集,冒泡排序算法的效率较高。可以通过判断数据集的有序程度,选择是否采用冒泡排序。


结论

冒泡排序是最简单的排序算法之一,它通过多次遍历、相邻元素的比较和交换来实现排序。然而,其时间复杂度较高,在大规模数据集上的性能不佳。为了提高冒泡排序算法的效率,可以采用一些优化策略,如设置标志位、减少比较次数和针对特定数据集进行优化。这些优化方法可以显著提高冒泡排序算法的性能。



引用参考地址: https://www.runoob.com/ http://data.biancheng.net/


标签:Sort,arr,元素,冒泡排序,99,算法,排序
From: https://blog.51cto.com/wamtar/7894867

相关文章

  • R语言使用Metropolis-Hastings采样算法自适应贝叶斯估计与可视化|附代码数据
    原文链接:http://tecdat.cn/?p=19889原文出处:拓端数据部落公众号 最近我们被客户要求撰写关于Metropolis-Hastings采样的研究报告,包括一些图形和统计输出。如果您可以写出模型的似然函数,则 Metropolis-Hastings算法可以负责其余部分(即MCMC)。我写了r代码来简化对任意模型的后......
  • (五)Julia并行算法简介:矩阵乘法
    在本章中,我们将开始学习一系列专门讨论几种分布算法的设计、分析和实现的会议。这些算法经过精心挑选,以说明分布式内存方法的算法并行化的不同方面和潜在陷阱。本系列的第一部分研究矩阵乘法。学习目标在学习完本章节后,我们应该能够:在多个处理器上并行执行矩阵乘法通过复杂度......
  • 笔试算法题分享
    草船借箭题目:题目描述:程序员小周同学这几天在看《三国演义》。今天他看到了“草船借箭”这一回,在钦佩诸葛亮巧借东风向曹操“借"箭的同时,小周想到这么一个问题:如果诸葛亮一共派出了N条放置草人的船来“借"箭。“悚慨”的曹操向第1条草船上射了A支箭、第2条草船上射了B支箭......
  • 算法·数学
    数学:证明方法:反证法,双向证明法质因数约数:试除法约数个数(a1+1)(a2+1)...(an+1)=\(\prod_1^{约数个数}(a_i+1)\)约数之和(p1^0+p1^1+...+p1^a1)...=\(\prod_1^{约数个数}\sum_{i=0}^{每个约数重复次数a_i}(b^i)\)gcd最大公约数——辗转相除法(欧几里得算法)证......
  • 文心一言 VS 讯飞星火 VS chatgpt (114)-- 算法导论10.2 7题
    七、用go语言,给出一个O(n)时间的非递归过程,实现对一个含n个元素的单链表的逆转。要求除存储链表本身所需的空间外,该过程只能使用固定大小的存储空间。文心一言:在Go语言中,你可以使用迭代方法来反转一个单链表。这种方法只需要O(1)的额外空间。首先,我们需要定义链表的数据结构:t......
  • 文心一言 VS 讯飞星火 VS chatgpt (114)-- 算法导论10.2 7题
    七、用go语言,给出一个O(n)时间的非递归过程,实现对一个含n个元素的单链表的逆转。要求除存储链表本身所需的空间外,该过程只能使用固定大小的存储空间。文心一言:在Go语言中,你可以使用迭代方法来反转一个单链表。这种方法只需要O(1)的额外空间。首先,我们需要定义链表的数据结构:type......
  • 计算机网络的分组转发算法例题解析
    例题展示例题解决将题目中要求的ip地址与相对应的子网掩码进行二进制上面的相与即可,若是与目的ip地址一致,那么就直接跳转到其对应的那个接口;否则就直接跳转到默认接口;本题答案为R2;......
  • 数据结构与算法 | 数组(Array)
    数组(Array)数组(Array)应该是最基础的数据结构之一,它由相同类型的元素组成的集合,并按照一定的顺序存储在内存中。每个元素都有一个唯一的索引,可以用于访问该元素。 //java数组示例 int[]numbers1={2,0,2,3,9,23}; //或者 int[]numbers2=newint[6];基本概念数组基......
  • sortable 拖拽后数据变更但视图不变
    问题表格中某两行拖拽换序,使用sortable.js在拖拽结束后调用换序接口,再更新数据列表。问题是数据变了,但视图不变。如下图所示:分析vue无法检测数组中顺序的变化。即使采用$set,$forceUpdate(),给组件添加key属性,仍然无法解决该问题。解决办法请求数据列表前,先重置列表。......
  • 基于X86六轮差速移动机器人运动控制器设计与实现(二)规划控制算法
    带输入约束的MPC路径跟踪控制MPC算法是一种基于控制对象模型的控制方法,其优势在于在控制中考虑了系统的多种物理约束,同时基于模型与当前机器人的反馈信息预估出未来机器人位姿信息的处理方法可以解决控制迟滞的问题。4.1MPC路径跟踪控制器框架根据第......