javascript中可能遇到的算法排序

算法复杂度

不是科班生的我,第一次看见时间复杂度之类的名词表示很懵逼,所以就找了网上的资料补习了下:

  1. 时间复杂度:是指执行算法所需要的计算工作量
  2. 空间复杂度:是指算法在计算机内执行时所需存储空间的度量
  3. 排序算法稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

这里不详细说

参考:算法的时间复杂度和空间复杂度-总结理解排序算法的稳定性算法和算法的复杂度

排序算法

排序算法
排序算法

名词解释

n:数据规模

k:“桶”的个数

In-place:占用常数内存,不占用额外内存

Out-place:占用额外内存

下面的算法实现升序

冒泡排序

顾名思义,从第一个开始比较相邻的两个,小的数网上冒。

实现

function bubleSort (arr) {
    var len = arr.length;
    var temp;
    for (var i=0; i<len-1; i++) {
        for(var j=0; j<len-1-i; j++) {
            //前一个大于后一个则交换位置
            if (arr[j] > arr[j+1]) {
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
    return arr
}

选择排序

假设第一个最小, 往后寻找比它小的,记下其index,一轮结束后将index上的数与开始假定的数交换位置。

实现

function selectionSort (arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i=0; i<len-1; i++) {
        minIndex = i;
        for (var j=i+1; j<len; j++) {
            if (arr[minIndex] > arr[j]) {
                minIndex = j
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}

插入排序

直接插入排序

打扑克的同志应该比较好理解。假设第一个元素是已经排序好的,将后一个元素提取出来,往前依次比较,比自己大的数往后挪,插入到第一次遇见比自己小的元素后面,组成一个新的序列。

实现
function insertionSort (arr) {
    var len = arr.length;
    var current, preIndex;
    for (var i=1; i<len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while (preIndex>=0 && current < arr[preIndex]) {
            arr[preIndex+1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex+1] = current;
    }
    return arr
}

希尔排序

实质为分组插入排序。为了方便理解,借用网上某哥的图,参考链接在下文。

希尔排序
希尔排序

因为是在已经分组排序过的基础上进行插入排序,所以效率高。

实现
//因为核心是插入排序,所以我们改造直接插入排序
function directInsertionSort(arr, gap) {
    var len = arr.length;
    var current, preIndex;
    for (var i=gap; i<len; i++) {
        current = arr[gap];
        preIndex = i - gap;
        while (preIndex>=0 && arr[preIndex] > current) {
            arr[preIndex+gap] = arr[preIndex];
            preIndex -= gap;
        }
        arr[preIndex+gap] = current;
    }
    return arr;
}
//编写希尔排序函数
function shellSort (arr) {
    var len = arr.length;
    var gap = 1;
    //设置gap(希尔增量),这里我们给出比较常用的h值为h = 3 * h + 1
    while (gap < len/3) {
        gap = gap * 3 + 1;
    }
    for (gap; gap>0; gap = Math.floor(gap/3)) {
        directInsertSort(arr, gap);
    }
    return arr;
}

遇见的问题,关于参数的传递:函数参数的传递可以分为按值传递和引用传递。
步长序列可以看一下wiki

折半插入排序

类似直接插入,后一个元素(拿来比较的元素)与已排序的中间值m = (i-1) >> 1(位移运算,相当于Math.floor((i-1)/2))进行比较,如果i上的值大于m上的值,则与高半区折半比较,最终将比i上值高的区域往后移,将i上的值插入。如

arr = [2, 6, 7, 6, 8]   
//前三个是已经排好的。
//range = [low, high] = [0, 2], i = 3, current = arr[i]
// m = 1, arr[i] >= arr[m], rang = [2, 2]
// m = 2, arr[i] < arr[m]
// 变换位置 ==> arr = [2, 6, 6, 7, 8]
...
...
实现
function binaryInsertionSort (arr) {
    var len = arr.length;
    var low, height, current, m;
    for (var i=1; i<len; i++) {
        current = arr[i];
        low = 0;
        height = i-1;
        while (low <= height) {
            m = (low + height) >> 1;
            if (current >= arr[m]) {// = 是为了保证稳定性
                low = m + 1;
            }else {
                height = m - 1;
            }
        }
        for (var j=i; j>low; j--) {
            arr[j] = arr[j-1];
        }
        arr[low] = current;
    }
    return arr;
}

归并排序

采取分而治之的思想。递归分组、比较产生两个已排序序列,再依次比较两组开头元素,较小的元素放入申请的新数组中。
归并函数可以通过递归、迭代实现。

递归

主要做的两件事就是分解、合并(下面并不是按照执行顺序,只是思路):

          [3, 5, 6, 2, 9]
--------------------------------------
分:   [3, 5]         [6, 2, 9]  
     [3]  [5]      [6]   [2, 9]
                        [2]  [9] 
--------------------------------------
合:                      [2, 9]
       [3, 5]         [2, 6, 9]   
          [2, 3, 5, 6, 9]  
实现
function merge (left, right) {
    var result = [];
    while (left.length && right.length) {
        var item = left[0] <= right[0] ? left.shift() : right.shift();
        result.push(item);
    }
    return result.concat(left.length ? left : right);
}
function mergeSort (arr) {
    var len = arr.length;
    if (len === 1) {
        return arr;
    }
    var m = len >> 1;
    var left = arr.slice(0, m);
    var right = arr.slice(m);
    return merge(mergeSort(left), mergeSort(right))
}

递归可能会造成堆栈溢出的问题。

迭代

主要做的两件事就是分解、合并(下面并不是按照执行顺序,只是思路):

          [3, 5, 6, 2, 9]
--------------------------------------
分:  [3]  [5]  [6]  [2]  [9] 
--------------------------------------
合:     [3, 5]  [2, 6]  [9]
           [2, 3, 5, 6] [9]
           [2, 3, 5, 6, 9] 
实现
function merge (left, right) {
    var result = [];
    while (left.length && right.length) {
        var item = left[0] <= right[0] ? left.shift() : right.shift();
        result.push(item);
    }
    return result.concat(left.length ? left : right);
}
function mergeSort (arr) {
    var len = arr.length;
    var result = [];
    //分组,每组有i个元素
    for (var i=1; i<=len; i*=2) {
        //比较相邻两组,有一组剩余就退出
        for (var j=0; j+i<len; j+=2*i) {
            left = arr.slice(j, j+i);
            right = arr.slice(j+i, j+2*i);
            result = merge(left, right);
            arr.splice(j, 2*i, ...result)
        }
    }
    return arr
}

快速排序

快速排序是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
步骤:选择一个元素作为基准,下面选择第一个,依次比较后面的元素,将小于基准的元素放在基准的左边,剩余放右边。形成左右两个分区,再递归按之前的步骤排序。

实现

function swap (arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
function partition (arr, left, right) {
    var pivot = left;
    var index = left + 1;
    for (var i=index; i<=right; i++) {
        if (arr[i] < arr[pivot]) {
            swap(arr, index, i);
            index++;
        }
    }
    swap(arr, index-1, pivot);
    return index-1
}
function quickSort (arr, left, right) {
    var len = arr.length;
    var partitionIndex;
    left = typeof left === 'number' ? left : 0;
    right = typeof right === 'number' ? right : len-1;
    if (left < right) {
        partitionIndex = partition (arr, left, right);
        quickSort(arr, left, partitionIndex-1);
        quickSort(arr, partitionIndex+1, right);
    }
    return arr;
}

快速排序排序效率非常高. 虽然它运行最糟糕时将达到O(n²)的时间复杂度, 但通常, 平均来看, 它的时间复杂为O(nlogn), 比同样为O(nlogn)时间复杂度的归并排序还要快. 快速排序似乎更偏爱乱序的数列, 越是乱序的数列, 它相比其他排序而言, 相对效率更高.
Chrome的v8引擎为了高效排序, 在排序数据超过了10条时, 便会采用快速排序. 对于10条及以下的数据采用的便是插入排序.

参考

十大经典排序算法
JS中可能用得到的全部的排序算法

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

推荐阅读更多精彩内容

  • 概述排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的...
    Luc_阅读 2,247评论 0 35
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,149评论 0 52
  • 概述:排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    每天刷两次牙阅读 3,723评论 0 15
  • 今天晨读跟大家分享的书是《人生定位:特劳特叫你营销自己》,作者通过三招帮助普通人快速成功。 001 学会"靠"别人...
    菲哈Fayhaa阅读 157评论 0 0
  • 生如夏花般绚烂,死如秋叶之静美。 总对自己有非一般的感觉,想象,美好的一面有,阳光,大气,理解包容。 最近因为大姨...
    Summer小王子阅读 180评论 0 0