前端算法 - 【6种排序】

52 阅读2分钟

种类

  • 冒泡排序
  • 计数排序
  • 插入排序
  • 选择排序
  • 归并排序
  • 快速排序

冒泡排序 时间复杂度O(n*n)

思路

  1. 从头到尾依次比较前者跟后者大小,若前者比后者大,两者互换位置;

  2. 针对n个元素重复以上步骤,每次循环排除当前最后一个。

  3. 重复步骤1、2,排序完成。

代码实现
const sort = function(arr){
  const len = arr.length;
  for (let i = 0; i < len; i++) {
    for (let j = 0; j < len; j++) {
      if(arr[j] > arr[j+1]){
        [arr[j], arr[j+1]] = [arr[j+1], arr[j]]
      }
    }
  }
  return arr;
}
sort([2,1,3,5,9,4,0]); //  [0, 1, 2, 3, 4, 5, 9]

计数排序 时间复杂度:O(n+k)

思路

  1. 计算出差值diff,最小值小于0,加上本身add
  2. 创建统计数组并统计对应元素个数
  3. 遍历统计数组,输出到结果数组
代码实现
const sort = function(arr){
  let len = arr.length,
      min = arr[0],
      max = arr[0];
  // 求最大/小值
  for (let i = 1; i < len; i++) {
    max = Math.max(arr[i], max)
    min = Math.min(arr[i], min)
  }

  // 最小值小于0时,应加上绝对值,以便获得实际最大与最小值的实际差额(此差额以长度表示,便于创建新数组)
  let add = (min < 0 ? -min : 0); 
  let diff = max - min + add + 1;
  let newArr = new Array(diff).fill(0); 

  // 存储对应值出现的次数
  for (let i = 0; i < len; i++) {
    const inx = arr[i] - min + add;
    newArr[inx] += 1;
  }

  return newArr.reduce((_arr, v, i)=>{
    const rArr = new Array(v).fill(i);
    v && ( _arr = _arr.concat(rArr));
    return _arr;
  }, []);
}

sort([0,2,1,5,8,7,6,3,4,0]); // [0, 0, 1, 2, 3, 4, 5, 6, 7, 8]

插入排序 时间复杂度: O(n*n)

思路

  1. 从第一个元素开始,该元素可以认为已经被排序;

  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;

  3. 如果该元素(已排序)大于当前索引元素,将该元素移到下一位置;

  4. 重复步骤3,结束后将筛选的最前面元素替换成当前索引元素;

  5. 重复步骤2~4。

const sort = function(arr){
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    let pInx = i - 1,
        cur = arr[i];

    while (pInx>=0&&cur<arr[pInx]) {
      arr[pInx+1] = arr[pInx]
      pInx--;
    }
    arr[pInx+1] = cur;
  }
  return arr;
}
sort([0,2,1,5,8,7,6,3,4,0]); // [0, 0, 1, 2, 3, 4, 5, 6, 7, 8]

选择排序 时间复杂度O(n*n)

思路

  1. 循环数组,记录当前索引

  2. 将该索引元素与之后的元素进行判断,若筛选到小于该数时,记录索引,结束循环后交换位置; (每趟仅记录一个最小值)


const sort = function(arr){
  let inx, len = arr.length;
  for (let i = 0; i < len; i++) {
    inx = i;
    for (let j = i+1; j < len; j++) {
      if(arr[j] < arr[inx]){
        inx = j;
      }
    }
    // 有更小值时执行
    if(inx !== i){
      [arr[inx], arr[i]] = [arr[i], arr[inx]]
    }
  }
  return arr;
}
sort([0,2,1,5,8,7,6,3,4,0]);  // [0, 0, 1, 2, 3, 4, 5, 6, 7, 8]

归并排序 时间复杂度:O(nlogn)

思路

  1. 将数组拆分成A、B两份,直至拆成单个元素;

  2. 从单元素数组开始对拆分数组进行排序处理;

  3. 重复步骤2;


 function sort(arr){
  const len = arr.length;
  if(len < 2){
    return arr;
  }
        
  const inx = Math.floor(len / 2);
  const leftArr = arr.slice(0,inx)
  const rightArr = arr.slice(inx);

  return menber(sort(leftArr), sort(rightArr))
 }

 function menber(left, right){
  let _arr = [];
  while (left.length && right.length) {
    _arr.push(left[0] > right[0] ? right.shift() : left.shift())
  }
  return _arr.concat(left,right);
 }
 sort([0,2,1,5,8,7,6,3,4,0]); // [0, 0, 1, 2, 3, 4, 5, 6, 7, 8]

快速排序 时间复杂度:O(nlogn)

思路

  1. 选择数组中间数作为基数

  2. 准备两个容器,遍历数组与基数比对,较小的放左边容器,较大的放右边容器;

  3. 递归处理,将处理后的数据与基数按大小合并成一个数组。

function sort(arr){
  let len = arr.length;
  if (len <= 1) return arr;

  let index = Math.floor(len / 2)

  let mean = arr.splice(index, 1)[0],
  left = [],
  right = [];

  for (let i = 0; i < len; i++) {
    mean > arr[i] ? left.push(arr[i]) : right.push(arr[i]);
  }
  return sort(left).concat([mean], sort(right));
}
sort([0,2,1,5,8,7,6,3,4,0]); // [0, 0, 1, 2, 3, 4, 5, 6, 7, 8]