前言
冒泡排序具备很强的教学意义,但是没有什么实践意义,这里作为第一个讲解的排序,目的是从简单开始讲解,方便理解
冒泡排序gif
冒泡排序单趟实现
冒泡排序一次只解决一个数字,交换一个数字之后,开始交换第二个数字
那么多趟实现就直接for循环多趟实现就可以了
冒泡排序多趟实现逻辑
举例2(无法理解可以先不看举例2,这里是参照组):
假设初始数组: [4, 2, 9, 1, 5]
第一轮后: [2, 4, 9, 1, 5] (9移到末尾)
第二轮后: [2, 4, 1, 5, 9] (9和5移到末尾)
第三轮后: [2, 1, 4, 5, 9] (9、5和4移到末尾)
第四轮后: [1, 2, 4, 5, 9] (9、5、4和2移到末尾)
第五轮后: [1, 2, 4, 5, 9] (数组已经排序完成)
冒泡排序注意事项
单趟循环需要注意事项
这里如果传参如果传递是是n,那么单趟实现的时候,我们不能循环n次数,只能循环n-1次数,因为
多趟循环需要注意事项
冒泡排序的交换逻辑
冒泡排序代码实现
//交换函数 void Swap(int* p1, int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } //冒泡排序 void BubbleSort(int* a, int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (a[j] < a[j + 1]) { Swap(&a[j], &a[j + 1]); } } } }
解释:
交换函数(Swap):
- 函数原型:
void Swap(int* p1, int* p2)
- 参数:
p1
和p2
是指向两个整数的指针。- 功能:交换两个指针所指向的整数的值。
- 实现:首先,使用一个临时变量
tmp
存储p1
指向的值。然后将p2
指向的值赋给p1
指向的位置,最后将临时变量tmp
(原来p1
的值)赋给p2
指向的位置。冒泡排序(BubbleSort):
- 函数原型:
void BubbleSort(int* a, int n)
- 参数:
a
是指向整数数组的指针,n
是数组中元素的数量。- 功能:对数组
a
进行原地排序,使数组中的元素按照非递减顺序排列。- 实现:冒泡排序通过重复遍历要排序的数组,比较相邻元素,如果它们的顺序错误就把它们交换过来。遍历数组的工作是在外层循环中完成的,内层循环负责实际的比较和交换。
- 交换函数就是一个交换函数,因为后面我还需要调用,所以这里我单独拿出来。
冒泡排序时间复杂度计算
冒泡排序的时间复杂度计算基于算法执行的比较和交换操作的次数。下面是冒泡排序的时间复杂度分析:
最佳情况:当数组已经完全有序时,冒泡排序只需要进行一次遍历即可确定数组已经有序,不需要进行任何交换。在这种情况下,时间复杂度是 O(n),其中 n 是数组的长度。
平均情况和最坏情况:在平均情况和最坏情况下,冒泡排序需要进行 n-1 次遍历,每次遍历中进行的比较次数依次减少。具体来说:
- 第一次遍历最多进行 n-1 次比较。
- 第二次遍历最多进行 n-2 次比较。
- ...
- 最后一次遍历进行 1 次比较。
总的比较次数可以表示为:(n-1) + (n-2) + ... + 1。这是一个等差数列求和问题,其和为 n(n-1)/2。因此,平均情况和最坏情况下的时间复杂度是 O(n^2)。
空间复杂度:冒泡排序是原地排序算法,它不需要额外的存储空间来创建另一个数组,只需要一个临时变量用于交换元素。因此,冒泡排序的空间复杂度是 O(1)。
稳定性:冒泡排序是稳定的排序算法,因为它不会改变相同元素之间的相对顺序。
总结来说,冒泡排序的时间复杂度是:
- 最佳情况:O(n)
- 平均情况:O(n^2)
- 最坏情况:O(n^2)
由于冒泡排序在大多数情况下效率不高,特别是对于大数据集,它通常不作为首选排序算法。然而,它的简单性和稳定性使其在某些特定情况下(如小数据集或基本有序的数据)仍然有用。