1 如何评价、分析一个排序算法?
很多语言、数据库都已经封装了关于排序算法的实现代码。所以我们学习排序算法目的更多的不是为了去实现这些代码,而是灵活的应用这些算法和解决更为复杂的问题,所以更重要的是学会如何评价、分析一个排序算法并在合适的场景下正确使用。
分析一个排序算法,主要从以下3个方面入手:
1.1 排序算法的执行效率
1)最好情况、最坏情况和平均情况时间复杂度
待排序数据的有序度对排序算法的执行效率有很大影响,所以分析时要区分这三种时间复杂度。除了时间复杂度分析,还要知道最好、最坏情况复杂度对应的要排序的原始数据是什么样的。
2)时间复杂度的系数、常数和低阶
时间复杂度反映的是算法执行时间随数据规模变大的一个增长趋势,平时分析时往往忽略系数、常数和低阶。但如果我们排序的数据规模很小,在对同一阶时间复杂度的排序算法比较时,就要把它们考虑进来。
3)比较次数和交换(移动)次数
内排序算法中,主要进行比较和交换(移动)两项操作,所以高效的内排序算法应该具有尽可能少的比较次数和交换次数。
1.2 排序算法的内存消耗
也就是分析算法的空间复杂度。这里还有一个概念—原地排序,指的是空间复杂度为O(1)的排序算法。
1.3 稳定性
如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变,那么这种排序算法叫做稳定的排序算法;如果前后顺序发生变化,那么对应的排序算法就是不稳定的排序算法。
在实际的排序应用中,往往不是对单一关键值进行排序,而是要求排序结果对所有的关键值都有序。所以,稳定的排序算法往往适用场景更广。
2 三种时间复杂度为O(n2)的排序算法
2.1 冒泡排序
2.1.1 原理
两两比较相邻元素是否有序,如果逆序则交换两个元素,直到没有逆序的数据元素为止。每次冒泡都会至少让一个元素移动到它应该在的位置。
2.1.2 实现
void BubbleSort(int *pData, int n) //冒泡排序
{
int temp = ;
bool orderlyFlag = false; //序列是否有序标志
for (int i = ; i < n && !orderlyFlag; ++i) //执行n次冒泡
{
orderlyFlag = true;
for (int j = ; j < n - - i; ++j) //注意循环终止条件
{
if (pData[j] > pData[j + ]) //逆序
{
orderlyFlag = false;
temp = pData