冒泡排序简述:
冒泡排序(Bubble Sort)本质是一种交换排序,不停比较前后两个元素大小,反序则交换,直到没有反序为止。
排序前的准备:
#define MAXSIZE 10
//顺序表结构
template <class T>
struct SqList
{
T s[MAXSIZE + 1] = { NULL,98,24,55,81,32,77,48,60,14,8 }; //数组s,s[0]可作为临时变量使用
int length = MAXSIZE; //s长度
void PrintArray();
};
typedef SqList<int> SList;
//交换l中的数组中下标为i和j的值
void Swap(SList *L, int i, int j)
{
int temp = L->s[i];
L->s[i] = L->s[j];
L->s[j] = temp;
}
默认的顺序表为98,24,55,81,32,77,48,60,14,8,并准备了一个交换函数,用于交换。
简单的冒泡排序:
//冒泡排序(简)
void BubbleSort1(SList *L)
{
int i, j;
for (i = 1; i < L->length; i++)
{ //从第一个元素开始
for (j = i + 1; j <= L->length; j++) //比较后面所有元素
{ //把最小的元素放到前面
if (L->s[i] > L->s[j])
{
//交换元素
Swap(L, i, j); //交换只把最小元素放到前面,不考虑其他元素位置,效率低
}
}
}
}
这个排序最为简单,便于理解,每次循环遍历后,都会把当前数组最小的一个元素放到遍历首位,但不会考虑其他元素的位置,可能会导致第二小的元素反而跑到数组末位,所以这种冒泡排序是低效的。
正宗的冒泡排序:
//冒泡排序
void BubbleSort2(SList *L)
{
int i, j;
for (i = 1; i < L->length; i++)
{
for (j = L->length - 1; j >= i; j--)
{ //j从后往前遍历
if (L->s[j] > L->s[j+1]) //前一个元素比后一个元素大,和前面算法不同
{
//交换元素
Swap(L, j, j + 1); //每次把最小元素放到前面的同时,下一个最小元素也会往前移动若干位置,效率高
}
}
}
}
这种冒泡排序严格遵循“冒泡"的概念,最小的元素像气泡一样冒到最上面,此时不仅最小的元素会到数组首位,第二小的元素也会前进若干位,所以这种冒泡效率更高。
优化后的冒泡排序:
我们发现当顺序表大部分为有序时,如312456789时,只用交换几次后元素就有序了,但仍然会最后整体遍历一次表,对比前面,这样就浪费了不少时间,所以我们在排序同时加入一个标志位,如果有序便不再遍历最后一次。
//冒泡排序优化
void BubbleSort3(SList *L)
{
int i, j;
int unordered = true; //标志位默认是无序的
for (i = 1; i < L->length && unordered; i++) //只有当是无序时才进行遍历
{
unordered = false; //假设此时有序,如果后面没有进行交换则不再进入第一层循环,减少最后一次遍历
for (j = L->length - 1; j >= i; j--)
{
if (L->s[j] > L->s[j+1])
{
Swap(L, j, j + 1); //交换元素
unordered = true; //改变标识符为无序
}
}
}
}
冒泡排序的时间复杂度:
冒泡排序比较次数最小的情况是有序数列,此时要比较n-1次,时间复杂度为O(n);
而最差的情况是完全反序,如987654321,此时要比较次数=1+2+3+(n-1),即((n)(n-1))/2,时间复杂度为O(n^2)。