JS排序算法 ᕦ(・ㅂ・)ᕤ走过路过不要错过

在这里插入图片描述
在这里插入图片描述

冒泡排序(Bubble Sort)

通过相邻元素的比较和交换,使得每一趟循环都能找到未排数组的最大值或最小值,把它放到后面;
在这里插入图片描述
普通冒泡排序

let arr = [2,44,3,13,11,35,8]

function sort(arr){
    for(let i=0,len=arr.length; i<len-1;i++){
        // flag用来标志每一轮是否有交换数据;如果没有,则说明数组已有序
        let flag = true
        for(let j=0;j<len-i-1;j++){
            if(arr[j]>arr[j+1]){
                [arr[j],arr[j+1]] = [arr[j+1],arr[j]];
                flag = false;
            }
        }
        if(flag) return;
    }
}

sort(arr)
console.log(arr) // [ 2, 3, 8, 11, 13, 35, 44 ]

双向冒泡排序

let arr = [2,44,3,13,11,35,8]

function sort(arr){
    let low = 0;
    let high = arr.length -1;
    while(low < high){
        let flag = true;
        // 找到最大值放在右边
        for(let i=low;i<high;i++){
            if(arr[i]>arr[i+1]){
                [arr[i],arr[i+1]] = [arr[i+1],arr[i]];
                flag = false;
            }
        }
        high--;
        // 找到最小值放在左边
        for(let j=high;j>low;j--){
            if(arr[j]<arr[j-1]){
                [arr[j],arr[j-1]] = [arr[j-1],arr[j]];
                flag = false;
            }
        }
        low++;
        if(flag)return;
    }    
}

sort(arr)
console.log(arr) // [ 2, 3, 8, 11, 13, 35, 44 ]

快速排序

  1. 从数组中取出一个数作为基准。
  2. 在原数组中进行移动,将大于基准的数放到基准右边,小于基准的数放到基准左边,在基准左右形成两个子数组。
  3. 在左右子数组中反复执行步骤1、2,直到所有子数组只剩下一个数。
let arr = [2,44,3,13,11,35,8]

function sort(arr){
    if(arr.length <=1) {
        return arr;
    }
    // 获取基准的索引
    let pivotIndex = Math.floor(arr.length/2);
    // 找到基准,把基准从原数组中删除
    let pivot = arr.splice(pivotIndex,1)[0];
    // 定义左右数组
    let left = [];
    let right = [];
    // 比基准小的放在left,比基准大的放在right
    arr.forEach(item => {
        if(item < pivot){
            left.push(item);
        }else {
            right.push(item);
        }
    });
    return sort(left).concat([pivot],sort(right));
}

console.log(sort(arr)) // [ 2, 3, 8, 11, 13, 35, 44 ]

直接插入排序

基本思想:
在已排序的数据序列从后向前扫描,找到对应的位置插入未排序的数据。
插入排序通常采用占位的形式,空间复杂度为O(1)。
因此,从后向前扫描的过程中,需要反复的把已排序的元素逐步向后挪位,为新插入元素提供插入的位置。

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到下一位置中
  6. 重复步骤2

在这里插入图片描述

let arr = [2,44,3,13,11,35,8]

function sort(arr){
    for(let i=1,len=arr.length;i<len;i++){
        let temp = arr[i]; // 需要排序的元素
        let j=i-1;         // 默认已排序元素的最后一位的索引
        while(j>=0 && arr[j]>temp){ // 在已排序好的队列中从后向前扫描
            arr[j+1]=arr[j];  // 已排序的元素大于新元素,将该元素移到下一个位置
            j--;
        }
        arr[j+1]=temp; // 当已排序的元素<=新元素或到头了,即可将新元素加到该元素后面或首位
    }
    return arr
}

console.log(sort(arr)) // [ 2, 3, 8, 11, 13, 35, 44 ]

希尔排序

希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高的版本,也称为缩小增量排序,同时该算法是冲破O(n^2)的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。

基本思路:
根据增量来分割数组,对每一组进行直接插入排序
在这里插入图片描述

let arr = [2,44,3,13,11,35,8]

function sort(arr){
    let len = arr.length;
    // gap 即为增量
    for (let gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
      for (let i = gap; i < len; i++) {
        let j = i;
        let current = arr[i];
        while(j - gap >= 0 && current < arr[j - gap]) {
          arr[j] = arr[j - gap];
          j = j - gap;
        }
        arr[j] = current;
      }
    }
    return arr;
}

console.log(sort(arr)) // [ 2, 3, 8, 11, 13, 35, 44 ]

简单选择排序

选择排序是一种简单直观的排序算法
基本思路:
首先在未排序序列中找到最小/最大 元素,存放在排序序列的起始位置;然后,再从剩余未排序元素中继续寻找最小/最大元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
在这里插入图片描述

let arr = [2,44,3,13,11,35,8]

function sort(arr){
   let len = arr.length;
   let minIndex,temp;
   for(let i=0;i<len-1;i++){
       minIndex=i;
       for(let j=i+1;j<len;j++){ // 循环寻找最小的数
           if(arr[j]<arr[minIndex]){
               minIndex=j;  // 保存最小数的索引
           }
       }
       [arr[i],arr[minIndex]] = [arr[minIndex],arr[i]];
   }
   return arr;
}

console.log(sort(arr)) // [ 2, 3, 8, 11, 13, 35, 44 ]

堆排序

参考网站
堆排序可以说是一种利用堆的概念来排序的选择排序,分为两种方法:

  1. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列
  2. 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列
    在这里插入图片描述
    在这里插入图片描述

我们用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

// 交换两个节点
function swap(A, i, j) {
    let temp = A[i];
    A[i] = A[j];
    A[j] = temp; 
  }
  
  // 将 i 结点以下的堆整理为大顶堆,注意这一步实现的基础实际上是:
  // 假设 结点 i 以下的子堆已经是一个大顶堆,shiftDown函数实现的
  // 功能是实际上是:找到 结点 i 在包括结点 i 的堆中的正确位置。后面
  // 将写一个 for 循环,从第一个非叶子结点开始,对每一个非叶子结点
  // 都执行 shiftDown操作,所以就满足了结点 i 以下的子堆已经是一大
  //顶堆
  function shiftDown(A, i, length) {
    let temp = A[i]; // 当前父节点
  // j<length 的目的是对结点 i 以下的结点全部做顺序调整
    for(let j = 2*i+1; j<length; j = 2*j+1) {
      temp = A[i];  // 将 A[i] 取出,整个过程相当于找到 A[i] 应处于的位置
      if(j+1 < length && A[j] < A[j+1]) { 
        j++;   // 找到两个孩子中较大的一个,再与父节点比较
      }
      if(temp < A[j]) {
        swap(A, i, j) // 如果父节点小于子节点:交换;否则跳出
        i = j;  // 交换后,temp 的下标变为 j
      } else {
        break;
      }
    }
  }
  
  // 堆排序
  function heapSort(A) {
    // 初始化大顶堆,从第一个非叶子结点开始
    for(let i = Math.floor(A.length/2-1); i>=0; i--) {
      shiftDown(A, i, A.length);
    }
    // 排序,每一次for循环找出一个当前最大值,数组长度减一
    for(let i = Math.floor(A.length-1); i>0; i--) {
      swap(A, 0, i); // 根节点与最后一个节点交换
      shiftDown(A, 0, i); // 从根节点开始调整,并且最后一个结点已经为当
                           // 前最大值,不需要再参与比较,所以第三个参数
                           // 为 i,即比较到最后一个结点前一个即可
    }
  }
  
  let arr = [50,45,40,20,25,35,30,10,15]
  heapSort(arr);
  console.log(arr) // [ 10, 15, 20, 25, 30, 35, 40, 45, 50 ]
 

二路归并排序

参考链接
归并排序采用的是分治的思想,首先是“分”,将一个数组反复二分为两个小数组,直到每个数组只有一个元素;其次是“治”,从最小数组开始,两两按大小顺序合并,直到并为原始数组大小,下面是图解:
在这里插入图片描述

// 融合两个有序数组,这里实际上是将数组 arr 分为两个数组
function mergeArray(arr, first, mid, last, temp) {
    let i = first; 
    let m = mid;
    let j = mid+1;
    let n = last;
    let k = 0;
    while(i<=m && j<=n) {
        if(arr[i] < arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    while(i<=m) {
        temp[k++] = arr[i++];
    }
    while(j<=n) {
        temp[k++] = arr[j++];
    } 
    for(let l=0; l<k; l++) {
        arr[first+l] = temp[l];
    }
    return arr;
}
// 递归实现归并排序
function mergeSort(arr, first, last, temp) {
    if(first<last) {
        let mid = Math.floor((first+last)/2);
        mergeSort(arr, first, mid, temp);    // 左子数组有序
        mergeSort(arr, mid+1, last, temp);   // 右子数组有序
        arr = mergeArray(arr, first, mid, last, temp);  
    }
    return arr;
}
  
// example
let arr = [2,44,3,13,11,35,8]
let temp = new Array();
console.log(mergeSort(arr, 0 ,arr.length-1, temp)); // [ 2, 3, 8, 11, 13, 35, 44 ]
  

在这里插入图片描述
持续更新中。。。

发布了75 篇原创文章 · 获赞 381 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/weixin_40693643/article/details/104734877