通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
快速排序在最坏情况下的时间复杂度为O( n 2 n^2 n2),但这种情况非常少见。它的平均时间复杂度为O( n l o g n n log n nlogn),并且由于排序过程是在原地进行分区,所以它不需要额外的存储空间,空间复杂度为O( l o g n log n logn)
// 快速排序主方法
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);
}
}
// 分区方法
private static int partition(int[] arr, int low, int high) {
int pivot = arr[low]; // 选择第一个元素作为基准
int i = low;
int j = high;
while (i < j) {
// 从右向左找第一个小于基准的元素
while (i < j && arr[j] >= pivot) {
j--;
}
if (i < j) {
arr[i++] = arr[j];
}
// 从左向右找第一个大于基准的元素
while (i < j && arr[i] < pivot) {
i++;
}
if (i < j) {
arr[j--] = arr[i];
}
}
// 将基准元素放到正确位置
arr[i] = pivot;
return i;
}
将数组分成若干个小组,先在每个小组内部进行排序,然后将有序的小组合并成更大的有序组,直到整个数组变得有序
归并排序的时间复杂度是 O( n l o g n n log n nlogn),这是因为数组每次都被分成两半(这是一个 l o g n log n logn的过程),并且在合并过程中需要遍历所有元素(这是一个 n n n 的过程)。此外,归并排序不是原地排序算法,因为它需要额外的存储空间来合并数组。
// 归并排序主方法(递归实现)
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);
}
}
// 合并两个有序子数组的方法
private static void merge(int[] arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
// 创建临时数组存储左右子数组
int[] leftArr = new int[n1];
int[] rightArr = new int[n2];
System.arraycopy(arr, left, leftArr, 0, n1);
System.arraycopy(arr, mid + 1, rightArr, 0, n2);
// 合并临时数组到原数组
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (leftArr[i] <= rightArr[j]) {
arr[k++] = leftArr[i++];
} else {
arr[k++] = rightArr[j++];
}
}
// 复制左子数组剩余元素
while (i < n1) {
arr[k++] = leftArr[i++];
}
// 复制右子数组剩余元素
while (j < n2) {
arr[k++] = rightArr[j++];
}
}
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
插入排序的时间复杂度在最坏情况下是 O( n 2 n^2 n2),平均情况也是 O( n 2 n^2 n2),但在数组几乎排序好的情况下,它可以达到 O( n n n)
public static void insertionSort(int[] array) {
int n = array.length;
for (int i = 1; i < n; i++) {
int key = array[i];
int j = i - 1;
// 将array[i]与已排序好的array[0...i-1]中的元素进行比较,找到合适的位置插入
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j = j - 1;
}
array[j + 1] = key;
}
}
重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端
冒泡排序的时间复杂度是O( n 2 n^2 n2),因为它需要比较所有相邻的元素对,并且在最坏的情况下需要交换所有的元素
// 基础冒泡排序实现
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 优化版冒泡排序(带标志位)
public static void optimizedBubbleSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
int n = arr.length;
boolean swapped;
for (int i = 0; i < n - 1; i++) {
swapped = false;
for (int j = 0; j < n - 1 - i; 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;
}
}
}
首先在未排序序列中找到最小(或最大)元素,存放到排序序列的起始位置,然后再从剩余未排序元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕
选择排序的时间复杂度无论是最好、平均还是最坏情况都是 O( n 2 n^2 n2)
// 选择排序主方法
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[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
以上就是常用的一些排序算法,虽然Java中有排序的方法,但是这些方法还是需要了解和掌握。