• 算法基本过程

    简单讲,就是分治。选一个基准元素,将序列分割成两部分,使得左边的都比基准小,右边的都比基准大。此时基准元素位置确定了,左右两侧也相对有序。再把左右子序列看成独立的序列,选基准,分割成左边小,右边大的两部分,一直分到无法再划分下去。

  • 快排快在哪?

    相比于冒泡排序每一轮只把一个元素冒泡到序列的一端。快排虽然每一轮也只能确定一个元素的最终位置,但它用分治的思路,每一轮使得左右两侧变得相对有序。

  • 实现细节

    • 基准元素的选择

      一般会选择第一个元素作为基准,为了避免逆序数列的极端情况,也可以随机选择一个元素作为基准,并让基准元素和首元素交换位置

    • 元素交换的过程

      • 双边循环

      • 单边循环

双边循环

//左右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;
	}
}