排序算法

170 阅读3分钟

选择排序

思想:每次选择最小的一次放在前面,直到所有的数据排序完成 代码:

function selectSort(arr) {
    let len = arr.length;
    let minIndex = 0;
    for (let i = 0; i < len - 1; i++) {
        minIndex = i;
        for (let j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }

        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }

    retrun arr;
}

注意地方:最小数值用一个变量minIndex即可。

冒泡排序

思想:每一次比较都是左右两个比较,最大的往右移动,每一次循环就可以找到1个最大值,n次循环,就可以将最大值从左到右依次排序。

代码:

function bubbleSort(arr) {
    let len = arr.length;
    for (let i = 0; i < len - 1; i++) {
        for (j = 0; i < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}

冒泡排序优化版本:

function bubbleSort(arr) {
    let len = arr.length;
    for (let i = 0; i < len - 1; i++) {
        let flag = true;
        for (j = 0; i < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                flag = false;
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }

        if (flag) {
            return arr;
        }
    }
    return arr;
}

插入排序

思想:第一个是默认已经排好序,从第二个开始,每一次将后面的数字插入到已经排好序的数组中,剩余数据全部插入完毕,也就是一个排好序的数组

代码:

function insertSort(arr) {
    for (let i = 1; i < arr.length; i++) {
        let temp = arr[i];
        for (j = i - 1; j >= 0; j--) {
            if (arr[j] > temp) {
                arr[j + 1] = arr[j];
            } else {
                break;
            }
        }
        arr[j + 1] = temp;
    }

    return arr;
}

使用temp保存当前要插入的数据。

归并排序

思想:

  • 1,首先将数组分割为长度为1的小数组
  • 2,然后两两进行合并
  • 3,按照步骤二循环进行合并操作,最终合成一个有序的数组

代码:

function mergeSort(arr) {
    if (arr.length === 1) {
        return arr;
    }
    let mid = Math.floor(arr.length / 2);
    let left = arr.slice(0, mid);
    let right = arr.slice(mid, arr.length);

    let sortedLeft = mergeSort(left);
    let sortedRight = mergeSort(right);
    let res = [];

    while (sortedLeft.length || sortedRight.length) {
        if (sortedLeft.length && sortedRight.length) {
            res.push(
                sortedLeft[0] > sortedRight[0]
                    ? sortedRight.shift()
                    : sortedLeft.shift()
            );
        } else if (sortedLeft.length) {
            res.push(sortedLeft.shift());
        } else if (sortedRight.length) {
            res.push(sortedRight.shift());
        }
    }

    return res;
}

const arr = [1, 5, 6, 7, 2, 3, 6, 4, 7];
console.log(mergeSort(arr));

快速排序

思想:

  • 找到一个基准数,将小于基准数的放在左边的数组,大于基准数的放在右边的数组
  • 对子数组重复操作1;
  • 最后返回左边数组 与 基准数 以及右边数组的合并数组

代码:

function quickArr(arr) {
   if (arr.length <= 0) {
       return arr;
   }

   const quirtIndex = Math.floor(arr.length / 2);
   const middleValue = arr.splice(quirtIndex, 1)[0];
   const leftArr = [];
   const rightArr = [];
   for (let i = 0; i < arr.length; i++) {
       if (arr[i] < middleValue) {
           leftArr.push(arr[i]);
       } else {
           rightArr.push(arr[i]);
       }
   }

   return quickArr(leftArr).concat(middleValue, quickArr(rightArr));
}

堆排序

思想,

  • 首先构建一个大顶堆,构建大顶堆的过程是依次从倒数第二层的节点对节点进行以及其子节点进行heapify处理,heapif要有递归,因为和顶节点交换之后的子堆可能不是一个大顶堆。
  • 然后每次都将最后一个节点予第一个节点进行交换,然后再对顶节点进行heapify处理,循环,直到依次找到当前循环的最大值,最后就是一个升序的数组。

代码:

function swap(arr, pos, max) {
   const temp = arr[pos];
   arr[pos] = arr[max];
   arr[max] = temp;
}

function heapify(arr, n, pos) {
   if (2 * pos + 1 >= n) {
       return;
   }
   let max = pos;
   const left = 2 * pos + 1;
   const right = 2 * pos + 2;
   if (left < n && arr[left] > arr[max]) {
       max = left;
   }

   if (right < n && arr[right] > arr[max]) {
       max = right;
   }

   if (max !== pos) {
       swap(arr, pos, max);
       heapify(arr, n, max);
   }
}

function buildHeap(arr) {
   for (let i = arr.length - 1; i >= 0; i--) {
       heapify(arr, arr.length, i);
   }
}

function heapSort(arr) {
   buildHeap(arr);
   for (i = arr.length - 1; i > 0; i--) {
       swap(arr, i, 0);
       heapify(arr, i, 0);
   }
}

const arr = [90, 9, 8, 9, 6, 7, 4, 5, 1, 3, 3, 2, 2, 100, 4, 5];
heapSort(arr);
console.log(arr);

希尔排序

思想:

  • 第一次选取间隔为k的元素分为若干组,然后每组进行插入排序,排序完成后,每一组内是有序的
  • 然后再次选取之前间隔的1/2分组,然后依旧堆所有元素组内进行插入排序
  • 依次循环,减小分组间隔k值,直到最后间隔为1,也就是所有元素为一组,这个时候数组是基本有序状态,然后组内进行插入排序,完成之后震哥哥数组就是一个有序数组

代码:

function shellSort(arr) {
   for (
       let gap = Math.floor(arr.length / 2);
       gap > 0;
       gap = Math.floor(gap / 2)
   ) {
       for (let i = gap; i < arr.length; i++) {
           const temp = arr[i];

           for (j = i - gap; j > 0; j--) {
               if (arr[j] > temp) {
                   arr[j + gap] = arr[j];
               } else {
                   break;
               }
           }

           arr[j + gap] = temp;
       }
   }
}

计数排序

思想: 计数排序是桶排序的一种,适用于范围比较小单数数据量比较大的数据

  • 首先循环数组,然后根据数组值对在另外一个数组tempArr进行计数
  • 循环tempArr计数数组,然后依次根据数组中的次数,进行循环输出,输出的数组即为有序数组
function countSort(arr) {
  const tempArr = new Array(12).fill(0);
  let resultArr = [];
  for (let i = 0; i < arr.length; i++) {
      tempArr[arr[i]] += 1;
  }

  console.log(tempArr);

  for (let i = 0; i < tempArr.length; i++) {
      if (tempArr[i] != 0) {
          resultArr = resultArr.concat(new Array(tempArr[i]).fill(i));
      }
  }
  return resultArr;
}

桶排序

思想:

  • 首先根据数据的范围划分为n个桶
  • 循环数组,将对应数据放到对应范围的桶里面
  • 然后对每个桶内数据进行排序
  • 最后合并桶。

代码:

function bucketSort(arr) {
  let resArr = [];
  let bucketSize = 5;
  let maxValue = arr[0];
  let minValue = arr[0];
  for (let i = 0; i < arr.length; i++) {
      if (arr[i] > maxValue) {
          maxValue = arr[i];
      }

      if (arr[i] < minValue) {
          minValue = arr[i];
      }
  }
  let buctetCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
  let bucket = new Array(buctetCount);
  for (let i = 0; i < buctetCount; i++) {
      bucket[i] = new Array();
  }

  for (i = 0; i < arr.length; i++) {
      bucket[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
  }

  for (i = 0; i < bucket.length; i++) {
      insertSort(bucket[i]);
      resArr = resArr.concat(bucket[i]);
  }

  return resArr;
}

function insertSort(arr) {
  for (let i = 1; i < arr.length; i++) {
      temp = arr[i];
      for (j = i - 1; j >= 0; j--) {
          if (arr[j] > temp) {
              arr[j + 1] = arr[j];
          } else {
              break;
          }
      }
      arr[j + 1] = temp;
  }
}

const arr = [1, 5, 4, 5, 2, 11, 2, 3, 6, 3, 4, 2, 55, 66, 77];
console.log(bucketSort(arr));

基数排序

思想:

  • 从个位起,每次向前取一位数,按照所取的数字对数组进行排序,因为取的数字只有一位,所以范围为0-1,分别放到0-9个篮子里
  • 一次排序完成后,将篮子里的数据依次取出,按照顺序放回原数组里
  • 继续对数组按照对应的位数进行排序,直到最高位的数字也进行排序,最后从篮子里依次取出的数据就是一个有序数组

代码:

function baseSort(arr) {
  let bucket = [];
  maxDigits = String(parseInt(Math.max(...arr))).length;
  for (let i = 0; i < 10; i++) {
      bucket[i] = [];
  }
  let dev = 10;
  let mod = 1;
  for (let i = 0; i < maxDigits; i++, dev *= 10, mod *= 10) {
      for (let j = 0; j < arr.length; j++) {
          let temp = parseInt((arr[j] % dev) / mod);
          bucket[temp].push(arr[j]);
      }
      let pos = 0;
      for (let i = 0; i < bucket.length; i++) {
          while (bucket[i].length > 0) {
              arr[pos++] = bucket[i].shift();
          }
      }
  }
}