JavaScript算法-排序算法

? 此生之路,我将走过;走过这一次,便再也无法重来。所有力所能及的善行,所有充盈于心的善意,我将毫不吝惜,即刻倾予。我将再不拖延,再不淡漠,只因此生之路,再也无法重来。

对计算机中存储的数据执行的两种最常见操作是排序和索引。下述阐述的排序方式,暂且都是用数组进行测试(从小到大)。

var dataAry = [5, 4, 3, 7, 1, 2, 8, 6, 9]; // 测试数组
/**
 *【工具方法】交换数组中两个值
 * @param ary 数组
 * @param i 下标i
 * @param j 下标j
 */
function swap(ary, i, j) {
    var temp = ary[i];
    ary[i] = ary[j];
    ary[j] = temp;
}

冒泡排序

? 之所以称为冒泡排序是因为使用这种排序算法时,数据值会像气泡一样从数组的一端漂浮到另一端。如,从小到大排序:其会比较相邻的数据,当左侧值大于右侧值时将它们进行交换。

冒泡排序算法的运作如下:(从小到大)

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
function bubbleSort(dataAry) {
    var len = dataAry.length;
    for(var i = 0; i < len; i++) {
        for(var j = 0; j < len - i; j++) {
            if(dataAry[j] > dataAry[j+1]) {
                swap(dataAry, j, j+1);
            }
        }
    }
    return dataAry;
}
// 测试
console.log(bubbleSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

选择排序

? 从数组的第一个数据开始,将第一个数据和其他数据进行比较。它的工作原理是每一次从待排序的数据中选出最小(或最大)的一个数据,存放在序列的起始位置,直到全部待排序的数据元素排完。

function selectionSort(dataAry) {
    var len = dataAry.length;
    for(var i = 0; i < len - 1; i++) {
        for(var j = i + 1; j < len; j++) {
            if(dataAry[i] > dataAry[j]) {
                swap(dataAry, i , j);
            }
        }
    }
    return dataAry;
}
// 测试
console.log(selectionSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

插入排序

? 插入排序有两个循环,外循环将数组元素挨个移动,而内循环则对外循环中选定的元素及它后面的那个元素比较。如果外循环中选中元素小,那么数组元素会向右移动,为内循环中的这个元素腾出位置。每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。

直接插入排序算法的说明<有1-9张标有是数字的卡片>:

1. 取出第一张卡片直接放到桌子最左侧

2. 取出第二张卡片,如果该卡片标注的数字小于第一张,则将第一张卡片向右移动一格;否则放到右侧;

3. 取出第n张卡片,将该卡片与第n-1、n-2···第1张对比,小于等于则右移继续对比,大于则停止对比将该值插入对应位置。

function insertionSort(dataAry) {
    var temp, inner;
    // 外循环(跳过第一个值,因为第一个直接放到最左侧)
    for(var outer = 1, len = dataAry.length; outer < len; outer++) {
        temp = dataAry[outer];
        inner = outer;
        // 内循环,比左侧值小就移动让位
        while(inner > 0 && (dataAry[inner - 1] >= temp)) {
            dataAry[inner] = dataAry[inner - 1];
            inner--;
        }
        dataAry[inner] = temp;
    }
    return dataAry
}
// 测试
console.log(insertionSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

希尔排序

? 该算法在插入排序的基础上做了相应改善。其需要预先(或动态)定义一个间隔序列来表示在排序过程中进行比较的元素之间的间隔。对被间隔的每组元素使用直接插入排序算法排序;随着间隔序列中的数逐渐减小,每组包含的元素越来越多,当间隔减至1时,整个文件恰被分成一组,算法便终止。这确保了在开始最后一次处理时,大部分元素都已在正确位置,必须再进行多次数据交换,这就是希尔排序比插入排序更高效的地方。

希尔排序算法说明:

1. 先取一个小于n的整数d1作为第一个间隔,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;

2. 取第二个间隔值d2<d1重复上述的分组和排序;

3. 直至所取的间隔为1,即所有记录放在同一组中进行直接插入排序为止。

/**
 * 希尔排序
 * @param dataAry 数据数组
 * @param gaps    间隔数组
 */
function shellSort(dataAry, gaps) {
    var temp, inner, currentGap;
    // 遍历间隔
    for(var g = 0, gLen = gaps.length; g < gLen; g++) {
        // 当前间隔
        currentGap = gaps[g];
        // 直接插入法外循环
        for(var outer = currentGap, len = dataAry.length; outer < len; outer++) {
           temp = dataAry[outer];
           inner = outer;
           // 直接插入法内循环(注意对比间隔值)
           while(inner >= currentGap && (dataAry[inner - currentGap] >= temp)) {
               dataAry[inner] = dataAry[inner - currentGap];
               inner = inner - currentGap;
           }
           dataAry[inner] = temp;
        }
    }
    return dataAry;
}
// 测试
console.log(shellSort(dataAry, [3, 2, 1])); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

归并排序

把一系列排好序的子序列合并成一个大的完整有序序列。归并排序通常使用递归来实现。

自顶向下的归并排序(递归)

function mergeSort(dataAry) {
    if(dataAry.length === 1) {
        return dataAry;
    }
    var mid = Math.floor(dataAry.length/2);
    var leftAry = dataAry.slice(0, mid);
    var rightAry = dataAry.slice(mid);
    return merge(mergeSort(leftAry), mergeSort(rightAry));
}
/**
 * 合并数组
 * @param dataAry
 */
function merge(leftAry, rightAry) {
    var result = [];
    while(leftAry.length > 0 && rightAry.length > 0) {
        if(leftAry[0] < rightAry[0]) {
            result.push(leftAry.shift());
        }else {
            result.push(rightAry.shift());
        }
    }
    return result.concat(leftAry).concat(rightAry);
}
// 测试
console.log(mergeSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

? 上述mergeSort()被执行了2n-1次(上述被执行了17次),一旦数组元素个数增多,采用递归的方式将不再可取,元素个数超过1500在Firfox下就会造成栈溢出!

自底向上的归并排序(迭代)

? 首先将数据集分解为一组只有一个元素的数组,然后通过创建一组左右数组将其合并,每次合并确保数据已排好序,直到最后所有数据排序完成。

function mergeSort(dataAry) {
    if(dataAry.length < 2) {
        return dataAry;
    }
    var step = 1; // 控制子序列大小
    var left, right; // 左、右下标
    while(step < dataAry.length) {
        left = 0;
        right = step;

        while((right + step) <= dataAry.length) {
            mergeArrays(dataAry, left, left+step, right, right+step);
            left = right + step;
            right = left + step;
        }
        // 不能被分组的元素
        if(right < dataAry.length) {
            mergeArrays(dataAry, left, left+step, right, dataAry.length);
        }
        step = step * 2;
    }
    return dataAry;
}

/**
 * 合并数组<包含头,不包含尾>
 * @param ary
 * @param startLeft
 * @param endLeft
 * @param startRight
 * @param endRight
 */
function mergeArrays(ary, startLeft, endLeft, startRight, endRight) {
    var leftAry = new Array(endLeft - startLeft + 1),
        rightAry = new Array(endRight - startRight + 1);

    var k = startLeft;
    for(var i = 0; i < (leftAry.length - 1); i++) {
        leftAry[i] = ary[k];
        k++;
    }
    leftAry[leftAry.length - 1] = Infinity; // 哨兵值

    k = startRight;
    for(var j = 0; j < (rightAry.length - 1); j++) {
        rightAry[j] = ary[k];
        k++;
    }
    rightAry[rightAry.length - 1] = Infinity; // 哨兵值

    // 对比左右元素大小
    var m = 0, n = 0;
    for(var k = startLeft; k < endRight; k++) {
        if(leftAry[m] <= rightAry[n]) {
            ary[k] = leftAry[m];
            m++;
        }else {
            ary[k] = rightAry[n];
            n++;
        }
    }
}
// 测试
console.log(quickSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

快速排序

? 快速排序是处理大数据集最快的排序算法之一。其核心思想为“分而治之”。设立基值,通过递归的方式将数据一次分解为包含较小元素和较大元素的不同子序列,然后不断重复,直到所有数据变为有序。

快速排序算法说明:

1. 选择一个基准元素,将列表分隔为两个子序列;

2. 将所有小于基准元素的数据放在基准值的左侧,大于基准值元素的数据放在基准值右侧;

3. 分别对较小元素的子序列和较大元素的子序列重复步骤1和2。

快速排序<方式一>:从左侧起开始循环数组与基值进行对比

function quickSort(dataAry) {
    if(dataAry.length === 0) {
        return [];
    }
    var left = [], right = [];
    var pivot = dataAry[0];
    for(var i = 1; i < dataAry.length; i++) {
        dataAry[i] < pivot ? left.push(dataAry[i]) : right.push(dataAry[i]);
    }
    return quickSort(left).concat(pivot, quickSort(right));
}
// 测试
console.log(quickSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

快速排序<方式二>:从左、右两侧一起移动,与基值进行对比

function quickSort(arr, left, right) {
    if (left > right) return;
    var temp,
        i = left,
        j = right;
    // 设基准值
    var pivot = arr[left];
    while (i != j) {
        // 先从右侧开始(找到小于基值的第一个数据)
        while (i < j && arr[j] >= pivot) {
            j--;
        }
        // 然后从左侧开始(找到大于基值的第一个数据)
        while (i < j && arr[i] <= pivot) {
            i++;
        }
        // 交换数据上述两个元素,继续查找,直到i和j相遇
        if (i < j) {
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    // 将基值与相遇数据进行交换<确保了基值位于元素中央位置>
    temp = arr[i];
    arr[i] = arr[left];
    arr[left] = temp;
    // 基值左侧数组按上述方式递归执行
    quickSort(arr, left, i - 1);
    // 基值右侧数组按上述方式递归执行
    quickSort(arr, i + 1, right);
    return arr;
}
// 测试
console.log(quickSort(dataAry, 0, dataAry.length - 1)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

总结

冒泡排序、选择排序、插入排序为基本排序算法,希尔排序、归并排序(迭代)、快速排序为高级排序算法

排序算法 100条所耗时间 10000条所耗时间 100000条所耗时间
冒泡排序 16毫秒 584毫秒 54619毫秒
选择排序 <1毫秒 183毫秒 18175毫秒
插入排序 <1毫秒 27毫秒 2660毫秒
希尔排序 <1毫秒 13毫秒 1384毫秒
归并排序(迭代) <1毫秒 6毫秒 40毫秒
快速排序(方式一) <1毫秒 17毫秒 98毫秒
快速排序(方式二) <1毫秒 3毫秒 13毫秒
时间: 2024-12-25 11:11:59

JavaScript算法-排序算法的相关文章

【JavaScript】【算法】JavaScript版排序算法

JavaScript版排序算法:冒泡排序.快速排序.插入排序.希尔排序(小数据时,希尔排序会比快排快哦) 1 //排序算法 2 window.onload = function(){ 3 var array = [0,1,2,44,4, 4 324,5,65,6,6, 5 34,4,5,6,2, 6 43,5,6,62,43, 7 5,1,4,51,56, 8 76,7,7,2,1, 9 45,4,6,7,8]; 10 //var array = [4,2,5,1,0,3]; 11 array

javascript常用排序算法实现

毕业后,由于工作中很少需要自已去写一些排序,所以那些排序算法都忘得差不多了,不过排序是最基础的算法,还是不能落下啦,于是找了一些资料,然后用Javascript实现了一些常用的算法,具体代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>

数据结构与算法-排序算法-partial

前言 都什么时代了,还写排序算法的总结? 原因有二.一是别人的精彩永远是别人的,你只有鼓掌的份儿:有些事情实际动手去做了才会有所体会. 二是排序算法是一类基础类的算法,不光是IT从业者真正入门的门槛,也是一些高级算法的关键部分或算法评估的benchmark. 计划说明的算法内容有哪些?  算法的思想.Java代码实现和平均算法复杂度.算法运行完整示例. 参考文献有哪些? wiki[EB/OL] Shaffer C. A. Data Structure and Algorithm Analysis

数据结构和算法-排序算法-冒泡排序

##################     排序算法        ###################### """ 排序算法, 我们想要把线性表中的无序序列,排成有序序列,的算法,就是排序算法, 排序算法的稳定性 举例:假设对下面的元组要以他们的第一个数字来排序. (4, 1) (3, 1) (3, 7)(5, 6) 如果你排序之后,(3, 1) (3, 7)和原来的顺序一样,就是稳定的,否则就是不稳定的, (3, 1) (3, 7) (4, 1) (5, 6) (维

Javascript之排序算法

常用排序方法 算法简介 算法描述 算法实现 算法分析 插入算法 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常采用in-place排序(即只需要用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间. 1.从第一个元素开始,该元素可以认为已经被排序: 2.取出下一个元素,在已经排序的元素序列中从后向前扫描: 3.如果该元素(已排序)大于新元素,将该元素移到下一个位置: 4.重复步

数据结构与算法——排序算法

常见排序算法主要有: 插入排序(直接插入排序.希尔排序) 选择排序(直接选择排序.堆排序) 交换排序(冒泡排序.快速排序) 归并排序 基数排序 外部排序 一.直接插入排序 算法思想:在一个待排序列中,从第二个元素开始,依次进行排序,每次都将待排序元素从后往前,依次与前面的元素进行比较,从而将带排序元素移动到一个合适的位置,直到最后一个待排序元素移动到合适位置,则排序完成. 算法特点:最好情况下时间复杂度O(n),最坏情况下时间复杂度O(n2),稳定排序算法 二.希尔排序 希尔排序算法基础:待排序

[数据结构与算法]排序算法(Python)

1.直接插入排序 给定一个数组后,从第二个元素开始,如果比第一个小,就跟他交换位置,否则不动:第三个元素如果比第二个小,把第三个跟第二个交换位置,在把第二个与第一个比较:..... def insert_sort(arr): length = len(arr) for i in range(1,length): if arr[i] < arr[i-1]: for j in range(i-1,-1,-1): if arr[j+1] < arr[j]: arr[j+1],arr[j] = arr

算法 排序算法--冒泡排序

冒泡排序是排序算法的一种,思路清晰,代码简洁,常被用在大学生计算机课程中. “冒泡”这个名字的由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名. 这里以从小到大排序为例进行讲解. 基本思想及举例说明 冒泡排序的基本思想就是不断比较相邻的两个数,让较大的元素不断地往后移.经过一轮比较,就选出最大的数:经过第2轮比较,就选出次大的数,以此类推. 下面以对 3  2  4  1 进行冒泡排序说明. 第一轮 排序过程3  2  4  1    (最初)2  3  4  2    (比较3和2,

算法 排序算法--选择排序

选择排序是排序算法的一种,这里以从小到大排序为例进行讲解. 基本思想及举例说明 选择排序(从小到大)的基本思想是,首先,选出最小的数,放在第一个位置:然后,选出第二小的数,放在第二个位置:以此类推,直到所有的数从小到大排序. 在实现上,我们通常是先确定第i小的数所在的位置,然后,将其与第i个数进行交换. 下面,以对 3  2  4  1 进行选择排序说明排序过程,使用min_index 记录当前最小的数所在的位置. 第1轮 排序过程 (寻找第1小的数所在的位置)3  2  4  1(最初, mi