快速排序-java
快排: 有两个指针left,right, 初值为数据表的最左端位置, 最右端位置, 设置基准点(pivot,或者称为分区点)
快排是一个原地,不稳定的排序算法
实现思路:
1.从right所指向的位置开始向左边搜索 ,找到的第一个小于基准值(pivotKey)的记录,就与基准点(pivot,基准值的位置)进行交换;
2.再从left所指向的位置向右边搜索,找到的第一个大于基准值的记录,就与基准点进行交换;
3.重复步骤 1,2 直到 left = right,完成一趟遍历
完成一趟遍历后: 获得一个基准值在该数据表的最终位置, 之后就不用管这个基准值了,直接对其他的元素重复1,2,3步骤即可,最后得到的就是一个有序的数据表
时间复杂度: \(最好 : O(NlogN),最坏: O(N^2),平均 : O(NlogN)\)
空间复杂度: \(O(1)\)
优化思路
使用三数取中法 , 在 非递归实现中 已经体现
递归实现
package sort.quicksort;
import java.util.Arrays;
// 快排 递归实现
public class QuickSort1 {
/**
* @param target 需要排序的数据表
* @param left 数据表的最左角标
* @param right 数据表的最右角标
*/
public static void quickSort(int[] target ,int left,int right){
int i = left;
int j = right;
int p = left; // 记录基准值的位置
int pivotKey = target[p]; // 基准值
// 每一次循环都会查找一次基准值的位置
while (i <= j){
// 从右端查找小于基准值的 数据, 如果查找到了,跳出循环
while ( j >= p && target[j] >= pivotKey){
j--;
}
// 将从右端查找到的小于基准值的数据,与放入数据表左端
if (j >= p){
target[p] = target[j];
p = j;
}
/* 问: 为什么要有下面两个if 判断呢? 没有行不行呢?
答: 是为了保证 我们每次循环结束的时候,保证 p都在左端, 当递归执行的时候,所有的搜索都是从 右端开始的搜索的.
如果 没有下面两个判断, 当p 指向的是表的右端的时候, 我们就要从 数据表的左端开始搜索数据,
即没有 下面两个if, 就在造成 p在左端,而又从左端开始搜索数据 , 这明显是错误的搜索方式
*/
// 从左端查找大于基准值的数据, 如果查到了,则 向右偏移
if (i <= p && target[i] <= pivotKey){
i++;
}
// 如果从左端查找到的大于基准值的数据, 则放入数据表右端
if (i <= p){
target[p] = target[i];
p = i;
}
System.out.println(Arrays.toString(target));
}
// 一趟遍历后, 找到一个基准值的最终位置
target[p] = pivotKey;
System.out.println(Arrays.toString(target));
System.out.println();
// 对于低子表进行递归排序
if (p - left >1){
quickSort(target,left,p-1);
}
// 对高子表进行递归排序
if (right-p >1){
quickSort(target,p+1,right);
}
}
/**
* @param target 要进行排序的数据表
*/
public static void QuickSort(int[] target){
quickSort(target,0,target.length-1);
}
public static void main(String[] args) {
int[] arr = {49,38,65,97,76,13,27,49};
System.out.println(Arrays.toString(arr));
System.out.println();
QuickSort(arr);
System.out.println();
System.out.println(Arrays.toString(arr));
}
}
非递归实现
package sort.quicksort;
import java.util.Arrays;
import java.util.Stack;
// 快排,非递归
public class QuickSort2 {
/**
* @param target 要排序的目标数组
*/
public static void quickSort(int[] target) {
int left = 0;
int right = target.length-1;
int pivotLoc; // 基准点 的位置
if (left >= right)
return;
Stack<Integer> stack = new Stack<>();
stack.push(left);
stack.push(right);
while (!stack.empty()) {
// 先弹出right,再弹出left
right = stack.pop();
left = stack.pop();
// 寻找出基准元素的位置
pivotLoc = partition(target, left, right);
// 先压left,再压 right,
if (pivotLoc - left > 1) {
stack.push(left);
stack.push(pivotLoc - 1);
}
if (right - pivotLoc > 1) {
stack.push(pivotLoc + 1);
stack.push(right);
}
}
}
/**
* @param target 子数组
* @param left 左端位置
* @param right 右端位置
* @return 返回基准值 pivot的插入位置
*/
public static int partition(int[] target, int left, int right) {
setPivot(target,left,right); // 使用三数取中法
int pivotKey = target[left]; // 用第一个元素作为基准元素
while (left < right) { // 两侧交替向中间扫描
while (left < right && target[right] >= pivotKey)
right--;
target[left] = target[right]; // 将比基准值小的数据移至基准值的左端
while (left < right && target[left] <= pivotKey)
left++;
target[right] = target[left]; // 将比基准值大的数据移至基准值的右端
}
target[left] = pivotKey; // 在中间位置放回基准值
return left; // 返回基准元素所在位置
}
// 算法优化 : 三数取中
public static void setPivot(int[] target, int left,int right){
// 三数取中,将中间元素放在第一个位置
if (target[left] > target[right]){
swap(target, left, right);
}
if (target[(left+ right)/ 2] > target[right]){
swap(target, (left+ right)/ 2, right);
}
if (target[left] < target[(left+ right)/ 2]){
swap(target, (left+ right)/ 2, left);
}
}
private static void swap(int[] target, int left, int right) {
int temp = target[left];
target[left] = target[right];
target[right] = temp;
}
public static void main(String[] args) {
int[] arr = {49,38,65,97,76,13,27,49};
System.out.println(Arrays.toString(arr));
System.out.println();
quickSort(arr);
System.out.println();
System.out.println(Arrays.toString(arr));
}
}
浙公网安备 33010602011771号