• 常见排序算法js


    开篇

    共总结了冒泡,选择,插入,归并,快速,希尔,堆七种排序方式,而V8引擎sort的排序:数组长度小于等于 10的用插入排序,其它的用快速排序。

    不稳定算法:快 希 选 堆

    以下是算法可能涉及到的公共函数:

    //排除非数组
    function checkArray(array) {
        if (!array instanceof Array)return;
    }
    //交换两个元素
    function swap(array, left, right) {
        let rightValue = array[right]
        array[right] = array[left]
        array[left] = rightValue
    }
    
    
    

    一.冒泡 时间复杂度是 O(n * n)

    function bubble(array) {//冒泡算法
        checkArray(array);
        for (let i = array.length - 1; i > 0; i--) {
          // 从 0 到 `length - 1` 遍历
          for (let j = 0; j < i; j++) {
            if (array[j] > array[j + 1]) swap(array, j, j + 1)
          }
        }
        return array;
      }
    function checkArray(array) {
        if (!array) return
    }
    function swap(array, left, right) {
        let rightValue = array[right]
        array[right] = array[left]
        array[left] = rightValue
    }
    

    变种:求数组中最小 的K个数字

    function GetLeastNumbers_Solution(input, k)
    { 
        let arr=[];
        // write code here
        for(let i = 0; i < k;i++){
            
            for(let j=0;j<input.length-i-1;j++){
                if(input[j]<input[j+1]){//将小的数冒泡到最后
                    let tmp=input[j];
                    input[j]=input[j+1];
                    input[j+1]=tmp;
                }
            }
            
           
                arr.push(input[input.length - i - 1]);//拿input最后值加入
            
           
        } 
        return arr;
    }
    

    二.选择排序 时间复杂度是 O(n * n)

    //遍历数组,定义一个等于当前索引的最小值索引。取出当前的值去与其他元素比较,得到最小值索引,把最小值索引代表的元素赋值给当前值。
    function choise(arr){
        for(let i=0;i<arr.length;i++){
            let min=i;
            for(let j=i+1;j<arr.length;i++){
                min=arr[min]>arr[j]?j:min;
                
            }
            swap(arr,min,i);
        }
    }
    
    
    

    三.插入排序 时间复杂度是 O(n * n)

    //默认前面是已经排好序的,遍历数组,取出当前值与前面排好的序列进行比较。
    //如果当前值比前一个数小,就与其交换位置。
    //重复以上步骤
    function insert(arr){
       for(let i=1;i<arr.length;i++){//默认第一个排好序
         for(let j=i;j>=0;j--){
             if(arr[j]<arr[j-1])swap(arr,j,j-1);//如果前一个数比后一个数字大
         }
         
       }
    return arr;
    
    }
    

    四.归并排序 时间复杂度为 O(N * logN)

    /*
    假如有数组[1,4,3,6,8,7,5],利用中间索引3,拆分得到[1,4,3,6]和[8,7,5],继续拆分得到[1,4]和[3,6]
    对[1,4]和[3,6]各自排序后,再对[1,4,3,6]排序,右边也是如此,最后合并
    先递归的分解数列,再合并数列
    */
    
    
      /*
      合并两个有序数列
    如何将将二个有序数列合并。这个非常简单,
    只要从比较二个数列的第一个数,谁小就先取谁,
    取了后就在对应数列中删除这个数。然后再进行比较,
    如果有数列为空,那直接将另一个数列的数据依次取出即可。
    */
    function mergeArray(left,mid,right,array){
        
        let help = [];
        let i = 0;
        let p1 = left;
        let p2 = mid + 1;
        //合并 :将两个序列a[first-middle],a[middle+1-end]合并
        while (p1 <= mid && p2 <= right) {
          help[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
        }
        while (p1 <= mid) {
          help[i++] = array[p1++];
        }
        while (p2 <= right) {
          help[i++] = array[p2++];
        }
        for (let i = 0; i < help.length; i++) {
          array[left + i] = help[i];
        }
        return array;
    
    
        
    }
    
    //递归分解
    function mergeSort(array, left, right) {
        // 左右索引相同说明已经只有一个数
        if (left === right) return;
        let mid = parseInt(left + ((right - left) >> 1));
        mergeSort(array, left, mid);
        mergeSort(array, mid + 1, right);
        //合并
        mergeArray(left,mid,right,array);
    
      }
    
     /*
      mergeSort(array, left, right)
      mergeSort(data, 0, 6) // mid = 3
      mergeSort(data, 0, 3) // mid = 1
        mergeSort(data, 0, 1) // mid = 0
          mergeSort(data, 0, 0) // 遇到终止,回退到上一步
        mergeSort(data, 1, 1) // 遇到终止,回退到上一步
        // 排序 p1 = 0, p2 = mid + 1 = 1,   mergeArray(left0,mid0,right1,array) [8,3]变成[3,8]
        // 回退到 `mergeSort(data, 0, 3)` 执行下一个递归
      mergeSort(2, 3) // mid = 2
        mergeSort(3, 3) // 遇到终止,回退到上一步
      // 排序 p1 = 2, p2 = mid + 1 = 3,    [2,1]变[1,2]
      // 回退到 `mergeSort(data, 0, 3)` 执行合并逻辑//mid=1
      // 排序 p1 = 0, p2 = mid + 1 = 2
      // 执行完毕回退
      // 左边数组排序完毕,右边也是如上轨迹
      */
    
    

    五.快速排序 平均时间复杂度:O(N*logN)

      function quickSort(array, left, right) {
        if (left < right) {
            // 用中间值作为基准值或者随机
            let mid=(left+right)>>1;
            console.log(mid);
            swap(array,left,mid);
          
             let i=left,j=right,x=array[left];//x为基准值
          while(i<j){
    
              while(i<j&&array[j]>=x)//因为是找比x小的,从右向左比较,array[j]大的时候,j--
                     j--;
             if(i<j){
                array[i++]=array[j];
             }
            while(i<j&&array[i]<x){//从左向右比较,i向前
                i++;
            }
            if(i<j){
                array[j--]=array[i];
            }
            
          }
          array[i]=x;//array[i]的左边是小于它的数,右边是大于的
          quickSort(array,left,i-1);
          quickSort(array,i+1,right);
    
    
    
    
        }
        return array;
      }
    console.log(quickSort(arr,0,arr.length-1))
    

    六.希尔排序 希尔排序的时间复杂度和增量序列是相关的

    希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。

    希尔算法步骤:选择一个增量,一般是初始增量gap=length/2,对利用gap分好的组进行插入排序,缩小增量继续以gap = gap/2的方式,重复以上步骤。当gap=1时,数组已经基本有序,而插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;

      //希尔排序
      /**
       * @msg:希尔排序
       * @param  {Array}arr 数组
       * @return:  {Array} 新数组
       */  
      console.log(shellSort([2,5,66,1,3,4]))
      function shellSort(arr){
       //增量gap初始为数组长度的一半,逐渐缩小,以gap/2的方式
       for(let gap=parseInt(arr.length/2);gap>0;gap=parseInt(gap/2)){
          //从第gap个元素开始,对用gap分好的组进行插入排序
          for(let i=gap;i<arr.length;i++){
            let j=i;
            //插入排序
            while(j-gap>=0&&arr[j]<arr[j-gap]){ 
              swap(arr,j,j-gap);
              j-=gap;//跳出循环
              
            }
          }
    
        
       }
     return arr;
    
        
      }
    
    
    

    七.堆排序(HeapSort) 平均时间复杂度:O(NlogN)

    堆排序是利用这种数据结构而设计的一种排序算法

    堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

    堆排序思路:

    1.先构造大顶堆

    2.将堆顶元素放入末尾

    3.再重新调整结构使其满足大顶堆

    4.重复2,3步骤

    /**
     * @msg: 堆排序
     * @param {Array} arr 数组
     * @return:  {Array} 新数组
     */
    heapSort([4,6,8,5,9]);
    function heapSort(arr) {
    
      // 1.构造大顶堆
      for (let i = parseInt(arr.length/2)-1; i >= 0; i--) {
        console.log(i);
        //i从第一个非叶子节点开始
        //数组[4,6,8,5,9]变成[ 9, 6, 8, 5, 4 ]
        adjustHeap(arr, i, arr.length);
        console.log(arr);
      }
    
      //2.将堆顶元素与末尾元素进行交换,再进行调整
      for(let j=arr.length-1;j>0;j--){
        swap(arr,0,j);//将堆顶元素与末尾元素进行交换
        adjustHeap(arr,0,j);//重新对堆进行调整
    }
    
    console.log(arr);
    
    }
    /**
     * @msg: 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
     * @param arr
     * @param i
     * @param length   * 
     * @return:  {array} 新数组
     */
    function adjustHeap(arr,i,length){
    
     //先取出当前元素
     let temp=arr[i];
     //从i节点的左子节点开始
     for(let k=i*2+1;k<length;k=k*2+1){
        if(k+1<length&&arr[k]<arr[k+1]){//如果k++左子节点小于右子节点
          //k指向右子节点
          k++;
          
        }
        if(arr[k]>temp){//如果子节点大于父节点
          //将子节点赋予父节点
         arr[i]=arr[k];
         i=k
          
        }else{
          break;
        }
    
     }
      arr[i]=temp;//temp会根据情况赋值给对应的节点,i可能变化
    
    }
    
  • 相关阅读:
    【PyQt5-Qt Designer】QSpinBox-微调框
    【PyQt5-Qt Designer】QProgressBar() 进度条
    【PyQt5-Qt Designer】QSlider滑块
    Tomcat eclipse 启动时一个工程影响另一个工程
    apache thrift 和 apache jersey 记录
    常用 Linux 命令
    mac 命令记录
    eclipse m2eclipse 从Maven的本地库中读取依赖库
    成功build Maven但eclipse中依然显示该工程有错误
    mac install: /usr/bin/unrar: Operation not permitted
  • 原文地址:https://www.cnblogs.com/listenMao/p/13041172.html
Copyright © 2020-2023  润新知