快速排序

快速排序是很流行的排序方法,因为在大多数情况下,快速排序都是最快的,时间复杂度为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;
    }
}

 

posted on 2017-02-10 04:44  LAHWF  阅读(159)  评论(0)    收藏  举报

导航