重温归并排序的时候,以前使用python实现过,发现对归并排序原理较为理解,但是使用C++实现时,对序号的传递不是那么得心应手,所以在网上查找到一篇实现比较简洁的归并排序(链接),本文在此基础上继续进行简化。
1 第一次简化
首先发现运行原代码时时出现overstack错误,也就是溢出,将最大数组改为500即可修复。其次是将merge函数的mid参数去除,简化传递参数量,修改了一下验证主函数。
#include<iostream>
using namespace std;
const int maxn = 500, INF = 0x3f3f3f3f;
int L[maxn / 2 + 2], R[maxn / 2 + 2];
void merge(int a[], int n, int left, int right)
{
int mid = (left + right) / 2;
int n1 = mid - left, n2 = right - mid;
for (int i = 0; i < n1; i++)
L[i] = a[left + i];
for (int i = 0; i < n2; i++)
R[i] = a[mid + i];
L[n1] = R[n2] = INF;
int i = 0, j = 0;
for (int k = left; k < right; k++)
{
if (L[i] <= R[j])
a[k] = L[i++];
else
a[k] = R[j++];
}
}
void mergeSort(int a[], int n, int left, int right)
{
if (left + 1 < right)
{
int mid = (left + right) / 2;
mergeSort(a, n, left, mid);
mergeSort(a, n, mid, right);
merge(a, n, left, right);
}
}
int main()
{
const int n = 2;
int a[n] = { 3,-5 };
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
mergeSort(a, n,0,n);
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
2 第二次简化
由于L与R需要事先知道大小才能分配数组空间,因此原程序提前申请了大量的内存,如果数组比较小时就浪费了空间,数组比较大时又限制了排序,因此考虑采用动态数组。
#include<iostream>
using namespace std;
void merge(int a[], int n, const int left,const int right)
{
const int mid = (left + right) / 2;
const int n1 = mid - left, n2 = right - mid;
int* L = new int[n1];
int* R = new int[n2];
for (int i = 0; i < n1; i++)
L[i] = a[left + i];
for (int i = 0; i < n2; i++)
R[i] = a[mid + i];
int i = 0, j = 0;
for (int k = left; k < right; k++)
{
if (L[i] <= R[j])
a[k] = L[i++];
else
a[k] = R[j++];
}
delete[] L;
delete[] R;
}
void mergeSort(int a[], int n, int left, int right)
{
if (left + 1 < right)
{
int mid = (left + right) / 2;
mergeSort(a, n, left, mid);
mergeSort(a, n, mid, right);
merge(a, n, left, right);
}
}
int main()
{
const int n = 5;
int a[n] = { 3,-5, 5, 2,0 };
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
mergeSort(a, n,0,n);
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
然而运行结果为:
可见失败了,明天再来找bug吧。
3 第二次简化修正
上面进一步的简化失败了,根据原来的代码,给new数组后面加入一个标志结尾的INF可以解决问题,但是具体原因仍旧不清楚。为甚么无穷大INF设置为0x3f3f3f3f
#include<iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
void merge(int a[], int n, int left, int right)
{
int mid = (left + right) / 2;
int n1 = mid - left, n2 = right - mid;
int *L = new int[n1+1];
int *R = new int[n2+1];
L[n1] = R[n2] = INF;
for (int i = 0; i < n1; i++)
L[i] = a[left + i];
for (int i = 0; i < n2; i++)
R[i] = a[mid + i];
int i = 0, j = 0;
for (int k = left; k < right; k++)
{
if (L[i] <= R[j])
a[k] = L[i++];
else
a[k] = R[j++];
}
delete[] L;
delete[] R;
L = R = NULL;
}
void mergeSort(int a[], int n, int left, int right)
{
if (left + 1 < right)
{
int mid = (left + right) / 2;
mergeSort(a, n, left, mid);
mergeSort(a, n, mid, right);
merge(a, n, left, right);
}
}
int main()
{
const int n = 5;
int a[n] = { 3,-5,-6,5,6 };
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
mergeSort(a, n, 0, n);
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
结果正确。
4 第二次简化使用INF的解释
设置L[n1] =INF,可以使得当L已经贴完的情况下,R剩下的元素总是比L[n1]要小,所以可以贴完R剩下的元素,然后以k达到条件作为循环出口。如果不使用INF,那么需要多加几条判断语句,增加代码量,但是也可以减少一定的运算量。
5 不使用INF版本
#include<iostream>
using namespace std;
void merge(int a[], int n, int left, int right)
{
int mid = (left + right) / 2;
int n1 = mid - left, n2 = right - mid;
int *L = new int[n1];
int *R = new int[n2];
for (int i = 0; i < n1; i++)
L[i] = a[left + i];
for (int i = 0; i < n2; i++)
R[i] = a[mid + i];
int i = 0, j = 0, k = left;
for (; k < right && i<n1 && j<n2; k++)//增加判断L、R是否贴完
{
if (L[i] <= R[j])
a[k] = L[i++];
else
a[k] = R[j++];
}
while (i < n1)//如果L没贴完,继续贴L
a[k++] = L[i++];
while (j < n2)//如果R没贴完,继续贴R
a[k++] = R[j++];
delete[] L;
delete[] R;
L = R = NULL;
}
void mergeSort(int a[], int n, int left, int right)
{
if (left + 1 < right)
{
int mid = (left + right) / 2;
mergeSort(a, n, left, mid);
mergeSort(a, n, mid, right);
merge(a, n, left, right);
}
}
int main()
{
const int n = 10;
int a[n] = { 3,-5,-6,5,6,56,-45,65,65,-190 };
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
mergeSort(a, n, 0, n);
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
system("pause");
return 0;
}