目录
前言
本文对常见排序算法做了总结,简单分析了排序算法的复杂度,适合复习回顾,不适合第一次学习。
准备工作
逆序数
// 如果你学习过线性代数,请跳过这个部分。
从逆序数的角度定性分析排序算法的时间复杂度,有时比直接分析代码容易,所以有必要了解逆序数的定义:
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
百度百科:逆序数
一个由N个元素组成的排列,最大逆序数是N(N-1)/2,这可以解释为什么简单排序的最坏情况时间复杂度是O(N^2)。
命名规范
本文所有排序算法模板使用统一的命名风格:
//注释
template<typename _Ty>
void x_sort(_Ty*element, int Size) {
//insert code
}
_Ty是排序数据的类型。x是排序算法的名称,函数名统一为x_sort,如:冒泡排序的函数名为bullle_sort。element和Size是统一的参数名,其中element是指向排序数据的指针,Size为排序数据量。
调试宏
为了随时检查排序结果,定义宏OUTPUT:
#ifndef OUTPUT
#define OUTPUT {\
for(int count=0;count!=Size;count++)\
cout<<element[count]<< ' '; \
cout.put(10);\
}
#endif
使用OUTPUT宏,输出element数组内的数据。
内联交换函数
排序离不开数据的交换,为增加代码可读性,定义SWAP函数:
template<typename _Ty>
inline void SWAP(_Ty&x, _Ty&y) {
auto swap = x;
x = y;
y = swap;
}
检查函数
为检测庞大的测试数据是否完成排序,定义isOrdered函数:
template<typename _Ty>
bool isOrdered(_Ty*element, int Size) {
_Ty*it = element;
for (int count = 1; count != Size; count++, it++)
if (*it > it[1])
return false;
return true;
}
使用isOrdered函数遍历element数组,检查数据是否有序。
简单排序
冒泡排序
遍历element数组,比较相邻两个元素大小,如果前一个元素大于后一个元素,则交换前后元素位置。
由于冒泡排序每次只能交换相邻两个元素,而交换相邻两个元素最多减少一个逆序数,当数据规模是N时,最大逆序数是N(N-1)/2,所以冒泡排序的时间复杂度是O(N^2)。
冒泡排序没有申请额外的空间,空间复杂度是O(1)。
//冒泡排序
template<typename _Ty>
void bullle_sort(_Ty*element, int Size) {
for (int j = Size - 1; j; j--) {
bool flag = false;
for (int i = 0; i != j; i++)
if (element[i] > element[i + 1]) {
SWAP(element[i], element[i + 1]);
flag = true;
}
if (!flag)return;
}
}
插入排序
插入排序的思路类似于整理一手扑克牌。
设需要排序的数据集合为U,已经排好序的数据集合为U1,未排好序的数据集合为U2,每次从U2中抽取一个数据p,然后遍历U1,找出合适的位置插入p。
插入排序相当于从U1末尾向前做冒泡排序,所以插入排序的时间复杂度依然是O(N^2)。
插入排序没有申请额外的空间,空间复杂度是O(1)。
//插入排序
template<typename _Ty>
void insertion_sort(_Ty*element, int Size) {
for (int i = 1; i != Size; i++) {
_Ty insert = element[i];
int j;
for (j = i - 1; j >= 0; j--)
if (element[j] > insert)
element[j + 1] = element[j];
else break;
element[j + 1] = insert;
}
}
本例中,需要排序的数据集合只有一个,当数据量庞大时,一个集合内的操作次数是N^2数量级的,这决定了插入排序不适合处理大规模数据。
如果把数据集合分成几个,情况就会有所改善,这就是希尔排序,这会在后文提到。
选择排序
选择排序也是把需要排序的数据分为两个集合,已经排好序的数据集合为U1,未排好序的数据集合为U2,每次从U2中选择一个最大的元素p,与U1末尾元素交换。
设U2的数据规模为N2,最坏情况下,U2中的最大元素在末尾,选择操作需要扫描N2次。
如果每次选择都是最坏情况,则U2的逆序数为N2(N2-1)/2,一次交换消除的逆序数为N2-1。
N2(N2-1)/2:N2-1∝N2,U2交换的时间复杂度为O(N),扫描的时间复杂度也为O(N),所以选择排序的时间复杂度为O(N^2)。
选择排序没有申请额外的空间,空间复杂度是O(1)。
//选择排序
template<typename _Ty>
void selection_sort(_Ty*element, int Size) {
for (int i = 0; i != Size; i++) {
int min = i;
for (int j = i + 1; j != Size; j++)
if (element[min] > element[j])
min = j;
if (min != i)
SWAP(element[i], element[min]);
}
}
简单排序的改进
希尔排序:改进插入排序
希尔排序的实现过程
希尔排序把数据集合分成几个子集,子集的数据量由增量序列决定,先对子集插入排序,再对全集插入排序。
增量序列
为了逐步使全集数据有序,子集的数据量应该逐步递增为全集数据量N,子集的数量应该逐步递减为1,我们称这个逐步递减为1的子集数量序列的反向序列为增量序列。
下面以Hibbard增量序列为例实现希尔排序:
Hibbard增量序列:{1, 3, …, 2^k-1}
//希尔排序:Hibbard增量序列
template<typename _Ty>
void shell_sort(_Ty*element, int Size) {
for (int step = log2(1 + Size); step; step = (step - 1) / 2)
for (int i = step; i < Size; i += step) {
_Ty insert = element[i];
int j;
for (j = i - step; j >= 0; j -= step)
if (element[j] > insert)
element[j + step] = element[j];
else break;
element[j + step] = insert;
}
}
堆排序:改进选择排序
//堆排序
template<typename _Ty>
void heap_sort(_Ty*element, int Size) {
//Create a MaxHeap:O(NlogN)
for (int i = 0; i != Size; i++) {
int j = i;
while (j&&element[(j - 1) / 2] < element[j]) {
auto swap = element[(j - 1) / 2];
element[(j-1) / 2] = element[j];
element[j] = swap;
j = (j - 1) / 2;
}
}
//O(NlogN)
for (int j = Size - 1; j; j--) {
//Swap first and last node:O(1)
auto swap = element[0];
element[0] = element[j];
element[j] = swap;
//Heaplify reduced array:O(lonN)
int i = 0;
while (2 * i + 2 <= j - 1)
if (element[2 * i + 1] > element[2 * i + 2])
if (element[i] < element[2 * i + 1]) {
auto swap = element[i];
element[i] = element[2 * i + 1];
element[2 * i + 1] = swap;
i = 2 * i + 1;
}
else break;
else if (element[i] < element[2 * i + 2]) {
auto swap = element[i];
element[i] = element[2 * i + 2];
element[2 * i + 2] = swap;
i = 2 * i + 2;
}
else break;
if (j - 1 == 2 * i + 1)
if (element[i] < element[j - 1]) {
auto swap = element[i];
element[i] = element[j - 1];
element[j - 1] = swap;
}
}
}
分而治之:归并排序
归并
//归并
template<typename _Ty>
void merge(_Ty*element1, int Size1, _Ty*element2, int Size2, _Ty*buffer) {
_Ty*APtr = element1, *BPtr = element2, *BufPtr = buffer;
int i, j;
for (i = Size1, j = Size2; i&&j; BufPtr++)
if (*APtr < *BPtr) {
*BufPtr = *APtr++;
i--;
}
else {
*BufPtr = *BPtr++;
j--;
}
while (i--)
*BufPtr++ = *APtr++;
while (j--)
*BufPtr++ = *BPtr++;
}
归并排序
//归并排序
template<typename _Ty>
void merge_sort(_Ty*element, int Size) {
_Ty*buffer = new _Ty[Size];
//O(N)
for (int count = 0; count != Size; count++)
buffer[count] = element[count];
//O(NlogN)
for (int step = 1; step < Size; step *= 2) {
_Ty*LPtr = buffer, *RPtr = buffer + step, *BufPtr = element;
while (RPtr + step < buffer + Size) {
merge(LPtr, step, RPtr, step, BufPtr);
LPtr += 2 * step;
RPtr += 2 * step;
BufPtr += 2 * step;
}
if (RPtr < buffer + Size)
merge(LPtr, step, RPtr, buffer + Size - RPtr, BufPtr);
for (int count = 0; count != Size; count++)
buffer[count] = element[count];
}
delete buffer;
}
随机应变:快速排序
选主元
//选主元
template<typename _Ty>
_Ty median(_Ty*element, int Size) {
_Ty&left = element[0], &mid = element[Size / 2], &right = element[Size - 1];
if (left > mid)
SWAP(left, mid);
if (mid > right)
SWAP(mid, right);
if (left > mid)
SWAP(left, mid);
SWAP(mid, right);
return right;
}
设置阈值
#ifndef CUTOFF
#define CUTOFF 100
#endif
快速排序
//快速排序
template<typename _Ty>
void quick_sort(_Ty*element, int Size) {
struct SubQuickSort{
_Ty*sub_head;
int sub_size;
}sub_quick_sort{ element,Size };
stack<SubQuickSort>S;
S.push(sub_quick_sort);
while (!S.empty()) {
sub_quick_sort = S.top();
S.pop();
_Ty*head = sub_quick_sort.sub_head;
int size = sub_quick_sort.sub_size;
int pivot = median(head, size);
_Ty*i = head, *j = head + size - 2;
while (i <= j) {
for (; *i < pivot; i++);
for (; *j > pivot; j--);
if (i <= j)
SWAP(*i++, *j--);
}
SWAP(*i, head[size - 1]);
if (j - head >= CUTOFF) {
sub_quick_sort.sub_size = j - head + 1;
S.push(sub_quick_sort);
}
else insertion_sort(head, j - head + 1);
if (head + size - 1 - i >= CUTOFF) {
sub_quick_sort.sub_head = i;
sub_quick_sort.sub_size = head + size - i;
S.push(sub_quick_sort);
}
else insertion_sort(i, head + size - i);
}
}
复杂数据类型:表排序
做表:插入排序
//表排序
template<typename ElementType, typename KeyType>
void table_sort(ElementType*element, const KeyType*key, int Size) {
//插入排序:O(NlogN)
int*table = new int[Size], i, j;
for (i = 0; i != Size; i++)
table[i] = i;
for (j = 1; j != Size; j++) {
int insert = table[j];
for (i = j - 1; i >= 0; i--)
if (key[table[i]] > key[insert])
table[i + 1] = table[i];
else break;
table[i + 1] = insert;
}
物理排序
//物理排序:O(mN)
i = 0;
while (true) {
while (table[i] == i)
if (Size == ++i)
goto _LOOP_END;
if (table[table[i]] == i) {
SWAP(element[table[i]], element[i]);
table[table[i]] = table[i];
table[i] = i;
}
else {
ElementType temp = element[i];
element[i] = element[table[i]];
SWAP(table[i], i);
while (table[table[i]] != table[i]) {
element[i] = element[table[i]];
SWAP(table[i], i);
}
element[i] = temp;
SWAP(table[i], i);
}
}
_LOOP_END:
delete[]table;
}