在 C++ 中实现归并排序
Posted
技术标签:
【中文标题】在 C++ 中实现归并排序【英文标题】:implementing merge sort in C++ 【发布时间】:2012-08-15 08:54:51 【问题描述】:我研究过归并排序的理论,但对如何在 C++ 中实现它一无所知。我的问题是,合并排序以递归方式创建数组。但是在实现的时候,我们如何在运行时创建数组呢?或者对此的一般方法是什么?
谢谢。
【问题讨论】:
其实归并排序的好处是它一开始就不需要数组。事实上,合并排序可以就地实现,使用要求相当低的序列(我认为您可以在前向迭代器上实现它)。看看std::merge_sort()
!
@DietmarKühl:std::merge_sort
是什么?你的意思是std::stable_sort
?
algorithmist.com/index.php/Merge_sort.cpp
@Blastfurnace:好点!我确信有 std::merge_sort()
但没有! std::list<T>
有一个 sort()
成员,我将使用归并排序来实现(因为我知道的其他排序算法都不符合要求),但它不需要归并排序。
@DietmarKühl:有一个std::inplace_merge
算法用于实现归并排序。
【参考方案1】:
回答这个问题:在运行时创建动态大小的数组是使用std::vector<T>
完成的。理想情况下,您会使用其中一种来获得您的输入。如果没有,很容易转换它们。例如,您可以像这样创建两个数组:
template <typename T>
void merge_sort(std::vector<T>& array)
if (1 < array.size())
std::vector<T> array1(array.begin(), array.begin() + array.size() / 2);
merge_sort(array1);
std::vector<T> array2(array.begin() + array.size() / 2, array.end());
merge_sort(array2);
merge(array, array1, array2);
但是,分配动态数组相对较慢,通常应尽可能避免。对于合并排序,您只需对原始数组的子序列进行排序并就地合并它们。看来,std::inplace_merge()
要求使用双向迭代器。
【讨论】:
【参考方案2】:基于这里的代码:http://cplusplus.happycodings.com/algorithms/code17.html
// Merge Sort
#include <iostream>
using namespace std;
int a[50];
void merge(int,int,int);
void merge_sort(int low,int high)
int mid;
if(low<high)
mid = low + (high-low)/2; //This avoids overflow when low, high are too large
merge_sort(low,mid);
merge_sort(mid+1,high);
merge(low,mid,high);
void merge(int low,int mid,int high)
int h,i,j,b[50],k;
h=low;
i=low;
j=mid+1;
while((h<=mid)&&(j<=high))
if(a[h]<=a[j])
b[i]=a[h];
h++;
else
b[i]=a[j];
j++;
i++;
if(h>mid)
for(k=j;k<=high;k++)
b[i]=a[k];
i++;
else
for(k=h;k<=mid;k++)
b[i]=a[k];
i++;
for(k=low;k<=high;k++) a[k]=b[k];
int main()
int num,i;
cout<<"*******************************************************************
*************"<<endl;
cout<<" MERGE SORT PROGRAM
"<<endl;
cout<<"*******************************************************************
*************"<<endl;
cout<<endl<<endl;
cout<<"Please Enter THE NUMBER OF ELEMENTS you want to sort [THEN
PRESS
ENTER]:"<<endl;
cin>>num;
cout<<endl;
cout<<"Now, Please Enter the ( "<< num <<" ) numbers (ELEMENTS) [THEN
PRESS ENTER]:"<<endl;
for(i=1;i<=num;i++)
cin>>a[i] ;
merge_sort(1,num);
cout<<endl;
cout<<"So, the sorted list (using MERGE SORT) will be :"<<endl;
cout<<endl<<endl;
for(i=1;i<=num;i++)
cout<<a[i]<<" ";
cout<<endl<<endl<<endl<<endl;
return 1;
【讨论】:
<iostream.h>
?!?!当然,你在开玩笑!这个标头在十多年前就不再使用了!更不用说使用全局变量和main()
返回void
。无论这些信息的来源是什么,最好不要管它......!
你是对的,当然。但是家伙问了有关数组和递归的问题。你在这里。您可以在我的回答中找到来源。
但我修复了 iostream 和 main:)
好吧,归并排序显然是递归的。但是,它绝对不需要数组。它也不需要任何额外的内存,尽管就地合并并非完全微不足道(我认为;我可以立即提出的内容相当容易,但不是一个简单的循环)。
虽然这可行,但这个解决方案根本不是惯用的。当学生们看到这个时,他们会远离 C++,认为它是 80 年代的某种语言。实际上,C++ 是一种更现代的语言,可以为您节省此解决方案中的大部分工作。来自<algorithm>
的std::inplace_merge
与此处的合并函数完全相同。【参考方案3】:
我已经完成了@DietmarKühl 的合并排序方式。希望对大家有帮助。
template <typename T>
void merge(vector<T>& array, vector<T>& array1, vector<T>& array2)
array.clear();
int i, j, k;
for( i = 0, j = 0, k = 0; i < array1.size() && j < array2.size(); k++)
if(array1.at(i) <= array2.at(j))
array.push_back(array1.at(i));
i++;
else if(array1.at(i) > array2.at(j))
array.push_back(array2.at(j));
j++;
k++;
while(i < array1.size())
array.push_back(array1.at(i));
i++;
while(j < array2.size())
array.push_back(array2.at(j));
j++;
template <typename T>
void merge_sort(std::vector<T>& array)
if (1 < array.size())
std::vector<T> array1(array.begin(), array.begin() + array.size() / 2);
merge_sort(array1);
std::vector<T> array2(array.begin() + array.size() / 2, array.end());
merge_sort(array2);
merge(array, array1, array2);
【讨论】:
我知道我参加聚会有点晚了,但是合并中的所有 k 是怎么回事? @briansrls 在合并排序的其他实现中,k
索引将用于跟踪较大数组的插入点。在这个实现中,这是没有必要的,因为您将较小的数组中的项目“推回/追加”到较大的数组中。【参考方案4】:
我重新排列了选择的答案,使用的数组指针和用于数字计数的用户输入不是预定义的。
#include <iostream>
using namespace std;
void merge(int*, int*, int, int, int);
void mergesort(int *a, int*b, int start, int end)
int halfpoint;
if (start < end)
halfpoint = (start + end) / 2;
mergesort(a, b, start, halfpoint);
mergesort(a, b, halfpoint + 1, end);
merge(a, b, start, halfpoint, end);
void merge(int *a, int *b, int start, int halfpoint, int end)
int h, i, j, k;
h = start;
i = start;
j = halfpoint + 1;
while ((h <= halfpoint) && (j <= end))
if (a[h] <= a[j])
b[i] = a[h];
h++;
else
b[i] = a[j];
j++;
i++;
if (h > halfpoint)
for (k = j; k <= end; k++)
b[i] = a[k];
i++;
else
for (k = h; k <= halfpoint; k++)
b[i] = a[k];
i++;
// Write the final sorted array to our original one
for (k = start; k <= end; k++)
a[k] = b[k];
int main(int argc, char** argv)
int num;
cout << "How many numbers do you want to sort: ";
cin >> num;
int a[num];
int b[num];
for (int i = 0; i < num; i++)
cout << (i + 1) << ": ";
cin >> a[i];
// Start merge sort
mergesort(a, b, 0, num - 1);
// Print the sorted array
cout << endl;
for (int i = 0; i < num; i++)
cout << a[i] << " ";
cout << endl;
return 0;
【讨论】:
【参考方案5】:#include <iostream>
using namespace std;
template <class T>
void merge_sort(T array[],int beg, int end)
if (beg==end)
return;
int mid = (beg+end)/2;
merge_sort(array,beg,mid);
merge_sort(array,mid+1,end);
int i=beg,j=mid+1;
int l=end-beg+1;
T *temp = new T [l];
for (int k=0;k<l;k++)
if (j>end || (i<=mid && array[i]<array[j]))
temp[k]=array[i];
i++;
else
temp[k]=array[j];
j++;
for (int k=0,i=beg;k<l;k++,i++)
array[i]=temp[k];
delete temp;
int main()
float array[] = 1000.5,1.2,3.4,2,9,4,3,2.3,0,-5;
int l = sizeof(array)/sizeof(array[0]);
merge_sort(array,0,l-1);
cout << "Result:\n";
for (int k=0;k<l;k++)
cout << array[k] << endl;
return 0;
【讨论】:
【参考方案6】:归并排序的问题在于归并,如果您实际上不需要实现归并,那么它非常简单(对于整数向量):
#include <algorithm>
#include <vector>
using namespace std;
typedef vector<int>::iterator iter;
void mergesort(iter b, iter e)
if (e -b > 1)
iter m = b + (e -b) / 2;
mergesort(b, m);
mergesort(m, e);
inplace_merge(b, m, e);
【讨论】:
b
、m
和 e
不是不言自明的。你的意思是:begin
、middle
和 end
您的排序实现有问题。迭代器之间的差值范围可能小于你要排序的范围的大小。【参考方案7】:
我知道这个问题已经得到解答,但我决定加两分钱。这是合并排序的代码,它仅在合并操作中使用额外的空间(并且额外的空间是临时空间,将在弹出堆栈时被销毁)。事实上,你会在这段代码中看到没有使用堆操作(没有在任何地方声明new
)。
希望这会有所帮助。
void merge(int *arr, int size1, int size2)
int temp[size1+size2];
int ptr1=0, ptr2=0;
int *arr1 = arr, *arr2 = arr+size1;
while (ptr1+ptr2 < size1+size2)
if (ptr1 < size1 && arr1[ptr1] <= arr2[ptr2] || ptr1 < size1 && ptr2 >= size2)
temp[ptr1+ptr2] = arr1[ptr1++];
if (ptr2 < size2 && arr2[ptr2] < arr1[ptr1] || ptr2 < size2 && ptr1 >= size1)
temp[ptr1+ptr2] = arr2[ptr2++];
for (int i=0; i < size1+size2; i++)
arr[i] = temp[i];
void mergeSort(int *arr, int size)
if (size == 1)
return;
int size1 = size/2, size2 = size-size1;
mergeSort(arr, size1);
mergeSort(arr+size1, size2);
merge(arr, size1, size2);
int main(int argc, char** argv)
int num;
cout << "How many numbers do you want to sort: ";
cin >> num;
int a[num];
for (int i = 0; i < num; i++)
cout << (i + 1) << ": ";
cin >> a[i];
// Start merge sort
mergeSort(a, num);
// Print the sorted array
cout << endl;
for (int i = 0; i < num; i++)
cout << a[i] << " ";
cout << endl;
return 0;
【讨论】:
int temp[size1+size2];不是有效的 C++ 代码——我们需要有一个已知的编译时间常量来以这种方式声明数组——你必须“新建”数组。【参考方案8】:这是一种实现方式,只使用数组。
#include <iostream>
using namespace std;
//The merge function
void merge(int a[], int startIndex, int endIndex)
int size = (endIndex - startIndex) + 1;
int *b = new int [size]();
int i = startIndex;
int mid = (startIndex + endIndex)/2;
int k = 0;
int j = mid + 1;
while (k < size)
if((i<=mid) && (a[i] < a[j]))
b[k++] = a[i++];
else
b[k++] = a[j++];
for(k=0; k < size; k++)
a[startIndex+k] = b[k];
delete []b;
//The recursive merge sort function
void merge_sort(int iArray[], int startIndex, int endIndex)
int midIndex;
//Check for base case
if (startIndex >= endIndex)
return;
//First, divide in half
midIndex = (startIndex + endIndex)/2;
//First recursive call
merge_sort(iArray, startIndex, midIndex);
//Second recursive call
merge_sort(iArray, midIndex+1, endIndex);
merge(iArray, startIndex, endIndex);
//The main function
int main(int argc, char *argv[])
int iArray[10] = 2,5,6,4,7,2,8,3,9,10;
merge_sort(iArray, 0, 9);
//Print the sorted array
for(int i=0; i < 10; i++)
cout << iArray[i] << endl;
return 0;
【讨论】:
【参考方案9】:这很容易理解:
#include <iostream>
using namespace std;
void Merge(int *a, int *L, int *R, int p, int q)
int i, j=0, k=0;
for(i=0; i<p+q; i++)
if(j==p) //When array L is empty
*(a+i) = *(R+k);
k++;
else if(k==q) //When array R is empty
*(a+i) = *(L+j);
j++;
else if(*(L+j) < *(R+k)) //When element in L is smaller than element in R
*(a+i) = *(L+j);
j++;
else //When element in R is smaller or equal to element in L
*(a+i) = *(R+k);
k++;
void MergeSort(int *a, int len)
int i, j;
if(len > 1)
int p = len/2 + len%2; //length of first array
int q = len/2; //length of second array
int L[p]; //first array
int R[q]; //second array
for(i=0; i<p; i++)
L[i] = *(a+i); //inserting elements in first array
for(i=0; i<q; i++)
R[i] = *(a+p+i); //inserting elements in second array
MergeSort(&L[0], p);
MergeSort(&R[0], q);
Merge(a, &L[0], &R[0], p, q); //Merge arrays L and R into A
else
return; //if array only have one element just return
int main()
int i, n;
int a[100000];
cout<<"Enter numbers to sort. When you are done, enter -1\n";
i=0;
while(true)
cin>>n;
if(n==-1)
break;
else
a[i] = n;
i++;
int len = i;
MergeSort(&a[0], len);
for(i=0; i<len; i++)
cout<<a[i]<<" ";
return 0;
【讨论】:
【参考方案10】:这是我的版本(简单易行): 仅使用原始数组大小的 两倍 内存。 [ a 是左数组 ] [ b 是右数组 ] [ c 用于合并 a 和 b ] [ p 是 c 的计数器 ]
void MergeSort(int list[], int size)
int blockSize = 1, p;
int *a, *b;
int *c = new int[size];
do
for (int k = 0; k < size; k += (blockSize * 2))
a = &list[k];
b = &list[k + blockSize];
p = 0;
for (int i = 0, j = 0; i < blockSize || j < blockSize;)
if ((j < blockSize) && ((k + j + blockSize) >= size))
++j;
else if ((i < blockSize) && ((k + i) >= size))
++i;
else if (i >= blockSize)
c[p++] = b[j++];
else if (j >= blockSize)
c[p++] = a[i++];
else if (a[i] >= b[j])
c[p++] = b[j++];
else if (a[i] < b[j])
c[p++] = a[i++];
for (int i = 0; i < p; i++)
a[i] = c[i];
blockSize *= 2;
while (blockSize < size);
【讨论】:
以上是关于在 C++ 中实现归并排序的主要内容,如果未能解决你的问题,请参考以下文章