常用排序算法

本文章算法实现皆使用js实现。

1. 冒泡排序

分析:原理如它名字一样,不断的让最大(最小)的数字冒出来,完成排序。时间复杂度为O(n²)。

算法实现:

let effervescent = (nums) => {
      let len = nums.length;

      for (let i = 0; i < len; i++) {
        for (let j = i + 1; j < len; j++) {
          if (nums[i] > nums[j]) {
            let temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
          }
        }
      }

      return nums;
    }
View Code

 

2. 选择排序

分析:遍历获取每次的最小(最大)值的下标,遍历完成后和边界位置置换,一直循环到全部数据,完成排序。 时间复杂度O(n²)。

算法实现:

let choose = (nums) => {
      let len = nums.length;
      for (let i = 0; i < len; i++) {
        let minIndex = i;

        for (let j = i + 1; j < len; j++) {
          if (nums[j] < nums[minIndex]) {
            minIndex = j;
          }
        }

        let temp = nums[minIndex];
        nums[minIndex] = nums[i];
        nums[i] = temp;
      }

      return nums;
    }
View Code

3. 快速排序

分析:选择一个基准元素,将数组中大于它的数放这个右边,小于它的数字放左边。然后将左边,右边递归调用这个方法进行排序。 时间复杂度 O(nlogn)。

算法实现

let quick = (nums, start = 0, end = 0) => {
      let len = nums.length;

      if(len < 2){
        return nums;
      }

      let leftIndex = start,
        rightIndex = end === 0 && start === 0 ? len - 1 : end;

      if(rightIndex - leftIndex < 1){
        return;
      }

      let flag = nums[start]; //基准

      while (leftIndex < rightIndex) {
        while (leftIndex < rightIndex) { //从右边找出一个小于基准的数字,放入左边位置
          if (flag > nums[rightIndex]) {
            nums[leftIndex++] = nums[rightIndex];
            break;
          }
          --rightIndex;
        }

        while (leftIndex < rightIndex) { //从左边找出一个大于基准的数字,放入右边位置
          if (flag <= nums[leftIndex]) {
            nums[rightIndex--] = nums[leftIndex];
            break;
          }

          ++leftIndex;
        }
      }

      nums[leftIndex] = flag; //将基准放入中间位置,该位置为上面移位后空余的位置

      quick(nums, start, leftIndex - 1);//left
      quick(nums, rightIndex + 1, end);//right
      
      return nums;
    }
View Code

 4. 插入排序

分析:将数组看成两个部分,前面部分为排序部分,后面部分为待排序部分,一次遍历待排序部分,插入排序部分。排序部分不断增加,待排序部分不断减少,直至变成目标排序数组。时间复杂度O(n²)。

算法实现:

var insert = (nums) => {
      let len = nums.length;
      for (let i = 1; i < len; i++) {
        let target = nums[i];

        //找出插入位置
        let j = 0;
        while (j < i) {
          if (target < nums[j]) {
            break;
          }
          j++;
        }

        //插入
        if (j < i) {
          let removeIndex = i;
          //后移
          while (removeIndex >= j) {
            nums[removeIndex] = nums[removeIndex - 1];
            removeIndex--;
          }

          //插入
          nums[j] = target;
        }
      }

      return nums;
    }
View Code

 5. 希尔排序

分析:设置插入比较的步长,不断进行插入排序,并且减少步长,他会逐渐使序列变得有序,当步长为1时,就是插入排序。它是插入排序的进阶版,主要优点是减少移动次数。时间复杂度O(n²)。

算法实现:
let shell = (nums) => {
      let len = nums.length;

      for (let step = Math.round(len / 2); step > 0; step = Math.floor(step / 2)) {//step 为分组长度
        for (let i = step; i < len; i += step) {
          let flag = nums[i];

          //找到插入位置
          let j = 0;
          while (j < i) {
            if (nums[j] > flag) {
              break;
            }

            j += step;
          }

          //后移
          if (j < i) {
            let removeIndex = i;
            while(removeIndex > j){
              nums[removeIndex] = nums[removeIndex - step];
              removeIndex -= step;
            }

            nums[j] = flag;
          }
        }
      }

      return nums;
    }
View Code

 6. 桶排序

分析:获取数组中最大值, 用该值初始化一个长度为该值的一个数组(桶),初始各项为0,遍历需要排序的数组,把每个出现的数字作为下标,标记桶数组该下标的数量。最后遍历桶,输出内容大于0的下标,内容有多少就输出多少次。这样做的缺点很明显,在出现一个特别大的数字情况下特别的浪费空间,不过在特定情况下,它的排序速度会比较快。时间复杂度为: O(x*n)。

算法实现为:
  let bucket = (nums) => {
    let arr = [];

    let len = nums.length;

    //获取最大值
    let max = nums[0];
    let i = 1;
    while(i < len){
      if(nums[i] > max){
        max = nums[i];
      }
      i++;
    }

    //初始化桶
    let bucketArr = new Array(max);

    //标记桶
    i = 0;
    while(i < len){
      let val = bucketArr[nums[i]];
      if(val && val > 0){
        bucketArr[nums[i]] ++;
      }else{
        bucketArr[nums[i]] = 1;
      }
      i++;
    }

    //得到数组
    i = 0;
    while(i <= max){
      if(bucketArr[i] && bucketArr[i] > 0){
        let j = 0;
        while(j < bucketArr[i]){
          arr.push(i);
          j++;
        }
      }
      i ++;
    }

    return arr;
  }
View Code

 7. 基数排序

分析:升级版本的桶排序。低位排序(LSD):初始化一个长度为10的数组(桶)。首先对待排序的个位数进行桶排序,获取排序结果。然后对结果进行十位数的桶排序。直至完成最大位数的桶排序,获取最后的排序结果。高位排序(MSD): 初始化一个长度为10的数组(桶)。首先对最高位进行桶排序,然后再对桶内的元素进行低一位的排序,一直到个位完成排序。两种方式不同之处在于,高位排序为真实排序,所以不必再和其它为进行比对。 时间复杂度: O(x*(log(r))*n)。

LSD算法实现为:

let radixLsd = (nums) => {
      let len = nums.length;

      let bucketArr = new Array(10);

      //找到最大位,确定排序次数
      let max = nums[0];
      for (let i = 1; i < len; i++) {
        if (nums[i] > max) {
          max = nums[i];
        }
      }

      let maxLen = max.toString().length;

      let index = 0; //[0, maxLen)
      while (index < maxLen) {
        bucketArr = new Array(10);
        //对该位桶排序
        let i = 0; // 遍历nums [0, len)

        //入桶
        while (i < len) {
          //获取nums[i] index位置的数字
          let valArr = nums[i].toString().split('');
          let numberIndex = valArr.length <= index ? -1 : valArr.length - index - 1;
          let number = numberIndex >= 0 ? parseInt(valArr[numberIndex]) : 0;

          if (bucketArr[number]) {
            bucketArr[number].push(nums[i]);
          } else {
            bucketArr[number] = [nums[i]];
          }
          i++;
        }

        nums = [];

        //出桶
        let j = 0; //遍历桶的索引 [0, 9]
        while (j <= 9) {
          if (bucketArr[j]) {
            for (let k = 0; k < bucketArr[j].length; k++) {
              nums.push(bucketArr[j][k]);
            }
          }
          j++;
        }

        index++;
      }

      return nums;
    }
View Code

MSD算法实现为:

let radixMsd = (nums) => {
      //递归获取排序桶
      let bucketArr = bucketMsd(nums, -1);

      //递归读取桶中排序数据
      let arr = [];

      readBucket(bucketArr, arr);

      return arr;
    }

    let readBucket = (bucket, arr) => {
      if (bucket instanceof Array) {
        for (let i = 0; i < bucket.length; i++) {
          if (bucket[i]) {
            readBucket(bucket[i], arr);
          }
        }
      } else {
        if (typeof(bucket) === 'number') {
          arr.push(bucket);
        }
      }
    }

    //对数组高位桶排序,返回桶
    let bucketMsd = (nums, index) => {
      //index 为排序位,-1时为最高位置
      let len = nums.length;

      let bucketArr = new Array(10);
      if (index === -1) {
        //找到最大位,确定排序次数
        let max = nums[0];
        for (let i = 1; i < len; i++) {
          if (nums[i] > max) {
            max = nums[i];
          }
        }

        let maxLen = max.toString().length;

        index = maxLen - 1; //控制桶排序次数 [0, max - 1]
      }

      //根据最高位入桶
      for (let i = 0; i < len; i++) {
        //获取nums[i] index位置的数字
        let valArr = nums[i].toString().split('');
        let numberIndex = valArr.length <= index ? -1 : valArr.length - index - 1;
        let number = numberIndex >= 0 ? parseInt(valArr[numberIndex]) : 0;

        if (bucketArr[number]) {
          bucketArr[number].push(nums[i]);
        } else {
          bucketArr[number] = [nums[i]];
        }
      }

      if (index > 0) {
        for (let k = 0; k < bucketArr.length; k++) {
          if (bucketArr[k] && bucketArr[k].length > 0) {
            bucketArr[k] = bucketMsd(bucketArr[k], index - 1);
          }
        }
      }

      return bucketArr;
    }
View Code

 

posted @ 2019-11-25 16:30  一生舍给一座山  阅读(163)  评论(0编辑  收藏  举报