原地归并算法

独自空忆成欢 提交于 2020-04-04 05:16:15


归并排序算法(mergesort)是将一个序列划分为同样大小的两个子序列,然后对两个子序列分别进行排序,最后进行合并操作,将两个子序列合成有序的序列.在合成的过程中,一般的实现都需要开辟一块与原序列大小相同的空间,以进行合并操作。这儿有比较全的排序算法:http://www.cnblogs.com/xkfz007/archive/2012/07/01/2572017.html
之前使用过的归并排序,都是需要重新开辟新空间的。一般需要开辟的空间为n(数组大小),所以空间复杂度是O(n). 其实可以对于归并可以不需要开辟新的空间就可以直接进行归并的方法,即原地归并。
其主要思想就是,因为需要归并的部分是有序的,所以需要找到两个序列中第二个序列小于第一个序列的部分,比如对序列[<1, 3, 5, 7>, <2, 4, 6, 8>]进行归并操作。2就是第二个序列中小于第一个序列中的<3,5,7>部分,需要调节<3,5,7>,与2的位置。然后不断的进行扫描。其中进行调整的时候,用到的是对序列的循环移动,循环左移或循环右移。这样就不需要新的空间了。
C++代码如下:

void rotate_right(int *v,int sz,int n){
    reverse(v,v+sz);
    reverse(v,v+n);
    reverse(v+n,v+sz);
}       
void merge(int *v,int sz,int pos){
    int first=0,second=pos;
    while(first<second&&second<sz){
        while(first<second&&v[first]<=v[second])
            first++;
        int n=0;
        while(second<sz&&v[first]>v[second])
            n++,second++;
        rotate_right(v+first,second-first,n);
        first+=n;
    }
}


其中rotate_right是利用STL中的算法reserve实现的,其实也可以自己实现,很简单的。
之前总结了归并排序的三种方法:递归,非递归,自然归并。现在重新利用新的merge算法将原来的代码重新写一遍。
C++代码如下:

void merge_sort1(int *v,int sz){
    if(sz<=1)
        return ;
    merge_sort1(v,sz/2);
    merge_sort1(v+sz/2,sz-sz/2);
    merge(v,sz,sz/2);
}
void merge_pass(int* v,int s,int n){
    int i=0;
    while(i+2*s-1<n){
        merge(v+i,2*s,s);
        i+=2*s;
    }
    if(i+s<n)
        merge(v+i,n-i,i+s);
}
void merge_sort2(int*v ,int n){
    int s=1;
    while(s<n){
        merge_pass(v,s,n);
        s+=s;
    }
}
void merge_sort3(int *v,int n){
    int *pos=new int[n];
    int ct=0;
    for(int i=0;i<n-1;i++)
        if(v[i]>v[i+1])
            pos[ct++]=i;
    pos[ct++]=n-1;
    print(pos,n);
    if(ct>1){
        int k=0;
        int m=pos[k++];
        int r;
        while(k<ct){
            r=pos[k++];                             
            merge(v,r+1,m+1);
            m=r;
        }
    }   
    delete[] pos;
}

主程序如下:

#include <iostream>
#include <algorithm>
#include <iterator>
#include <cstdlib>
using namespace std; 
void print(int *v,int sz){
    copy(v,v+sz,ostream_iterator<int>(cout," "));
    cout<<endl;

}
int main(){
    const int N=20;
    int a[N];
//    srand(time(0));
    for(int i=0;i<N;i++)
        a[i]=rand()%100;
    print(a,N);
    merge_sort3(a,N);
    print(a,N);
}

参考:http://www.cppblog.com/converse/archive/2008/09/28/63008.html


鸡尾酒排序, 是冒泡排序的改进版, 从两边分别排序, 每次是将未排序序列中的最大和最小的数移到正确的位置.
代码如下:

template <class T>
void cocktail(T arr[],int n){                
    int left=0,right=n-1;
    while(left<right){
        for(int i=left;i<right;i++){
            if(arr[i]>arr[i+1])
                swap(arr[i],arr[i+1]);
        }
        right--;
        for(int i=right;i>left;i--)
            if(arr[i]<arr[i-1])
                swap(arr[i],arr[i-1]);
        left++;
    }
}


计数排序
快速排序(非递归)
奇偶排序
交換排序法 冒泡排序 | 鸡尾酒排序 | 奇偶排序 | 梳排序 | 侏儒排序 | 快速排序 |臭皮匠算法 | Bogo排序
選擇排序法 选择排序 | 堆排序 | Smooth排序 | 笛卡尔树排序 | 锦标赛排序 | 循环排序
插入排序法 插入排序 | 希尔排序 | 二叉查找树排序 | 图书馆排序 | Patience排序
归并排序法 归并排序 | 多相归并排序 | Strand排序
分布排序法  美国旗帜排序 | 珠排序 | 桶排序 | 爆炸排序 | 计数排序 | 鸽巢排序 | 相邻图排序 | 基数排序 | 闪电排序
混合排序法 Tim排序 | 内省排序 | Spread排序 | 反移排序 | J排序
其他     双调排序器 | Batcher归并网络 | 两两排序网络

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!