八大基礎排序
1. 氣泡排序(Bubble Sort)
基本思想:依次比較相鄰的兩個元素,若它們的順序錯誤則進行交換。
特點:穩定排序,但效率較低,時間複雜度為O(n^2),空間複雜度為O(1)。
程式碼例項
public class BubbleSortExample {
public static void main(String[] args) {
// 示例陣列
int[] arr = {64, 34, 25, 12, 22, 11, 90};
// 使用氣泡排序對陣列進行排序
bubbleSort(arr);
// 輸出排序後的陣列
for (int num : arr) {
System.out.print(num + " ");
}
}
// 氣泡排序方法
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
boolean swapped = false; // 標記位,表示這一趟是否有交換
for (int j = 0; j < n - i - 1; j++) {
// 交換如果元素找到比下一個元素大
if (arr[j] > arr[j + 1]) {
// 交換元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
// 如果有交換,則標記為true
swapped = true;
}
}
// 如果沒有交換髮生,說明陣列已經有序
if (!swapped) {
break;
}
}
}
}
2. 選擇排序(Selection Sort)
基本思想:在未排序序列中找到最小(或最大)元素,存放到排序序列的起始位置,然後再從剩餘未排序元素中繼續尋找最小(或最大)元素,然後放到已排序序列的末尾。
特點:不穩定排序,時間複雜度為O(n^2),空間複雜度為O(1)。
例項程式碼
public class SelectionSortExample {
public static void main(String[] args) {
// 示例陣列
int[] arr = {64, 34, 25, 12, 22, 11, 90};
// 使用選擇排序對陣列進行排序
selectionSort(arr);
// 輸出排序後的陣列
for (int num : arr) {
System.out.print(num + " ");
}
}
// 選擇排序方法
public static void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
// 找到剩餘部分中的最小元素
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 將找到的最小元素與第一個未排序的元素交換
if (minIndex != i) {
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
}
}
3. 插入排序(Insertion Sort)
基本思想:將待排序的資料分成已排序和未排序兩部分,每次將一個元素插入到已排序部分的正確位置。
特點:穩定排序,時間複雜度為O(n^2),空間複雜度為O(1)。
例項程式碼
public class InsertionSortExample {
public static void main(String[] args) {
// 示例陣列
int[] arr = {64, 34, 25, 12, 22, 11, 90};
// 使用插入排序對陣列進行排序
insertionSort(arr);
// 輸出排序後的陣列
for (int num : arr) {
System.out.print(num + " ");
}
}
// 插入排序方法
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i]; // 要插入的元素
int j = i - 1;
// 將比key大的元素向後移動
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key; // 將key插入到正確的位置
}
}
}
4. 快速排序(Quick Sort)
基本思想:透過一趟排序將待排記錄分割為獨立的兩個部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,然後對這兩部分記錄繼續進行排序,以達到整個序列有序。
特點:不穩定排序,但效率較高,時間複雜度為O(nlogn),空間複雜度為O(logn)。
示例程式碼
public class QuickSortExample {
public static void main(String[] args) {
// 示例陣列
int[] arr = {64, 34, 25, 12, 22, 11, 90};
// 使用快速排序對陣列進行排序
quickSort(arr, 0, arr.length - 1);
// 輸出排序後的陣列
for (int num : arr) {
System.out.print(num + " ");
}
}
// 快速排序方法
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// pi是分割槽索引,arr[p]現在已經到位
int pi = partition(arr, low, high);
// 分別對分割槽前後部分進行排序
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
// 分割槽操作
public static int partition(int[] arr, int low, int high) {
int pivot = arr[high]; // 選擇最右邊的元素作為軸點
int i = (low - 1); // 最小元素索引
for (int j = low; j <= high - 1; j++) {
// 如果當前元素小於或等於軸點
if (arr[j] <= pivot) {
i++; // 遞增索引
// 交換 arr[i] 和 arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 將軸點元素放到最終的位置
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
}
5. 歸併排序(Merge Sort)
基本思想:將兩個或兩個以上的有序表合併成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的,然後再把有序子序列合併為整體有序序列。
特點:穩定排序,時間複雜度為O(nlogn),但空間複雜度較高,為O(n)。
public class MergeSortExample {
public static void main(String[] args) {
// 示例陣列
int[] arr = {64, 34, 25, 12, 22, 11, 90};
// 使用歸併排序對陣列進行排序
arr = mergeSort(arr, 0, arr.length - 1);
// 輸出排序後的陣列
for (int num : arr) {
System.out.print(num + " ");
}
}
// 歸併排序方法
public static int[] mergeSort(int[] arr, int left, int right) {
if (left < right) {
// 找到中間索引
int mid = left + (right - left) / 2;
// 對左半部分進行歸併排序
arr = mergeSort(arr, left, mid);
// 對右半部分進行歸併排序
arr = mergeSort(arr, mid + 1, right);
// 合併兩個已排序的部分
merge(arr, left, mid, right);
}
return arr;
}
// 合併兩個已排序的部分
public static void merge(int[] arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
// 建立臨時陣列
int[] L = new int[n1];
int[] R = new int[n2];
// 複製資料到臨時陣列
for (int i = 0; i < n1; ++i)
L[i] = arr[left + i];
for (int j = 0; j < n2; ++j)
R[j] = arr[mid + 1 + j];
// 合併臨時陣列到arr[left..right]
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
// 複製L[]的剩餘元素
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
// 複製R[]的剩餘元素
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
}
6. 堆排序(Heap Sort)
基本思想:利用堆這種資料結構的特性,首先將待排序的資料構建成一個最大(或最小)堆,然後將堆頂元素與最後一個元素交換,並重新調整堆,這樣就得到了最大(或最小)值。重複這個過程直到所有資料元素有序。
特點:不穩定排序,但效率較高,時間複雜度為O(nlogn),空間複雜度為O(1)。
public class HeapSortExample {
public static void main(String[] args) {
// 示例陣列
int[] arr = {64, 34, 25, 12, 22, 11, 90};
// 使用堆排序對陣列進行排序
heapSort(arr);
// 輸出排序後的陣列
for (int num : arr) {
System.out.print(num + " ");
}
}
// 堆排序方法
public static void heapSort(int[] arr) {
int n = arr.length;
// 構建最大堆
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// 一個個從堆頂取出元素
for (int i = n - 1; i >= 0; i--) {
// 將當前最大的元素arr[0]和arr[i]交換
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 重新對剩下的元素進行堆化
heapify(arr, i, 0);
}
}
// 堆化方法
public static void heapify(int[] arr, int n, int i) {
int largest = i; // 初始化最大值為根
int left = 2 * i + 1; // 左子節點
int right = 2 * i + 2; // 右子節點
// 如果左子節點比根大
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
// 如果右子節點比當前最大的還大
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
// 如果最大元素不是根
if (largest != i) {
// 交換
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// 遞迴堆化受影響的子樹
heapify(arr, n, largest);
}
}
}
7. 希爾排序(Shell Sort)
基本思想:先將整個待排記錄序列分割為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。
例項程式碼
public class ShellSortExample {
public static void main(String[] args) {
// 示例陣列
int[] arr = {64, 34, 25, 12, 22, 11, 90};
// 使用希爾排序對陣列進行排序
shellSort(arr);
// 輸出排序後的陣列
for (int num : arr) {
System.out.print(num + " ");
}
}
// 希爾排序方法
public static void shellSort(int[] arr) {
int n = arr.length;
int gap = n / 2; // 初始間隔
// 動態定義間隔序列
while (gap > 0) {
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j;
// 對每個子序列進行插入排序
for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
arr[j] = arr[j - gap];
}
arr[j] = temp;
}
// 更新間隔
gap /= 2;
}
}
}
特點:不穩定排序,是插入排序的一種更高效的改進版本,時間複雜度依賴於增量序列的選擇。
8. 基數排序(Radix Sort)
基本思想:將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。然後,從最低位開始,依次進行一次排序。
特點:穩定排序,適用於整數排序,時間複雜度取決於整數的位數和採用的基數。
示例程式碼
public class RadixSortExample {
public static void main(String[] args) {
// 示例陣列
int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};
// 使用基數排序對陣列進行排序
radixSort(arr);
// 輸出排序後的陣列
for (int num : arr) {
System.out.print(num + " ");
}
}
// 基數排序方法
public static void radixSort(int[] arr) {
// 找到陣列中的最大數
int max = findMax(arr);
// 獲取最大數的位數
int maxDigit = getMaxDigit(max);
// 初始化臨時陣列
int[][] temp = new int[10][arr.length];
int[] count = new int[10]; // 用於計數
// 對每一位進行排序
for (int i = 1; i <= maxDigit; i++) {
// 將陣列元素分配到臨時陣列
for (int j = 0; j < arr.length; j++) {
int digit = getDigit(arr[j], i);
temp[digit][count[digit]] = arr[j];
count[digit]++;
}
// 將臨時陣列中的元素複製回原陣列
int index = 0;
for (int k = 0; k < 10; k++) {
if (count[k] != 0) {
for (int l = 0; l < count[k]; l++) {
arr[index++] = temp[k][l];
}
count[k] = 0; // 重置計數
}
}
}
}
// 找到陣列中的最大數
private static int findMax(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
// 獲取最大數的位數
private static int getMaxDigit(int num) {
int count = 0;
while (num != 0) {
num /= 10;
count++;
}
return count;
}
// 獲取一個數的第i位(從最低位開始計數)
private static int getDigit(int num, int i) {
return (num / (int) Math.pow(10, i - 1)) % 10;
}
}