算法——選擇排序、插入排序和希爾排序(改進的插入排序)

選擇排序java

首先,找到數組中最小的那個元素,其次,將它和數組的第一個元素交換位置(若是第一個元素就是最小元素那麼它就和本身交換)。再次,在剩下的元素中找到最小的元素,將它與數組的第二個元素交換位置。如此以往,直到 將整個數組排序。算法

性能:算法的時間效率取決於比較的次數。對於長度爲N的數組,選擇排序須要大約N²/2次比較和N次交換。數組

特色:1.運行時間和輸入無關:好比即便輸入一個有序的數列,仍是會和隨機數列同樣,進行比較、交換處理。less

           2.數據移動是最少的:共N次交換,即交換次數和數組大小是線性關係。其餘任何算法都不具有這個特徵,大部分都是線性對數或是平方級別的。函數

函數實現以下:性能

public class Selection {
	public static void sort(Comparable[] a) {
		//將a[]按升序排列
		int N=a.length;
		for(int i=0;i<N;i++) {
			int min=i;
			for(int j=i+1;j<N;j++) {
				if(less(a[j],a[min]))min=j;
			}
			exch(a,i,min);
		}
	}
}

插入排序學習

就像一般人們整理橋牌的方法同樣,一張一張來,將每一張牌插入到其餘已經有序的牌中的適當的位置。spa

在計算機中的實現中,爲了給要插入的元素騰出空間,咱們須要將其他全部元素在插入以前都向右移動一位。這種算法叫作插入排序。code

和選擇排序不一樣的是,插入排序所需的時間取決於輸入中元素的初始順序。例如,對於一個很大的且其中已經有序(或接近有序)的數組進行排序會比對隨機順序的數組或是逆序數組進行排序快的多。排序

性能(長度爲N且主鍵不重複):平均狀況——N²/4次比較和N²/4次交換

                                                    最壞狀況——N²/2次比較和N²/2次交換

                                                    最好狀況——N-1次比較和0次交換

函數實現以下:

public class Insertion {
	public static void sort(Comparable[] a) {
		//將a[]按升序排列
		int N=a.length;
		for(int i=0;i<N;i++) {
			//將a[i]插入到a[i-1],a[i-2],a[i-3]...之中
			for(int j=i;j>0&&less(a[j],a[j-1]);j--)
			exch(a,i,j-1);
		}
	}
}

由於是從頭到尾循環每一個元素,因此當循環到某個元素(設爲a)時,它左邊已是有序數列,右邊是無序數列,只需將a元素依次向右和每一個元素比較,當找到它大於某個元素(設爲b)時,只需將a插入到b以後便可。

部分有序:若是數組中倒置的數量小於數組大小的某個倍數,那麼咱們說這個數組是部分有序的。

插入排序對部分有序的數組頗有效,而選擇排序否則(因速度與輸入無關)。

插入排序的改進:希爾排序

對於大規模亂序數組插入排序慢,由於他只會交換相鄰的元素(見插入排序實現代碼),所以元素只能一點一點地從數組的一段移動到另外一端。例如,若是主鍵最小的元素正好在數組的盡頭,要將它挪到正確的位置就須要N-1次移動。希爾排序爲了加快速度簡單地改進了插入排序,交換不相鄰的元素以對數組的局部進行排序,並最終用插入排序將局部有序的數組排序。

希爾排序的思想是使數組中任意間隔爲h的元素都是有序對的。這樣的數組被稱爲h有序數組

在這裏在實現時,先將h具體化爲一個h序列,如:1,4,13,40,121,364,1093...

實現:咱們先將隨機數列變成364(假設此序列最大h可取364)有序數列,實現數組中間隔爲364的元素都是有序對;再將數列變成121有序數列;……直到h變爲1,實現咱們所求的有序數列。

經過這種方式,對於任意以1爲結尾的h序列,咱們都能將數組排序,這就是希爾排序。

函數實現以下:

public class Shell {
	public static void sort(Comparable[] a) {
		//將a[]按升序排列
		int N=a.length;
		int h=1;
		while(h<N/3)h=3*h+1;//1,4,13,40,121,364,1093...取得適合此序列的最大h值
		while(h>1) {//每次循環都將數組變爲h有序數組
			for(int i=h;i<N;i++) {
				//將a[i]插入到a[i-h],a[i-2*h],a[i-2*h]...之中
				//劃重點!!此處體現了插入排序,希爾排序只是將插入排序移動增量1變爲了h
				for(int j=i;j>=h&&less(a[j],a[j-h]);j-=h;)
					exch(a,j,j-h);
			}
			h=h/3;
		}
	}
}

性能:希爾排序的性能很難論證,可是僅針對上述代碼的性能,在最壞的狀況下,它的比較次數和N(3/2)次方成正比。由插入排序到希爾排序,一個小小的改變,就突破了平方級別的運行屏障。

對於中等大小的數組希爾排序的運行時間是能夠接受的,它的代碼量很小,且不須要使用額外的內存空間。以後咱們學習的更加高效的算法,但除了對於很大的N,他們可能只會比希爾排序快兩倍,並且更復雜。

相關文章
相關標籤/搜索