算法说明:
- 冒泡排序:重复比较相邻元素,将较大的元素向后移动
- 选择排序:每次选择最小元素放到已排序序列末尾
- 插入排序:将元素插入到已排序序列的正确位置
- 希尔排序:改进的插入排序,通过分组减少移动次数
- 归并排序:分治法,递归排序子数组后合并
- 快速排序:选择枢轴元素,将数组分为两部分递归排序
- 堆排序:利用堆数据结构进行选择排序
- 桶排序:将元素分配到多个桶中,分别排序后合并
代码案例:
java
import java.util.Arrays;
public class SortingAlgorithms {
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组: " + Arrays.toString(arr));
// 测试不同排序算法(每次测试前复制数组)
testSort("冒泡排序", SortingAlgorithms::bubbleSort, arr.clone());
testSort("选择排序", SortingAlgorithms::selectionSort, arr.clone());
testSort("插入排序", SortingAlgorithms::insertionSort, arr.clone());
testSort("希尔排序", SortingAlgorithms::shellSort, arr.clone());
testSort("归并排序", SortingAlgorithms::mergeSort, arr.clone());
testSort("快速排序", SortingAlgorithms::quickSort, arr.clone());
testSort("堆排序 ", SortingAlgorithms::heapSort, arr.clone());
testSort("桶排序 ", SortingAlgorithms::bucketSort, arr.clone());
}
private static void testSort(String name, RunnableSorter sorter, int[] arr) {
long startTime = System.nanoTime();
sorter.run(arr);
long endTime = System.nanoTime();
System.out.printf("%s: %s (耗时: %.3f ms)%n",
name,
Arrays.toString(arr),
(endTime - startTime) / 1_000_000.0);
}
@FunctionalInterface
interface RunnableSorter {
void run(int[] arr);
}
// 八大排序实现方法
// 1. 冒泡排序:重复比较相邻元素,将较大的元素向后移动
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;
swapped = true;
}
}
// 如果没有发生交换,说明已经有序
if (!swapped) break;
}
}
// 2. 选择排序:每次选择最小元素放到已排序序列末尾
public static void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIdx]) {
minIdx = j;
}
}
// 将最小元素交换到当前位置
int temp = arr[minIdx];
arr[minIdx] = arr[i];
arr[i] = temp;
}
}
// 3. 插入排序:将元素插入到已排序序列的正确位置
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--;
}
arr[j + 1] = key;
}
}
// 4. 希尔排序:改进的插入排序,通过分组减少移动次数
public static void shellSort(int[] arr) {
int n = arr.length;
// 初始间隔为数组长度的一半
for (int gap = n / 2; gap > 0; gap /= 2) {
// 对各个间隔分组进行插入排序
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;
}
}
}
// 5. 归并排序:分治法,递归排序子数组后合并
public static void mergeSort(int[] arr) {
if (arr.length > 1) {
int mid = arr.length / 2;
int[] left = Arrays.copyOfRange(arr, 0, mid);
int[] right = Arrays.copyOfRange(arr, mid, arr.length);
mergeSort(left);
mergeSort(right);
// 合并两个有序数组
int i = 0, j = 0, k = 0;
while (i < left.length && j < right.length) {
if (left[i] < right[j]) {
arr[k++] = left[i++];
} else {
arr[k++] = right[j++];
}
}
// 处理剩余元素
while (i < left.length) arr[k++] = left[i++];
while (j < right.length) arr[k++] = right[j++];
}
}
// 6. 快速排序:选择枢轴元素,将数组分为两部分递归排序
public static void quickSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
}
private static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
// 交换元素
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;
}
// 7. 堆排序:利用堆数据结构进行选择排序
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--) {
// 将当前根节点(最大值)移到数组末尾
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 对剩余元素重新堆化
heapify(arr, i, 0);
}
}
private 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);
}
}
// 8. 桶排序:将元素分配到多个桶中,分别排序后合并
// 桶排序 (假设元素范围在0-100)
public static void bucketSort(int[] arr) {
if (arr.length == 0) return;
// 确定最大值
int maxVal = Arrays.stream(arr).max().getAsInt();
// 创建桶
int bucketCount = maxVal / 10 + 1;
int[][] buckets = new int[bucketCount][0];
// 分配元素到桶中
for (int num : arr) {
int bucketIndex = num / 10;
buckets[bucketIndex] = Arrays.copyOf(buckets[bucketIndex], buckets[bucketIndex].length + 1);
buckets[bucketIndex][buckets[bucketIndex].length - 1] = num;
}
// 对每个桶排序(这里使用插入排序)
int index = 0;
for (int[] bucket : buckets) {
if (bucket.length > 0) {
insertionSort(bucket); // 可以使用任意排序算法
for (int num : bucket) {
arr[index++] = num;
}
}
}
}
}
注意:桶排序的性能取决于数据分布和桶的数量,代码中假设数据范围在0-100,实际使用时需要根据数据范围调整桶的数量。
时间复杂度对比:
算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|
冒泡 | O(n²) | O(n²) | O(1) | 稳定 |
选择 | O(n²) | O(n²) | O(1) | 不稳定 |
插入 | O(n²) | O(n²) | O(1) | 稳定 |
希尔 | O(n log n) | O(n²) | O(1) | 不稳定 |
归并 | O(n log n) | O(n log n) | O(n) | 稳定 |
快速 | O(n log n) | O(n²) | O(log n) | 不稳定 |
堆 | O(n log n) | O(n log n) | O(1) | 不稳定 |
桶 | O(n+k) | O(n²) | O(n+k) | 稳定 |