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]) {
// 交换元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
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 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;
}
}
}
3、插入排序(Insertion Sort)
插入排序是一种简单的排序算法,它通过构建有序序列,对未排序的部分逐个进行插入操作。
以下是插入排序的基本步骤:
- 假设第一个元素已经被排序(可以看作只有一个元素的有序序列)。
- 取出下一个未排序的元素,在已排序的序列中从后向前扫描。
- 如果该元素(已排序)大于新元素,则将该元素移到下一位置。
- 重复步骤3,直到找到已排序的元素小于或等于新元素的位置。
- 将新元素插入到该位置。
- 重复步骤2~5,直到所有元素都被排序。
public class SelectionSort {
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 表示法都需要 O(N2) 时间级别。一般不会选择冒泡排序,虽然冒泡排序书写是最简单的,但是平均性能是没有选择排序和插入排序好的。
4、快速排序
快速排序(Quick Sort)是一种高效的排序算法,它采用了分治的思想,通过递归地将数据分解成较小的部分来实现排序。
以下是快速排序的基本步骤:
- 选择一个基准元素(通常是数组中的第一个元素、最后一个元素或者中间元素)。
- 将小于基准元素的元素移动到基准元素的左边,将大于基准元素的元素移动到右边,这个过程称为分区(Partition)操作。
- 分别对基准元素左右两边的子序列进行递归排序。
public class QuickSort {
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pivotIndex = partition(arr, low, high);
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 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; 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;
}
}
5、归并排序(Merge Sort)
归并排序(Merge Sort)是一种稳定的排序算法,它也采用了分治的思想,将待排序的序列分成若干个子序列,每个子序列都是有序的。然后再将子序列合并成一个整体有序序列。
以下是归并排序的基本步骤:
- 将待排序的序列不断地二分,直到无法继续划分。
- 对每个子序列进行排序,可以使用递归实现排序操作。
- 合并相邻的两个子序列,得到新的有序序列,重复上述操作,直到最后排序完成。
public class MergeSort {
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;
int j = mid + 1;
int 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 (int m = 0; m < temp.length; m++) {
arr[left + m] = temp[m];
}
}
}
6、堆排序(Heap Sort)
堆排序(Heap Sort)是一种利用堆的数据结构来进行排序的算法,它利用了最大堆或最小堆这种特殊的数据结构来完成排序过程。
以下是堆排序的基本步骤:
- 将待排序的序列构建成一个最大堆(或最小堆)。
- 交换堆顶元素和堆中最后一个元素,并将堆的大小减一,然后对剩下的元素重新进行堆调整,使其满足堆的性质。
- 重复步骤2,直到堆的大小为1,排序完成。
public class HeapSort {
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);
}
}
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);
}
}
}
7、计数排序(Counting Sort)
计数排序(Counting Sort)是一种非比较排序算法,它通过确定每个元素在排序后的序列中的位置来进行排序。计数排序适用于待排序元素范围较小且已知的情况。
以下是计数排序的基本步骤:
- 找出待排序序列中的最大值,并创建一个计数数组
count
,长度为最大值加一。 - 遍历待排序序列,统计每个元素出现的次数,并将结果存储在计数数组
count
中对应元素的位置上。 - 对计数数组
count
进行累加操作,得到每个元素在排序后序列中的最终位置。 - 创建一个与待排序序列长度相同的辅助数组
result
。 - 从后向前遍历待排序序列,根据元素的计数值和计数数组中的位置信息,将元素放入辅助数组
result
中对应的位置上。 - 将辅助数组
result
的内容复制回原始的待排序序列。
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 < n; i++) {
count[arr[i]]++;
}
// 计算每个元素在排序后序列中的最终位置
for (int i = 1; i <= max; i++) {
count[i] += count[i - 1];
}
// 创建辅助数组
int[] result = new int[n];
// 根据计数值和计数数组中的位置信息,将元素放入辅助数组中
for (int i = n - 1; i >= 0; i--) {
result[count[arr[i]] - 1] = arr[i];
count[arr[i]]--;
}
// 将辅助数组的内容复制回原始的待排序序列
for (int i = 0; i < n; i++) {
arr[i] = result[i];
}
}
}
8、桶排序(Bucket Sort)
桶排序(Bucket Sort)是一种排序算法,它将待排序数据分割成若干个有序的桶(或称为容器),每个桶内的数据进行排序,最后将桶中的数据依次取出,即可得到有序序列。
以下是桶排序的基本步骤:
- 确定桶的数量和范围。根据待排序数据的特点,确定适当的桶的数量和每个桶的范围。
- 将待排序数据分配到对应的桶中。遍历待排序数据,根据每个数据的大小,将其放入对应的桶中。
- 对每个桶中的数据进行排序。可以使用其他排序算法,如插入排序、快速排序等,在每个桶内部进行排序。
- 按顺序将每个桶中的数据取出。将排序后的桶中数据按顺序取出,组成最终的有序序列。
import java.util.ArrayList;
import java.util.Collections;
public class BucketSort {
public static void bucketSort(int[] arr, int bucketSize) {
if (arr.length <= 1) {
return;
}
// 确定桶的数量和范围
int minValue = arr[0];
int maxValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i];
} else if (arr[i] > maxValue) {
maxValue = arr[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 < arr.length; i++) {
int bucketIndex = (arr[i] - minValue) / bucketSize;
buckets.get(bucketIndex).add(arr[i]);
}
// 对每个桶中的数据进行排序
int currentIndex = 0;
for (int i = 0; i < bucketCount; i++) {
ArrayList<Integer> bucket = buckets.get(i);
Collections.sort(bucket);
for (int j = 0; j < bucket.size(); j++) {
arr[currentIndex++] = bucket.get(j);
}
}
}
}
9、基数排序(Radix Sort)
基数排序(Radix Sort)是一种非比较型的排序算法,它将整数按照位数进行排序。基数排序可以分为 LSD(Least Significant Digit)和 MSD(Most Significant Digit)两种方式。
以下是基数排序(LSD方式)的基本步骤:
- 确定待排序整数的最大位数,并按照位数创建 10 个桶(0-9)。
- 从整数的最低位开始,按照每个位上的数字将整数放入对应的桶中。
- 按照桶的顺序依次取出每个整数,组成新的序列。
- 重复第 2 步和第 3 步,直到所有位都被处理。
import java.util.Arrays;
public class RadixSort {
public static void radixSort(int[] arr) {
if (arr.length <= 1) {
return;
}
// 找到数组中的最大值
int max = arr[0];
for (int value : arr) {
if (value > max) {
max = value;
}
}
int exp = 1;
int n = arr.length;
int[] temp = new int[n];
while (max / exp > 0) {
int[] count = new int[10];
// 统计每个桶中的元素个数
for (int value : arr) {
count[(value / exp) % 10]++;
}
// 将 count 转换为当前位的实际位置
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 根据当前位的值将数据存储到临时数组 temp 中
for (int i = n - 1; i >= 0; i--) {
temp[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
// 将临时数组的内容复制回原始数组
System.arraycopy(temp, 0, arr, 0, n);
exp *= 10;
}
}
}
总结
性能比较