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

来源:互联网 发布:比特彗星mac版本 编辑:程序博客网 时间:2024/04/27 23:39

冒泡排序

冒泡排序是大多数人学到的第一个排序,其基本思想把最小的元素像冒泡一样浮到顶端,时间复杂度为O(n^2)

//BubleSort//Each iteration, the least element buble up.void BubleSort(int *P, int n){    for(int i=0;i<n;i++){        for(int j=n-1; j>i; j--){            if(P[j] < P[j-1])                swap(P[j],P[j-1]);        }    }}

插入排序

插入排序是一个比较方便的实现的排序算法,它的排序思想就是像整理扑克牌一样,从左到右选择每次选择一个扑克牌,然后插入到其右边相应的位置,时间复杂度为O(n^2)

//insertion sort//Form the left to right, in each iteration, insert the element in appropriate placevoid InsertionSort(int *P, int n){    for(int i=1;i<n;i++){        for(int j=i;j>0;j--){        if(P[j] < P[j-1])            swap(P[j], P[j-1]);        }    }}

选择排序

选择排序的基本思想是每次再未排序的元素中选择出一个最小的元素,然后放到相应的位置,时间复杂度为O(n^2)

//selection sort//Each iteration, the greatest element in the unsorted part is selected to put them in order.void SelectionSort(int *P, int n){    int curr;    for(int i=0; i<n ;i++){        curr = i;        for(int j=i+1; j<n ;j++){            if(P[curr]>P[j]){                curr = j;            }        }        if(i != curr)        swap(P[i],P[curr]);    }}

Shell排序

Shell sort的思想是,使数组接近排好序的状态。以下代码实现中,调用了特别版本的插入排序来作为shell sort的子程序,其实该插入排序也只是增加了步长参数。Shell排序的时间复杂度为(n^1.5)

//insertion sort for shell sortvoid insert2(int *P, int n, int incr){    for(int i=1;i<n;i+=incr){        for(int j=i;j>0;j-=incr){        if(P[j] < P[j-1])            swap(P[j], P[j-incr]);        }    }}//shell sortvoid shellsort(int *P, int n){    for(int i=n/2; i>2 ;i/=2){        for(int j=0;j<i;j++){            insert2(&P[j], n-j, i);        }    }    insert2(P, n, 1);}

分治法处理排序问题

合并排序

合并排序的基本思想就是二分数组,把两个子数组排好序之后,在合并起来,关键在于合并算法的实现。其时间复杂度为O(nlogn)

//meger sortvoid MergeSort(int *P, int *temp, int begin, int end){    if(end == begin)    return;        int mid = (begin + end)/2;        MergeSort(P, temp, begin, mid);        MergeSort(P, temp, mid+1, end);        //copy the P array        for(int i=begin; i<end+1;i++)            temp[i] = P[i];        //merge the tew sorted part into one sorted array        int i1 = begin;        int i2 = mid+1;        for(int curr=begin;curr<end+1;curr++){            if(i1 == mid+1)                P[curr] = temp[i2++];            else if(i2 > end)                P[curr] = temp[i1++];            else if(temp[i1] < temp[i2])                P[curr] = temp[i1++];            else                 P[curr] = temp[i2++];           }}

快速排序

快速排序是选择数组中的一个元素作为关键元素,然后把小于关键元素的元素放到其左边,相应的大于关键元素的放到其右边,实践中,快速排序通常能达到很好的效果。其中快速排序的关键元素选择是该算法速度的关键,快速排序的时间复杂度为O(nlogn)。

/quick sortvoid Qsort(int *P, int begin, int end){    if (begin < end){        //The right most element is chosen to be pivot element        int Pivot = P[end];        //put the element less than pivot on the left handside, and the greater element on the other side           int i = begin-1;        for(int j=begin; j<end; j++){            if(P[j] < Pivot){                i = i+1;                swap(P[i],P[j]);            }        }        swap(P[i+1],P[end]);        //Quick sort the left hand sides array of pivot        Qsort(P,begin, i);        //Quick sort the right hand sides array of pivot        Qsort(P,i+2, end);    }}

堆排序

堆排序就是利用堆的性质,每次拿出最小的元素后,重新堆化,再拿出最小的元素。时间复杂度为O(nlogn)
该算法与其他算法最大的不同是,当你想要最小的前某几个元素的时候,不需要进行全部元素的排列。

//最小堆化void Min_Heapify(int *P, int i, int n){    int lc = i*2;    int rc = i*2+1;    int min;    if(lc < n && P[lc]<P[i])         min = lc;    else        min = i;    if(rc < n && P[rc]<P[min])        min = rc;    if( i != min){        swap(P[min], P[i]);        Min_Heapify(P, min, n);    }}//建最小堆void BuildHeap(int *P, int n){    for(int i=n/2; i>=0; i--){        Min_Heapify(P, i, n);    }}//heapsortvoid HeapSort(int *P, int n){    BuildHeap(P,n);    int heapsize = n;    for(int i=n;i>=2;i--){        swap(P[0], P[i-1]);        Min_Heapify(P,0,--heapsize);    }}

非比较排序

前面所有的排序都是基于比较来产生顺序的,已经可以证明比较排序的最快只能达到O(nlogn),然而通过牺牲空间,用非比较排序的方法可以得到线性时间复杂度的排序,即O(n)

桶排序

桶排序的思想很简单,就是把每个元素放进对应它自身的一个桶中,序列自然就出来了。

//binsortvoid BinSort(int *P, int n){    const int size = 10000;    int bin[1000];    for(int i=0;i<n;i++){        bin[P[i]] = 1;     }    int j = 0;    for(int i=0;i<1000;i++){        if(bin[i] == 1)            P[j++] = i;        if(j == n)            break;    }}

计数排序

顾名思义,计数排序就是数出每个元素有多少个,然后计算出小于和等于每个元素的元素有多少个,那该元素的位置不就可以知道了吗
例如 序列:1 5 3 2 2 4,小于等于2的元素有 1 2 2共三个,那我们自然可以知道从左边数起的第二个2在3号位置。

//countsortvoid CountSort(int *P, int n){    int C[10];    int B[10];    for(int i=0;i<10;i++){        C[i] = 0;        B[i] = 0;    }    //memset(B,0,sizeof(B));    //memset(C,0,sizeof(C));    for(int i=0;i<n;i++){        C[P[i]] += 1;    }    for(int i=1;i<n;i++){        C[i] += C[i-1];    }    for(int i=n-1;i>=0;i--){        B[C[P[i]]-1] = P[i];        C[P[i]]--;      }    for(int i=0;i<n;i++){        P[i] = B[i];    }}

基数排序

基数排序就是利用稳定排序方法(如计数排序,后面会解释为什么叫稳定),按元素的第1位数字,第2位数字……分别进行排序

void CountForRadix(int *P, int *B, int *radix, int n){    int C[10];    for(int i=0;i<10;i++){        C[i] = 0;    }    for(int i=0;i<n;i++){        C[radix[i]] += 1;    }    for(int i=1;i<n;i++){        C[i] += C[i-1];    }    for(int i=n-1;i>=0;i--){        B[C[radix[i]]-1] = P[i];        C[radix[i]]--;      }       }//radixsort void RadixSort(int *P, int K, int n){    int radix[10];    int B[100];    for(int i=0;i<100;i++){        B[i] = 0;     }    for(int i=0;i<10;i++){        radix[i] = 0;     }    //memset(Bucket, -1, sizeof(Bucket))    for(int i=0;i<K;i++){        for(int j=0;j<n;j++){            radix[j] = (P[j]/pow(10,i))%10;         }        CountForRadix(P,B,radix,n);        for(int i=0;i<n;i++){        P[i] = B[i];        }    } }

关于稳定排序

稳定排序算法就是排序后,不改变原数组中相同大小元素的相对位置

在以上的非比较排序中,通常需要一些额外的数组来保存数据,这是一种时间和空间上的权衡,所以非比较排序的一个缺点是不但要用更多的空间,而且还要预估所需空间的大小,以上非比较排序算法的数组大小可以根据需要恰当地改变

排序中用到的一些辅助函数

//幂函数int pow(int a, int P){    int pro = 1;    if(P == 0)        return 1;    for(int i=0;i<P;i++){        pro = pro*a;    }    return pro;}//交换数组中的两个元素void swap(int &a, int &b ){    int c = a;    a = b;    b = c;}