排序總結
前言:排序的知識從很早以前就學了,但一直記的不是很牢固,所以今天就把主要的排序都做一些整理,以後需要用到的時候可以進行快速回憶。以下的所有討論都是基於非遞減排序的。
冒泡排序
流程:每次都進行相鄰元素的對比,如果前一個數字大於後一個數字,則進行交換,之後對每一對元素都進行相同的操作,經過一輪掃描之後最大的元素就被放置在最後一個元素。然後再從頭進行同樣的流程,只是接下來掃描的截止位置減少了1個。
代碼實現:
public static void bubbleSort(int[] arr) {
for (int i = arr.length - 1; i >= 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
選擇排序
流程:第一次從待排序的數字中選擇最小的元素,與數組的起始位置進行交換,然後再從接下來的待排序數字中繼續尋找最小的元素,與已排序數組的後一個序列進行交換,依此類推。因爲每次都將最小的元素插入到已排序數組的後面,所以最後一定能把數組排好序。
public static void selectionSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
選擇排序是O(n ^ 2)的排序算法中唯一一個不穩定的排序,所以通常是最差的選擇。
插入排序
流程:插入排序每次都將一個數字插入到一個已經排序好的數組裏面,然後繼續遍歷數組,找到每個數字在前面排好序的數組中正確的位置。
public static void insertionSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
注意插入排序的時間複雜度會受到數據狀況的影響,因爲每次插入數據的時候,前面的數組已經排好序了,如果這個數字比前面的所有數字都大,那麼就不需要進行插入操作了。如1 2 3 4 5進行插入排序,時間複雜度只有O(1);如果是5 4 3 2 1的話,每一輪都要進行插入操作,時間複雜度是O(n)。
歸併排序
流程:將一個數組不斷二分,然後把二分的兩部分數組合併爲一個有序數組,這個有序數組可以跟另一對有序數組進行合併,最終就可以使整個數組變成有序數組了。
public class mergeSort {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
mergeSort(arr, 0, arr.length - 1);
}
public static void mergeSort(int[] arr, int left, int right) {
if (left >= right) {
return;
}
int mid = left + ((right - left) >> 1);
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
public static void merge(int[] arr, int left, int mid, int right) {
int[] help = new int[right - left + 1];
int p1 = left;
int p2 = mid + 1;
int i = 0;
while (p1 <= mid && p2 <= right) {
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= mid) {
help[i++] = arr[p1++];
}
while (p2 <= right) {
help[i++] = arr[p2++];
}
for (i = 0; i < help.length; i++) {
arr[left++] = help[i];
}
}
}
堆排序
思路:將數組先建立成一個大根堆,然後每次都將堆頂交換到堆尾,然後將堆的大小減去1,再將新的堆繼續調整爲大根堆,重複以上過程。
public class heapSort {
public static void heapSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
int size = arr.length;
swap(arr, 0, --size);
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
}
}
public static void heapInsert(int[] arr, int i) {
while (arr[i] > arr[(i - 1) / 2]) {
swap(arr, i, (i - 1) / 2);
}
}
public static void heapify(int[] arr, int i, int size) {
int left = i * 2 + 1;
while (left < size) {
int largest = left + 1 < size && arr[left] < arr[left + 1] ? left + 1 : left;
largest = arr[i] < arr[largest] ? largest : i;
if (largest == i) {
break;
}
swap(arr, i, largest);
i = largest;
left = i * 2 + 1;
}
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
快速排序
思路:快速排序是基於分區的過程的,將一個數字作爲分區的基準,經過一輪分區,會把小於該數的數字放到數字的左邊,大於該數的數字放在數字的右邊。然後基準數字就找到自己的位置,再對基準數字的左邊和右邊進行以上的遞歸操作,這樣每一輪分區之後,基準數字都會找到自己的位置,到最後就能把數組進行排序了。
桶排序
常見疑問
1.插入排序和選擇排序有什麼不同?
基於非遞增排序說明,插入排序是每次都跟前面相鄰的數字進行對比,如果小於的話則進行交換,否則進行下一輪插入查找。而選擇排序則是每次都找尋未排序數組中的最小數字去進行交換。