「学习笔记」归并排序的空间优化
归并排序的时间复杂度为 \(O_{n \log n}\),一般的空间复杂度为 \(O_n\),但事实上我们可以将这个空间复杂度优化到 \(O_1\)
对于 \(a_i\) 和 \(a_j\) 这两个元素,我们要把他们都存到 \(a_k\) 这个位置里,我们可以这样操作
找一个比 \(\max(a_i, a_j)\) 大的数 \(maxx\),让 \(a_k = a_k + \max(a_i, a_j) \times maxx\)
这样,我们可以通过这个式子来表示出原本的 \(a_k\) 和 \(\max(a_i, a_j)\)
\[\begin{aligned}
&a_k = a_k \bmod maxx\\
&a_j = \lfloor \frac{a_k}{maxx} \rfloor
\end{aligned}
\]
这样,我们就不必再开一个和原数组长度一样的辅助数组了,但是由于会多次取模,所以时间上会比空间复杂度为 \(O_n\) 的归并排序慢
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mid ((l + r) >> 1)
const int N = 1e5 + 5;
int n;
ll _max;
ll a[N];
void mergearray(int l, int mi, int r, ll maxn) {
int i = l, j = mi + 1, k = l;
while (i <= mi && j <= r) {
if (a[i] % maxn <= a[j] % maxn) {
// a[i] % maxn 与 a[j] % maxn 是 a[i] 与 a[j] 原本的值
a[k] = a[k] + (a[i] % maxn) * maxn;
++ k, ++ i;
}
else {
a[k] = a[k] + (a[j] % maxn) * maxn;
++ k, ++ j;
}
}
while (i <= mi) {
a[k] = a[k] + (a[i] % maxn) * maxn;
++ k, ++ i;
}
while (j <= r) {
a[k] = a[k] + (a[j] % maxn) * maxn;
++ k, ++ j;
}
for (int i = l; i <= r; ++ i) {
a[i] /= maxn;
}
}
void mergesort(int l, int r, ll maxn) {
if (l < r) {
mergesort(l, mid, maxn);
mergesort(mid + 1, r, maxn);
mergearray(l, mid, r, maxn);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++ i) {
scanf("%lld", &a[i]);
_max = max(_max, a[i]);
}
_max += 1;
mergesort(1, n, _max);
for (int i = 1; i <= n; ++ i) {
printf("%d%c", a[i], " \n"[i == n]);
}
return 0;
}
朝气蓬勃 后生可畏