Java資料結構之 常見排序
一. 氣泡排序
思想:大的沉下去,小的升上來。
這裡寫的是已經優化好的氣泡排序,如果陣列 本身有序,就不需要進行那麼多趟的比較。因此用count來記錄第一趟裡if執行的次數,一次都沒有執行,說明陣列本身有序。public class TestDemo1 { //氣泡排序優化 public static void bubbleBetterSort(int[] array){ int tmp = 0;//交換用於存值 int count = 0;//用於判斷陣列是否有序 for(int i = 0;i <array.length-1;i++){ for(int j = 0;j < array.length-i-1;j++){ if(array[j] > array[j+1]){ tmp = array[j]; array[j] = array[j+1]; array[j+1] = tmp; count++;//如果交換過,count自增 } } if(count == 0)//當前陣列已經有序 break; } System.out.println(Arrays.toString(array)); }
二. 選擇排序
思想:每一次從待排序的資料元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的資料元素排完。
public static void selectSort(int[] array){ int min=0;//找最小值的下標 int tmp;//用於交換 int j; for(int i = 0;i < array.length-1;i++){ min = i; for(j = i+1;j < array.length;j++){//從i+1位置開始找最小值 if(array[j]<array[min]){ min = j; } } //將最小值放到本趟比較的最前面 tmp = array[i]; array[i] = array[min]; array[min] = tmp; } System.out.println(Arrays.toString(array)); }
三. 直接插入排序
思想:假設待排序的資料是陣列A[1….n]。初始時,A[1]自成1個有序區,無序區為A[2….n]。在排序的過程中,依次將A[i] (i=2,3,….,n)從後往前插入到前面已排好序的子陣列A[1,…,i-1]中的適當位置,當所有的A[i] 插入完畢,陣列A中就包含了已排好序的輸出序列。
public class TestDemo3 { public static void insrtSort(int[] array){ int tmp = 0; int j; for(int i = 1;i < array.length;i++){//從i號位值開始 tmp = array[i]; for(j = i-1;j >= 0;j--){//找合適的位置進行插入 if(array[j] > tmp){ array[j+1] = array[j]; } //在迴圈中找到了比tmp小的第一個元素跳出迴圈 else{ break; } } //把tmp賦值到最小值的後面 array[j+1] = tmp; } System.out.println(Arrays.toString(array)); }
四. 希爾排序(shell排序)
思想:是直接插入排序的一種更高效的版本,又稱“縮小增量排序”。希爾排序是把記錄按下標的一定增量分組,組內採用直接插入排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個陣列恰被分成一組,演算法便終止。
增量序列如何選取?
①互為質數 ②最後一個增量為一
一般情況下選取[5,3,1]作為增量序列
接下來以圖來進行希爾排序
原始序列:
①將序列分為5組
如圖,相同顏色的線為同一組。接下來組內進行直接插入排序。
排序後結果:
②:第一次排序後的序列分為三組
排序後:
③最後對這個序列整個進行直接插入排序
排序後結果:
接下來實現shell排序
public static void shell(int[] array,int gap){//gap表示分成幾組
int tmp = 0;//存放要交換的資料元素
for(int i = gap;i < array.length;i++){//第一次要排序的元素是gap,不能寫成i=i+gap
tmp = array[i];
int j = 0;
for(j = i-gap;j >= 0;j -= gap){
if(array[j] > tmp){
array[j+gap] = array[j];
}else{
break;
}
}
array[j+gap] = tmp;
}
}
public static void shellSort(int[] array){
int[] d = {5,3,1};//增量序列
for(int i = 0;i < d.length;i++){
shell(array,d[i]);//每一次分的組進行直接插入排序
}
}
五. 快速排序
思想:①在陣列中找一個數作為基準(我們以零號陣列下標作為第一個基準)
②比基準小的放在它的左邊,比基準大的放在它的右邊
③對左右區域分別重複第二步操作,直至左右區域都只剩下一個元素。
1.快速排序的遞迴實現
原始序列:
①:從high開始往前找,找到第一個比基準21小的,將high指向的4放在low指向的地方。執行後如圖:
②:從low往後找,找到第一個比基準21大的,將low指向的32放在high指向的位置。執行後如圖:
③:high繼續往前找比low小的,執行如圖:
因為high和low相遇,此時將low(或high)位置的數用基準替代。執行後如圖
現在發現基準21左邊都比它小,右邊都比它大。這樣第一趟快速排序就結束了。
之後再分別對左右兩邊用與上述相同的方法排序,就會得到一個有序的序列了。
程式碼實現:
public static int partition(int[] array,int start,int end){//返回基準的下標
int low = start;
int high = end;
int tmp = array[low];
while(low < high){
while(low < high&&array[high] >= tmp){//從後往前找比基準小的後退出迴圈
--high;
}
if(low >= high){//判斷low與high是否相遇
break;
}else{
array[low] = array[high];
}
while(low < high&&array[low] <= tmp){//從前往後找比基準大的退出迴圈
++low;
}
if(low >= high){
break;
}else{
array[high] = array[low];
}
}
array[low] = tmp;
return low;
}
public static void quick(int[] array,int start,int end){
int par = partition(array, start, end);
if(par > start+1){//左半部分在進行快排
quick(array, start, par-1);
}
if(par < end - 1){//右半部分進行快排
quick(array, par+1, end);
}
}
public static void quickSort(int[] array){
int low = 0;
int high = array.length - 1;
quick(array, low, high);
}
2.非遞迴實現
用棧來實現
public static int partition(int[] array,int start,int end){//返回基準的下標
int low = start;
int high = end;
int tmp = array[low];
while(low < high){
while(low < high&&array[high] >= tmp){
--high;
}
if(low >= high){
break;
}else{
array[low] = array[high];
}
while(low < high&&array[low] <= tmp){
++low;
}
if(low >= high){
break;
}else{
array[high] = array[low];
}
}
array[low] = tmp;
return low;
}
public static void quickSort(int[] array){
int[] stack = new int[array.length];
int top = 0;
int low = 0;
int high = array.length-1;
//入棧
int par = partition(array,low,high);
if(par > low+1){
stack[top++] = low;
stack[top++] = par-1;
}
if(par < high-1){
stack[top++] = par+1;
stack[top++] = high;
}
//出棧
while(top > 0){
high = stack[--top];
low = stack[--top];
par = partition(array, low, high);
if(par >low+1){
stack[top++] = low;
stack[top++] = par-1;
}
if(par < high-1){
stack[top++] = par+1;
stack[top++] = high;
}
}
}
以上排序的複雜度與穩定性:
時間複雜度 | 穩定性 | |
氣泡排序 | O(n²) | 穩定 |
選擇排序 | O(n²) | 不穩定 |
直接插入排序 | O(n²)(最好O(n)) | 穩定 |
希爾排序 | O(n^1.3)最壞(O(n²)) | 不穩定 |
快速排序 | O(nlogn)(最壞O(n²)) | 不穩定 |