一些說明
我將會寫一系列關於算法的博客,因為我是程序員,並不是計算機科學家,也即我是搞工程的,並不是搞學術的,所以對於我來說,最重要的就是
1.有哪些算法
2.這些算法的原理
3.這些算法的實現
4.這些算法的效率
而其他的,相對而言,並沒有那么重要,比如算法的證明,所以以后的博客都會按照上述的思維撰寫。
一、首先定義一個抽象類,里面集成了排序算法所需要的共同的方法:
public abstract class SortBase { public abstract Integer[] sort(Integer[] a); public static void print(Integer[] arrayForSort) { System.out.print("["); for(int i=0;i<arrayForSort.length;i++) { if(i == arrayForSort.length - 1) { System.out.print(arrayForSort[i]); } else { System.out.print(arrayForSort[i] + " ,"); } } System.out.println("]"); } public static void print(String prefix,Integer[] arrayForSort) { System.out.print(prefix + ": "); System.out.print("["); for(int i=0;i<arrayForSort.length;i++) { if(i == arrayForSort.length - 1) { System.out.print(arrayForSort[i]); } else { System.out.print(arrayForSort[i] + " ,"); } } System.out.println("]"); } }
二、選擇排序:
選擇排序可以說是最簡單的一種排序方法:
1.找到數組中最小的那個元素
2.將最小的這個元素和數組中第一個元素交換位置
3.在剩下的元素中找到最小的的元素,與數組第二個元素交換位置
重復以上步驟,即可以得到有序數組。
代碼如下:
public class SelectionSort extends SortBase { public Integer[] sort(Integer[] a) { print("init",a); Integer minIndex = 0; Integer temp = 0; for(int i=0;i<a.length;i++) { minIndex = i; for(int j=i+1;j<a.length;j++) { if(a[j] < a[minIndex]) { minIndex = j; } } temp = a[i]; a[i] = a[minIndex]; a[minIndex] = temp; print((i+1) + "",a); } return a; } public static void main(String[] args) { Integer[] a = {2,1,5,9,0,6,8,7,3}; print("result",(new SelectionSort()).sort(a)); } }
我在代碼中打出了每次排序的結果,運行結果如下:
init: [2 ,1 ,5 ,9 ,0 ,6 ,8 ,7 ,3] 1: [0 ,1 ,5 ,9 ,2 ,6 ,8 ,7 ,3] 2: [0 ,1 ,5 ,9 ,2 ,6 ,8 ,7 ,3] 3: [0 ,1 ,2 ,9 ,5 ,6 ,8 ,7 ,3] 4: [0 ,1 ,2 ,3 ,5 ,6 ,8 ,7 ,9] 5: [0 ,1 ,2 ,3 ,5 ,6 ,8 ,7 ,9] 6: [0 ,1 ,2 ,3 ,5 ,6 ,8 ,7 ,9] 7: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9] 8: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9] 9: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9] result: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9]
效率:對於長度為N的數組,選擇排序需要大約N²/2次比較和N次交換。也即最好、最差、平均時間效率均為O(n²),只需要一個輔助變量幫助交換元素。
選擇排序可以看成是冒泡排序的擴展,一個是把最小或最大的選出來,再交換,一個是一直交換直到最大最小的出現在正確的位置上,選擇排序相對於冒泡排序,比較次數是一樣的,但是交換次數要少很多。
三、插入排序:
插入排序類似整理撲克牌,將每一張牌插到其他已經有序的牌中適當的位置。
插入排序由N-1趟排序組成,對於P=1到N-1趟,插入排序保證從位置0到位置P上的元素為已排序狀態。
簡單的說,就是插入排序總共需要排序N-1趟,從index為1開始,講該位置上的元素與之前的元素比較,放入合適的位置,這樣循環下來之后,即為有序數組。
代碼實現:
public class InsertionSort extends SortBase { @Override public Integer[] sort(Integer[] a) { // TODO Auto-generated method stub print("init",a); Integer temp = 0; for(int i=1;i<a.length;i++) { //只能從當前索引往前循環,因為索引前的數組皆為有序的,索引只要確定當前索引的數據的為止即可 for(int j=i;j>0 && a[j] < a[j-1];j--) { temp = a[j]; a[j] = a[j-1]; a[j-1] = temp; } print(i +"",a); } print("result",a); return a; } public static void main(String[] args) { Integer[] a = {2,1,5,9,0,6,8,7,3}; (new InsertionSort()).sort(a); } }
運行結果:
init: [2 ,1 ,5 ,9 ,0 ,6 ,8 ,7 ,3] 1: [1 ,2 ,5 ,9 ,0 ,6 ,8 ,7 ,3] 2: [1 ,2 ,5 ,9 ,0 ,6 ,8 ,7 ,3] 3: [1 ,2 ,5 ,9 ,0 ,6 ,8 ,7 ,3] 4: [0 ,1 ,2 ,5 ,9 ,6 ,8 ,7 ,3] 5: [0 ,1 ,2 ,5 ,6 ,9 ,8 ,7 ,3] 6: [0 ,1 ,2 ,5 ,6 ,8 ,9 ,7 ,3] 7: [0 ,1 ,2 ,5 ,6 ,7 ,8 ,9 ,3] 8: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9] result: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9]
效率:如果目標是把n個元素的序列升序排列,那么采用插入排序存在最好情況和最壞情況。最好情況就是,序列已經是升序排列了,在這種情況下,需要進行的比較操作需(n-1)次即可。最壞情況就是,序列是降序排列,那么此時需要進行的比較共有n(n-1)/2次。插入排序的賦值操作是比較操作的次數加上 (n-1)次。平均來說插入排序算法的時間復雜度為O(n^2)
四、希爾排序
把記錄按步長 gap 分組,對每組記錄采用直接插入排序方法進行排序。
隨着步長逐漸減小,所分成的組包含的記錄越來越多,當步長的值減小到 1 時,整個數據合成為一組,構成一組有序記錄,則完成排序。
實現代碼:
public class ShellSort extends SortBase { @Override public Integer[] sort(Integer[] a) { // TODO Auto-generated method stub print("init",a); Integer h = a.length; Integer temp = 0; while(h >= 1) { for(int i=h;i<a.length;i++) { for(int j=i;j>=h && a[j] < a[j-h];j -= h) { temp = a[j]; a[j] = a[j-h]; a[j-h] = temp; } } h /= 9; } print("result",a); return a; } public static void main(String[] args) { Integer[] a = {2,1,5,9,0,6,8,7,3}; (new ShellSort()).sort(a); } }
運行結果:
init: [2 ,1 ,5 ,9 ,0 ,6 ,8 ,7 ,3] 1: [0 ,1 ,5 ,7 ,2 ,6 ,8 ,9 ,3] 2: [0 ,1 ,2 ,6 ,3 ,7 ,5 ,9 ,8] 3: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9] result: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9]
效率:
最壞情況時間復雜度為:O(n^1.5),平均時間復雜度為O(nlogn)。