各种排序算法的C++实现

  内排序的方法有许多种,按所用策略不同,可归纳为五类:插入排序、选择排序、交换排序、归并排序和分配排序。其中,插入排序主要包括直接插入排序和希尔排序两种;选择排序主要包括直接选择排序和堆排序;交换排序主要包括气(冒)泡排序和快速排序。

  稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法是稳定的。其中冒泡,插入,基数,归并属于稳定排序,选择,快速,希尔,堆属于不稳定排序。

  就地排序:若排序算法所需的辅助空间并不依赖于问题的规模n,即辅助空间为O(1),则称为就地排序。

  1、插入排序:

插入排序
//插入排序
template <class T>
void Inser_Sort(vector<T> &v){
    for(int i=1;i<v.size();i++){
        T tmp = v[i];
        int j;
        for(j=i-1;j>=0 && v[j]>tmp;j--){//注意j>=0&&v[j]>tmp的顺序
            v[j+1]=v[j];
        }
        v[j+1]=tmp;//注意是(j+1)
    }
}

//有哨兵的插入排序
template <class T>
void Inser_Sort_Guard(vector<T> &v){
    for(int i=2;i<v.size();i++){
        v[0]=v[i];//v[0]是哨兵
        int j;
        for(j=i-1;v[j]>v[0];j--){
            v[j+1]=v[j]; 
        }
        v[j+1]=v[0];//注意是(j+1)
    }
}

int Delta[]={5,3,2,1};

//一趟希尔排序
template <class T>
void ShellInsert(vector<T> &v, int dk){
    for(int i=dk+1;i<v.size();i++){
        T tmp=v[i];
        int j;
        for(j=i-dk;j>=0 && v[j]>tmp;j-=dk){
            v[j+dk]=v[j];
        }
        v[j+dk]=tmp;
    }
}

//希尔排序,增量序列(delta[0,...,n-1])
template <class T>
void ShellSort(vector<T> &v, int delta[], int n){
    for(int k=0;k<n;k++){ShellInsert(v, delta[k]);}
}

 

  2、交换排序:

交换排序
//冒泡排序
template <class T>
void BubbleSort(vector<T> &v){
    bool change;
    for(int i=v.size()-1,change=true;i>=1 && change==true;i--){//i为每一趟比较的次数
        for(int j=0;j<i;j++){
            change=false;
            if(v[j]>v[j+1]){
                int tmp=v[j];
                v[j]=v[j+1];
                v[j+1]=tmp;
                change=true;
            }
        }
    }
}

template <class T>
void exchange(T &a,T &b)
{
    int temp;
    temp=a;
    a=b;
    b=temp;
}

//快排的partition函数(算法导论)
template <class T>
int Partition1(vector<T> &v, int low, int high)
{
    int i=low-1;//i是比MidVal小的值集合的边界角标
    T MidVal=v[high];

    for(int j=low;j<high;j++)
    {
        if(v[j]<=MidVal)//把比MidVal小的数往前放     
        {
            i++;
            exchange(v[j],v[i]);   
        }        
    }
    exchange(v[high],v[i+1]);
    return i+1;
}

//快速排序一次划分(严蔚敏)
template <class T>
int Partition2(vector<T> &v, int low, int high){
    T pivot=v[low];//pivot为枢轴元素记录

    while(low<high){//当相等时终止,返回中间元素的下标
        while(low<high && v[high]>=pivot) high--;
        v[low]=v[high];
        while(low<high && v[low]<=pivot) low++;
        v[high]=v[low];
    }

    v[low]=pivot;
    return low;
}


//递归形式的快速排序
template <class T>
void QSort(vector<T> &v, int low, int high){
    if (low < high)
    {
        int pivotloc=Partition1(v, low, high);
        QSort(v, low, pivotloc-1);
        QSort(v, pivotloc+1, high);
    }
}


struct Boundarys{
    int begin;
    int end;
};

//快速排序的非递归写法(stack)
template <class T>
void QSort_NoRecursive_S(vector<T> &v, int low, int high){
    stack<Boundarys> st;
    int pivot_0=Partition2(v, low, high);

    Boundarys bound_L, bound_R;
    if(low<pivot_0-1){
        bound_L.begin=low; bound_L.end=pivot_0-1; st.push(bound_L);
    }
    if(high>pivot_0+1){
        bound_R.begin=pivot_0+1; bound_R.end=high; st.push(bound_R);
    }

    while(!st.empty()){
        Boundarys bound=st.top();
        st.pop();

        int pivot=Partition2(v, bound.begin, bound.end);
        Boundarys bound_L, bound_R;
        if(bound.begin<pivot-1){
            bound_L.begin=low; bound_L.end=pivot-1; st.push(bound_L);
        }
        if(bound.end>pivot+1){
            bound_R.begin=pivot+1; bound_R.end=high; st.push(bound_R);
        }
    }
}

 

  3、选择排序

选择排序
//选择排序
template <class T>
void SelectSort(vector<T> &v){
    vector<T>::iterator it_out;

    for(it_out=v.begin();it_out<v.end()-1;it_out++){
        vector<T>::iterator it_in;
        vector<T>::iterator it_min=it_out;
        for(it_in=it_out+1;it_in<v.end();it_in++){//it_out为每次比较的起始位置
            if(*it_min>*it_in) it_min=it_in;
        }
        if(it_min!=it_out){
            T tmp=*it_out;
            *it_out=*it_min;
            *it_min=tmp;
        }
    }
}

//一次调整过程,v中的关键字v[s...m],除去v[s]外,其余均满足堆定义,调整v[s...m]重新形成大顶堆
template <class T>
void HeapAdjust(vector<T> &v, int s, int m){
    T tmp=v.at(s);
    for(int i=2*s+1;i<=m;i=2*i+1){//i=2*s+1为第一个孩子结点
        if(i<m && v.at(i)<v.at(i+1)) i++;//右孩子关键字更大
        
        if(tmp>=v.at(i)) break;//根节点已经是最大

        v.at(s)=v.at(i);//更新根节点
        s=i;//更新s
    }
    v.at(s)=tmp;
}

//对顺序表进行堆排序
template <class T>
void HeapSort(vector<T> &v){
    //建立大顶堆
    for(int i=v.size()/2-1;i>=0;i--){//i的范围(vec.size()/2-1~0)(i为下标,按树的节点编号来算应相应加1)
        HeapAdjust(v, i, v.size()-1);//i~vec.size()
    }

    //将堆顶记录和当前未经排序的子序列中的最后一个记录交换,然后将交换后的的子序列重建为大顶堆
    for(int j=v.size()-1;j>0;j--){//j指示未经排序的子序列中的最后一个记录的下标
        T tmp;
        tmp=v.at(j);
        v.at(j)=v.at(0);
        v.at(0)=tmp;

        HeapAdjust(v, 0, j-1);
    }

}

 

  4、归并排序

归并排序
//两路归并排序,归并算法
template <class T>
void Merge(vector<T> &v, int low, int mid, int high){
    vector<T> tmp(high-low+1);
    int i, j, k;
    for (i=low,j=mid+1,k=0;i<=mid&&j<=high;++k){//将有序的v归并到tmp
        if (v[j]<v[i])
            tmp[k]=v[j++];
        else
            tmp[k]=v[i++];
    }

    if (i<=mid)
        while(i<=mid)
            tmp[k++]=v[i++];

    if (j<=high)
        while(j<=high)
            tmp[k++]=v[j++];

    for ( k=low, i=0;k<=high;++k){//将归并排序好序列tmp复制回原序列v
        v[k]=tmp[i++];
    }
}

//两路归并排序
template <class T>
void MSort(vector<T> &v,int low,int high){
    if (low<high){
        int mid=(low+high)/2;
        MSort(v,low,mid);
        MSort(v,mid+1,high);
        Merge(v,low,mid,high);
    }
}

 

测试代码
void main()
{
    while (1)
    {
        vector<int> _v;
        int n;
        cout<<"请输入有序顺序表的元素个数n:"<<endl;
        cin>>n;
        srand((unsigned)time(0));
        for (int i=0;i<n;i++)
        {
            _v.push_back(rand()%100);
        }

        for (int i=0;i<_v.size();i++)
        {
            cout<<_v[i]<<" ";
        }
        cout<<endl;

        //Inser_Sort(_v);
        //Inser_Sort_Guard(_v);
        //ShellSort(_v, Delta, 4);
        //BubbleSort(_v);
        //QSort(_v, 0, _v.size() - 1);
        //QSort_NoRecursive_S(_v, 0, _v.size() - 1);
        //SelectSort(_v);
        //HeapSort(_v);
        MSort(_v, 0, _v.size() - 1);

        for (int i=0;i<_v.size();i++)
        {
            cout<<_v[i]<<" ";
        }
        cout<<endl;
        system("pause");
    }
}

 

  按平均时间将排序分为四类:

  (1)平方阶(O(n2))排序:一般称为简单排序,例如直接插入、直接选择和冒泡排序;

  (2)线性对数阶(O(nlgn))排序:如快速、堆和归并排序;

  (3)O(n1+£)阶排序:£是介于0和1之间的常数,即0<£<1,如希尔排序;

  (4)线性阶(O(n))排序:如桶、箱和基数排序。

  不同条件下,排序方法的选择:

  (1)若n较小(如n≤50),可采用直接插入或直接选择排序。当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。

  (2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜。

  (3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
       快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
       堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
       若要求排序稳定,则可选用归并排序。但本章介绍的从单个记录起进行两两归并的  排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的。

posted @ 2012-11-02 18:56  杨小明  阅读(298)  评论(0编辑  收藏  举报