
constexpr int N = 1e+5 + 10;
int arr[N];
// n待排列数组的元素个数
void bubbleSort(int n)
{
for(int i = 0; i < n; ++i)
for(int j = 0; j < n - 1 - i; ++j)
if(arr[j] > arr[j + 1]) swap(arr[j], arr[j + 1]);
}
constexpr int N = 1e+5 + 10;
int arr[N];
// n待排列数组的元素个数
void selectSort(int n)
{
for(int i = 0; i < n; ++i)
{
int t = i;
for(int j = i + 1; j < n; ++j)
if(arr[t] > arr[j]) t = j;
swap(arr[t], arr[i]);
}
}
constexpr int N = 1e+5 + 10;
int arr[N];
// n待排列数组的元素个数
void insertSort(int n)
{
for(int i = 1; i < n; ++i)
{
int j = i, x = arr[i];
while(j && arr[j - 1] > x) arr[j] = arr[j - 1], --j;
arr[j] = x;
}
return;
}
constexpr int N = 1e+5 + 10;
int arr[N];
// n待排列数组的元素个数
void shellSort(int n)
{
int gap = n / 2;
while(gap)
{
for(int i = gap; i < n; ++i)
{
int j = i, x = arr[i];
while(j >= gap && arr[j - gap] > x) arr[j] = arr[j - gap], j -= gap;
arr[j] = x;
}
gap /= 2;
}
}
一般书上都说希尔排序的时间复杂度是,但是学过算法的都知道有最坏时间复杂度的,希尔排序的时间复杂度其实是和增列序列有关系的,即我们程序实现的步长,{1,2,4,8,...}这种序列就是我们程序中实现的这种,并不是很好的增量序列,使用这个增量序列的时间复杂度(最坏情形)是,Hibbard提出了另一个增量序列{1,3,7,...,}(质数增量),这种序列的时间复杂度(最坏情形)为,这个提高就很厉害了,只是修改一个算法的一个参数;Sedgewick提出了几种增量序列,其最坏情形运行时间为,其中最好的一个序列是{1,5,19,41,109,..}
constexpr int N = 1e+5 + 10;
int arr[N];
// l为待排列数组的左端点, r为待排列数组的右端点
void quickSort(int l, int r)
{
if(l >= r) return;
int i = l - 1, j = r + 1, x = arr[l + r >> 1];
while(i < j)
{
while(arr[++i] < x);
while(arr[--j] > x);
if(i < j) swap(arr[i], arr[j]);
}
quick_sort(l, j);
quick_sort(j + 1, r);
}
constexpr int N = 1e+5 + 10;
int arr[N], t[N];
// l为待排列数组的左端点, r为待排列数组的右端点
void mergeSort(int l, int r)
{
if(l >= r) return;
int mid = l + r >> 1;
merge_sort(l, mid);
merge_sort(mid + 1, r);
int i = l, j = mid + 1, k = 0;
while(i <= mid && j <= r)
if(arr[i] <= arr[j]) t[k++] = arr[i++];
else t[k++] = arr[j++];
while(i <= mid) t[k++] = arr[i++];
while(j <= r) t[k++] = arr[j++];
for(int i = l, j = 0; i <= r; ++i, ++j) arr[i] = t[j];
}

constexpr int N = 1e+5 + 10;
int h[N];
// 大顶堆下沉操作, x为进行下沉操作的位置下标, 该下标由 1 开始
void down(int x, int n)
{
int t = x;
if(2 * x <= n && h[t] < h[2 * x]) t = 2 * x;
if(2 * x + 1 <= n && h[t] < h[2 * x + 1]) t = 2 * x + 1;
if(t != x) swap(h[x], h[t]), down(t, n);
}
// 将数组整理为大顶堆, n为元素个数
void heapCreate(int n)
{
// 从完全二叉树的第一个非叶子结点开始往前直到根结点, 依次进行下沉操作
for(int i = n / 2; i; --i) down(i, n);
}
// 堆排序, n为元素个数
void heapSort(int n)
{
while(n - 1)
{
swap(h[1], h[n]);
down(1, --n);
}
}






// 元素个数 100000, 元素的取值范围为 1 ~ 100000
constexpr int N = 1e+5 + 10;
int arr[N], cnts[N], res[N];
void countSort(int n)
{
for(int i = 1; i <= n; ++i) cnts[arr[i]]++;
for(int i = 1; i < N ; ++i) cnts[i] += cnts[i - 1];
for(int i = n; i; --i) res[cnts[arr[i]]--] = arr[i];
} // 元素个数 100000, 元素取值范围 1 ~ 10^9, 此时无法开辟这么大的静态数组
// 这里可以用 C++ 的 map 容器, 来存储并求前缀和
void countSort(int n)
{
map<int, int> cnts;
for(int i = 1; i <= n; ++i) cnts[arr[i]]++;
int t = 0;
for(auto [k, v] : cnts)
{
cnts[k] += t;
t += v;
}
for(int i = n; i; --i) res[cnts[arr[i]]--] = arr[i];
}








constexpr int N = 1e+5 + 10;
int arr[N], b[10][N]; // b二维数组为收集队列, 第二维度的第一个元素存储元素个数
void radixSort(int n)
{
int base = 1;
for(int i = 0; i < 10; ++i)
{
for(int j = 0; j < n; ++j)
{
int t = arr[j] / base % 10;
b[t][++b[t][0]] = arr[j];
}
for(int j = 0, k = 0; j < 10; ++j)
{
for(int x = 1; x <= b[j][0]; ++x) arr[k++] = b[j][x];
b[j][0] = 0;
}
base *= 10;
}
}$空间复杂度 : O(k), k为辅助队列

计数排序和基数排序都是桶排序的特殊情况, 单独的桶排序用得不多, 桶排序可以解决计数和基数解决不了的小数或者负数之类的问题. 基于桶排序的算法, 需要特别注意常数项, 其常数项往往很大, 所以一般只适于一些特殊的场合