算法排序-快速排序

91 阅读2分钟

快排一种分而治之的算法,目前有较为常用的有两种方法:双指针,填挖法。

双指针

改方法通过前后两个左右指针分别向右和左进行移动,左指针找比Pivot的数,右指针找比Pivot小的数,然后进行交互。

// 双指针法
public int partitionTwoPointers(int[] data, int lo, int hi) {
    int v = data[lo];
    int i = lo;
    int j = hi + 1;
    while (true) {
        while (data[++i] <= v) {// 直到一个比v大的
            if (i == hi) {
                break;
            }
        }
        while (data[--j] >= v) {// 找到一个比v小的
            if (j == lo) {
                break;
            }
        }
        if (i >= j) {// 两个指针相遇,跳出循环
            break;
        }
        // 交换位置
        swap(data, i, j);
    }
    // data[j]要么比v小,要么等于j==lo
    swap(data, j, lo);// 此时将较小的j移动到lo处,以保证data[lo..j-1]<=data[j]<=data[j+1..hi]
    return j;
}

填挖法

所谓填挖,分两个操作一个挖,一个填。第一步就是将Pivot处挖出来,第二步然后从右开始找一个比Pivot小的数,挖出来填过去,第三步从左找个大于Pivot的值,挖出来填到刚刚右侧的地方。然后重复到左右在挖的地方相遇,这个时候把pivot值填过去。

public int partitionLomuto(int[] data, int lo, int hi) {
    int pivot = data[lo];
    int holeIndex = lo;
    int i = lo+1 ;
    int j = hi;
    boolean isFromRightToLeft = true;
    while (i <=j) {
        if (isFromRightToLeft) {//从右到左扫描
            if (data[j] < pivot) {// swap
                swap(data, holeIndex, j);
                holeIndex = j;
                isFromRightToLeft = false;//交换方向
            }
            j--;//继续扫描
        } else {//从左到右扫描
            if (data[i] > pivot) {// swap
                swap(data, holeIndex, i);
                holeIndex = i;
                isFromRightToLeft = true;//交换方向
            }
            i++;//继续扫描
        }
    }
    data[holeIndex]=pivot;//填充回去
    return holeIndex;
}

sort方法

public void sort(int[] data, int lo, int hi) {
        if (lo >= hi) {
            return;
        }
        int partition = partitionXXX(data, lo, hi);
        sort(data, lo, partition - 1);
        sort(data, partition + 1, hi);
    }

复杂度

快排本身受原始数据顺序影响较大,取决于pivot是否能够很好的均分整个数据。最差情况为O(n^2),平均情况O(nlogn)