各种排序算法实现及总结

来源:互联网 发布:希拉里失败原因 知乎 编辑:程序博客网 时间:2024/06/05 21:55

本文主要比较一下各种排序的性能(平均时间复杂度和最差情况)和基本实现。
这个默认按照从小到大排列,输入的数据可以重复,假设输入的数组为A,下标从0到N-1

注意在比较算法复杂度时,我们会关注键值的比较次数和交换次数。

1、冒泡排序
冒泡排序如果不是因为名字比较好记,没有任何优势。它的思路是一趟又一趟的比较数组(或者链表也可以)中相邻的两个元素,如果前一个比后一个大,则交换。这样,每一轮之后,最大的那个元素被“沉”到数组的最后去。

void Bubble_Sort(int A[], int N){    for(int i = 0; i<=N-2; i ++)        for(int j = 0; i<=N-2-i; j++)            if(A[j]>A[j+1])                swap(A+j,A+j+1);}

可以发现,在平均情况和最差情况是,键值的比较次数和交换次数都是O(N^2)

在最好情况下,键值的比较次数还是O(n^2),但是代码经过优化后键值的交换次数为O(N)

public void bubbleSort(int arr[]) {    boolean didSwap;    for(int i = 0, len = arr.length; i < len - 1; i++) {        didSwap = false;        for(int j = 0; j < len - i - 1; j++) {            if(arr[j + 1] < arr[j]) {                swap(arr, j, j + 1);                didSwap = true;            }        }        if(didSwap == false)            return;    }    }

参见http://www.cnblogs.com/melon-h/archive/2012/09/20/2694941.html

同时,冒泡排序还是一个稳定的算法。

2、插入排序
插入排序的思想是对于一个A[i],我们假设它之前的都已经排序好了,这时关键是向前寻找A[i]的位置,j=i-1~0,比较A[i]和A[j],如果A[j]大,则A[j]向后移,直到A[j]小时,就是A[i]的位置。

void Insertion_Sort(int A[], int N){    int tmp,i,j;    for(i = 1; i<N; i++){        tmp = A[i];        //比较i之前的元素和A[i]的大小        for(j = i; j!=0&&A[j-1]>tmp; j--)            A[j] = A[j-1];  //移出空位        A[j] = tmp;    }}

插入排序是对冒泡排序的改进,也是稳定的算法。
最差的情况是逆序(从大到小排列)时间复杂度是O(N^2)

3、希尔排序
以D间隔进行排序,为了每次不止消除一个逆序对。
D=2^k-1(比较好的 D)

void Shell_Sort(int A[], int N){    int tmp,i,j;    for(int D = N/2; D>0; D/=2){//shell增量        for(i = D; i<N; i++){   //插入排序            tmp = A[i];            //比较i之前的元素和A[i]的大小            for(j = i; j!=0&&A[j-D]>tmp; j-=D)                A[j] = A[j-D];  //移出空位            A[j] = tmp;        }    }}

4、选择排序
对当前的下标位置i时,我们要找到i+1到N-1中的最小的下标min_i与i位置的元素交换

void Selection_Sort(int A[], int N){    int i,j,min_i;    for(i = 0; i <N-1; i++){        min_i = i;        //找最小元交换        for(j = i+1; j<N; j++)            if(A[j]<A[min_i])   min_i =j;        swap(A+j,A+min_i);    }}

5、堆排序
首先构建一个最大堆,这样最大堆的第一个元素就是最大的元素,将它与堆的最后一个元素交换后,堆的规模减一再将剩下的堆重新调整为最大堆,重复操作即可。

先考虑堆的建立,有两种方法
方法1:通过插入操作,将N个元素一一插入到一个初始为空的堆,O(nlgN)
这样,堆排序的操作为一直deleteMin,将最小的元素存起来,

void Heap_Sort(int A[], int N){    BuilDHeap(A);   //O(N)    for(i = 0; i<N; i++)        TmpA[i]=DeleteMin(A);   //O(log(N))    for(i =0; i<N; i++)     //O(N)        A[i]=TmpA[i];}

上面的问题是多使用了一个额外的数组来储存。

另外一个种堆排序,

#define LeftChild(i) (2*(i)+1)  //以0为起点的堆void PercDown(int A[], int i, int N){    //从i向左右儿子过滤,建成以i为根的最大堆    int child,parent;    int tmp;    tmp = A[i]; //寻找tmp需要放的位置    for(parent = i; parent*2<=N-1; parent = child){        child = LeftChild(i);        if(child!=N-1&&A[child]<A[child+1])//右儿子较大            child++;        if(tmp>A[child])    break;        else    A[parent]=A[child];    }    A[parent]=tmp;  }void Heap_Sort(int A[], int N){    int i;    for(i=N/2; i>=0; i--)   //build heap            PercDown(A,i,N);    for(i=N-1; i>0;i--){        swap(&A[0],&A[i]);  //DeleteMax        PercDown(A,0,i);    }}

优点,堆排序是一种在位的排序方法,适合数据量非常大的场合。

6、快速排序
在数据量一般的情况下,是性能最好的排序方法。主要的思想是分治,它本身有很多trick,stl里中的sort就是使用快速排序的,它的源码也值得好好研究下。这里主要说下主要的几个trick

1、主元pivot的选择,先使用一个median3的函数,选择出left(0),right(N-1),center三个位置上的中位数,并将中位数放在right-1的位置。(这样只要考虑A[left+1,right-2]事实证明主元的选择对性能有较大影响,这种方式较好。
2、i从left开始,j从right-1开始移动,直到两者相交
3、停止的时候,交换A[i]和A[right-1],递归左右两个子序列
4、当序列的长度小于阈值时,不递归而使用才插入排序 ,这样也能显著调高速度

//Swap two numbers void swap(int *a, int *b){     int tmp;     tmp = *a;     *a = *b;     *b = tmp; } //choose the median of left, center and right int median3(int *A, int left, int right){     int center = (left+right)/2;     if(A[left]>A[center])        swap(A+left,A+center);     if(A[left]>A[right])        swap(A+left,A+right);     if(A[center]>A[right])        swap(A+center,A+right);     //put median in right-1     swap(A+center,A+right-1);    return A[right-1]; } //Insertion sort ---for small size array and index array void Insertion_Sort(int *A, int N){     int p; int i; int tmp;     for(p = 1; p < N; ++p){         tmp = A[p];         for(i = p; i!=0&&A[i-1]>tmp; --i)                A[i]=A[i-1];         A[i] = tmp;     } } void Quicksort(int *A, int left, int right){    if(right-left>10){        int pivot = median3(A, left, right);        int i = left; int j = right-1;        for(;;){            while(A[++i]<pivot){}            while(A[--j]>pivot){}            if(i<j)                swap(A+i,A+j);            else                break;        }        swap(A+i,A+right-1);        Quicksort(A,index_A,left,i-1);        Quicksort(A,index_A,i+1,right);    }    else        Insertion_Sort(A+left,right-left+1); }

具体的性能比较可以参考http://blog.sina.com.cn/s/blog_77795cad01011txt.html

7、位排序
还有一个处理大数据的方法,参见之前的一篇文章
海量数据处理

0 0
原创粉丝点击