写一下那些我会的排序算法

765 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

算法设计期末考试要来了,借用这个机会手写一下那些我会的排序算法,常见的算法有哈希排序,冒泡排序、插入排序、计数排序、归并排序、快速排序、选择排序等。

哈希排序

这个可以说是我最开始学排序的时候学的了,其思想很简单就是借助数组的下标是有序的来进行排序,我们直接来看实现:

let arr=[1,5,6,4,9,31];
function haxiSort(arr){
  let tf=new Array(Math.max(...arr)+1).fill(false);
  arr.forEach(item => {
    tf[item]=true; 
  });
  for(let i=0;i<tf.length;i++){
    tf[i]&&console.log(i);
  }
}
haxiSort(arr);

代码很简单,我们只要通过判断这个数组是否出现过就可以,但是这个排序需要通过消耗大量的空间,同时如果数组存在相同的元素的时候就会出现问题。

计数排序

这个可以说是哈希排序的升级版了,由于我们前面说的哈希排序只能相同的元素,所以我们转变到计数排序来解决这个问题,其主要思想也是借助数组的下标是有序的来进行排序,但是区别于哈希的是其哈希数组存放的是数字不再是Boolean类型,这样我们就可以统计数字出现的次数。我们来看下代码:

let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function coutSort(arr) {
  let tf = new Array(Math.max(...arr) + 1).fill(0);
  arr.forEach((item) => {
    tf[item]++;
  });
  for (let i = 0; i < tf.length; i++) {
    for (let j = 0; j < tf[i]; j++) {
      console.log(i);
    }
  }
}
coutSort(arr);

这样我们就完成了我们的计数排序。同时它还有另外一个优势,有的时候我们的数据不是直接通过数组存放,而是读入的形式给你的(如c中的scanf),但是给的数据量特别大,数据范围特别小,这个时候也可以使用我们的计数排序来进行排序,这样我们声明的存放数据就可以以范围大小来定大小,而不是数据量。

直接选择排序

let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function selectSort(arr){
  for(let i=0;i<arr.length-1;i++)  {
    let index=i;
    for(let j=i+1;j<arr.length;j++){
      if(arr[i]>arr[j]){
        index=j;
      }
    }
    let tem=arr[i];
    arr[i]=arr[index];
    arr[index]=tem;
  }
}
selectSort(arr);
console.log(arr);

这个排序的主要思想就是每次都找剩余数组中的最大/最小值,直到倒数第二个的最大值/最小值找到后,那么整个数组就有序了。

直接插入排序

这种排序算法适合数组已经基本有序,其基本思想是在原本已经有序的数组中插入新的元素,我们直接上代码:

let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function insertSort(arr){
  for(let i=1;i<arr.length;i++){
    if(arr[i-1]>arr[i]){
      let value=arr[i];
      let index=i-1;
      do{
        arr[index+1]=arr[index];
        index--;
      }while(index>0&&arr[index]>=value);
      arr[index+1]=value;
    }
  }
}
insertSort(arr);
console.log(arr);

我们通过不断覆盖的方式找到新加入元素的位置,从局部有序到最后的全局有序。

冒泡排序

这可以说是最经典的一种排序算法了,其关键思想就是寻找数组中的逆序对,然后将其进行交换直到数组中不存在逆序对即可,我们来看一下它的代码:

let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function bubollSort(arr){
  let sjh=arr.length-1;
  do{
    let jh=0;
    for(let i=0;i<sjh;i++){
      if(arr[i]>arr[i+1]){
        let tem=arr[i];
        arr[i]=arr[i+1];
        arr[i+1]=tem;
        jh=i;
      }
    }
    sjh=jh;
  }while(sjh!==0);
}
bubollSort(arr);
console.log(arr);

注意这里我们每次查找逆序对只用找到上次最后交换的地方,而不用遍历整个数组。

快速排序

这是一种基于分治思想的排序算法,主要思想是确定一个数的位置,后将数组以该位置一分为二进行同样的递归处理,知道整个数组有序

let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function quickSort(arr, left, right) {
  if (left < right) {
    let mid = findPos(arr, left, right);
    quickSort(arr, left, mid - 1);
    quickSort(arr, mid + 1, right);
  }
}
function findPos(arr, left, right) {
  let temValue = arr[left];
  while (left < right) {
    while (left < right && arr[right] >= temValue) {
      right--;
    }
    arr[left] = arr[right];
    while (left < right && arr[left] <= temValue) {
      left++;
    }
    arr[right] = arr[left];
  }
  arr[left] = temValue;
  return left;
}
quickSort(arr, 0, arr.length - 1);
console.log(arr);

归并排序

这也是基于分治思想的一种排序算法,其基本思想是将数组不断的分组,对最小组进行排序后再进行合并,得到最终有序的数组

let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function mergeSort(arr, left, right) {
  if (left < right) {
    let mid = Math.trunc((left + right) / 2);
    mergeSort(arr, left, mid);
    mergeSort(arr, mid + 1, right);
    merge(arr, left, mid, mid + 1, right);
  }
}
function merge(arr, left1, right1, left2, right2) {
  let temArr = arr.slice(left1, right1 + 1);
  let i=0;
  while(left2<=right2&&i<temArr.length){
    if(temArr[i]<arr[left2]){
      arr[left1++]=temArr[i];
      i++;
    }else{
      arr[left1++]=arr[left2++];
    }
  }
  if(i!==temArr.length){
    for(let j=i;j<temArr.length;j++){
      arr[left1++]=temArr[j];
    }
  }else{
    for(let j=left2;j<right2;j++){
      arr[left1++]=arr[j];
    }
  }
}
mergeSort(arr,0,arr.length-1);
console.log(arr);