一、插入排序
1. 核心思想
共進行 N - 1 趟排序,對於 P = 1 到 P = N - 1 趟,保證從位置0到位置P上的元素爲排序狀態。在第P趟,將位置P上的元素移動到前P + 1個元素的正確位置上,比其更大的元素都向右移動一個位置。
2. 分析
如果輸入數據已預先排序,則時間複雜度爲O(N);平均時間複雜度和最壞時間複雜度均爲O(N^2),空間複雜度爲O(1)。插入排序只用在小的或是非常接近排好序的輸入數據上。
3. 代碼實現
public void InsertSort(int[] arr) {
int tmp, p, i;
for(p = 1; p < arr.length; p++) {
tmp = arr[p];
for(i = p; i > 0 && arr[i - 1] > tmp; i--) {
arr[i] = arr[i - 1];
}
arr[i] = tmp;
}
}
二、選擇算法
1. 核心思想
每趟均在數組中找到第k小的元素文章,將其放置k - 1的位置,共進行N - 1趟排序。
2. 分析
無論什麼樣本數據,時間複雜度均爲O(N^2),空間複雜度爲O(1),不適合大量數據的排序。
3. 代碼實現
public void selectionSort(int[] arr) {
for(int i = 0; i < arr.length - 1; i++) {
int min = i;
for(int j = i + 1; j < arr.length; j++) {
if(arr[min] > arr[j]) {
min = j;
}
}
swap(arr, min, i);
}
}
三、冒泡算法
1. 核心思想
每趟排序都對數組中未排序數據進行遍歷,相鄰元素兩兩比較,如果順序與預先規定的順序不一致,則交換,每次遍歷的結果都會使最大元素上浮到頂端。之後再重複操作直至數組有序,共進行N - 1趟排序。
2. 分析
當輸入數據爲正序時,時間複雜度最小,爲O(N),當輸入數據爲反序時,時間複雜度最大,爲O(N^2),平均時間複雜度爲O(N^2)。當數據量很大時,冒泡排序效率不高。
3. 代碼實現
public void bubbleSort(int[] arr) {
for(int i = 0; i < arr.length - 1; i++) {
for(int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
四、希爾排序(縮小增量排序)
1. 核心思想
希爾排序使用一個增量序列h1,h2,...,ht,在使用增量hk的一趟排序後,對於每個i都有arr[i] <= A[i + hk],所有相隔hk的元素都被排序。
2. 分析
希爾排序的時間複雜度與增量序列的選取有關。使用希爾增量時希爾排序的時間複雜度的最壞時間複雜度爲O(N^2),最好時間複雜度爲O(NlogN)。
3. 代碼實現
public void shellSort(int[] arr) {
int n = arr.length;
for(int increment = n / 2; increment > 0; increment /= 2) {
for(int i = increment; i < n; i++) {
int tmp = arr[i], j;
for(j = i; j >= increment && arr[j] < arr[j - increment]; j -= increment) {
arr[j] = arr[j - increment];
}
arr[j] = tmp;
}
}
五、堆排序
1. 核心思想
將無序數組構造成一個大頂堆(排序後可獲得升序數組),完成後將堆頂元素與末尾元素交換,此時末尾爲最大值。然後繼續調整堆,再次將堆頂與末尾元素互換,此時可得到第二大元素,如此反覆進行交換、重建。
2. 分析
堆排序的最好、最壞、平均時間複雜度均爲O(NlogN)。
3. 代碼實現
public void heapSort(int[] arr) {
int n = arr.length;
for(int i = n / 2; i >= 0; i--) {
percDown(arr, i, n); // 初始化堆
}
for(int i = n - 1; i > 0; i--) {
swap(arr, 0, i);
percDown(arr, 0, i);
}
}
public void percDown(int[] arr, int i, int n) {
int tmp = arr[i], child;
for(; 2 * i + 1 < n; i = child) {
child = 2 * i + 1;
if(child != n - 1 && arr[child] < arr[child + 1]) child++;
if(tmp < arr[child])
arr[i] = arr[child];
else
break;
}
arr[i] = tmp;
}
六、歸併排序
1. 核心思想
該算法是經典的分治策略,它將無序數組分成兩個序列,分別對它們進行排序,最後合併。而拆分的子序列繼續拆分成兩個序列再分別進行排序,直至序列只有一個元素。
2. 分析:
歸併算法的最好、最壞、平均時間複雜度均爲O(NlogN),空間複雜度爲O(N),因此很難用於主存排序,因爲合併兩個排序的表時需要線性附加內存。
3. 代碼實現
public void mergeSort(int[] arr) {
int n = arr.length;
int[] tmp = new int[n];
mergeSort(0, n - 1, arr, tmp);
}
public void mergeSort(int left, int right, int[] arr, int[] tmp) {
if(left < right) {
int mid = (left + right) / 2;
mergeSort(left, mid, arr, tmp);
mergeSort(mid + 1, right, arr, tmp);
merge(left, mid, right, arr, tmp);
}
}
public void merge(int left, int mid, int right, int[] arr, int[] tmp) {
int k = 0, i = left, j = mid + 1;
while(i <= mid && j <= right) {
if(arr[i] <= arr[j])
tmp[k++] = arr[i++];
else
tmp[k++] = arr[j++];
}
while(i <= mid) {
tmp[k++] = arr[i++];
}
while(j <= right) {
tmp[k++] = arr[j++];
}
for(int t = 0; t < k; t++) {
arr[left + t] = tmp[t];
}
}
七、快速排序
1. 核心思想
也是一種分治算法,在無序數組中選取一個元素作爲樞紐元,使得樞紐元左邊的元素均小於它,而右邊的元素均大於它,隨後繼續對兩邊的子序列進行快速排序。
2. 分析
平均時間複雜度爲O(NlogN),最壞時間複雜度爲O(N^2),但這種情況並不多見,空間複雜度爲O(logN)。
3. 代碼實現
public void quickSort(int[] arr) {
int n = arr.length;
quickSort(arr, 0, n - 1);
}
public void quickSort(int[] arr, int left, int right) {
if(left >= right) {
return;
} else {
int pivot = arr[left];
int partition = partition(arr, left, right, pivot);
quickSort(arr, left, partition);
quickSort(arr, partition + 1, right);
}
}
public int partition(int[] arr, int left, int right, int pivot) {
while(left < right) {
while (left < right && arr[right] > pivot) right--;
swap(arr, left, right);
while (left < right && arr[left] < pivot) left++;
swap(arr, left, right);
}
return left;
}
八、總結
排序算法 | 平均時間複雜度 | 最好時間複雜度 | 最壞時間複雜度 | 空間複雜度 | 穩定性 |
插入算法 | O(N^2) | O(N) | O(N^2) | O(1) | 穩定 |
選擇算法 | O(N^2) | O(N^2) | O(N^2) | O(1) | 不穩定 |
冒泡算法 | O(N^2) | O(N) | O(N^2) | O(1) | 穩定 |
希爾排序 | O(NlogN) | O(Nlog^2N) | O(Nlog^2N) | O(1) | 不穩定 |
堆排序 | O(NlogN) | O(NlogN) | O(NlogN) | O(1) | 不穩定 |
歸併排序 | O(NlogN) | O(NlogN) | O(NlogN) | O(N) | 穩定 |
快速排序 | O(NlogN) | O(NlogN) | O(N^2) | O(logN) | 不穩定 |
1. 穩定性即排序前後兩個相等的數其位置不變。其中插入、冒泡、歸併排序均爲穩定排序,選擇、希爾、堆、快速排序均爲不穩定排序。
2. 除歸併、快速排序外,其他排序空間複雜度均爲O(1),其中歸併排序爲O(N),快速排序爲O(logN)。
3. 選擇、堆、歸併排序的時間複雜度在三種情況下均相同,其中選擇排序的均爲O(N^2),堆、歸併排序的均爲O(NlogN)。