1. 選擇排序(Selection Sort)
選擇出數組中的最小元素,將它與數組的第一個元素交換位置。再從剩下的元素中選擇出最小的元素,將它與數組的第二個元素交換位置。不斷進行這樣的操作,直到將整個數組排序。把0索引的元素,和索引1以後的元素都進行比較,第一次完畢,最小值出現在了0索引。同理,其他的元素就可以排好。
public static void selectSort(int[] arr) {
for(int x=0; x<arr.length-1; x++) {
for(int y=x+1; y<arr.length; y++) {
if(arr[y] < arr[x]) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
}
}
}
算法分析
表現最穩定的排序算法之一,因爲無論什麼數據進去都是O(n2)的時間複雜度,所以用到它的時候,數據規模越小越好。唯一的好處可能就是不佔用額外的內存空間了吧。理論上講,選擇排序可能也是平時排序一般人想到的最多的排序方法了吧。
2. 插入排序(Insertion Sort)
插入排序從左到右進行,每次都將當前元素插入到左側已經排序的數組中,使得插入之後左部數組依然有序。
第 j 元素是通過不斷向左比較並交換來實現插入過程:當第 j 元素小於第 j - 1 元素,就將它們的位置交換,然後令 j 指針向左移動一個位置,不斷進行以上操作。
public static void sort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j >= 0; j--) {
if (arr[j] < arr[j - 1])
swap(arr, j, j - 1); // 大量的交換會消耗時間
else
break;
}
}
}
// 改進版插入排序(減少了數組元素的操作次數)public static void better_sort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int e = arr[i];
int j = i;
for (; j > 0; j--) {
if (e < arr[j - 1])
arr[j] = arr[j - 1];
else
break;
}
arr[j] = e;
}
}
private static void swap(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
算法分析
插入排序在實現上,通常採用in-place排序(即只需用到O(1)的額外空間的排序),因而在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間。
3. 冒泡排序(Bubble Sort)
通過從左到右不斷交換相鄰逆序的相鄰元素,在一輪的交換之後,可以讓未排序的元素上浮到右側。
在一輪循環中,如果沒有發生交換,就說明數組已經是有序的,此時可以直接退出。即相鄰元素兩兩比較,大的往後放,第一次完畢,最大值出現在了最大索引處。同理,其他的元素就可以排好。
private static void sort(int[] arr) {
for (int i = arr.length - 1; i > 0; i--) { // 從最後一位開始確定
boolean swapped = false;
for (int j = 0; j < i; j++) {
if(arr[j] > arr[j+1]){
swapped = true;
swap(arr,j,j+1);
}
}
if(!swapped)
return;
}
}
private static void swap(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
4. 希爾排序(Shell Sort)
1959年Shell發明,第一個突破O(n2)的排序算法,是簡單插入排序的改進版。它與插入排序的不同之處在於,它會優先比較距離較遠的元素。希爾排序又叫縮小增量排序。
算法描述
先將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序,具體算法描述:
- 選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
- 按增量序列個數k,對序列進行k 趟排序;
- 每趟排序,根據對應的增量ti,將待排序列分割成若干長度爲m 的子序列,分別對各子表進行直接插入排序。僅增量因子爲1 時,整個序列作爲一個表來處理,表長度即爲整個序列的長度。
代碼實現
// 希爾排序public static void sort(int[] arr) {
int n = arr.length;
for (int h = n / 2; h > 0; h = h / 2) {
// 內部是一個插入排序
for (int i = 0; i < n; i = i + h) {
int e = arr[i];
int j = i;
for (; j > 0; j = j - h) {
if (e < arr[j - h])
arr[j] = arr[j - h];
else
break;
}
arr[j] = e;
}
}
}
// 希爾排序2public static void sort2(int[] arr) {
int n = arr.length;
// 計算 increment sequence: 1, 4, 13, 40, 121, 364, 1093...
int h = 1;
while (h < n / 3) h = 3 * h + 1;
System.out.println(h);
while (h >= 1) {
// h-sort the array
for (int i = h; i < n; i++) {
// 對 arr[i], arr[i-h], arr[i-2*h], arr[i-3*h]... 使用插入排序
int e = arr[i];
int j = i;
for (; j >= h && e < arr[j - h]; j -= h)
arr[j] = arr[j - h];
arr[j] = e;
}
h /= 3;
}
}
算法分析
對於大規模的數組,插入排序很慢,因爲它只能交換相鄰的元素,每次只能將逆序數量減少 1。
希爾排序的出現就是爲了改進插入排序的這種侷限性,它通過交換不相鄰的元素,每次可以將逆序數量減少大於 1。
希爾排序使用插入排序對間隔 h 的序列進行排序。通過不斷減小 h,最後令 h=1,就可以使得整個數組是有序的。