常见排序算法的总结

/*排序算法总结
以实现从小到大的序列为例 
*/ 
#include<iostream>
using namespace std;
#define MAXSIZE 10
void swap(int a[], int i, int j)
{
    int temp=a[i];
    a[i]=a[j];
    a[j]=temp;
}

void print(int a[], int len)
{
    int i;
    for(i=0;i<len; i++){
        cout<< a[i] <<" ";
    }
    cout<< endl;
}

/*冒泡排序  O(n^2) 
 两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止
 */ 
void BubbleSort(int a[], int len)
{
    int i,j;
    for(i=0; i<len; i++) {
        for(j=len-1;j>=i;j--) {   //注意是j从后面开始往前循环   此时较小的数字如同气泡一样慢慢浮到上面,故命名为冒泡排序 
            if(a[j-1]> a[j]) {    //若前者大于后者则交换
                swap(a,j-1,j);
            }
        }
    }

}

//当你的序列本身就是 局部有序时,上述程序就会浪费很多的比较时间 ,为此我们增加一个标记变量flag来实现这一算法的改进
//flag 为true时表示有数据交换, c初始化为false
//当循环一次没有发生任何交换时,表明序列已经有序,此时flag会维持初始状态,从而直接退出循环不再进行比较 
void BubbleSort1 (int a[], int len)
{
    int i,j;
    int flag=true;
    for(i=0; i<len&& flag; i++) {  //若flag为false则退出循环 
        flag=false;  //初始状态为false
        for(j=len-1;j>=i; j--) {
            if(a[j-1]> a[j]){
                swap(a,j-1,j);
                flag=true;
            }
        }
        
    }
}

/*简单选择排序   O(n^2) 
通过n-i次关键字之间的比较,从 n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n) 个记录交换。 
即在排序时找到合适的关键字再做交换,并且只移动一次就能完成相应关键字的排序定位
*/ 
void SelectSort(int a[], int len)
{
    int i,j,min;
    for(i=0; i<len; i++) {
        min=i;   //将当前下标定义为最小值下标 
        for(j=i+1; j<len ;j++) {  //循环剩下的n-i个数据 
            if(a[j]< a[min]){    //若第j个数比min对应的最小值 小,则将该值的小标赋给min 
                min=j;
            }
        }
        if(i!=min) {  //若min不等于i,说明找到n-i+1个数中的最小值 ,交换 
            swap(a,i,min);
        }
    }
}

/*直接插入排序   O(n^2) 
将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表
即假设前i-1个数已经排好序,现在需要插入第i个
假设 第i个数比i-1大,则无需交换,若比第i-1个数小,则需要将其插入到前i-1个数中合适的位置 。 
插入的过程就是从后往前比较,将比第i个数大的值后移一位,直至找到第i个数应该存放的位置。 
在记录数少或者基本有序时效率很高效 
*/ 
void InsertSort(int a[], int len)
{
    int i,j;
    int m; //用于存放第i个数,作为哨兵判断哪些数需要后移 
    for(i=1; i<len; i++) {
        if(a[i]<a[i-1]){  //需将a[i]插入到前面 
            m=a[i];  //设置哨兵 
            for(j=i-1; a[j]>m; j--){
                a[j+1]=a[j];  //把比a[i]大的数后移 
            }
            a[j+1]=m; //插入到正确位置 
        }
    }
}

/*希尔排序(shell排序)    O(n^3/2)
将相距某个“增量”的记录组成一个子序列,然后对每一个子列内分别进行直接插入排序。
然后得到的结果是基本有序使,载对全体记录进行一次直接插入排序 
即将整体分割成部分,然后部分进行直接插入排序后,组合回去再进行直接插入排序 
关键并不是随便分组后各自排序,而是将相隔某个“增量”的记录组成一个子序列,实现跳跃式的移动,使得排序的效率提高 
*/ 
void ShellSort(int a[], int len) 
{
    int i,j;
    int m;    //用于存放需要插入的数字 
    int increment=len;   //初始化增量为长度 
    do{
        increment= increment/3+1;   //增量序列,直至为1。此时相隔increment的数形成了一个子列,排序在这个子列里面发生。子列逐渐缩小,直至只剩一个数 
        for(i=increment; i<len; i++) {
            if(a[i]< a[i-increment]) {   //需要将a[i]插入有序增量子表 
                m=a[i];
                for(j=i-increment; j>=0 && m<a[j]; j-=increment) {  //此时j的增量不为1,而是设置的跳跃的增量值 
                    a[j+increment]=a[j];
                }
                a[j+increment]= m;  //把a[i]放入正确位置 
            }
        }
    } while(increment>1);
}

/* 堆排序   O(nlogn) 
对简单选择排序进行的一种改进
堆一种完全二叉树。大顶堆:每个结点的值大于或者等于其左右孩子结点的值。小顶堆: 每个结点的值小于或者等于其左右孩子结点的值
基本思想:将待排序序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。
将他移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值 ),然后将剩余n-1个序列重新构成一个堆,这样就会得到n个元素中的此最大值。
此反复执行,便能得到一个有序序列 
*/ 
void HeapAdjust(int a[], int m, int n) 
{
    int temp,j;
    temp=a[m];
    for(j=2*m+1; j<n; j=2*j+1) {   //沿关键字较大的孩子结点向下筛选 
        if(j+1<n && a[j]<a[j+1]) {  
            ++j;    //j为孩子结点值较大的记录的下标 
        }
        if(temp>=a[j]) {
            break;
        }else    { 
        //此处实现了父节点与子结点的交换,但是只是将父结点的值改变,子结点由于还要进行跟其子结点比较,所以并未直接将现父节点的值赋给它 
            a[m]=a[j];  //将更大的值赋给父节点 
            m=j;    //将大值的下标赋给m,往下以m结点为父节点进行调节   
        }
    }
    a[m]=temp; //将最初的最小的值赋给最后子结点的位置    
}
void HeapSort(int a[],int len) 
{
    int i;
    //完成的是将现在待排序序列构建成一个大顶堆 。即从下往上、从右往左,将每个非终端结点(非叶结点)当做根节点,将其及其子树调整成大顶堆。 
    for(i=len/2-1; i>=0; i--) {   //n个结点,其最后一个非叶结点的为[n/2]。注意数组的下标从0开始,所以为[n/2]-1 
        HeapAdjust(a,i,len); 
    }
    //完成的是逐步将每个最大值的根结点与末尾元素交换,并且再调整其成为大顶堆 
    for(i=len-1; i>0; i--) {
        swap(a,0,i);
        HeapAdjust(a,0,i);  //此处为i而不是i-1,传入的是数组删除顶部数据剩下的数据数目 
    }    
}

/*归并排序  O(nlogn)
利用归并的思想,假设初始序列有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,
然后两两合并,得到[n/2] 个长度为2或1的有序子序列;
再两两合并 ,...,如此重复,直至得到一个长度为n的有序序列为止 
*/
//将 a2[s...m]与 a2[m+1...t]归并为有序的a1[s...t]  
void Merge(int a2[], int a1[], int s, int m, int t) 
{
    int i,j,k,l;
    //两个子序列一一比较,哪个序列的元素小就放进a1序列里面,然后位置加1,再与另外一个序列原来位置的元素比较,
    //如此反复,可以把两个有序的序列合并成一个有序的序列 
    for(i=s,j=m+1,k=s;i<=m,j<=t;k++) {    //k代表a1从s到t的下标; i代表a2从s到m; j代表a2中从m+1到n 
        if(a2[i]<a2[j]) {
            a1[k]=a2[i++];
        }else {
            a1[k]=a2[j++];
        }
    }
    //将两个子序列的剩下部分都放入a1序列中
    if(i<=m) {
        for(l=0; l<=m-i;l++){
            a1[k+l]=a2[i+l];   //将剩余a2[i...m]复制到a1 
        }
    }
    if(j<=t) {
        for(l=0; l<=t-j;l++){  
            a1[k+l]=a2[j+l];   //将剩余a2[j...n]复制到a1 
        }
    }
}
//将a(s..t)归并排序为a1(s..t) 
void Msort(int a[], int a1[], int s, int t) 
{
    int m,i;
    int a2[MAXSIZE+1] ;
    if(s==t) {
        a1[s]=a[t];
    } else {
        m=(s+t)/2;   //将a[s..t]平分为a[s...m]和a[m+1...t] 
        Msort(a, a2, s, m);   //递归将a[s...m]归并排序为a2[s...m] 
        Msort(a, a2, m+1, t);  //递归将a[s...m]归并排序为a2[m+1...t] 
        Merge(a2, a1, s, m, t);  //将 a2[s...m]与 a2[m+1...t]归并到a1[s...t] 
    }

for(i=s;i<=t;i++){
        a[i]=a1[i];
    }

}
void MergeSort(int a[], int len)
{
    int a1[MAXSIZE];
    Msort(a,a1,0,len-1);
}

/*快速排序  O(nlogn)
 通过一趟排序x选取一个轴值(pivot),将待排序记录分割成独立的两部分,左侧的元素均小于轴值,右侧的元素均大于或等于轴值,
然后分别对这两部分记录继续进行排序,以达到整个序列有序的目的 
**/
//分割求轴值函数,主要用于选取关键字,并将其放置合适的位置,使得它左边的值都比图小,右边的都比它大 ,并返回其所在位置 
int Partition(int a[], int low, int high) 
{
    while(low<high) { //从表的两端交替向中间扫描 
        while(low<high && a[high]>= a[low]){   //从右边开始,向左遍历 
            high--;
        }
        //跳出循环说明low>high或者a[high]小于pivotkey
        //前者说明pivotkey右边的数都比其大,故无需进行交换,后者说明出现比pivotkey小的数,那就需要将其交换到低端 
        swap(a, low, high);
        //对另外一边进行同样的操作 
        while(low<high && a[low]<= a[high]) {
            low ++;
        }
        swap(a ,low, high);
    }
    return low;  //返回轴值所在位置 
}
void Qsort(int a[], int low, int high) 
{
    int pivot; //用于记录轴值的下标 
    if(low<high){
        pivot=Partition(a, low, high);  //将a一分为二,算出轴值的所在位置
        //对轴值左右进行递归 
        Qsort(a, low, pivot-1);
        Qsort(a, pivot+1, high);
    }
}
void QuickSort(int a[], int len)
{
    Qsort(a,0,len-1);

int main()
{
    int a[MAXSIZE];
    int i;
    for(i=0; i<MAXSIZE; i++){
        cin>>a[i];
    }
//    BubbleSort(a,MAXSIZE);
//    SelectSort(a,MAXSIZE);
//    InsertSort(a,MAXSIZE);
//    ShellSort(a,MAXSIZE);
//    QuickSort(a,MAXSIZE);
//    HeapSort(a,MAXSIZE);
    MergeSort(a,MAXSIZE);
    print(a,MAXSIZE);


}

各种算法的复杂度及稳定性统计

参考资料:《大话数据结构》

猜你喜欢

转载自blog.csdn.net/m0_37698185/article/details/81708828