在 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;


【讨论】:

&lt;iostream.h&gt;?!?!当然,你在开玩笑!这个标头在十多年前就不再使用了!更不用说使用全局变量和main() 返回void。无论这些信息的来源是什么,最好不要管它......! 你是对的,当然。但是家伙问了有关数组和递归的问题。你在这里。您可以在我的回答中找到来源。 但我修复了 iostream 和 main:) 好吧,归并排序显然是递归的。但是,它绝对不需要数组。它也不需要任何额外的内存,尽管就地合并并非完全微不足道(我认为;我可以立即提出的内容相当容易,但不是一个简单的循环)。 虽然这可行,但这个解决方案根本不是惯用的。当学生们看到这个时,他们会远离 C++,认为它是 80 年代的某种语言。实际上,C++ 是一种更现代的语言,可以为您节省此解决方案中的大部分工作。来自&lt;algorithm&gt;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);
    

【讨论】:

bme 不是不言自明的。你的意思是:beginmiddleend 您的排序实现有问题。迭代器之间的差值范围可能小于你要排序的范围的大小。【参考方案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++ 中实现归并排序的主要内容,如果未能解决你的问题,请参考以下文章

归并排序分段故障 NASM

排序算法的c++实现——归并排序

排序算法总结二(归并排序)(C++版本)

归并排序算法的C++实现

C++归并排序求逆序对_模板

归并排序 c++ and python