排序算法基础

205 阅读3分钟

选择排序

选择排序是一个简单直观的排序方法。首先从未排序序列中走到最大的元素,放到已排序的末尾,重复上述步骤,知道所有元素排序完毕

const arr = [23,2,6,1];
function checkSort (arr) {
  if (arr.length <= 1) return arr;
  for (let i = 0; i < arr.length; i++){
    let index = i;
    for (let j = i + 1; j < arr.length; j++){
      if (Number(arr[index]) > Number(arr[j])) {
        index = j;
      }
    }
    if (index !== i) {
      [arr[i], arr[index]] = [arr[index], arr[i]];
    }
  }
  return arr;
}
checkSort(arr); // [1, 2, 6, 23]

插入排序

插入排序算法类似打牌,我们将手里的牌会按照从小到大的顺序排列,单再次拿起一张牌时,会根据牌的大小插入到合适的位置。类似这样的一种排序方式就是插入排序

function insertSort (arr) {
  if (arr.length <= 1) return arr;
  for (let i = 1; i < arr.length; i++){
    for (let j = i - 1; j >= 0; j--){
      if (+arr[j] > +arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      } else if (+arr[j + 1] >= +arr[j]) {
        break;
      }
    }
  }
  return arr;
}
insertSort(arr); // [1, 2, 3, 4, 23, 56]

冒泡排序

冒泡排序就是将相邻的数据进行两两比较,大的放后面,小的放前面。


const sarr = [3,1,5,2,4,90,134,1346];
function maopaoSort (arr) {
  if (arr.length <= 1) return arr;
  for (let i = 0; i < arr.length; i++){
    for (let j = 0; j < arr.length - i - 1; j++){
      if (+arr[j] > +arr[j+1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr;
}
maopaoSort(arr); // [1, 2, 3, 4, 5, 90, 134, 1346]

希尔排序

希尔排序本质上是一种插入排序,但是对数列进行了等间隔分组处理,在每一组中做插入排序。
希尔排序是按一定的间隔对数列进行分组,然后在每一个分组中做插入排序;随后逐次缩小间隔,在每一个分组中做插入排序,直到间隔等于1,做一次查处片吸塑后结束。

const str = [1,4,6,8,2,6,34,123];
function shellSort (arr) {
  if (arr.length <= 1) return arr;
  let len = arr.length
  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 temp = arr[j];
      for (; j > 0; j -= gap){
        if (+temp >= +arr[j - gap]) {
          break;
        }
        arr[j] = arr[j - gap];
      }
      arr[j] = temp;
    }
  }
  return arr;
}
shellSort(str); // [1, 2, 4, 6, 6, 8, 34, 123]

快速排序

在数组中,设置一个基准值(一般为数组的中间一个)。准备两个空数组,遍历该数组,小于基准值的放在一个数组中,大于基准值的放在另一个数组中,使用递归重复该步骤

const arr = [2, 4, 7, 1, 6, 9, 3,35,214];
function quickSort (arr) {
  if (arr.length <= 1) return arr;
  const middelIndex = Math.floor(arr.length / 2);
  const middle = arr.splice(middelIndex, 1)[0];
  const left = [];
  const right = [];
  for (let i = 0; i < arr.length; i++){
    if (+arr[i] < +middle) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  return quickSort(left).concat([middle], quickSort(right))
}
quickSort(arr) // [1, 2, 3, 4, 6, 7, 9, 35, 214]

计数排序

计数排序-顾名思义就是记录下来当前出现的数字出现的次数。
计数排序不是基于比较的排序方法。其核心在于将输入的数据值转化为键存储在额外开辟的数组中。
作为一种线性时间复杂度的排序算法,计数排序要求输入的数据必须是有确定范围的整数。

算法描述

  • 找出待排序数组中最大和最小的元素,确定新开辟数组的长度。
  • 统计待排序数组中每个值为 i 出现的次数,存入新数组的第 i 项,没出现一次,数组下标对应的值就 + 1
  • 数组每一个下标位置的值,代表了数列中对应整数出现的次数。
  • 遍历数组,输出数组元素的下标值,元素的值是几,就输出几次。

代码

function countSort(arr){
  if(!arr || arr.length<=1) return arr;
  const len = arr.length;
  // 输出的结果数组
  const resArr = new Array(len);
  // 计数数组
  let countArr;
  let min = max = arr[0];
  // 统计出现次数
  for(let i=0;i<len;i++){
    // 若是存在这个值就证明出现过,出现次数+1/若不存在证明是第一次出现就为 1
    // countArr[arr[i]] = countArr[arr[i]] ? countArr[arr[i]]+1 : 1;
    // 找到最小值
    // min = min <= arr[i] ? min : arr[i];
    // 找到最大值
    max = max >= arr[i] ? max : arr[i];
  }
  // 确定计数数组的长度
  countArr = new Array(max + 1).fill(0);
  // 填充统计数组
  for(let i=0;i<len;i++){
    // 以数组中出现的数字作为填充数组的下标,每出现一次就+1
    countArr[arr[i]]++;
  }
  console.log('countArr', countArr);
  // 遍历统计数组,输出结果
  let index = 0;
  for(let i=0;i<countArr.length;i++){
    for(let j=0;j<countArr[i];j++){
      // 只有当j有值时才会像结果数组中添加值
      // i 是下标,代表了原数组中的值
      resArr[index++] = i;
    }
  }
  return resArr;
}
const arr = [1,3,6,8,11,2,5,7,3,23];
const res = countSort(arr);
console.log(res, res.length);  // [1, 2, 3,  3,  5,6, 7, 8, 11, 23] 10