题目:排序算法
1. 冒泡排序 (Bubble Sort)
解题思路:
- 依次比较相邻的元素,将较大的元素交换至右侧。
- 重复这个过程,直到整个数组有序。
实现代码:
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// Swap arr[j] and arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
优缺点:
- 优点:
- 实现简单,代码短小。
- 缺点:
- 效率较低,时间复杂度为O(n^2)。
2. 选择排序 (Selection Sort)
解题思路:
- 从数组中选择最小元素,与第一个元素交换位置。
- 从剩余未排序的部分中选择最小元素,与第二个元素交换位置,依此类推。
实现代码:
public class SelectionSort {
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;
}
}
}
优缺点:
- 优点:
- 实现简单,不占用额外空间。
- 缺点:
- 效率较低,时间复杂度为O(n^2)。
3. 插入排序 (Insertion Sort)
解题思路:
- 将数组分为已排序和未排序两部分,逐步将未排序的元素插入到已排序的部分。
实现代码:
public class InsertionSort {
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;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
}
优缺点:
- 优点:
- 实现简单,对小规模数据或接近有序的数据排序效率高。
- 缺点:
- 效率较低,时间复杂度为O(n^2)。
4. 归并排序 (Merge Sort)
解题思路:
- 将数组递归分成子数组,然后合并这些子数组,合并过程中保持有序。
实现代码:
public class MergeSort {
public static void merge(int[] arr, int left, int middle, int right) {
int n1 = middle - left + 1;
int n2 = right - middle;
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[middle + 1 + j];
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++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
public static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int middle = left + (right - left) / 2;
mergeSort(arr, left, middle);
mergeSort(arr, middle + 1, right);
merge(arr, left, middle, right);
}
}
}
优缺点:
- 优点:
- 稳定,时间复杂度为O(n log n)。
- 缺点:
- 需要额外的内存空间。
5. 快速排序 (Quick Sort)
解题思路:
- 选择一个基准元素,将数组分为小于基准和大于基准的两部分,然后递归地对这两部分进行排序。
实现代码:
public class QuickSort {
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public 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++;
swap(arr, i, j);
}
}
swap(arr, i + 1, high);
return i + 1;
}
public 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);
}
}
}
优缺点:
- 优点:
- 效率高,时间复杂度平均情况下为O(n log n)。
- 缺点:
- 不稳定。
6. 堆排序 (Heap Sort)
解题思路:
- 构建最大堆(或最小堆),将堆顶元素与最后一个元素交换,然后将堆的大小减一并重新维护堆的性质。
实现代码:
public class HeapSort {
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 temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
heapify(arr, n, largest);
}
}
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);
}
}
}
优缺点:
- 优点:
- 高效的原地排序算法,时间复杂度为O(n log n)。
- 缺点:
- 不稳定。
7. 计数排序 (Counting Sort)
解题思路:
- 统计数组中每个元素的出现次数,然后根据元素值和出现次数重新构建数组。
实现代码:
public class CountingSort {
public static void countingSort(int[] arr) {
int n = arr.length;
int max = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > max)
max = arr[i];
}
int[] count = new int[max + 1];
for (int i = 0; i <= max; i++)
count[i] = 0;
for (int i = 0; i < n; i++)
count[arr[i]]++;
for (int i = 1; i <= max; i++)
count[i] += count[i - 1];
int[] output = new int[n];
for (int i = n - 1; i >= 0; i--) {
output[count[arr[i]] - 1] = arr[i];
count[arr[i]]--;
}
for (int i = 0; i < n; i++)
arr[i] = output[i];
}
}
优缺点:
- 优点:
- 适用于元素范围不大的情况,时间复杂度为O(n + k),k为最大元素值。
- 缺点:
- 对于元素范围很大的数据效率较低。
8. 桶排序 (Bucket Sort)
解题思路:
- 将元素分配到不同的桶中,对每个桶中的元素进行排序,然后合并所有桶。
实现代码:
import java.util.ArrayList;
import java.util.Collections;
public class BucketSort {
public static void bucketSort(int[] arr) {
int n = arr.length;
int max = arr[0];
int min = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
int bucketCount = max - min + 1;
ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(bucketCount);
for (int i = 0; i < bucketCount; i++)
buckets.add(new ArrayList<>());
for (int i = 0; i < n; i++) {
int bucketIndex = (arr[i] - min) * (bucketCount - 1) / (max - min);
buckets.get(bucketIndex).add(arr[i]);
}
int index = 0;
for (int i = 0; i < bucketCount; i++) {
Collections.sort(buckets.get(i));
for (int j = 0; j < buckets.get(i).size(); j++)
arr[index++] = buckets.get(i).get(j);
}
}
}
优缺点:
- 优点:
- 适用于元素分布均匀的情况,时间复杂度为O(n + k),k为桶的个数。
- 缺点:
- 占用较多内存。
总结和推荐
- 推荐的排序算法: 归并排序和快速排序
- 归并排序和快速排序都是高效的排序算法,时间复杂度为O(n log n),适用于各种规模的数据集。
- 归并排序是稳定的,但需要额外的内存空间,适用于所有数据类型。
- 快速排序是不稳定的,但在实践中通常比归并排序更快,适用于大规模数据集。
这里推荐归并排序作为首选,因为它是稳定的且不会对原始数据造成修改。如果在内存受限的情况下考虑,可以选择快速排序。Bubble Sort、Selection Sort 和 Insertion Sort 适用于小规模数据集或教学目的,不推荐用于实际应用。