1.未改进的归并排序
#include <iostream>
void merge(int a[], int left, int mid, int right) {
if (left >= right)
return;
int *t = new int[right - left + 1];
int i = left, j = mid + 1, cnt = 0;
while(true) {
// 超出某部分范围说明其中一块已经用完了
if (i == mid + 1 || j == right + 1)
break;
if (a[i] >= a[j]) {
t[cnt++] = a[j];
j++;
}
else {
t[cnt++] = a[i];
i++;
}
}
// 将剩下的补充到临时数组
if (i == mid + 1) {
for (int k = j; k <= right; ++k) {
t[cnt++] = a[k];
}
}
else {
for (int k = i; k <= mid; ++k) {
t[cnt++] = a[k];
}
}
// 赋值给原数组
for (int k = 0; k < right - left + 1; ++k) {
a[left + k] = t[k];
}
}
void mergeSort(int a[], int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(a, left, mid);
mergeSort(a, mid + 1, right);
merge(a, left, mid, right);
}
}
int main() {
int a[] = {2, 4, 22, 1, 78, 23, 90, 11, 73};
mergeSort(a, 0, 8);
for (int i = 0; i < 9; ++i) {
std::cout << a[i] << " ";
}
return 0;
}
输出:
1 2 4 11 22 23 73 78 90
问题:频繁使用临时数组进行元素移动。
2.改进:不使用临时数组,使用链接表
#include <iostream>
int merge(int a[], int link[], int p, int q) {
int i = p, j = q, k = 0;
while (i != 0 && j != 0) {
if (a[i] > a[j]) { // 找较小的关键字,链接
link[k] = j;
k = j;
j = link[j];
}
else {
link[k] = i;
k = i;
i = link[i];
}
}
if (i == 0) { // 较大者连接到小者的后面
link[k] = j;
}
else {
link[k] = i;
}
return link[0]; // 返回r表
}
int mergeSort(int a[], int link[], int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
int p = mergeSort(a, link, left, mid); // p表
int q = mergeSort(a, link, mid + 1, right); // q表
// 返回r表
return merge(a, link, p, q);
}
// 单个节点的r表即此节点的下标
return left;
}
int main() {
int a[] = {2, 4, 22, 1, 78, 23, 90, 11, 73};
int *link = new int[9];
for (int i = 0; i < 9; ++i) {
link[i] = 0;
}
mergeSort(a, link, 1, 8);
int k = link[0];
std::cout << a[k] << " ";
while (link[k] != 0) {
std::cout << a[link[k]] << " ";
k = link[k];
}
return 0;
}
输出:
1 4 11 22 23 73 78 90
3.链接过程
第15次递归时,合并了所有数组。最终的从小到大的顺序下标为:
link[0] = 2 (最小下标)
link[2] = 5
link[5] = 3
link[3] = 4
link[4] = 7
link[7] = 1
link[1] = 8
link[8] = 6
link[6] = 0 (不包括)