面试官,您要的快排

今天看到 V2EX 上有人讨论 社招还会问 “请手写选择排序算法” 吗?,看来仍是有不少人关心的。结合本身最近面试的经历,我能够明确的告诉你们,相似这种问题,只要你的工做经验小于 10 年,基本上逃不掉。劝你们不如抽点时间早作准备。面试

简式快排

面试中遇到问快排的,如上面那个帖子中的状况。你就能够上一份简式快排了,何谓简式?最短的代码表述快排的思想。算法

快排的思想,实质是分治法。基于什么来分?找一个支点来分,一般称之为 pivot, 而这个分的过程称之为 partition, 基于以上两点,咱们用递归的方式描述快排:ui

void quicksort(int arr[], int l, int r) {
    if (l < r) {
        int pivot = partition(arr, l, r);
        quicksort(arr, l, pivot-1);
        quicksort(arr, pivot+1, r);
    }
}

如何?简单吧。有人说面试的时候手写快排,若是提早没有背下来的话,确定歇菜。我不认为这样基础的算法是须要背的,上面这个递归,如此简洁,如此美,真的须要硬记?code

有人说,这个好理解,关键在于 partition 如何实现。的确,partition 是快排的灵魂。CLRS 里采用了以尾巴为支点的策略,我在这里与其保持一致:排序

int partition(int arr[], int l, int r) {
    int k = l, pivot = arr[r];
    for (int i = l; i < r; ++i)
        if (arr[i] <= pivot) std::swap(arr[i], arr[k++]);
    std::swap(arr[k], arr[r]);
    return k;
}

这算法用白话说,就是从头至尾迭代,和支点比较,大的无论,小的换。换了才日后看。最后支点戳中间,算是分界线。递归

get

3,7,8,5,2,1,9,5,4

这么来一下,就成了:it

3,2,1,4,7,8,9,5,5
^^^^^ | ^^^^^^^^^

而后一样的手法分别解决两边,这样递归的解下去。io

来份三明治

其实上面的那份,已经能够解决面试中的问题了。但其实有很大的缺陷,如当全部元素都相同的状况下,partition 将一直返回 r, 递归的深度高达 N,每一次递归中 partition 又循环 N 次,时间复杂度直接飙到了 O(N^2). 这显然很是的不值当。class

因而咱们以为分两份太粗,分三份试试?

5 7 4 3 1 2 6 5 5

也来那么一下,成为:

4 3 1 2 5 5 5 7 6
^^^^^^^ |   | ^^^

这就是三明治的原理了,左边是小于 pivot 的,中间是等于 pivot 的,右边是大于 pivot 的。中间部分不参与递归,分治的是两边。

咱们需稍稍改变下 partition 的实现,显然咱们此次但愿返回的两个支点(左右边界):

// 3-way partition
std::pair<int, int> partition(int arr[], int l, int r) {
    int k = l, p = r;
    for (int i = l; i < p; )
        if (arr[i] < arr[r]) std::swap(arr[i++], arr[k++]);
        else if (arr[i] == arr[r]) std::swap(arr[i], arr[--p]);
        else ++i;
    // move pivots to centre
    int m = std::min(p-k, r-p+1);
    for (int i = 0; i < m; ++i)
        std::swap(arr[k+i], arr[r-i]);
    return std::make_pair(k, r-p+k);
}

quicksort 也须要稍稍改变一点:

void quicksort(int arr[], int l, int r) {
    if (l < r) {
        auto pivot = partition(arr, l, r);
        quicksort(arr, l, pivot.first-1);
        quicksort(arr, pivot.second+1, r);
    }
}

若是在遇到所有元素相同的状况,时间复杂度成功的减小到 O(N). 算是一个突破吧。

东北乱炖

其实 CLRS 随后还提到了以随机位置为 pivot 的思路,我称之为东北乱炖。那是只针对 pivot 选取的改变,基于上述代码,改造起来是很是容易的。这里就不作过多实现,留做感兴趣者本身练习吧。


上述三道菜,基本可以解决面试中可能遇到的种种状况了。让面试官吃饱,颇有必要~

相关文章
相关标签/搜索