快速排序
快速排序是很流行的排序方法,因为在大多数情况下,快速排序都是最快的,时间复杂度为O(N*log(N))。快速排序本质上通过把一个数组划分为两个子数组,然后递归调用自身为每一个子数组进行快速排序。算法还必须选择枢纽(Pivot)以及对小的划分区域进行排序。
基本的递归快速排序的代码实现很简单:
public void recQuickSort(int left, int right){ if (right - left <= 0) //长度是1 return; //已经排序完成 else{ int partition = partitionIt(left, right); recQuickSort(left, partition - 1); //对左半部分排序 recQuickSort(partition, right); //对右半部分排序 } }
主要有三个步骤:
1. 把数组或者子数组划分为左边(较小的一组),和右边(较大的一组);
2. 调用自身对左边一组进行排序;
3. 再次调用自身对右边的一组进行排序。
经过一次划分后,所有在左边子数组的数据项都小于在右边子数组的数据项。只要对左边子数组和右边子数组分别进行排序,整个数组就是有序的了。对子数组的排序通过递归调用自身实现。
recQuickSort()方法的参数决定了要排序数组的左右两端的位置。这个方法首先检查数组是否只包含一个数据项,如果只包含一个数据项,那么就定义数组已经有序,方法return,这就是递归的base condition。
如果数组包含两个或者更多的数据项,算法就调用partitionIt()方法对数组进行划分。方法返回分割边界的下标数值,指向右边子数组最左边的数据项。
partitionIt()方法:
public int partitionIt(int left, int right, long pivot){ int leftPtr = left -1; int rightPtr = right + 1; while (true){ while (leftPtr < right && theArray[++leftPtr] < pivot) ; //nop while (rightPtr > leftPtr && theArray[--rightPtr] > pivot) ; //nop if (leftPtr >= rightPtr) //两个指针重合 break; else swap(leftPtr, rightPtr); //交换 } return leftPtr; //返回划分 } public void swap(int dex1, int dex2){ long temp; temp = theArray[dex1]; theArray[dex1] = theArray[dex2]; theArray[dex2] = temp; }
划分算法由两个指针开始工作,两个指针分别指向数组的两头。在左边的指针,leftPtr,向右移动,而在右边的指针,rightPtr,向左移动。注意到,leftPtr初始化是在第一个数据项的左边一位,rightPtr是在最后一个数据项的右边一位,这是因为在它们工作之前,它们都要分别的加一和减一。
停止和交换
当leftPtr遇到比枢纽小的数据项时,它继续右移,因为这个数据项的位置已经处在数组的正确一边了。但是当遇到比枢纽大的数据项时,它就停下来。类似的,当rightPtr遇到大于枢纽的数据项时,它继续左移,但是当发现比枢纽小的数据项时,它就停下来。两个内层的while循环,第一个应用于leftPtr,第二个应用于rightPtr,控制整个扫描过程。
当两个while循环都退出后,leftPtr和rightPtr都指着数组中的错误位置(左边的数比枢纽大,右边的数比枢纽小),交换两个数据项。交换之后,继续移动两个指针,当出现以上的情况时,再次交换数据项。当两个指针最终相遇时,划分过程结束,退出外层的while循环。
对数组进行划分后,recQuickSort()递归调用自身,数组左边的部分调用一次,从left到partition - 1位置上的数据项进行排序,右边数组的部分也调用一次,对从partition到right的位置上的数据项进行排序。
选择枢纽
应该选择具体的一个数据项的关键字的值作为枢纽;
可以选择任意一个数据项作为枢纽,我们假设总是选择待划分的子数组的最右端的数据项作为枢纽。
划分完成后,如果枢纽被插入到了左右子数组之间的分界处,那么枢纽就落在排序之后的最终位置上了。
下面运用以上的快速排序实现一个数组:
class ArrayIns{ private long[] theArray; private int nElems; public ArrayIns(int max){ theArray = new long[max]; nElems = 0; } public void insert(long value){ theArray[nElems] = value; nElems++; } public void display(){ System.out.println("A="); for (int j = 0; j < nElems; j++) System.out.print(theArray[j] + " "); } public void QuickSort(){ recQuickSort(0, nElems - 1); } public void recQuickSort(int left, int right){ if (right - left <= 0) return; else { long pivot = theArray[right]; //rightmost item int partition = partitionIt(left, right, pivot); recQuickSort(left, partition - 1); recQuickSort(partition + 1, right); } } public int partitionIt(int left, int right, long pivot){ int leftPtr = left -1; int rightPtr = right + 1; while (true){ while (leftPtr < right && theArray[++leftPtr] < pivot) ; //nop while (rightPtr > leftPtr && theArray[--rightPtr] > pivot) ; //nop if (leftPtr >= rightPtr) //两个指针重合 break; else swap(leftPtr, rightPtr); //交换 } return leftPtr; //返回划分 } public void swap(int dex1, int dex2){ long temp; temp = theArray[dex1]; theArray[dex1] = theArray[dex2]; theArray[dex2] = temp; } }
浙公网安备 33010602011771号