快速排序

JavaScript实现

网上大部分都是基于这种写法:

//JavaScript
function quicksort(arr) {
    if(arr.length <= 1) {
        return arr;
    }
    //这里直接选取中间元素为枢轴
    var pivotIndex = Math.floor(arr.length/2);
    var pivot = arr.splice(pivotIndex, 1)[0];
    var left = [];
    var right = [];
    for (var i = 0; i < arr.length; i++){
        if(arr[i] < pivot) {
            left.push(arr[i]);
        }else {
            right.push(arr[i]);
        }
    }
    return quicksort(left).concat([pivot], quicksort(right));
}

但是这种个写法很明显是需要0(n)的额外空间的,那如果我们基于原地分区该怎样实现呢?

partition的不同版本

记得在学习数据结构的时候,我们是下面这样实现的,通过两个指针low,high从两端往中间逼近。

//C++
int partition(int arr[], int low, int high){
    //直接选取第一个元素为枢轴
    int pivot = arr[low];
    while(low < high) {
        //注意下面的while内部也要进行越界判断!
        while(low < high && arr[high] > pivot) { 
            high--;
        }
        while(low < high && arr[low] <= pivot) {
            low++;
        }
        arr[low] = pivot;
        return low;
    }
}

void quicksort(int arr[], int low, int high) {
    int loc = 0;
    if(low < high) {
        loc = partition(arr, low, high);
        quicksort(arr, low, loc - 1);
        quicksort(arr, loc + 1, high);
    }
}

因为partition函数是快排的核心,所以partition的优化方法有很多,可以分为两大类,单向扫描版本双向扫描版本,上面就是双向扫描的例子,方法还是很好理解的。《算法导论》那本书上还提出了另一种基于单向扫描的partition方法,如下:

int partition(int arr[], int low, int high){    
    x = arr[high]; //这里直接选取最后一个元素为比较元素,后面还可以根据random来改进
    int i = low - 1; //这个i为慢速移动下标,必须为比最小的下标p小1,否则两个元素的序列(比如2,1)就无法交换
        int j = low;
    for(; j < high; j++) {
        if(arr[j] <= x){
            swap(&arr[++i], &arr[j]);
        }
    }
    swap(&arr[i+1], &a[j]);
    return i + 1;
}

quicksort(int arr[], int low, int high) {
    if(low < high) {
        int index = partition(arr, low, high);
        quicksort(arr, low, index - 1);
        quicksort(arr, index + 1, high);
    }
}

算法示意图如下:


单向扫描优化版本

可以优化的地方:

  1. <=x 改为<x,减少交换次数
  2. i的初始值直接设为low
  3. i = j时的交换是多余的可以优化掉
//cpp优化版本
int partition (int a[], low, high) {
    int x = a[high];
    int i = low;
        int j = low;
    for(; j < high; j++) {
        if(a[j] < x) {
            if(i != j){
                swap(&a[i], &a[j]); 
            }
                        i++;
        }
    }
    swap(&a[i], &a[j]);
    return i;
}

随机化版本

之前的方法都是基于一个前提:所有的排列都是等概率的,但是实际工程中并不会总是成立。有时候我们可以通过在算法中引入随机性,从而使得算法对所有的输入都能获得较好的期望,比如针对一个几乎有序的输入序列进行排序的问题。很多人都选择随机化版本作为大数据输入情况下的快排。
思路如下:

randomPartition(arr, low, high){
    i = random(low, high);
    swap(a[i], a[high]);
    return partition(arr, low, high);
}

randomQuicksort(arr, low, high) {
    if(low < high){
        int index = randomPartition(arr, low, high);
        randomQuicksort(arr, low, index - 1);
        randomQuicksort(arr, index + 1, high)
    }
}

其实就是多了最开始的枢轴随机化一个数这步,然后再与最后的元素交换,后面都一样一样滴。

单向扫描版本的JavaScript实现

//js
function quicksort (arr) {
    function swap (arr, m, n) {
        var tmp = arr[m];
        arr[m] = arr[n];
        arr[n] = tmp;
    }
    function partition (arr, low, high) {
        var x = arr[high];
        var i = low - 1;
        for(var j = low; j < high; j++) {
            if(arr[j] <= x){
                i++;
                swap(arr, i, j);
            }
        }
        swap(arr, i + 1, high);
        return i + 1;
    }
    function sort (arr, low, high) {
        if(low < high) {
            var pivotIndex = partition(arr, low, high);
            sort(arr, low, pivotIndex - 1);
            sort(arr, pivotIndex + 1, high);
        }
        return arr;
    }

    return sort(arr, 0, arr.length - 1);
}

同理,其中的partition也可以优化

    function partition (arr, low, high) {
        var x = arr[high];
        var i = low;
        for(var j = low; j < high; j++){
            if(arr[j] < x){
                if(x != j){
                    swap(arr, i, j);
                    i++;
                }
            }
        }
        swap(arr, i, high);
        return i;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容