基本思想
快速排序(QuickSort)是对冒泡排序的一种改进。快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:
1.从要排序的数据中取一个数为“基准数”。
2.通过一趟排序将要排序的数据分割成独立的两部分,其中左边的数据都比“基准数”小,右边的数据都比“基准数”大。
3.然后再按步骤2对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
该思想可以概括为:挖坑填数 + 分治法。
分治法
分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如快速排序,归并排序,傅立叶变换(快速傅立叶变换)等等
可能大家看到这里还是不太懂,没关系,对照着下边代码以及注释看就很清楚了
快速排序递归实现
递归体
public static void QuickSort(int []array,int left,int right){
if(left > right)return;
int index = getindex(array,left,right);
//根据基准值把数组分为左右两个区间。
// 其中左区间全部小于基准值,右区间全部大于基准值
// 递归处理左右区间
QuickSort(array, left, index - 1);
QuickSort(array, index + 1, right);
}
获取基准值代码
public static int getindex(int []array,int left,int right){
int begin = left;
int end = right;
int index = array[right]; //取基准值为最右侧。
while (begin < end){
// 然后从左开始找,找到一个数大于基准值停止。
while (begin < end && array[begin] <= index){
begin++;
}
//然后从右继续找,找到小于基准值停止,交换他们位置。
// 如果左右指针不重合,也就是begin<end,继续循环。
while (begin < end && array[end] >= index){
end--;
}
//如果循环结束,也就是左右指针重合,此时重合位置一定大于基准值。
//证明:1.如果因为begin++导致和end重合。因为重合之前begin最后一次循环是找到了一个大于基准值的数,交换给了end,array[end]一定大于基准值,而begin和其重合。则说明重合位置在end处,也是大于基准值的
//2.如果end—-导致和begin重合。由于能运行到end—-,说明上边begin++循环停止。也就是begin找到了一个比基准值大的数,停止了循环。此时end和begin重合,说明该位置为array[begin]一定比基准值大
swap(array,begin,end);
}
// 将重合的位置元素和基准值元素交换,然后return重合位置的下标,作为下一步划分左右区间的基准元素
swap(array,begin,right);
return begin;
}
交换函数(标准三连)
public static void swap(int []array,int a,int b){
int tem = array[a];
array[a] = array[b];
array[b] = tem;
}
快速排序非递归实现
采用非递归的方法,首先要想到栈的使用,通过阅读递归调用部分的代码,思考如何用栈来代替。
递归调用的核心代码是 getindex(array, left, right);
每次循环都必须包含这句核心代码,可以想到,如果要对该行代码实现循环,只能对left和right采取操作,所以我们在栈中压入left和right,每个循环弹出一对left和right,用于模拟递归核心代码的实现,
当栈空后就说明没有需要排序的部分了,结束循环
具体代码如下:
public static void QuickSort2(int []array){
//stack 存下标。
Stack<Integer> stack = new Stack<>();
//初始操作,边界入stack 的顺序和pop的顺序相反。
stack.push(array.length-1);
stack.push(0);
while (!stack.isEmpty()) {
//弹出左边界和右边界。
int left = stack.pop();
int right = stack.pop();
//如果left>=rigth,说明栈里只有一个元素,则排序完成。
if(left>=right)continue;
//使用上边递归时定义的getindex(array,left,right),获取到基准值,将数组分为:左区间-元素全部小于基准值。右区间-元素全部大于基准值
int index = getindex(array,left,right);
//栈push左右区间的边界,继续处理
// [index+1,right]为右区间
stack.push(right);
stack.push(index+1);
//[left,right-1]为左区间
stack.push(index-1);
stack.push(left);
}
}