概要:本期主要学习排序算法中的选择排序,会着重讲解算法的核心思想、时空复杂度分析以及代码的实现。
一、选择排序
选择排序很类似插入排序,也会见数组分为已排序区域和未排序区域。它和插入排序的区别是:选择排序每次会将未排序区域中的最小元素插入已排序区域的末尾。
二、核心思想
假定有一个n个元素的无序数组(初始化 i = 0):
- 选择数组中的第i元素作为最小值参照,与数组中其余的n-1个元素做对比。比较过程中动态标记其中最小的元素,完成一轮比较后,交换最小元素和第i个元素的位置。到这里,一轮排序结束,确定了一个元素的最终位置
- 执行n-1次上述过程,当i == n-1时,完成数组排序,并且数组为升序排列。
跳转到这个网页,直观地感受选择排序吧:)
三、时空复杂度分析
时间复杂度的分析依旧从三个层面分析:最好、最坏和平均。
- 最好的情况是数组有序,需要执行n个元素的n轮比较,时间复杂度为O(n^2)。
- 最坏的情况是数组逆序,也需要执行n个元素的n轮比较,时间复杂度为O(n^2)。
- 一般情况下,选择排序也是需要执行n个元素的n轮比较,时间复杂度为O(n^2)。
强调一点,选择排序不是稳定排序,排序执行过程中都会与未排序区域的最小值元素交换位置,这破坏了算法的稳定性。
空间复杂度为O(1),因为只有在发生交换元素时才会占用常数级的空间。选择排序是原地排序算法。
四、代码实现
下面展示C++的实现源码:
void SortFuncation::SelectSort(QVector<int> _vec)
{
if(_vec.length() <= 1)
{
return ;
}
QTime _beginTime = QTime::currentTime();
qDebug()<<QString::fromLocal8Bit("开始时间:")<<_beginTime.toString("hh:mm:ss:zzzz");
int _iLen = _vec.length();//记录vector的长度
for(int i = 0;i < _iLen-1;i ++)
{
int _iMin = i;
for(int j = i;j < _iLen;j ++)
{
if(_vec.at(_iMin) > _vec.at(j))
{
_iMin = j;
}
}
/* 将未排序区域的最小元素插入已排序区域的末尾 */
int _iTemp = _vec.at(i);
_vec[i] = _vec[_iMin];
_vec[_iMin] = _iTemp;
}
QTime _endTime = QTime::currentTime();
qDebug()<<QString::fromLocal8Bit("结束时间: ")<<_endTime.toString("hh:mm:ss:zzzz");
qDebug()<<QString::fromLocal8Bit("选择排序耗时:")<<_beginTime.msecsTo(_endTime)<<"ms";
return ;
}
结尾
到这里,我们针对平均时间复杂度为O(n^2)的排序算法的学习已经结束了,下面浅浅做一些总结:
算法名称 | 是否为原地排序 | 是否为稳定排序 | 最好的时间复杂度 | 最坏的时间复杂度 | 平均的时间复杂度 |
---|---|---|---|---|---|
冒泡排序 | 是 | 是 | O(n) | O(n^2) | O(n^2) |
插入排序 | 是 | 是 | O(n) | O(n^2) | O(n^2) |
选择排序 | 是 | 否 | O(n^2) | O(n^2) | O(n^2) |
欧克,今天的学习就到这,下期我们学习归并排序:)