• 常用排序算法


    本文章算法实现皆使用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
  • 相关阅读:
    洛谷 P1464 Function【动态规划(递推)/记忆化搜索(递归)】
    洛谷 P1426 小鱼会有危险吗【模拟/题意理解】
    洛谷 P2089 烤鸡【DFS递归/10重枚举】
    洛谷 P1579 哥德巴赫猜想(升级版)【筛素数/技巧性枚举/易错】
    洛谷 P1618 三连击(升级版)【DFS/next_permutation()/技巧性枚举/sprintf】
    sprintf 心得
    洛谷 P1478 陶陶摘苹果(升级版)【贪心/结构体排序/可用01背包待补】
    吉首大学 问题 L: 小李子的老年生活
    AtCoder Beginner Contest 084 D
    一维差值维护心得
  • 原文地址:https://www.cnblogs.com/heshuaiblog/p/11926757.html
Copyright © 2020-2023  润新知