基本流程:
a.选取第一个元素作为pivot
b0.先处理R处的元素
b1.R处的元素与pivot比较,如果比pivot元素大,元素覆盖R位置,R左移,直到与L重合
b2.R处的元素与pivot比较,如果比pivot元素小,元素覆盖L位置,L右移,直到与L重合
b3.如果处理L和R处的元素没有变化时,继续前移,如果发生了元素覆盖,继续处理另一个方向(R和L)的元素
c1.用同样的逻辑处理pivot左侧的(经过b处理过的左侧)子序列(递归)
c2.用同样的逻辑处理pivot右侧的(经过b处理过的右侧)子序列(递归)
这里用一副图来展示整个过程:
思考过程中总结的要点:
1.这个方法是void返回,所以传入的int[] array就是我们处理的对象,返回的也是这个对象,只不过是排序完成的对象。如果说是传入一个局部处理的int[] subArray,使用0,subArray.length-1进行处理的话,其实就完全没必要传入L,R了,但如此一来我们需要对Array进行不停地拼装,且在递归中,这种拼装消耗性能,且处理麻烦;
2.L,R都是在变化的,在方法体中,排序完成后,需要对左子序列和右子序列的头尾坐标进行标记,并进行后续的处理;
3.处理的边界就是L与R相等了,此时说明其中只有一个元素,不需要排序了;
代码与一些说明:
public class QuickSort { /** * 这是一个void方法,不用返回处理完的子序列,所有的操作完成了对子序列的处理与值的覆盖后,整个array自然排序完成 * * @param array 待处理的数组,所有的内部递归调用的都是这个数组本身,不同的只是处理的下标区间 * @param low 下标低位 * @param high 下标高位 */ public static void quickSort(int[] array, int low, int high) { //如果两者相等,说明只有一个元素了。尽可能在外部判断好 if (low >= high) { return; } //选取标定值,一般就选取左边位置的标定值 int pivotValue = array[low]; //之所以要进行赋值,是因为left与right是动态变化的,而low和high要在最后作为递归的确定边界,不能被覆盖 int left = low; int right = high; //这里的的left==right其实就是循环的边界条件。达到了相等说明这一轮的处理结束 while (left < right) { //如果右侧的值大于等于标定值,移动R,边界条件是L==R while (array[right] >= pivotValue && left < right) { right--; } //到了这一步有两个条件,一个是右值比标定值小了,需要进行值覆盖;另一个是L==R了 //这里将右侧的这个值付给了左侧,那么需要left++吗?其实不需要,因为天然地这个小于pivot的关系符合下一个循环 if (array[right] < pivotValue) { array[left] = array[right]; } while (array[left] <= pivotValue && left < right) { left++; } if (array[left] > pivotValue) { array[right] = array[left]; } //如果此时L==R,那么pivot就赋给array[left]或者array[right]; //如果此时L<R,那么说明还有一些中间元素需要处理。即没有与pivot比较的元素还存在, // 那么上一步已经把大于pivot的元素放到了右侧,这里的位置将会在下一轮被右侧传入的值覆盖,所以设置什么值也无所谓了。 if (left == right) { array[left] = pivotValue; } } quickSort(array, low, left - 1); quickSort(array, left + 1, high); } public static void main(String[] args) { int[] array = {1, 3, 2, 44, 33, 12, 222, 32, 11, 22, 33, 12, 3, 2, 1}; quickSort(array, 0, array.length - 1); System.out.println(Arrays.toString(array)); } }
加入了排序的方向:
public class QuickSort { private Order order; /** * 排序方向 */ enum Order { /** * 默认,顺序 */ ASC, /** * 可选,逆序 */ DES; Order() { } } public QuickSort(Order order) { this.order = order; } /** * 可设置排序方向 * * @param order */ public void setOrder(Order order) { this.order = order; } /** * 这是一个void方法,不用返回处理完的子序列,所有的操作完成了对子序列的处理与值的覆盖后,整个array自然排序完成 * * @param array 待处理的数组,所有的内部递归调用的都是这个数组本身,不同的只是处理的下标区间 * @param low 下标低位 * @param high 下标高位 */ public void quickSort(int[] array, int low, int high) { //如果两者相等,说明只有一个元素了。尽可能在外部判断好 if (low >= high) { return; } //选取标定值,一般就选取左边位置的标定值 int pivotValue = array[low]; //之所以要进行赋值,是因为left与right是动态变化的,而low和high要在最后作为递归的确定边界,不能被覆盖 int left = low; int right = high; //这里的的相等其实就是循环的边界条件。达到了相等说明这一轮的处理结束 while (left < right) { //如果右侧的值大于等于标定值,移动R,边界条件是L==R right = processRights(array, pivotValue, left, right); left = processLeft(array, pivotValue, left, right); //如果此时L==R,那么pivot就赋给array[left]或者array[right]; //如果此时L<R,那么说明还有一些中间元素需要处理。即没有与pivot比较的元素还存在, // 那么上一步已经把大于pivot的元素放到了右侧,这里的位置将会在下一轮被右侧传入的值覆盖,所以设置什么值也无所谓了。 setPivotIfEqual(array, pivotValue, left, right); } if (low < left - 1) { quickSort(array, low, left - 1); } if (left + 1 < high) { quickSort(array, left + 1, high); } } private void setPivotIfEqual(int[] array, int pivotValue, int left, int right) { if (left == right) { array[left] = pivotValue; } } private int processLeft(int[] array, int pivotValue, int left, int right) { if (order == Order.ASC) { while (array[left] <= pivotValue && left < right) { left++; } if (array[left] > pivotValue) { array[right] = array[left]; } return left; } else { while (array[left] >= pivotValue && left < right) { left++; } if (array[left] < pivotValue) { array[right] = array[left]; } return left; } } private int processRights(int[] array, int pivotValue, int left, int right) { if (order == Order.ASC) { while (array[right] >= pivotValue && left < right) { right--; } //到了这一步有两个条件,一个是右值比标定值小了,需要进行值覆盖;另一个是L==R了 //这里将右侧的这个值付给了左侧,那么需要left++吗?其实不需要,因为天然地这个小于pivot的关系符合下一个循环 if (array[right] < pivotValue) { array[left] = array[right]; } return right; } else { while (array[right] <= pivotValue && left < right) { right--; } //到了这一步有两个条件,一个是右值比标定值小了,需要进行值覆盖;另一个是L==R了 //这里将右侧的这个值付给了左侧,那么需要left++吗?其实不需要,因为天然地这个小于pivot的关系符合下一个循环 if (array[right] > pivotValue) { array[left] = array[right]; } return right; } } public static void main(String[] args) { int[] array = {1, 3, 2, 44, 33, 12, 222, 32, 11, 22, 33, 12, 3, 2, 1}; new QuickSort(Order.DES).quickSort(array, 0, array.length - 1); System.out.println(Arrays.toString(array)); } }