前端常见的排序算法

207 阅读2分钟

前端比较常问到排序其实也就几种,冒泡,插入,归并,希尔,快速,桶。

接下来分别概述一下它们的特点:

类型/特性冒泡插入归并希尔快速
时间复杂度O(n^2)O(n)~O(n^2)O(nlogn)O(n)~O(n^2)O(nlogn)~O(n^2)O(n)~O(n^2)
空间复杂度O(1)O(1)O(n)O(1)O(logn)O(n)
稳定性稳定稳定稳定不稳定不稳定稳定

冒泡排序

目前大多数程序员都会的就是冒泡排序,简单明了,两两比对,大的放后面,小的放前面,第一次循环之后,最大的元素就跑到数组最后面去了。

function bubbleSort (nums) {
  const length = nums.length;
  for (let i = 0;i < length;++i) {
    for (let j = 0;j < length - 1 - i;++j) {
      if (nums[j] > nums[j+1]) [nums[j+1], nums[j]] = [nums[j], nums[j+1]]
    }
  }
}

由于会进行两次遍历所以时间复杂度依旧是O(n^2)

插入排序

这种排序也是进行两次遍历,不过它会与前面的进行比较,每次比较将数插入到对应的位置

function insertSort (nums) {
  const length = nums.length;
  for (let i = 1;i < length;++i) {
    let curIndex = i;
    while (curIndex >= 0 && nums[--curIndex] > nums[i]) continue;
    nums.splice(curIndex + 1, 0, nums.splice(i, 1)[0])
  }
}

归并排序

它是把数组进行拆分,分别进行排序,在将排完序的数组进行合并

function mergeSort (nums) {
  const length = nums.length;
  if (length < 2) return nums;
  const mid = ~~(nums.length / 2);
  const left = mergeSort(nums.slice(0, mid)),right = mergeSort(nums.slice(mid));
  return merge(left, right);
}
function merge (left, right) {
  const result = [];
  while (left.length && right.length) {
    if (left[0] > right[0]) {
      result.push(right.shift());
    } else {
      result.push(left.shift());
    }
  }
  if (left.length) {
    result.push(...left);
  }
  if (right.length) {
    result.push(...right);
  }
  return result;
}

希尔排序

它是插入排序的一种优化,设置了一个间隔序列,提高速度

function shellSort (nums) {
  const length = nums.length;
  let temp, gap = 1;
  while (gap < length / 3) {
    gap = gap * 3 + 1;
  }
  for (;gap > 0;gap = ~~(gap / 3)) {
    for (let i = gap;i < length; ++i) {
      temp = nums[i];
      for (let j = i - gap;j >= 0 && nums[j] > temp; j-=gap) {
        nums[j + gap] = nums[j];
      }
      nums[j + gap] = temp;
    }
  }
  return nums;
}

快速排序

在顺序比较乱的时候,快排的执行效率比较高,它跟归并都是将数组拆分成再进行合并,不过它的常数因子更小,也就更快

function quickSort (nums) {
  if (nums.length < 2) {
    return nums;
  }
  const mid = ~~(nums.length / 2),
        num = nums.splice(mid, 1)[0],
        left = [],
        right = []
  for (let i = 0,length = nums.length;i < length; ++i) {
    if (nums[i] < num) {
      left.push(nums[i]);
    } else {
      right.push(nums[i]);
    }
  }
  return quickSort(left).concat(num, quickSort(right));
}

桶排序

它是通过映射进行快速排序的,所以映射越多,内存消耗越大。

function bucketSort (nums, bucketSize) {
  if (nums.length) {
    return nums;
  }
  let i;
  let minValue = nums[0];  // 输入最小值
  let maxValue = nums[0];  // 输入最大值
  for (i = 1;i < nums.length; ++i) {
    if (nums[i] < minValue) {
      minValue = nums[i]
    } else if (nums[i] > maxValue) {
      maxValue = nums[i]
    }
  }
  const DEFAULT_BUCKET_SIZE = 5;  // 设置桶的默认值为5
  bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
  const bucketCount = ~~((maxValue - minValue) / bucketSize);
  const buckets = Array.from({ length: bucketCount }, () => []);
  // 将数据分配给桶进行映射
  for (i = 0;i < nums.length; ++i) {
    buckets[~~((nums[i] - minValue) / bucketSize)].push(nums[i]);
  }
  nums.length = 0;
  for (i = 0;i < buckets.length; ++i) {
    insertSort(buckets[i]);
    for (let j = 0;j < buckets[i].length; ++j) {
      nums.push(buckets[i][j]);
    }
  }
  return nums;
}