一、排序算法简介
1、排序的稳定性
根据关键字对待排序记录进行排序,有可能存在两个或以上的关键字相等的记录,所以排序的结果可能会存在不唯一的情况;比如根据关键字进行排序时发现记录A与记录B相等,如果在排序前记录A是排在记录B之前的,而且在排序后出现记录A与记录B相等的情况下,记录A还是排在记录B的前面,则称所用的排序算法是稳定的;反之则是不稳定的。
2、内排序与外排序
根据在排序过程中待排序的记录是否全部被放置在内存中,排序分为内排序和外排序:
- 内排序:在整个排序过程中,待排序的所有记录全部被放置在内存中
- 外排序:由于排序的记录数过多,不能同时全部放置在内存中,所以整个排序过程需要在内外存之间多次交换数据才能进行
3、内排序的分类
根据排序过程中的主要操作,内排序分为:插入排序、交换排序、选择排序和归并排序。
二、冒泡排序
1、冒泡排序(Bubble Sort)是一种交换排序,基本思想是:两两比较相邻记录的关键字,如果不符合按关键字排序的规则则交换这两个记录,直到全部符合排序规则为止。
2、第一种实现,如下代码所示,注意:待排序记录数组的下标为0的位置用来存储待排序记录的个数
#include <stdio.h>
// 定义待排序记录类型
typedef int RecordType;
/**
* 将待排序记录数组的下标i和下标j的两个记录进行交换
*/
void Swap(RecordType *rs, int i, int j)
{
RecordType tmp = *(rs + i);
*(rs + i) = *(rs + j);
*(rs + j) = tmp;
}
/**
* 打印待排序记录数组
*/
void PrintRs(RecordType rs[])
{
int i, count = rs[0];
for (i = 1; i <= count; i++)
{
printf("%d ", rs[i]);
}
printf("\n");
}
/**
* 对记录列表进行排序
*/
void Sort(RecordType *rs)
{
int i, j, count = rs[0];
for (i = 1; i < count; i++)
{
for (j = i + 1; j <= count; j++)
{
if (*(rs + i) > *(rs + j))
{
Swap(rs, i, j);
}
}
}
}
int main()
{
RecordType rs[] = {5, 1, 5, 3, 2, 4};
printf("排序前记录列表排序为:\t\t");
PrintRs(rs);
Sort(rs);
printf("从小到大排序后,记录列表排序为:");
PrintRs(rs);
return 0;
}
严格来说,这个实现并不是真正的冒泡排序算法,因为它不满足"两两比较相邻记录"的冒泡排序思想,所以这个实现只能算是一种最简单的交换排序。这个算法实现的基本思路是:让每一个记录的关键字都和它后面的每一个记录的关键字进行比较,如果前面大于后面则进行交换,这样每一趟比较都能将当次比较的记录中关键字最小的记录排在前面去,但是每一趟的排序对下一趟排序没有什么帮助,反而有可能将比较小的记录(不是本趟排序中关键字最小的记录)排在了更后面,所以效率是很低的。
3、第二种实现:真正的冒泡实现
/**
* 对记录列表进行冒泡排序
*/
void BubbleSort(RecordType *rs)
{
int i, j, count = rs[0];
for (i = 1; i < count; i++)
{
for (j = count - 1; j >= i; j--)
{
if (*(rs + j) > *(rs + j + 1))
{
Swap(rs, j, j + 1);
}
}
}
}
要注意的是这种冒泡实现方式是每次从最后面的两个记录开始比较,选择出其中较小的一个记录排在另一个的前面,然后在向上一个位置选取两个记录继续做比较,直到比较完本次待排序的记录位置,如此循环,每次选择最小的记录排在前面。相比第一种简单的交换排序而言,每次不仅能够将最小的记录排在前面,而且能够把相对较小的记录排在更靠前的位置,所以相比第一种算法来说效率有所提高。
4、冒泡排序优化
如果待排序记录列表是{2、1、3、4、5},那么在第一趟排序后整个记录就是从小到大有序排列的了,如果按照以上的实现算来操作的话,仅仅第一趟排序后就是有序的了,还是会将剩下的每个记录都循环一次(虽然不会交换记录的位置),所以剩下的这些比较就是多于的了,所以可以增加一个标志变量来表示是否还需要继续循环,如果在上一次排序后整个记录就是有序的了,那么下一次循环就不会在交换任何记录的位置,说明不需要再次循环比较,则算法就可以结束了,省去了不必要的浪费。
void BubbleSort(RecordType *rs)
{
int i, j, count = rs[0];
int flag = 1;
for (i = 1; i < count && flag == 1; i++)
{
flag = 0;
for (j = count - 1; j >= i; j--)
{
if (*(rs + j) > *(rs + j + 1))
{
Swap(rs, j, j + 1);
flag = 1;
}
}
}
}
5、冒泡排序时间复杂度
根据优化后的冒泡排序实现来看,在最好的情况下就是待排序记录列表本身就是有序的,则只需要n-1次比较,不需要交换元素位置,则时间复杂度为O(n);最坏的情况就是待排序记录列表是倒序排列的,此时就需要1+2+3+.......+(n - 1) = n(n - 1) / 2次比较,且需要相等次数的记录交换,所以时间复杂度为O(n^2)。
参考书籍:《大话数据结构》