算法思想:
冒泡排序顾名思义就是整个过程就像气泡一样往上升,单向冒泡排序的基本思想是(假设由小到大排序):对于给定的n个记录,从第一个记录开始依次对相邻的两个记录进行比较,当前面的记录大于后面的记录时,交换其位置,进行一轮比较和换位后,n个记录中的最大记录将位于第n位;然后对前(n - 1)个记录进行第二轮比较;重复该过程直到进行比较的记录只剩下一个时为止。
#include <iostream>
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
// 最初的冒泡排序 (每一轮由上向下比较)
void bubbleSort(int arr[], int len)
{
for (int i = 0; i < len - 1; i++) { // 需要len -1 趟排序,效率很低
for (int j = 0; j < len - 1 - i; j++) { // 从头部开始比较,让每一轮比较中最大的数沉到最下方。
if (arr[j] > arr[j + 1]) {
swap(&arr[j], &arr[j + 1]);
}
}
}
}
// 最初的冒泡排序(稍作调整,改变初始的比较位置和方向,每一轮由下向上比较)
void bubbleSort_1(int arr[], int len)
{
for (int i = 0; i < len - 1; i++) { // 需要len -1 趟排序,效率很低
for (int j = len - 1; j > i; j--) { // 从尾部开始比较,让每一轮比较中最小的数漂到最上方。
if (arr[j] < arr[j - 1]) {
swap(&arr[j], &arr[j - 1]);
}
}
}
}
// 冒泡排序的第一次改进
void bubbleSort_2(int arr[], int len)
{
bool flag; // 为什么在此处声明?防止循环中多次创建;
for (int i = 0; i < len - 1; i++) {
flag = true;
for (int j = 0; j < len - 1 -i; j++) {
if (arr[j] > arr[j + 1]) {
swap(&arr[j], &arr[j + 1]);
flag = false;
}
}
if (flag) break;
}
}
// 冒泡排序的第二次改进
void bubbleSort_3(int arr[], int len)
{
bool flag;
int lastSwapPos = len - 1;
int lastSwapPosTemp = 0;
for (int i = 0; i < len -1; i++) { // 控制 循环次数
flag = true;
for (int j = 0; j < lastSwapPos; j++) { // 控制 比较次数
if (arr[j] > arr[j + 1]) {
swap(&arr[j], &arr[j + 1]);
flag = false;
lastSwapPosTemp = j; // 不能在此处执行 lastSwapPos = j; 这样会立刻改变内部for循环的范围,而我们是需要下次循环时才改变;
} // lastSwapPosTemp 的值,在一次循环中是不断变化的,我们需要的是最终的值;
}
lastSwapPos = lastSwapPosTemp; // 一次循环下来后,取得该次循环中最后一次交换的位置,并赋值给lastSwapPos;
if (flag) break;
}
}
int main(int argc, const char * argv[]) {
int arr[] = {2,5,1,3,6,8};
int len = sizeof(arr) / sizeof(int);
// bubbleSort(arr, len);
bubbleSort_1(arr, len);
// bubbleSort_2(arr, len);
// bubbleSort_3(arr, len);
for (int i = 0; i < len; i++) {
printf("%d\n", arr[i]);
}
return 0;
}
冒泡排序总结:
(1)冒泡排序就是依次比较相邻两个元素,如果违反排序结果要求的就交换。
(2)普通的冒泡排序:最好、最坏和平均情况,时间复杂度都是O(n^2)。
bubble sort的定义是:进行n-1次排序,每次排序都要比较所有相邻的两个元素,但第i次排序时array尾部的i-1个元素都是排序好的,所以内层循环的长度是n-i-1(i是从0开始的)。
(3)第一次优化:最好情况时间复杂度为O(n),最坏和平均情况时间复杂度为O(n^2)
我们可以想到这种极端情况:array本身就是排序好的,但是依照如上算法,我们仍然要遍历n平方/2次。原因在于,我们没有机制检查数组是否是排序好的。解决办法就是加入标识项,如果发现一次内层循环没有经过任何交换,则说明array已经排序完毕。
(4)第二次优化:最好情况时间复杂度为O(n),最坏和平均情况时间复杂度为O(n^2)。
我们再想象一种极端情况:array长度为n,但是只有0,1元素未排序,那么依照上一种优化算法我们依然要遍历2*n次。原因在于我们内层循环长度的设定依据是,i次排序array的后i-1个元素是排序好的,但实际上i次排序的循环长度取决于i-1次排序时最后的交换位置,例如i-1次排序的最后交换位置是index,则表明index之后的元素都已经排序完毕,我们只需要记录这个Index就得到了下次(i次)的循环长度。