-
算法基本过程
简单讲,就是分治。选一个基准元素,将序列分割成两部分,使得左边的都比基准小,右边的都比基准大。此时基准元素位置确定了,左右两侧也相对有序。再把左右子序列看成独立的序列,选基准,分割成左边小,右边大的两部分,一直分到无法再划分下去。
-
快排快在哪?
相比于冒泡排序每一轮只把一个元素冒泡到序列的一端。快排虽然每一轮也只能确定一个元素的最终位置,但它用分治的思路,每一轮使得左右两侧变得相对有序。
-
实现细节
-
基准元素的选择
一般会选择第一个元素作为基准,为了避免逆序数列的极端情况,也可以随机选择一个元素作为基准,并让基准元素和首元素交换位置
-
元素交换的过程
-
双边循环
-
单边循环
-
-
双边循环
//左右2个指针
//右指针开始往左移动,直到发现比pivot小的,停止移动
//左指针往右移动,直到发现比pivot大的,此时交换左右指针
//回到第二行的步骤
//最后将pivot和左子序列的最后一个元素交换
public void quickSortDual(int[] arr,int left,int right){
if (left >= right)
return ;
int pivot = arr[left];
int posLeft = left + 1;
int posRight = right;
boolean moveRight = true;
while (posLeft <= posRight){
if (moveRight){
if (arr[posRight] < pivot){
moveRight = false;
}else
posRight--;
}else{
if (arr[posLeft] > pivot){
int temp = arr[posLeft];
arr[posLeft] = arr[posRight];
arr[posRight] = temp;
posRight--;
moveRight = true;
}
posLeft++;
}
}
//退出循环时,posRight是最后一个小于pivot的元素
//所以交换posRight和pivot
arr[left] = arr[posRight];
arr[posRight] = pivot;
//下面两行交换对结果没有影响
quickSortDual(arr,left,posRight - 1);
quickSortDual(arr,posRight + 1,right);
}
单边循环
//单边循环的核心思想是,不断扩大左子序列的边界。
//从左向右遍历,发现比pivot小的,就和左侧的第一个比pivot大的元素交换
//然后往右扩大左子序列边界的位置
//最后把pivot和左子序列的最后一个元素交换
public void quickSortUni(int[] arr,int left,int right){
if (left >= right)
return ;
int pivot = arr[left];
int posLeft = left + 1;
int mark = left;
while (posLeft <= right){
if (arr[posLeft] < pivot){
mark++;
int temp = arr[mark];
arr[mark] = arr[posLeft];
arr[posLeft] = temp;
}
posLeft++;
}
arr[left] = arr[mark];
arr[mark] = pivot;
//下面两行交换对结果没有影响
quickSortUni(arr,left,mark - 1);
quickSortUni(arr,mark + 1,right);
}
非递归单边循环
//注意递归需要知道的信息是left,right边界
//故把边界信息(一对int)压栈
public void quickSortNonRecursive(int[] arr,int start,int end){
MyStack<MyPair<Integer,Integer>> stack = new MyStack<>();
stack.push(new MyPair<>(start,end));
while (!stack.isEmpty()){
MyPair<Integer,Integer> range = stack.pop();
int left = range.getLeft();
int right = range.getRight();
//退出条件
if (left >= right)
continue;
int pivot = arr[left];
int posLeft = left + 1;
int mark = left;
while (posLeft <= right){
if (arr[posLeft] < pivot){
mark++;
int temp = arr[mark];
arr[mark] = arr[posLeft];
arr[posLeft] = temp;
}
posLeft++;
}
arr[left] = arr[mark];
arr[mark] = pivot;
//下面两行顺序交换,结果也正确
//无非是从左侧开始分治还是从右侧开始分治
stack.push(new MyPair<>(mark + 1,right));
stack.push(new MyPair<>(left,mark - 1));
}
附上自实现的stack和pair
package utils;
import java.util.Deque;
import java.util.LinkedList;
/**
* Non Concurrent Safe
* @param <T>
*/
public class MyStack<T>{
private Deque<T> stack = new LinkedList<>();
private int size = 0;
public T pop(){
T ele = stack.pollFirst();
if (ele != null)
size--;
return ele;
}
public void push(T e){
stack.addFirst(e);
size++;
}
public T peek(){
T ele = stack.peekFirst();
return ele;
}
public int getSize(){
return this.size;
}
public boolean isEmpty(){
return size == 0;
}
}
package utils;
public class MyPair<V1,V2> {
private V1 left;
private V2 right;
public MyPair(){}
public MyPair(V1 left, V2 right) {
this.left = left;
this.right = right;
}
public V1 getLeft() {
return left;
}
public void setLeft(V1 left) {
this.left = left;
}
public V2 getRight() {
return right;
}
public void setRight(V2 right) {
this.right = right;
}
}