几种常用的排序算法

下面是几种常用的排序算法。插入排序,选择排序,冒泡排序,堆排序,希尔排序,快速排序,归并排序,基数排序。排序问题是对一个现有的队伍,我们希望其中的元素以某种顺序进行排列而产生的算法。我简单的把他们分为2类。

1.基础排序算法

插入排序,选择排序,冒泡排序和希尔排序是基础排序算法,其时间复杂度都是n^2。其中插入和选择排序的过程如下图。

插入排序是假设当前的i已经有序(i从第一个开始),对于下面的元素j,我们把j插入到有序数组里的相应位置上得到的队伍也是有序的。

选择排序是从无序队伍里选择一个最小的排在最前面,继续执行后面的操作。这一过程保证了前面是有序的。

冒泡排序则相对简单粗暴一点,当初我大一的时候凭自己想到的也是这种排序。其结构是两重循环,思想是如果两个数无序(我们称之为无序对),就交换他们的位置,那么最后没有无序对的话,队伍就是有序的了。

希尔排序通过将数据分成不同的组,分组的依据如3的倍数,9的倍数等等。先对每一组进行排序,然后再对所有的元素进行一次插入排序,以减少数据交换和移动的次数。平均效率是O(nlogn)。它是一种不稳定的排序算法。

2.高级排序算法

归并排序是严格的时间复杂度为nlogn的算法。它是递归执行的,每次将现有无序队伍分成两半,递归执行得到有序队列后在进行合并操作。此算法的缺点是空间消耗量很大。


快速排序顾名思义是很快的排序,它运用了分治的思想,每次找一个基准值,把队伍分成比这个值大的和比这个值小的两个队伍,再递归的执行这一操作。最后得到了有序的队伍。这个算法由于卓越的时间和空间复杂度被广泛应用。虽然时间复杂度最坏是n^2,但是极少情况能达到,事实上,快速排序的效率是相当高的。


堆排序是运用二叉堆来实现的排序。在建立这个堆的时候就有了某种特定的顺序,最后调整的时候再不断地进行sink操作最后得到的就是一个有序的队伍了。另外,它是一种不稳定的排序算法。时间复杂度是nlogn。


3.另外

基数排序也是一种排序方法。

package DS;

import java.util.*;

public class AllSort {
	static int min(int a,int b){
		return a>b?b:a;
	}
	static int max(int a,int b){
		return a>b?a:b;
	}
	static void charu(int a[],int n){
	    for(int i=1;i<n;i++){
	        for(int j=i;j>0&&a[j]<a[j-1];j--){
	            int t = a[j];
	            a[j]=a[j-1];
	            a[j-1]=t;
	        }
	    }
	}
	static void maopao(int a[],int n){
	    for(int i=0;i<n;i++)
	        for(int j=0;j<n;j++)
	        if(a[i]>a[j]) {
	        	int t = a[i];
	        	a[i]=a[j];
	        	a[j]=t;
	        }
	}
	static void xuanze(int a[],int n){
	    for(int i=0;i<n;i++)
	    {
	        int mi=1<<31-1,m=i;
	        for(int j=i+1;j<n;j++)
	            if(a[j]<mi) {mi=a[j];m=j;}
	        int t = a[i];
	        a[i]=mi;
	        a[m]=t;
	    }
	}
	static void xir(int a[],int n)		//希尔排序
	{
	    int h=1;
	    while(h<n/3) h=3*h+1;//1,4,13,40,121,364,1093...
	    while(h>=1)
	    {
	        for(int i=h;i<n;i++)
	        {
	            for(int j=i;j>=h&&a[j]<a[j-h];j-=h)
	            {
	            	int t= a[j];
	            	a[j]=a[j-h];
	            	a[j-h]=t;
	            }
	        }
	        h/=3;
	    }
	}
	static int[] b = new int[10000000];
	static void guibing(int a[],int lo,int mid,int hi)
	{
	    int i=lo;
	    int j=mid+1;
	    for(int k=lo;k<=hi;k++)
	        b[k]=a[k];
	    for(int k=lo;k<=hi;k++)
	        if(i>mid) a[k]=b[j++];
	        else if(j>hi) a[k]=b[i++];
	        else if(b[j]<b[i]) a[k]=b[j++];
	        else a[k]=b[i++];
	}
	static void guibingsort(int a[],int lo,int hi)//归并排序化整为零
	{
	    if(hi<=lo) return ;
	    int mid=lo+(hi-lo)/2;
	    guibingsort(a,lo,mid);
	    guibingsort(a,mid+1,hi);
	    guibing(a,lo,mid,hi);
	}
	static void guibingxiadaoshang(int a[],int n)//归并排序归零为整
	{
	    for(int sz=1;sz<n;sz+=sz)
	        for(int lo=0;lo<n-sz;lo+=sz+sz)
	        guibing(a,lo,lo+sz-1,min(lo+sz+sz-1,n-1));
	}
	static int qie(int a[],int lo,int hi){
		int i=lo+1,j=hi;
		int tem = a[lo];
		while(i<=j){
			while(i<=j&&a[j]>tem) j--;
			while(i<=j&&a[i]<tem) i++;
			if(i>=j) break;
			int t=a[i];a[i]=a[j];a[j]=t;
		}
		int t = a[lo];a[lo]=a[j];a[j]=t;
		return j;
	}
	static int qiefen(int a[],int lo,int hi)//快排切分函数
	{
	    int i=lo;int j=hi+1;
	    int v = a[lo];
	    while(true)
	    {	while(v<a[--j]) if(j==lo) break;
	        while(a[++i]<v) if(i==hi) break;
	        if(i>=j) break;
	        int t=a[i];
	        a[i]=a[j];
	        a[j]=t;
	    }
	    int t = a[lo];
	    a[lo]=a[j];
	    a[j]=t;
	    return j;
	}
	static void quicksort(int a[],int lo,int hi)//快排
	{
	    if(hi<=lo) return ;
	    int j=qie(a,lo,hi);
	    quicksort(a,lo,j-1);
	    quicksort(a,j+1,hi);
	}
	static void sink(int a[],int k,int n)//堆排序的sink操作
	{
	    while(2*k<=n)
	    {
	        int j=2*k;
	        if(j<n && a[j]<a[j+1])j++;
	        if(a[k]>=a[j]) break;
	        int t = a[k];
	        a[k]=a[j];
	        a[j]=t;
	        k=j;
	    }
	}
	static void duipaixu(int a[],int n)//堆排序
	{
	    for(int k=n/2;k>=1;k--)
	        sink(a,k,n);		//初始化堆
	    while(n>1){			//开始搞事
	    	
	    	int t=a[1];
	    	a[1]=a[n];
	    	a[n--]=t;
	        sink(a,1,n);
	    }
	}
	public static void main(String[] args) {
		final int len=10000;
		int[] te = new int[len];
		Random re = new Random();
		for(int i=0;i<len;i++)
			te[i]=re.nextInt();
		double sttim = System.currentTimeMillis();
		xuanze(te,len);
		
		double endt = System.currentTimeMillis();
		/*for(int i=0;i<len;i++){
			System.out.print(te[i]+" ");
		}
		*/
		System.out.println(endt-sttim);
	} 

}
/*	排序结果如下
 		10000	100000	200000	500000	1000000	10000000
 	charu 	48 , 	4000 	16057			384000
 	xuanze 	27  	2000	8238			193571
 	maopao 	47 	4378	19000			491793
	duipa 	7	18	61	71	147	2695
	guibin 	5	14	33	69	117	1608
	quick	2	20	28   	73	115	1206
	xir	4	17	37	90	201	2905
*/

我们用java的Radom方法来生成数组进行测试,结果如上。最上方是测试规模。每个值都是我多次测试取中位数所得。


更新:基数排序

基数排序是O(n)的算法,也可以用n的空间来完成,但是数据局限性很大,一般用于位数较少的数的排序。后缀数组必用。

首先排序个位,在个位有序的基础上再排序十位,在十位和个位有序的情况下排序百位......最后在O(n)的复杂度下完成了排序。

下面的count数组记录的是数位,也就是0到9,的右限界。这就避免了重复赋值。


#include<iostream>
using namespace std;

#define   MAXSIZE   10000
//获取一个数的长度
int length(int a){
    int num = 0;
    while(a){
        a/=10;
        num++;
    }
    return num;
}
//获取数组最长的数的长度
int maxLength(int ar[],int n) {
        int malen = 0;
        for (int i = 0; i < n; i++) {
            int currentLength = length(ar[i]);
            if (malen < currentLength) {
                malen = currentLength;
            }
        }
        return malen;
    }
//获取x右往左从0开始的第d位数字
int getdigit(int x, int d) {
        int a[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
        return ((x / a[d]) % 10);
}

void lsdradix_sort(int arr[],int d,int n)
{
    const int radix = 10;
    int count[radix], i, j;
    int *bucket = new int[n];

    //按照分配标准依次进行排序过程
    for(int k = 1; k <= d; ++k)
    {
        for(i = 0; i < radix; i++)
        {
            count[i] = 0;
        }
        //统计各个桶中所盛数据个数
        for(i = 0; i < n; i++)
        {
           count[getdigit(arr[i], k)]++;
        }
        //count[i]表示第i个桶的右边界索引
        for(i = 1; i < radix; i++)
        {
            count[i] = count[i] + count[i-1];
        }
        for(i = n-1;i >= 0; --i)        //这里要从右向左扫描,保证排序稳定性
        {
            j = getdigit(arr[i], k);
            bucket[count[j]-1] = arr[i];
            /**
            这里要注意的是,count的内容是不会重复的
            因为在上一个循环中就已经规定了各位占多少位数了。
            */
            --count[j];
        }
        for(i = 0,j = 0; i < n; ++i, ++j)
        {
            arr[i] = bucket[j];
        }
    }
    delete [] bucket;
}

int main()
{
    int  br[10] = {789, 80, 90, 589, 998, 965, 852, 123, 456, 20};
    int len = maxLength(br,10);
    lsdradix_sort(br,len,3);
    for(int i=0;i<10;i++)
        cout<<br[i]<<" ";
    cout<<endl;
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章