排序的基本概念
插入排序
直接插入排序
- 從空間分析:空間復雜度 o (1),只要一個元素的輔助空間用于位置的交換,也稱原地排序算法。
- 從時間分析:時間復雜度 o(n^2),對隨機順序的數據來說,移動和比較的次數接近最壞情況。
- 由于直接插入算法的元素移動是順序的,該排序算法是穩定的
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > temp) {
arr[j + 1] = arr[j];
if (j == 0) {
arr[0] = temp;
}
} else {
arr[j + 1] = temp;
break;
}
}
}
折半插入排序
前提條件:有序
- 過程:利用二分查找法來確定要插入的位置,將該位置及以后的位置依次向后移動一位,然后將要插入的數插入該位置
public void binaryInsertionSort(int[] arr, int n) {
for (int i = 1; i < n; i++) {
int temp = arr[i];
int left = 0, right = i - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] > temp) {
right = mid - 1;
} else if (arr[mid] < temp) {
left = mid + 1;
}
}
for (int j = i - 1; j >= left; j--) {
arr[j + 1] = arr[j];
}
arr[left] = temp;
}
}
希爾排序
- 會跳著排,不穩定、時間復雜度在 nlogn 和 n^2 之間,所以定位很尷尬,算法本身比較復雜,但在 nlogn級別 時間復雜度大行其道的高級排序算法根本沒存在的意義。。。不做過深研究
交換排序
冒泡排序
- 只進行元素間的順序移動,所以是一個穩定的排序算法
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]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
快速排序
快速排序是名副其實的,因為在實際應用中,它幾乎是最快的排序算法,被評為20世紀十大算法之一。
快排在實踐中有兩種常用的分割策略:
- 第一種分割策略
- 首先用變量備份軸元素
- 取兩個指針 left 和 right,它們的初始值分別指向序列最左邊的下標,序列最右邊的下標。當left 小于 right時
- 從right所指的位置向左搜索,找到第一個小于等于軸的元素,把這個元素移動到left的位置,再從left所指的位置開始向右搜索,找到第一個大于軸的元素,把它移動到right所指的位置。
- 重復這個過程,直到left等于right,最后把軸元素放在left所指的位置。
public int partition1(int[] arr, int left, int right) {
int pivot = arr[left];
while (left < right) {
while (left < right && arr[right] > pivot) {
right--;
}
arr[left] = arr[right];
while (left < right && arr[left] <= pivot) {
left++;
}
arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}
- 第二種分割策略
- 首先用變量備份軸元素
- 取兩個指針 left 和 right,它們的初始值分別指向序列左邊第二個元素下標,序列最右邊的下標。當left不大于right時
- 向右移動left,使其停在第一個大于軸元素的元素位置,同時向左移動right,使其停在第一個不大于軸元素的位置,然后交換left和right位置的元素,然后繼續移動left、right,交換相應的元素
- 重復這個過程,直至left大于right。
public int partition2(int[] arr, int start, int end) {
int pivot = arr[start];
int left = start, right = end;
while (left <= right) {
while (left <= right && arr[left] <= pivot)
left++;
while (left <= right && arr[right] > pivot)
right--;
if (left < right) {
swap(arr[left], arr[right]);
left++;
right--;
}
swap(arr[start], arr[right]);
return right;
}
}
快排就是遞歸調用上述分割策略來不斷劃分子序列從而完成排序
public void quickSort(int[] arr, int left, int right) {
int p = partition1(arr, left, right);
quickSort(arr, left, p - 1);
quickSort(arr, p + 1, right);
}
快排空間的開銷主要是遞歸調用時所使用的棧,因此快排空間開銷和遞歸調用的棧的深度成正比,故最好的空間復雜度為 logn,最壞的空間復雜度為 n
選擇排序
簡單選擇排序
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[i]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}