这里是常用的九种排序算法的C++实现过程,附有详细的代码注释。因为要放假走人了所以具体细节日后再补充。
(以下所有代码均在VS2015与WIN10环境下执行成功)
2019.7.23更新:借鉴引用[3]文章的一张图来便于理解
1. 插入排序
时间复杂度平均O(n2),最好O(n),最差O(n2),空间复杂度O(1),就地、稳定的排序;
思路为:从第一与第二个元素开始,不断考虑后缀的元素,将其插入到前缀中合适其的位置,随着插入,其后的所有位置都需要改变。类似于打扑克时的抽牌,不过这里的牌是全部知道的。
template<typename T> void insertionsort(T data[], int n)
{
for (int i = 1, j; i < n; i++)
{
T tmp = data[i];
for (j = i; j > 0 && tmp < data[j - 1]; j--) //注意只有<才会移动,=不动的
data[j] = data[j - 1]; //插入的数(也就是每次后缀的第一个数)不断与其前面的数字比较,比其大就向前交换,直至比它小或到数组头
data[j] = tmp; //最后退出的那个位置被tmp赋值进去补充即可
}
}
2. 选择排序:最近似于人查找的方法
时间复杂度平均O(n2),最好O(n2),最差O(n2),空间复杂度O(1),就地、不稳定的排序;
思路为:遍历当前数组,查找数组中最小(大)的元素,将其与首元素(末元素)进行交换,然后降低搜索区间与交换区间。
template<typename T> void selectionsort(T data[], int n)
{
for (int i = 0, j, least; i < n - 1; i++) //i的最大值定为n-1,是为了保证在j在n时停止循环防止越界
{
for (j = i + 1, least = i; j < n; j++) //每层外循环,将最小值位置i向后移动一位
if (data[j] < data[least]) //如果处于最小值位置之后的元素比j小,则一直将位置传递给least
least = j;
if (i != least) //此条件表示当前数已是最小时不需要交换,增加n-1次索引比较,减少某些特殊的交换次数
swap(data[i], data[least]); //最后直接交换i和least的元素即可,由于前面的循环,此时的least必然是后缀中最小元素对应的位置
//这里就出现不稳定的原因,当数组中存在多个相同的数a时,可能在前面的a会被交换到后面的位置去(小的数在数组偏后的地方)
}
}
3. 冒泡排序
时间复杂度平均O(n2),最好O(n),最差O(n2),空间复杂度O(1),就地、稳定的排序;
思路为:从头开始向尾遍历,如果后面的数比前面的数小(大),就交换,一直换到最后,再减小区间继续交换。
template<typename T> void bubblesort(T data[], int n)
{
bool flag = false;
while (!flag)
{
flag = true; //作为标识符,当遍历之后仍为true,表示当前数组已经有序,直接跳出即可
for (int i = 0; i < n - 1; i++)
{
if (data[i] > data[i + 1]) //遍历式交换,最终当前区间的最后一个数必然是区间内的最大数
{
swap(data[i], data[i + 1]); //交换过程没有出现交叉,所以是稳定的
flag = false;
}
}
n--; //n递减,因为每过一次循环,当前区间的最后一个数都已经排序完成,所以通过此来减小区间
}
}
4. 希尔排序
时间复杂度平均O(n1.3),最好O(n),最差O(n2),空间复杂度O(1),就地、不稳定的排序;
思路为:根据增量序列将整个数组区间划分为多个h子序列,之后根据增量序列依次递减重新划分重新排序,最后在一个序列中重排实现;
算法核心:增量序列,即shell序列的选取和确定。
template<typename T> void shellsort(T data[], int n)
{
int k = 0, m = 1, increments[20];
while (m < n / 3) // 动态定义间隔序列(这是最常见的shell增量序列,这几十年对此的研究层出不穷,有更好的存在)
{
increments[k++] = m;
m = m * 3 + 1; // {1,4,13,40,121,....的通用shell序列
}
for (m = k-1;