码迷,mamicode.com
首页 > 编程语言 > 详细

各种排序算法实现及总结

时间:2015-08-21 23:29:27      阅读:268      评论:0      收藏:0      [点我收藏+]

标签:

本文主要比较一下各种排序的性能(平均时间复杂度和最差情况)和基本实现。
这个默认按照从小到大排列,输入的数据可以重复,假设输入的数组为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、位排序
还有一个处理大数据的方法,参见之前的一篇文章
海量数据处理

版权声明:本文为博主原创文章,未经博主允许不得转载。

各种排序算法实现及总结

标签:

原文地址:http://blog.csdn.net/whzyb1991/article/details/47842351

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!