是一种常用的排序算法,它采用分治的策略来对待排序的序列进行排序。快速排序的基本思想是选择一个基准元素,通过一趟排序将序列分割成两个子序列,其中一个子序列的所有元素都小于基准元素,另一个子序列的所有元素都大于基准元素。然后对这两个子序列分别进行递归排序,最终将整个序列排序完成。
package com.algorithm.sort;
/**
* 快速排序的原理可以描述如下:
*
* 1.选择一个基准元素(通常为序列的第一个或最后一个元素)。
* 2.将序列分成两部分,所有比基准元素小的元素放在基准元素的左边,所有比基准元素大的元素放在基准元素的右边。
* 3.对左右两个子序列分别进行递归排序,直到子序列的长度为1或0,即序列已经有序。
*/
public class QuickSort {
/**
* 快速排序算法实现
*
* @param arr 待排序数组
* @param low 数组的最低索引
* @param high 数组的最高索引
*/
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); // 对右子序列进行递归排序
}
}
/**
* 将序列分成两部分
*
* @param arr 待排序数组
* @param low 数组的最低索引
* @param high 数组的最高索引
* @return 基准元素的位置
*/
public static int partition(int[] arr, int low, int high) {
int pivot = arr[high]; // 选择最后一个元素作为基准元素
int i = low - 1; // i 表示小于基准元素的区域的边界
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
swap(arr, i, j); // 将比基准元素小的元素放到小于区域的右边
}
}
swap(arr, i + 1, high); // 将基准元素放到小于区域和大于区域的中间
return i + 1; // 返回基准元素的位置
}
/**
* 交换数组中两个元素的位置
*
* @param arr 待排序数组
* @param i 数组下标
* @param j 数组下标
*/
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 测试方法
*
* @param args todo
*/
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
quickSort(arr, 0, arr.length - 1);
System.out.println("排序后的数组:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
在上述代码中,quickSort 方法接受一个整数数组 arr、数组的最低索引 low 和最高索引 high 作为输入,并使用快速排序算法对指定范围内的元素进行排序。partition 方法用于将序列分成两部分,并返回基准元素的位置。swap 方法用于交换数组中两个元素的位置。main 方法中创建了一个示例数组 arr,并调用 quickSort 方法进行排序。最后,打印排序后的数组结果。
排序后的数组:
11 12 22 25 34 64 90
Process finished with exit code 0
- 快速排序的平均时间复杂度为O(nlogn),它是一种高效的排序算法,在大多数情况下具有较好的性能。
- 然而,最坏情况下的时间复杂度为O(n^2),当序列已经有序或基本有序时,快速排序的性能会下降。
- 为了解决这个问题,可以使用随机化的快速排序或者优化的快速排序算法。
============================================ 优化快排算法 ============================================
随机化选择基准元素:在上述代码中,基准元素的选择是固定的,通常选择第一个或最后一个元素。但如果序列已经有序或近乎有序,固定选择基准元素可能导致快速排序的性能下降。为了解决这个问题,可以随机选择基准元素,例如从待排序序列中随机选择一个元素作为基准元素。
三数取中法选择基准元素:选择一个合适的基准元素可以避免快速排序在某些特定情况下的退化。一种常用的方法是使用三数取中法,即从待排序序列的第一个、中间和最后一个元素中选择中间大小的元素作为基准元素。
插入排序优化:对于小规模的子序列,快速排序的递归调用和分割操作可能会带来额外的开销。可以设置一个阈值,在子序列长度小于阈值时,使用插入排序或其他简单排序算法来代替快速排序。
package com.algorithm.sort;
/**
* 快速排序算法的优化,可以考虑以下几个方面:
*
* 1.随机化选择基准元素:
* 在上述代码中,基准元素的选择是固定的,通常选择第一个或最后一个元素。
* 但如果序列已经有序或近乎有序,固定选择基准元素可能导致快速排序的性能下降。
* 为了解决这个问题,可以随机选择基准元素,例如从待排序序列中随机选择一个元素作为基准元素。
* 2.三数取中法选择基准元素:
* 选择一个合适的基准元素可以避免快速排序在某些特定情况下的退化。
* 一种常用的方法是使用三数取中法,即从待排序序列的第一个、中间和最后一个元素中选择中间大小的元素作为基准元素。
* 3. 插入排序优化:
* 对于小规模的子序列,快速排序的递归调用和分割操作可能会带来额外的开销。
* 可以设置一个阈值,在子序列长度小于阈值时,使用插入排序或其他简单排序算法来代替快速排序。
*/
import java.util.Random;
public class QuickSortOptimized {
/**
* 优化后的快速排序算法实现
*
* @param arr 待排序数组
* @param low 数组的最低索引
* @param high 数组的最高索引
*/
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
if (high - low + 1 <= 10) { // 设置阈值为10
insertionSort(arr, low, high); // 使用插入排序
} else {
int pivotIndex = randomizedPartition(arr, low, high); // 随机化选择基准元素
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
}
}
/**
* 随机化选择基准元素
*
* @param arr 待排序数组
* @param low 数组的最低索引
* @param high 数组的最高索引
* @return 分成两部分的序列
*/
public static int randomizedPartition(int[] arr, int low, int high) {
int randomIndex = new Random().nextInt(high - low + 1) + low; // 随机选择索引
swap(arr, randomIndex, high);
return partition(arr, low, high);
}
/**
* 将序列分成两部分
*
* @param arr 待排序数组
* @param low 数组的最低索引
* @param high 数组的最高索引
* @return 基准元素的位置
*/
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;
}
/**
* 插入排序算法实现:在子序列长度小于阈值时,使用插入排序代替快速排序。
*
* @param arr 待排序数组
* @param low 数组的最低索引
* @param high 数组的最高索引
*/
public static void insertionSort(int[] arr, int low, int high) {
for (int i = low + 1; i <= high; i++) {
int key = arr[i];
int j = i - 1;
while (j >= low && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
/**
* 交换数组中两个元素的位置
*
* @param arr 待排序数组
* @param i 数组下标
* @param j 数组下标
*/
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 测试方法
*
* @param args todo
*/
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
quickSort(arr, 0, arr.length - 1);
System.out.println("排序后的数组:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
在上述优化代码中,randomizedPartition 方法用于随机化选择基准元素,并将其交换到待排序序列的最后一个位置。quickSort 方法中加入了阈值判断,当子序列长度小于等于阈值时,使用插入排序代替快速排序。其他部分与之前的代码类似。
排序后的数组:
11 12 22 25 34 64 90
Process finished with exit code 0
- 运行优化后的代码将得到与之前相同的排序结果。
- 这些优化措施可以提高快速排序算法的性能,在处理大规模数据或特定情况下的排序任务时,能够更好地发挥快速排序的优势。