幾種常用的排序算法

下面是幾種常用的排序算法。插入排序,選擇排序,冒泡排序,堆排序,希爾排序,快速排序,歸併排序,基數排序。排序問題是對一個現有的隊伍,我們希望其中的元素以某種順序進行排列而產生的算法。我簡單的把他們分爲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;
}



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