JavaScript排序算法

126 阅读4分钟

冒泡排序

冒泡排序(Bubble Sort)是最易懂的排序算法,但是效率较低,生产环境中很少使用。

它的基本思想:依次比较相邻的两个数,如果不符合排序规则,则调换两个数的位置。这样一遍比较下来,能够保证最大(或最小)的数排在最后一位。然后,再对最后一位以外的数组,重复前面的过程,直至全部排序完成。由于每进行一遍这个过程,在最后一个位置上,正确的数会自己冒出来,就好像“冒泡”一样,这种算法因此得名。

image

实现:

//冒泡排序
function bubbleSort(arr) {   
     for(let i = 0; i < arr.length - 1; i++){        
         let flag = false;        
         for(let j = 0; j<arr.length- 1-i; j++){            
            if(arr[j] > arr[j+1]){                
                [arr[j], arr[j+1]] = [arr[j+1], arr[j]];                
                flag = true;            
            }            
            if(!flag){ 
               break;            
            }        
        }        
    }        
    return arr;
}

选择排序(Selection Sort)

  先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

image

实现:

function selectSort(arr) {    for (let i = 0; i < arr.length - 1; i++) {        let minIndex = i,            flag = true;        for (let j = i + 1; j < arr.length; j++) {            if (arr[minIndex] > arr[j]) {                minIndex = j;                flag = false;            }        }        if (!flag) {            [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];        }        console.log("第" + i + "轮:", arr, minIndex);    }    return arr;}console.log(selectSort([4, 3, 5, 2, 1]))

插入排序(Insertion Sort)

工作原理:

通过构建有序序列对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

image

实现1:

function insertionSort(myArray) {    var len = myArray.length, // 数组的长度               value, // 当前比较的值                i, // 未排序部分的当前位置               j; // 已排序部分的当前位置        for (i = 0; i < len; i++) {        // 储存当前位置的值               value = myArray[i];        /**          * 当已排序部分的当前元素大于value,                  * 就将当前元素向后移一位,再将前一位与value比较                 * */        for (j = i - 1; j > -1 && myArray[j] > value; j--) {            myArray[j + 1] = myArray[j];        }        myArray[j + 1] = value;    }    return myArray;}console.log(insertSort2([4, 3, 5, 2, 1]))

实现2:

function insertSort(arr) {    if (!arr || arr.length == 0) {        return;    }    let sortArr = [arr[0]];    for (let i = 1; i < arr.length; i++) {        let replaceIndex = sortArr.length - 1,            flag = true;        for (let j = sortArr.length - 1; j >= 0; j--) {            if (arr[i] < sortArr[j]) {                replaceIndex = j;                flag = false;            } else {                break;            }        }        if (!flag) {            sortArr.splice(replaceIndex, 0, arr[i]);        } else {            sortArr.push(arr[i]);        }        console.log("第" + i + "轮:", sortArr, replaceIndex);    }    return sortArr;}console.log(insertSort([4, 3, 5, 2, 1]))

归并排序

归并排序是用分治思想,分治模式在每一层递归上有三个步骤:

  • 分解(Divide):将n个元素分成个含n/2个元素的子序列。
  • 解决(Conquer):用合并排序法对两个子序列递归的排序。
  • 合并(Combine):合并两个已排序的子序列已得到排序结果。
基本思想:**将两个已经排序的数组合并**,要比从头开始排序所有元素来得快。因此,可以将数组拆开,分成n个只有一个元素的数组,然后不断地两两合并,直到全部排序完成。

以对数组[6, 5, 3, 1, 8 , 7, 2, 4] 进行从小到大排序为例,步骤如下:

  1. 将数组分成[6, 5, 3, 1]和[8 , 7, 2, 4]两部分。
  2. 将[6, 5, 3, 1]分成[6, 5]和[3, 1]两部分。
  3. 将 [8 , 7, 2, 4]分成[8,7]和[2,4]两部分
  4. 将[6, 5]分成[6]和[5]两部分,将[3, 1]分成[3]和[1]两部分,将[8, 7]分成[8]和[7]两部分, 将[2, 4]分成[2]和[4]两部分
  5. 然后将[6]和[5]合并成[5, 6]。然后[3]和[1]合并成[1, 3]。然后[8]和[7]合并成[7, 8]。然后[2]和[4]合并成[2, 4]。
  6. 将[5, 6]和[1, 3]合并成[1,3,5,6]。将[7, 8]和[2, 4]合并成[2, 4,7,8]。
  7. 将[1,3,5,6]和[2, 4,7,8]合并成,然后合并成[1,2,3,4,5,6,7,8]。

function merge(left, right){
    var result  = [],
        il      = 0,
        ir      = 0;

    while (il < left.length && ir < right.length){
        if (left[il] < right[ir]){
            result.push(left[il++]);
        } else {
            result.push(right[ir++]);
        }
    }

    return result.concat(left.slice(il)).concat(right.slice(ir));
}
function mergeSort(arr) {  //采用自上而下的递归方法
    var len = arr.length;
    if(len < 2) {
        return arr;
    }
    var middle = Math.floor(len / 2),
        left = arr.slice(0, middle),
        right = arr.slice(middle);
    return merge(mergeSort(left), mergeSort(right));
}

快速排序(Selection Sort)

基本思想:先确定一个“支点”(pivot),将所有小于“支点”的值都放在该点的左侧,大于“支点”的值都放在该点的右侧,然后对左右两侧不断重复这个过程,直到所有排序完成。

具体做法是:

  1. 确定“支点”(pivot)。虽然数组中任意一个值都能作为“支点”,但通常是取数组的中间值
  2. 建立两端的指针。左侧的指针指向数组的第一个元素,右侧的指针指向数组的最后一个元素。
  3. 左侧指针的当前值与“支点”进行比较,如果小于“支点”则指针向后移动一位,否则指针停在原地。
  4. 右侧指针的当前值与“支点”进行比较,如果大于“支点”则指针向前移动一位,否则指针停在原地。
  5. 左侧指针的位置与右侧指针的位置进行比较,如果前者大于等于后者,则本次排序结束;否则,左侧指针的值与右侧指针的值相交换。
  6. 对左右两侧重复第2至5步。

image

实现:

function quickSort(arr){
    //如果arr.length<=1,则直接返回arr
    if(arr.length<=1){return arr}
    // arr的元素个数/2,再下去整,将值保存在pivotIndex中
    var pivotIndex=Math.floor(arr.length/2);
    // 将arr中pivotIndex位置的元素,保存在变量pivot中
    var pivot=arr[pivotIndex];
    //声明空数组left和right
    var left=[];
    var right=[];
    for(var i=0;i<arr.length;i++){  // 遍历arr中每个元素
        if(i !== pivotIndex){ // 如果i !== pivotIndex
            if(arr[i]<=pivot){ // 如果当前元素值<pivot
                left.push(arr[i]); // 就将当前值压入left
            }else{
                right.push(arr[i]); // 就将当前值压入right
            }
        }
    }
    //递归
    return quickSort(left).concat(pivot, quickSort(right)); // 链接多个数组到 left 从小到大
}