使用Java实现快速排序函数。
快速排序是一种常见的排序算法,采用分治策略。快速排序的实现方式主要分为递归与非递归两种方式。在本文中,我们将使用Java语言来实现快速排序算法。
1.递归方式实现快速排序
递归方式实现快速排序比较简单。其基本思路是:选取一个元素作为“枢轴”,将数组中比这个元素小的元素都移到它的左边,比这个元素大的元素都移到它的右边,然后以相同的方式分别对左右两个部分进行快速排序。
Java代码如下:
public static void quickSort(int[] arr, int left, int right) {
if (left >= right) {
return;
}
int pivot = arr[left]; //选取 个元素作为枢轴
int i = left;
int j = right;
while (i < j) {
while (i < j && arr[j] >= pivot) { //从数组右边开始查找 个小于 pivot 的元素
j--;
}
while (i < j && arr[i] <= pivot) { //从数组左边开始查找 个大于 pivot 的元素
i++;
}
if (i < j) { //如果 i 和 j 还没有相遇
swap(arr, i, j); //交换它们找到的那两个元素
}
}
arr[left] = arr[i];
arr[i] = pivot; //把枢轴放到它应该在的位置
quickSort(arr, left, i - 1); //递归解决左边的部分
quickSort(arr, i + 1, right); //递归解决右边的部分
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
在上面的代码中,我们使用了递归方式来实现快速排序。我们首先选取 个元素作为“枢轴”,然后定义两个指针 i 和 j,i 从数组左边开始扫描,j 从数组右边开始扫描。我们要找到 个比枢轴元素大的元素 i 和 个比枢轴元素小的元素 j,然后将这两个元素进行交换。重复这个操作,直到 i 和 j 相遇。最后,我们把枢轴放到它应该在的位置上,然后递归对左右两部分进行快速排序。
2.非递归方式实现快速排序
非递归方式实现快速排序需要用到栈来保存要递归的子数组的起始和终止位置。它的基本思路与递归方式类似:先选取一个元素作为“枢轴”,然后将数组中比这个元素小的元素都移到它的左边,比这个元素大的元素都移到它的右边,然后以相同的方式分别对左右两个部分进行快速排序。不同的是,我们需要用栈来保存要递归的子数组的起始和终止位置,以便能够回到它们进行排序。
Java代码如下:
public static void quickSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
Stack<Integer> stack = new Stack<>(); //用来保存要递归的子数组的起始和终止位置
stack.push(0);
stack.push(arr.length - 1);
while (!stack.isEmpty()) { //只要栈不为空就一直循环
int right = stack.pop();
int left = stack.pop();
if (left >= right) {
continue;
}
int pivot = arr[left]; //选取 个元素作为枢轴
int i = left;
int j = right;
while (i < j) { //从两端交替扫描,直到 i 和 j 相遇
while (i < j && arr[j] >= pivot) { //从数组右边开始查找 个小于 pivot 的元素
j--;
}
while (i < j && arr[i] <= pivot) { //从数组左边开始查找 个大于 pivot 的元素
i++;
}
if (i < j) { //如果 i 和 j 还没有相遇
swap(arr, i, j); //交换它们找到的那两个元素
}
}
arr[left] = arr[i];
arr[i] = pivot; //把枢轴放到它应该在的位置
stack.push(left);
stack.push(i - 1); //把左边的子数组的起始和终止位置入栈
stack.push(i + 1);
stack.push(right); //把右边的子数组的起始和终止位置入栈
}
}
在上面的代码中,我们使用了非递归方式来实现快速排序。我们首先将要递归的子数组的起始和终止位置入栈,然后不断从栈中弹出起始和终止位置,进行快速排序。与递归方式不同的是,我们需要使用栈来保存要递归的子数组的起始和终止位置,以便能够回到它们对数组进行排序。同样,我们首先选取 个元素作为“枢轴”,然后定义两个指针 i 和 j,i 从左边开始扫描,j 从右边开始扫描,然后将枢轴元素和比它小的元素交换,直到 i 和 j 相遇,然后把枢轴元素放到它应该在的位置上。
3.分析
快速排序算法的时间复杂度为 O(nlogn) 或 O(n2),具体取决于 partition 操作(用于找到枢轴元素)的效率。在最坏情况下,快速排序的时间复杂度为 O(n2),此时数组已经有序或基本有序,而选取 个元素作为枢轴时,子数组被划分成两个长度差为 0 和 n-1 的子数组,这显然会导致递归树退化成一个单链表。在 情况下,快速排序的时间复杂度为 O(nlogn),此时每次划分产生的两个子数组的长度接近 n/2,这会使递归树的高度为 O(logn)。
快速排序算法所需的空间复杂度为 O(logn),这是因为它是一种递归算法,递归调用的深度与数组的长度成正比。非递归方式实现快速排序所需的空间复杂度也为 O(logn),这是因为它使用了栈来保存要递归的子数组的起始和终止位置。
