js 常用排序

 冒泡排序

原理:从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素
 function bubbleSort(arr) {
      if (Array.isArray(arr)) {
        for (var i = arr.length - 1; i > 0; i--) {
          for (var j = 0; j < i; j++) {
            if (arr[j] > arr[j + 1]) {
              [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
          }
        }
        return arr;
      }
    }

插入排序

原理:第一个元素默认是已排序元素,取出下一个元素和当前元素比较,如果当前元素大就交换位置。那么此时第一个元素就是当前的最小数,所以下次取出操作从第三个元素开始,向前对比,重复之前的操作。

function insertSort(arr) {
      if (Array.isArray(arr)) {
        for (var i = 1; i < arr.length; i++) {
          var preIndex = i - 1;
          var current = arr[i]
          while (preIndex >= 0 && arr[preIndex] > c) {
            arr[preIndex + 1] = arr[preIndex];
            preIndex--;
          }
          arr[preIndex + 1] = current;
        }
        return arr;
      }
    }

选择排序

原理:遍历数组,设置最小值的索引为 0,如果取出的值比当前最小值小,就替换最小值索引,遍历完成后,将第一个元素和最小值索引上的值交换。如上操作后,第一个元素就是数组中的最小值,下次遍历就可以从索引 1 开始重复上述操作。

    function selectSort(arr) {
      if (Array.isArray(arr)) {
        for (var i = 0; i < arr.length - 1; i++) {
          var minIdex = i;
          for (var j = i + 1; j < arr.length; j++) {
            minIdex = arr[j] < arr[minIdex] ? j : minIdex;
          }
          [arr[i], arr[minIdex]] = [arr[minIdex], arr[i]];
        }
        return arr;
      }
    }

快速排序

原理:在数据集之中,找一个基准点,建立两个数组,分别存储左边和右边的数组,利用递归进行下次比较。

function quickSort(arr) {
      if (!Array.isArray(arr)) return;
      if (arr.length <= 1) return arr;
      var left = [], right = [];
      var num = Math.floor(arr.length / 2);
      var numValue = arr.splice(num, 1)[0];
      for (var i = 0; i < arr.length; i++) {
        if (arr[i] > numValue) {
          right.push(arr[i]);
        } else {
          left.push(arr[i]);
        }
      }
      return [...quickSort(left), numValue, ...quickSort(right)]
    }

希尔排序

原理:

选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

 

 function shellSort(arr) {
      var len = arr.length,
        temp,
        gap = 1;
      // 动态定义间隔序列,也可以手动定义,如 gap = 5;
      while (gap < len / 5) {
        gap = gap * 5 + 1;
      }
      for (gap; gap > 0; gap = Math.floor(gap / 5)) {
        for (var i = gap; i < len; i++) {
          temp = arr[i];
          for (var j = i - gap; j >= 0 && arr[j] > temp; j -= gap) {
            arr[j + gap] = arr[j];
          }
          arr[j + gap] = temp;
        }
      }
      return arr;
    }

归并排序

原理:

(1) 把长度为n的输入序列分成两个长度为n/2的子序列;
(2)对这两个子序列分别采用归并排序;
(3) 将两个排序好的子序列合并成一个最终的排序序列。
let arr = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];

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));
    }

    function merge(left, right) {
      var result = [];
      while (left.length && right.length) {
        // 不断比较left和right数组的第一项,小的取出存入res
        left[0] < right[0] ? result.push(left.shift()) : result.push(right.shift());
      }
      return result.concat(left, right);
    }

console.log(mergeSort(arr))
// [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

堆排序(Heap Sort)

 

  • 将初始待排序关键字序列 (R1, R2 .... Rn) 构建成大顶堆,此堆为初始的无序区;
  • 将堆顶元素 R[1] 与最后一个元素 R[n] 交换,此时得到新的无序区 (R1, R2, ..... Rn-1) 和新的有序区 (Rn) ,且满足 R[1, 2 ... n-1] <= R[n]。
  • 由于交换后新的堆顶 R[1] 可能违反堆的性质,因此需要对当前无序区 (R1, R2 ...... Rn-1) 调整为新堆,然后再次将 R[1] 与无序区最后一个元素交换,得到新的无序区 (R1, R2 .... Rn-2) 和新的有序区 (Rn-1, Rn)。不断重复此过程,直到有序区的元素个数为 n - 1,则整个排序过程完成。

  let arr = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]
  // 堆排序
const heapSort = array => {
    console.time('堆排序耗时');
    // 初始化大顶堆,从第一个非叶子结点开始
    for (let i = Math.floor(array.length / 2 - 1); i >= 0; i--) {
        heapify(array, i, array.length);
    }
    // 排序,每一次 for 循环找出一个当前最大值,数组长度减一
    for (let i = Math.floor(array.length - 1); i > 0; i--) {
        // 根节点与最后一个节点交换
        swap(array, 0, i);
        // 从根节点开始调整,并且最后一个结点已经为当前最大值,不需要再参与比较,所以第三个参数为 i,即比较到最后一个结点前一个即可
        heapify(array, 0, i);
    }
    console.timeEnd('堆排序耗时');
    return array;
};

// 交换两个节点
const swap = (array, i, j) => {
    let temp = array[i];
    array[i] = array[j];
    array[j] = temp;
};

// 将 i 结点以下的堆整理为大顶堆,注意这一步实现的基础实际上是:
// 假设结点 i 以下的子堆已经是一个大顶堆,heapify 函数实现的
// 功能是实际上是:找到 结点 i 在包括结点 i 的堆中的正确位置。
// 后面将写一个 for 循环,从第一个非叶子结点开始,对每一个非叶子结点
// 都执行 heapify 操作,所以就满足了结点 i 以下的子堆已经是一大顶堆
const heapify = (array, i, length) => {
    let temp = array[i]; // 当前父节点
    // j < length 的目的是对结点 i 以下的结点全部做顺序调整
    for (let j = 2 * i + 1; j < length; j = 2 * j + 1) {
        temp = array[i]; // 将 array[i] 取出,整个过程相当于找到 array[i] 应处于的位置
        if (j + 1 < length && array[j] < array[j + 1]) {
            j++; // 找到两个孩子中较大的一个,再与父节点比较
        }
        if (temp < array[j]) {
            swap(array, i, j); // 如果父节点小于子节点:交换;否则跳出
            i = j; // 交换后,temp 的下标变为 j
        } else {
            break;
        }
    }
};

console.log(heapSort(arr))

 睡眠排序

  1. 当出现一个很大的数字,会睡眠很长时间(效率低)

  2. 当数字相差很小,会不精准(不精准)

  3. 不能处理负数(虽然可以在加上一个正数,再进行睡眠排序)

  let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]

  var sleepSort = function (arr, callback) {
    let res = [];
    arr.forEach(item => {
      setTimeout(() => {
        res.push(item)
        // 如果执行完毕,回调
        if (res.length === arr.length) callback(res);
      }, item)
    });
  }
  sleepSort(arr, res => { console.log(res) })

 

posted @ 2019-08-23 15:11  echo丶若梦  阅读(431)  评论(0编辑  收藏  举报