排序之-归并排序

1、归并排序(分治思想)

归并排序(merge sort)是一个时间复杂度为O(nlogn)的基于比较的排序算法(comparison based sorting algorithm)。 归并排序大多数实现(implementation)都将其实现成了一个stable sort, 所谓的stable sort的意思就是the implementation preserves the input order of equal elements in the sorted order。

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。 若将两个有序表合并成一个有序表,称为二路归并

merge sort 也采用了divide and conquer(分治技术) 的排序算法。 由John von Neumann于1945年发明的。 

我们知道, 快排序的平均情况下时间复杂度是O(nlogn), 最坏情况下为O(n^2) 归并排序的好处就是时间复杂度总是O(nlogn).。 所以归并排序在时间方面可以beats quick sort。

下面对merge sort做出详细说明:

例如, 我们想要对如下数组精心排序:

merge sort 的思想是将一个problem break into 2 subproblems。 

(1)divide the array into 2 equal halves(假如分隔位置在数组的中间(记为mid位置), mid 之前为the first subarray, 之后为the second subarray):

扫描二维码关注公众号,回复: 5916833 查看本文章

(2) 现在假如我们somehow 将这两个子数组排好序了, 接下来我们的任务就是将这两个子数组merge称为一个大的排好序的数组。 注意这三个数组时独立位于内存之中的(可见merge sort 的空间复杂度会比较大)。

我们的merge算法如下。 现在我们将上图左边的数组记为L, 右边的数组记为R。  首先第一步,比较L的第一个未选中的数据和R中第一个未选中的数据, 找出最小的数据, 为位于L中的1. 我们将这个值放在A中。 然后移动L的索引到下一个元素2, R的索引记录不懂, 比较, 发现2大, 将2 放到A的第二个位置。 在移动L中的索引, 到达4, R的不动, 4 比3大, 所以将3 放在A的第三个位置, 移动R的索引记录到下一个元素。 以此类推, 直至R(或者L)的元素均位于A中, 然后将L(或者R)剩余的元素均拷贝到A中。 即完成排序。

上述算法的伪代码如下:

现在我们说说如何排序。

解决办法是我们可以进一步的将数组或者list 进行进一步的细分:

到达最后一步的时候, 然后开始从下网上归并(merge)。

最终结果如下:

归并排序的伪代码如下:

C++实现的归并排序程序如下:

#include <cstdio>
#include <iostream>
 
using namespace std;
 
// Function to Merge Arrays L and R into A.
// lefCount = number of elements in L
// rightCount = number of elements in R.
void Merge(int *A,int *L,int leftCount,int *R,int rightCount) {
    int i,j,k;
 
    // i - to mark the index of left aubarray (L)
    // j - to mark the index of right sub-raay (R)
    // k - to mark the index of merged subarray (A)
    i = 0; j = 0; k =0;
 
    while(i<leftCount && j< rightCount) {
        if(L[i]  < R[j]) A[k++] = L[i++];
        else A[k++] = R[j++];
    }
    while(i < leftCount) A[k++] = L[i++];
    while(j < rightCount) A[k++] = R[j++];
}
 
// Recursive function to sort an array of integers.
void MergeSort(int *A,int n) {
    int mid,i, *L, *R;
    if(n < 2) return; // base condition. If the array has less than two element, do nothing.
 
    mid = n/2;  // find the mid index.
 
    // create left and right subarrays
    // mid elements (from index 0 till mid-1) should be part of left sub-array
    // and (n-mid) elements (from mid to n-1) will be part of right sub-array
    L = new int[mid];
    R = new int [n - mid];
 
    for(i = 0;i<mid;i++) L[i] = A[i]; // creating left subarray
    for(i = mid;i<n;i++) R[i-mid] = A[i]; // creating right subarray
 
    MergeSort(L,mid);  // sorting the left subarray
    MergeSort(R,n-mid);  // sorting the right subarray
    Merge(A,L,mid,R,n-mid);  // Merging L and R into A as sorted list.
    // the delete operations is very important
    delete [] R;
    delete [] L;
}
 
int main() {
    /* Code to test the MergeSort function. */
 
    int A[] = {9,5,8,1,9,10,15,13,12,17}; // creating an array of integers.
    int i,numberOfElements;
 
    // finding number of elements in array as size of complete array in bytes divided by size of integer in bytes.
    // This won't work if array is passed to the function because array
    // is always passed by reference through a pointer. So sizeOf function will give size of pointer and not the array.
    // Watch this video to understand this concept - http://www.youtube.com/watch?v=CpjVucvAc3g
    numberOfElements = sizeof(A)/sizeof(A[0]);
 
    // Calling merge sort to sort the array.
    MergeSort(A,numberOfElements);
 
    //printing all elements in the array once its sorted.
    for(i = 0;i < numberOfElements;i++)
       cout << " " << A[i];
    return 0;
}

归并排序程序运行结果:

总结:

      归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。从上文的图中可看出,每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。

本文参考链接及学习资料:

【1】https://blog.csdn.net/a130737/article/details/38228369

猜你喜欢

转载自blog.csdn.net/xiu351084315/article/details/89301418