目录
- [一、冒泡排序(Bubble Sort)](#一、冒泡排序(Bubble Sort))
- [二、选择排序(Selection Sort)](#二、选择排序(Selection Sort))
- [三、插入排序(Insertion Sort)](#三、插入排序(Insertion Sort))
- [四、希尔排序(Shell Sort)](#四、希尔排序(Shell Sort))
- [五、归并排序(Merge Sort)](#五、归并排序(Merge Sort))
- [六、快速排序(Quick Sort)](#六、快速排序(Quick Sort))
- [七、堆排序(Heap Sort)](#七、堆排序(Heap Sort))
- [八、计数排序(Counting Sort)](#八、计数排序(Counting Sort))
- [九、桶排序(Bucket Sort)](#九、桶排序(Bucket Sort))
- [十、基数排序(Radix Sort)](#十、基数排序(Radix Sort))
一、冒泡排序(Bubble Sort)
通过重复地遍历待排序序列,比较相邻的两个元素,如果顺序错误则交换它们的位置,直到没有更多的元素需要交换为止。
java
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]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
简单实现,但效率较低,时间复杂度为O(n^2)。
二、选择排序(Selection Sort)
在待排序序列中找到最小(或最大)的元素,将其放到已排序序列的末尾,然后再从剩余未排序的元素中找到最小(或最大)的元素,放到已排序序列的末尾,重复此过程,直到所有元素都排序完毕。
java
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;
}
}
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
比冒泡排序略好,时间复杂度仍为O(n^2)。
三、插入排序(Insertion Sort)
从第二个元素开始,将每个元素插入到已排序序列中的适当位置,直到所有元素都排序完毕。
java
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)。
四、希尔排序(Shell Sort)
先将待排序序列分割成若干个子序列,分别进行插入排序,然后逐渐合并子序列,直到整个序列都有序。
java
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 = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
gap /= 2;
}
}
是插入排序的改进版,通过增加间隔元素来减少比较次数,效率比插入排序高,但不稳定。
五、归并排序(Merge Sort)
将待排序序列分成若干个子序列,对每个子序列进行排序,然后将有序的子序列合并成一个有序序列,重复此过程,直到整个序列都有序。
java
public static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
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[] temp = new int[right - left + 1];
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= right) {
temp[k++] = arr[j++];
}
for (i = left, k = 0; i <= right; i++, k++) {
arr[i] = temp[k];
}
}
归并排序是有效、稳定、基于比较的排序算法,时间复杂度为O(n log n)
六、快速排序(Quick Sort)
选择一个枢轴元素,将待排序序列分成两个子序列,一个子序列的元素都比枢轴元素小,另一个子序列的元素都比枢轴元素大,然后对两个子序列分别进行快速排序。
java
public class QuickSort {
public static void sort(int[] array, int left, int right) {
if (left < right) {
int pivotIndex = partition(array, left, right);
sort(array, left, pivotIndex - 1);
sort(array, pivotIndex + 1, right);
}
}
private static int partition(int[] array, int left, int right) {
int pivot = array[right];
int i = left;
for (int j = left; j < right; j++) {
if (array[j] < pivot) {
swap(array, i, j);
i++;
}
}
swap(array, i, right);
return i;
}
private static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
快速排序是一种高效的排序算法,平均时间复杂度为O(n log n)。它使用分治策略,通过选择一个基准元素将数组分为两部分,然后递归地对这两部分进行排序。然而,在最坏情况下(当输入数组已经有序或逆序时),快速排序的时间复杂度会退化到O(n^2)。
七、堆排序(Heap Sort)
将待排序序列构造成一个大根堆(或小根堆),然后将堆顶元素(最大值或最小值)与末尾元素交换,再调整剩余元素为堆结构,重复此过程,直到所有元素都排序完毕。
java
public class HeapSort {
public static void sort(int[] array) {
int n = array.length;
// 构建最大堆
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(array, n, i);
}
// 一个个从堆顶取出元素
for (int i = n - 1; i >= 0; i--) {
int temp = array[0];
array[0] = array[i];
array[i] = temp;
heapify(array, i, 0);
}
}
private static void heapify(int[] array, int n, int i) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < n && array[left] > array[largest]) {
largest = left;
}
if (right < n && array[right] > array[largest]) {
largest = right;
}
if (largest != i) {
int swap = array[i];
array[i] = array[largest];
array[largest] = swap;
heapify(array, n, largest);
}
}
}
堆排序是一种基于二叉堆的排序算法,时间复杂度为O(n log n)。它首先构建一个最大堆,然后交换堆顶元素(最大值)和最后一个元素,接着调整剩余元素以维持最大堆的性质,重复此过程直到所有元素排序完毕。堆排序是不稳定的排序算法。
八、计数排序(Counting Sort)
根据待排序序列中每个元素出现的次数,计算小于等于每个元素的元素个数,然后从后往前遍历待排序序列,将每个元素放在相应位置。
java
public class CountingSort {
public static void sort(int[] array, int maxValue) {
int n = array.length;
int[] count = new int[maxValue + 1];
int[] output = new int[n];
// 计算每个元素的频率
for (int i = 0; i < n; i++) {
count[array[i]]++;
}
// 累计频率
for (int i = 1; i <= maxValue; i++) {
count[i] += count[i - 1];
}
// 根据频率将元素放到正确的位置
for (int i = n - 1; i >= 0; i--) {
output[count[array[i]] - 1] = array[i];
count[array[i]]--;
}
// 将排序后的元素复制回原数组
System.arraycopy(output, 0, array, 0, n);
}
public static void main(String[] args) {
int[] array = {4, 2, 2, 8, 3, 3, 1};
sort(array, 9);
for (int i : array) {
System.out.print(i + " ");
}
}
}
计数排序是一种线性时间复杂度的排序算法,适用于一定范围内的整数排序。它通过统计每个元素出现的次数,然后根据统计结果将元素放到正确的位置上。计数排序是稳定的排序算法,但只适用于整数且范围不大的情况。
九、桶排序(Bucket Sort)
将待排序序列分到有限数量的桶中,每个桶单独排序,然后将所有桶的元素按照顺序合并成一个序列。
java
public class BucketSort {
public static void sort(int[] array, int bucketSize) {
if (array.length == 0) {
return;
}
int minValue = array[0];
int maxValue = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] < minValue) {
minValue = array[i];
}
if (array[i] > maxValue) {
maxValue = array[i];
}
}
int bucketCount = (maxValue - minValue) / bucketSize + 1;
ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(bucketCount);
for (int i = 0; i < bucketCount; i++) {
buckets.add(new ArrayList<>());
}
// 将元素放入桶中
for (int i = 0; i < array.length; i++) {
int index = (array[i] - minValue) / bucketSize;
buckets.get(index).add(array[i]);
}
// 对每个桶中的元素进行排序
int k = 0;
for (int i = 0; i < buckets.size(); i++) {
Collections.sort(buckets.get(i));
for (int j = 0; j < buckets.get(i).size(); j++) {
array[k++] = buckets.get(i).get(j);
}
}
}
public static void main(String[] args) {
int[] array = {4, 2, 2, 8, 3, 3, 1};
sort(array, 5);
for (int i : array) {
System.out.print(i + " ");
}
}
}
桶排序是计数排序的升级版,它将待排序的元素分到有限数量的桶中,然后对每个桶中的元素进行排序。桶排序的时间复杂度取决于桶的数量和桶内元素的排序算法。当桶的数量足够多,且桶内元素足够少时,桶排序的时间复杂度接近O(n)。桶排序是稳定的排序算法,但不适合元素分布不均匀的情况。
十、基数排序(Radix Sort)
将待排序元素按位数切割成不同的数字,然后按每个位数分别进行排序,重复此过程,直到所有元素都排序完毕。
java
public class RadixSort {
// 基数排序
public static void radixSort(int[] arr) {
// 找到最大数,确定位数
int max = getMax(arr);
int exp = 1;
while (max / exp > 0) {
// 使用稳定排序算法如计数排序对每一位进行排序
countingSort(arr, exp);
exp *= 10;
}
}
// 计数排序,用于基数排序中对每一位的排序
private static void countingSort(int[] arr, int exp) {
int n = arr.length;
int[] output = new int[n]; // 输出数组
int[] count = new int[10]; // 计数数组
// 初始化计数数组
for (int i = 0; i < 10; i++) {
count[i] = 0;
}
// 统计每个桶中的元素个数
for (int i = 0; i < n; i++) {
count[(arr[i] / exp) % 10]++;
}
// 修改计数数组,使每个元素表示小于等于当前元素的个数
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 从后往前遍历原数组,将元素放到正确的位置上
for (int i = n - 1; i >= 0; i--) {
output[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
// 将排序后的元素复制回原数组
for (int i = 0; i < n; i++) {
arr[i] = output[i];
}
}
// 找到数组中的最大数
private static int getMax(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
}
基数排序是一种稳定的排序算法,时间复杂度为O(d * (n + r)),其中d是最大数的位数,n是待排序数组的长度,r是桶的数量(在基数排序中通常为10)。由于它只需要O(n + r)的额外空间,因此也是一种空间效率较高的排序算法。基数排序特别适用于对整数进行排序,尤其是当整数的范围比较小或者知道整数的分布时。然而,对于浮点数或者范围很大的整数,基数排序可能不是最佳选择。