美文网首页
排序算法

排序算法

作者: 郝同学1208 | 来源:发表于2022-04-01 17:38 被阅读0次

    文章序

    排序算法作为算法的入门,也是在日常开发中十分常用的,故在此整理出十大排序算法,方便自己和需要的朋友查阅

    先定义交换函数

      //不使用额外空间交换算法,不能自己跟自己换
      function swap(array, i, j) {
        if (i === j) {
          return array;
        }
        let arr = array;
        arr[i] = arr[i] + arr[j];
        arr[j] = arr[i] - arr[j];
        arr[i] = arr[i] - arr[j];
        return arr;
      }
    
      //使用额外空间交换算法,可以自己跟自己换
      function swap(array, i, j) {
        let arr = array;
        let temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
        return arr;
      }
    

    1冒泡排序

    原理:数组长度为 n,遍历 n - 1 趟,每趟从第 1 个元素向后两两比较,如果左边的元素大于右边的元素则将两个元素据交换,直到左边的元素为第 n - 1 个元素,交换完毕后,此时数组最右端的元素即为该数组中最大的元素。接着对该数组剩下的 n-1 个元素继续冒泡排序,直到整个数组有序排列
    特性:时间复杂度:O(n^2),空间复杂度:O(1) ,稳定排序 ,原地排序
    方法:两层循环嵌套,外层共遍历 n - 1 趟,内层遍历从第 1 个元素到 n - 1 - i 个元素,进行两两比较交换

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

    2选择排序

    原理:遍历 n - 1 趟,每趟遍历找到剩余最小的元素放在前面,依次类推,直到数组有序排列
    特性:时间复杂度:O(n^2) ,空间复杂度:O(1) ,非稳定排序,原地排序
    方法:两层循环嵌套,外层共遍历 n - 1 趟,内层遍历从第 i 个元素开始,找到最小的元素和第 i 个元素交换

      function selectionSort(arr) {
        let res = arr;
        let n = arr.length;
        let minIndex = 0;
        for(let i = 0; i < n - 1; i++) {
          minIndex = i;
          for(let j = i + 1; j < n; j++) {
            if(res[j] < res[minIndex]) {
              minIndex = j;
            }
          }
          if(minIndex !== i) {
            res = swap(res, i, minIndex);
          }
        }
        return res;
      }
    

    3插入排序

    原理:从第 1 个元素开始,将后面的元素与该元素比较,大则放在右边,小则放在左边,此时该两元素构成数组有序,剩余元素构成的数组无序,依次将剩余的元素向前面已有序数组中插入,直到整个数组有序
    特性:时间复杂度:O(n^2),空间复杂度:O(1) ,稳定排序 ,原地排序
    方法:两层循环嵌套,外层共遍历 n - 1 趟代表插入 n - 1 个元素,内层遍历前面已有序数组直到找到合适的插入的元素的位置

      function insertSort(arr) {
        let res = arr;
        let n = arr.length;
        for (let i = 1; i < n; i++) {
          for (let j = i - 1; j >= 0; j--) {
            if (res[j] < res[j + 1]) {
              break;
            }
            swap(res, j, j + 1);
          }
        }
        return res;
      }
    

    4希尔排序

    原理:定义增量(gap) h,将待排数组分割成为 h 个子数组分别进行插入排序,操作之后减小 h 的值,再次分别进行插入排序,直到 h = 1,则再进行最后一次插入排序,完成后整个数组排序完成
    特性:时间复杂度:O(nlogn) ,空间复杂度:O(1) ,非稳定排序,原地排序
    方法:三层遍历,最外层每次减少增量h的值,内两层做插入排序,注意间隔不再是 1 而是增量 h

      function shellSort(arr) {
        let res = arr;
        let n = arr.length;
        let h = parseInt(n / 2);
        while (h >= 1) {
          //进行插入排序
          let i = 0;
          let temp = 0;
          while(temp < h) {
            for (let j = i - h; j >= 0; j = j - h) {
              if (res[j] < res[j + h]) {
                break;
              }
              swap(res, j, j + h);
            }
            i = i + h;
            if(i >= n) {
              temp++;
              i = temp;
            }
          }
          h = parseInt(h / 2);
        }
        return res;
      }
    

    5快速排序

    原理:选择一个元素,将比他大的都放到右侧,比它小的都放在左侧,此时数组分成两个数组,再次对两个数组进行快排,这样递归下去直到整个数组有序
    特性:时间复杂度:O(nlogn),空间复杂度:O(nlogn) ,非稳定排序,原地排序
    方法:设指针 cur 指向第 1 个元素开始,指针 i 从 cur 指向元素向右走,指针 j 从最后一个元素向前走,j 先走,当 j 找到比 cur 小的元素停止,当 i 找到比 cur 大的元素停止,交换 i j指向的元素,直到i j相遇,交换此时cur 和 i 指向元素的位置,对此时指针左右两侧数组递归进行快排

      function quickSort(arr) {
        let n = arr.length;
        handleSort(arr, 0, n - 1);
        return arr;
      }
      const handleSort = (arr, start, end) => {
        if (start >= end) {
          return;
        }
        let cur = start,
          i = start + 1,
          j = end;
        let mid = start;
        while (true) {
          while (j >= start && j > i) {
            if (arr[j] < arr[cur]) {
              break;
            }
            j--;
          }
          while (i <= end && i < j) {
            if (arr[i] > arr[cur]) {
              break;
            }
            i++;
          }
          if (i === j) {
            if (arr[i] < arr[cur]) {
              swap(arr, cur, i);
              mid = i;
            }
            break;
          }
          swap(arr, i, j);
        }
        handleSort(arr, start, mid - 1);
        handleSort(arr, mid + 1, end);
      };
    

    6归并排序

    原理:将数组除2拆分,直到拆分到仅有一个元素的数组,此时该数组有序,将该数组与上一次拆分的另一个数组合并,此时本次拆分的数组有序,继续向上和上一次拆分的另一个数组合并,直到整个数组合并完毕,整个数组有序
    特性:时间复杂度:O(nlogn),空间复杂度:O(n),稳定排序,非原地排序
    方法:递归操作,先定义拆分函数mergeSort,再定义合并函数merge,拆分函数负责将入参数组除2拆分,并对拆分过的数组继续调用拆分函数,当两次拆分函数都调用完毕返回结果则调用合并函数

      function mergeSort(arr) {
        let n = arr.length;
        if (n < 2) {
          return arr;
        }
        let mid = parseInt(n / 2);
        let left = arr.slice(0, mid);
        let right = arr.slice(mid, n);
        let sortedLeft = mergeSort(left);
        let sortedRight = mergeSort(right);
        return merge(sortedLeft, sortedRight);
      }
      const merge = (left, right) => {
        let res = [];
    
        while (left.length > 0 && right.length > 0) {
          if (left[0] < right[0]) {
            res.push(left.shift());
          } else {
            res.push(right.shift());
          }
        }
        if (left.length > 0) {
          res = res.concat(left);
        }
        if (right.length > 0) {
          res = res.concat(right);
        }
        return res;
      };
    

    7堆排序

    原理:大顶堆为二叉树数结构,任意节点的值均大于左右子节点,数组可以构建一颗二叉树结构,将此数组调整为二叉树符合大顶堆的顺序,再依次交换第 0 个元素和第 n 个元素,并重新调整堆使之符合大顶堆定义,n依次递减直到n = 1,此时排序完成
    特性:时间复杂度:O(nlogn),空间复杂度:O(1),非稳定排序,原地排序
    方法:parseInt(n / 2)该元素为最后一个非叶子节点的数据,从这里开始遍历,构建大顶堆。大顶堆构建完成后将第 0 个元素和最后一个元素交换,此时剩下的堆不符合大顶堆定义,调整堆使符合大顶堆定义,再次交换第 0 个元素和此时最后一个元素,循环 n - 1 次结束获得有序数组

      function heapSort(arr) {
        let n = arr.length;
    
        //从最后一个非叶子节点开始遍历,构建大顶堆
        for (let i = parseInt(n / 2); i >= 0; i--) {
          heapify(arr, i, n);
        }
        //大顶堆构建完毕,开始从最后一个元素开始交换
        for (let j = arr.length - 1; j > 0; j--) {
          n--;
          swap(arr, 0, j);
          heapify(arr, 0, n);
        }
        return arr;
      }
      function heapify(arr, i, n) {
        //调整堆
        let leftChild = 2 * i + 1;
        let rightChild = 2 * i + 2;
        let largest = i;
    
        if (leftChild < n && arr[leftChild] > arr[largest]) {
          largest = leftChild;
        }
        if (rightChild < n && arr[rightChild] > arr[largest]) {
          largest = rightChild;
        }
    
        if (largest !== i) {
          swap(arr, i, largest);
          heapify(arr, largest, n);
        }
      }
    

    8计数排序

    原理:开辟新的数组 newArray,长度为 k,将待排数组内的元素值作为 newArray 的下标,将数组 newArray 从头遍历依次输出赋值给原数组即完成排序
    特性:时间复杂度:O(n + m),空间复杂度:O(n + m),稳定排序,非原地排序
    方法:

      function countingSort(arr) {
        let newArray = [];
        let k = 0;
        for (let i = 0; i < arr.length; i++) {
          newArray[arr[i]] = arr[i];
        }
        for (let j = 0; j < newArray.length; j++) {
          if (newArray[j]) {
            arr[k] = newArray[j];
            k++;
          }
        }
        return arr;
      }
    

    9桶排序

    原理:创建多个桶空间用来存放一定范围内的数据,每个桶内再排序,将数据按顺序从非空桶中取出,排序完成
    特性:时间复杂度:O(n + m),空间复杂度:O(n + m),稳定排序,非原地排序
    方法:创建一个数组,该数组内元素为数组表示不同的桶

      function bucketSort(arr) {
        let n = arr.length;
        let max = arr[0];
        let min = arr[0];
        //找到最大最小值
        arr.forEach(item => {
          if (max < item) {
            max = item;
          } else if (min > item) {
            min = item;
          }
        });
        //设数组长度为桶长度,获取桶数量
        let bucketCount = parseInt((max - min) / n);
        let bucketList = new Array(bucketCount + 1);
        for (let i = 0; i < bucketList.length; i++) {
          bucketList[i] = [];
        }
        //将每一个数据放入到合适的桶
        for (let j = 0; j < n; j++) {
          let index = parseInt((arr[j] - min) / n);
          bucketList[index].push(arr[j]);
        }
        //对每一个桶进行排序,这里选择计数排序
        bucketList.forEach(bucket => {
          countingSort(bucket);
        });
        for (let k = 0; k < bucketList.length; k++) {
          if (k === 0) {
            arr = [];
          }
          arr = arr.concat(bucketList[k]);
        }
        return arr;
      }
    

    10基数排序

    原理:将数组按照从低位到高位的顺序排序,当每一趟都排序完成之后整个数组有序
    特性:时间复杂度:O(nm),空间复杂度:O(n + m),稳定排序,非原地排序
    方法:先找到最大的数并获取其位数 n,然后从个位开始向上遍历,共 n 次,每次遍历获取该数当前位的数并和桶里的数的当前位的数比较,没有当前位则补 0

      function radixSort(arr) {
        let max = arr[0];
        //找到最大的数并获取其位数
        arr.forEach(number => {
          if (number > max) {
            max = number;
          }
        });
        const n = max.toString().length;
        // let divide = Math.pow(10, n);
        let divide = 1;
    
        for (let i = 0; i < n; i++) {
          let bucket = new Array(arr.length);
          for (let j = 0; j < arr.length; j++) {
            let tempNum = parseInt(arr[j] / divide);
            while (tempNum >= 10) {
              tempNum %= 10;
            }
            for (let k = 0; k < bucket.length; k++) {
              if (bucket[k]) {
                let tempBucketNum = parseInt(bucket[k] / divide);
                while (tempBucketNum >= 10) {
                  tempBucketNum %= 10;
                }
                if (tempNum < tempBucketNum) {
                  bucket.splice(k, 0, arr[j]);
                  bucket.pop();
                  break;
                }
              } else {
                bucket[k] = arr[j];
                break;
              }
            }
          }
          divide *= 10;
          arr = bucket;
        }
        return arr;
      }
    

    相关文章

      网友评论

          本文标题:排序算法

          本文链接:https://www.haomeiwen.com/subject/hpcllrtx.html