详解JavaScript如何实现四种常用排序

目录
  • 一、插入排序
    • 直接插入排序
  • 二、交换排序
    • (1)冒泡排序
    • (2)快速排序
  • 三、选择排序
    • (1)简单选择排序
    • (2)堆排序
  • 四、归并排序

一、插入排序

插入排序有直接插入排序,折半插入排序,希尔排序,这里只实现常用的直接插入排序

直接插入排序

将左侧序列看成一个有序序列,每次将一个数字插入该有序序列。

插入时,从有序序列最右侧开始比较,若比较的数较大,后移一位。

function insertSort(array) {
//第一个默认已经排好
      for (let i = 1; i < array.length; i++) {
        let target = i;
        for (let j = i - 1; j >= 0; j--) {
          if (array[target] < array[j]) {
            [array[target], array[j]] = [array[j], array[target]]
            target = j;
          } else {
            break;
          }
        }
      }
      return array;
    }

复杂度

时间复杂度:O(n2)

空间复杂度:O(1)

稳定性

稳定

二、交换排序

(1)冒泡排序

循环数组,比较当前元素和上一个元素,如果当前元素比上一个元素小,向下冒泡。

这样一次循环之后最前一个数就是本数组最小的数。

下一次循环继续上面的操作,不循环已经排序好的数。

优化:当一次循环没有发生冒泡,说明已经排序完成,停止循环。

    function bubbleSort(array) {
        //第一个循环是所需次数
      for (let j = 0; j < array.length; j++) {
        let complete = true;
        for (let i = array.length-1; i>j; i--) {
          // 比较相邻数
          if (array[i] < array[i -1]) {
            [array[i], array[i - 1]] = [array[i - 1], array[i]];
            complete = false;
          }
        }
        // 没有冒泡结束循环
        if (complete) {
          break;
        }
      }
      return array;
    }

复杂度

时间复杂度:O(n2)

空间复杂度:O(1)

稳定性

稳定

(2)快速排序

快速排序:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据比另一部分的所有数据要小,再按这种方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,使整个数据变成有序序列。

实现步骤:

  • 选择一个基准元素target(一般选择第一个数)
  • 将比target小的元素移动到数组左边,比target大的元素移动到数组右边
  • 分别对target左侧和右侧的元素进行快速排序

从上面的步骤中我们可以看出,快速排序也利用了分治的思想(将问题分解成一些小问题递归求解)

下面是对序列6、1、2、7、9、3、4、5、10、8排序的过程:

//JS自带的sort()就是快排
function quickSort(array, start, end) {
      if (end - start < 1) {
        return;
      }
      const target = array[start];
      let l = start;
      let r = end;
      while (l < r) {
        while (l < r && array[r] >= target) {
          r--;
        }
        array[l] = array[r];
        while (l < r && array[l] < target) {
          l++;
        }
        array[r] = array[l];
      }
      array[l] = target;
      quickSort(array, start, l - 1);
      quickSort(array, l + 1, end);
      return array;
    }

复杂度

时间复杂度:平均O(nlogn),最坏O(n2),实际上大多数情况下小于O(nlogn)

空间复杂度:O(logn)(递归调用消耗)

稳定性

不稳定

三、选择排序

(1)简单选择排序

每次循环选取一个最小的数字放到前面的有序序列中。

 function selectionSort(array) {
      for (let i = 0; i < array.length - 1; i++) {
        let minIndex = i;
        for (let j = i + 1; j < array.length; j++) {
          if (array[j] < array[minIndex]) {
            minIndex = j;
          }
        }
        [array[minIndex], array[i]] = [array[i], array[minIndex]];
      }
    }

复杂度

时间复杂度:O(n2)

空间复杂度:O(1)

稳定性

不稳定

(2)堆排序

创建一个大顶堆,大顶堆的堆顶一定是最大的元素。

交换第一个元素和最后一个元素,让剩余的元素继续调整为大顶堆。

从后往前以此和第一个元素交换并重新构建,排序完成。

 function heapSort(array) {
      creatHeap(array);
      console.log(array);
      // 交换第一个和最后一个元素,然后重新调整大顶堆
      for (let i = array.length - 1; i > 0; i--) {
        [array[i], array[0]] = [array[0], array[i]];
        adjust(array, 0, i);
      }
      return array;
    }
    // 构建大顶堆,从第一个非叶子节点开始,进行下沉操作
    function creatHeap(array) {
      const len = array.length;
      const start = parseInt(len / 2) - 1;
      for (let i = start; i >= 0; i--) {
        adjust(array, i, len);
      }
    }
    // 将第target个元素进行下沉,孩子节点有比他大的就下沉
    function adjust(array, target, len) {
      for (let i = 2 * target + 1; i < len; i = 2 * i + 1) {
        // 找到孩子节点中最大的
        if (i + 1 < len && array[i + 1] > array[i]) {
          i = i + 1;
        }
        // 下沉
        if (array[i] > array[target]) {
          [array[i], array[target]] = [array[target], array[i]]
          target = i;
        } else {
          break;
        }
      }
    }

复杂度

时间复杂度:O(nlogn)

空间复杂度:O(1)

稳定性

不稳定

四、归并排序

利用归并的思想实现的排序方法。

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

  • 将已有序的子序列合并,得到完全有序的序列
  • 即先使每个子序列有序,再使子序列段间有序
  • 若将两个有序表合并成一个有序表,称为二路归并

分割:

  • 将数组从中点进行分割,分为左、右两个数组
  • 递归分割左、右数组,直到数组长度小于2

归并:

如果需要合并,那么左右两数组已经有序了。

创建一个临时存储数组temp,比较两数组第一个元素,将较小的元素加入临时数组

若左右数组有一个为空,那么此时另一个数组一定大于temp中的所有元素,直接将其所有元素加入temp

function mergeSort(array) {
      if (array.length < 2) {
        return array;
      }
      const mid = Math.floor(array.length / 2);
      const front = array.slice(0, mid);
      const end = array.slice(mid);
      return merge(mergeSort(front), mergeSort(end));
    }

    function merge(front, end) {
      const temp = [];
      while (front.length && end.length) {
        if (front[0] < end[0]) {
          temp.push(front.shift());
        } else {
          temp.push(end.shift());
        }
      }
      while (front.length) {
        temp.push(front.shift());
      }
      while (end.length) {
        temp.push(end.shift());
      }
      return temp;
    }

做题时,上面多了删除过程,特别大的例子,时间也可能会超,用下面的方法

function merge(left, right){
    let leftLen = left.length, rightLen = right.length;
    let i = 0, j = 0;
    let temp = new Array(leftLen + rightLen);
    for(let cur = 0; cur < leftLen + rightLen; cur++){
        // 检查i, j有没有超界
        if(i >= leftLen) temp[cur]= right[j++];
        else if(j >= rightLen) temp[cur] = left[i++];
        else if(left[i] <= right[j]){
            temp[cur] = left[i++];
        }else{
            temp[cur] = right[j++];
        }
    }
    return temp;
}

复杂度

时间复杂度:O(nlogn)

空间复杂度:O(n)

稳定性

稳定

到此这篇关于详解JavaScript如何实现四种常用排序的文章就介绍到这了,更多相关JavaScript排序内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript希尔排序、快速排序、归并排序算法

    以var a = [4,2,6,3,1,9,5,7,8,0];为例子. 1.希尔排序. 希尔排序是在插入排序上面做的升级.是先跟距离较远的进行比较的一些方法. function shellsort(arr){ var i,k,j,len=arr.length,gap = Math.ceil(len/2),temp; while(gap>0){ for (var k = 0; k < gap; k++) { var tagArr = []; tagArr.push(arr[k]) for (i

  • JS排序之选择排序详解

    本文为大家分享了JS选择排序的具体代码,供大家参考,具体内容如下 说明 时间复杂度指的是一个算法执行所耗费的时间 空间复杂度指运行完一个程序所需内存的大小 稳定指,如果a=b,a在b的前面,排序后a仍然在b的前面 不稳定指,如果a=b,a在b的前面,排序后可能会交换位置 --JS选择排序-- 原理 首先从原始数组中找到最小的元素,并把该元素放在数组的最前面,然后再从剩下的元素中寻找最小的元素,放在之前最小元素的后面,知道排序完毕. 时间复杂度,空间复杂度,稳定性 平均时间复杂度O(n*n) 最好

  • JavaScript数组排序的六种常见算法总结

    前言 着急用的话,选择前两个就行了,后面的看看就好. 开发中,遇到数组排序的需求很频繁,这篇文章会介绍几个常见排序思路. 一.希尔排序(性能最好) 如果要从大到小排列,则 while(arr[n] > arr[n - interval] && n > 0) . // 希尔排序算法 function xier(arr){ var interval = parseInt(arr.length / 2);//分组间隔设置 while(interval > 0){ for(var

  • JavaScript实现的七种排序算法总结(推荐!)

    目录 前言 冒泡排序 基础算法 第二种写法是在基础算法的基础上改良而来的: 选择排序 基础算法 二元选择排序-优化 插入排序 交换法插入排序 移动法 希尔排序 堆排序 快速排序 归并排序 总结 前言 所谓排序算法,即通过特定的算法因式将一组或多组数据按照既定模式进行重新排序.这种新序列遵循着一定的规则,体现出一定的规律,因此,经处理后的数据便于筛选和计算,大大提高了计算效率.对于排序,我们首先要求其具有一定的稳定性,即当两个相同的元素同时出现于某个序列之中,则经过一定的排序算法之后,两者在排序前

  • js交换排序 冒泡排序算法(Javascript版)

    比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 针对所有的元素重复以上的步骤,除了最后一个. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较. function sort(elements){ for(var i=0;i<elements.length-1;i++){ for(var j=0;j<elements.length-i-1;j++){ if(elemen

  • JS插入排序简单理解与实现方法分析

    本文实例讲述了JS插入排序简单理解与实现方法.分享给大家供大家参考,具体如下: 在这里,我详细的讲一下我个人对于插入排序的理解. 每个人对于事物的理解都是不一样的,因为每个人对世界万物的看法和思考方式都不一样.因此,对于排序算法,我想每个人都有自己的理解方式,所以,虽然博客园里有很多关于排序的文章,但那只是其他人对这几个排序的理解方式,而笔者也有自己的理解方式,所以,笔者也就没有在意博客园写了那么多关于排序的文章而还在这里写下个人的见解了. 对于插入排序,笔者是这么理解的: 插入排序就是把一组数

  • 详解JavaScript如何实现四种常用排序

    目录 一.插入排序 直接插入排序 二.交换排序 (1)冒泡排序 (2)快速排序 三.选择排序 (1)简单选择排序 (2)堆排序 四.归并排序 一.插入排序 插入排序有直接插入排序,折半插入排序,希尔排序,这里只实现常用的直接插入排序 直接插入排序 将左侧序列看成一个有序序列,每次将一个数字插入该有序序列. 插入时,从有序序列最右侧开始比较,若比较的数较大,后移一位. function insertSort(array) { //第一个默认已经排好 for (let i = 1; i < arra

  • 详解js中的几种常用设计模式

    工厂模式 function createPerson(name, age){ var o = new Object(); // 创建一个对象 o.name = name; o.age = age; o.sayName = function(){ console.log(this.name) } return o; // 返回这个对象 } var person1 = createPerson('ccc', 18) var person2 = createPerson('www', 18) 工厂函数

  • 详解JavaScript中的4种类型识别方法

    具体内容如下: 1.typeof [输出]首字母小写的字符串形式 [功能] [a]可以识别标准类型(将Null识别为object) [b]不能识别具体的对象类型(Function除外) [实例] console.log(typeof "jerry");//"string" console.log(typeof 12);//"number" console.log(typeof true);//"boolean" console

  • 详解java中的四种代码块

    在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. 3.静态代码块: 用static{}包裹起来的代码片段,只会执行一次.静态代码块优先于构造块执行. 4.同步代码块: 使用synchronized(){}包裹起来的代码块,在多线程环境下,对共享数据的读写操作是需要互斥进行的,否则会导致数据的不一致性.同步代码块需要写在方法中. 二.静态代码块和构造

  • JavaScript创建对象的四种常用模式实例分析

    本文实例讲述了JavaScript创建对象的四种常用模式.分享给大家供大家参考,具体如下: 这里介绍了javascript中创建对象常用的几种模式,包括:工厂模式,构造函数模式,原型模式,组合构造函数与原型的模式,动态原型模式. 一.工厂模式 看如下代码: function getMySon(name,sex){ var o={}; o.name=name; o.sex=sex; o.sayName = function(){ alert(this.name); } return o; } so

  • 详解Java数组的四种拷贝方式

    目录 深拷贝与浅拷贝的区别 1.for循环进行拷贝 拷贝数值类型 拷贝引用类型 2.copyof/copyOfRange 拷贝数值类型 拷贝引用类型 3.arraycopy 拷贝数值类型 拷贝引用类型 4.clone 拷贝数值类型 拷贝引用类型 5.总结 深拷贝与浅拷贝的区别 假设现在有原数组A以及拷贝后的数组B,若是改变A中的某一个值,B数组随之相应的发生变化的拷贝方式称为浅拷贝,反之B数组不受影响,则称为深拷贝:简单总结一下两者的概念: 深拷贝:拷贝后,修改原数组,不会影响到新数组: 浅拷贝

  • 详解JavaScript中哪一种循环最快呢

    了解哪一种 for 循环或迭代器适合我们的需求,防止我们犯下一些影响应用性能的低级错误. JavaScript 是 Web 开发领域的"常青树".无论是 JavaScript 框架(如 Node.js.React.Angular.Vue 等),还是原生 JavaScript,都拥有非常庞大的粉丝基础.我们来谈谈现代 JavaScript 吧.循环一直是大多数编程语言的重要组成部分,而现代 JavaScript 为我们提供了许多迭代或循环值的方法. 但问题在于,我们是否真的知道哪种循环或

  • Java详解Swing中的几种常用按钮的使用

    目录 Swing中的常用按钮 AbstractButton的常用方法 JRadionButton(单选按钮) 单选按钮的构造方法 复选框(JCheckBox) 复选框的构造方法 组合框(JComboBox) 组合框的构造方法 下拉列表框的常用方法 小结 Swing中的常用按钮 在Swing中,常见的按钮组件有JButton,JCheckBox,JRadioButton等,它们都是抽象类AbstractButton类的直接或间接子类.在AbstractButton类中提供了按钮组件通用的一些方法.

  • Java详解实现多线程的四种方式总结

    目录 前言 一.四种方式实现多线程 1.继承Thread类创建线程 2.实现Runnable接口创建线程 3.实现Callable接口 4.实现有返回结果的线程 二.多线程相关知识 1.Runnable 和 Callable 的区别 2.如何启动一个新线程.调用 start 和 run 方法的区别 3.线程相关的基本方法 4.wait()和 sleep()的区别 5.多线程原理 前言 Java多线程实现方式主要有四种: ① 继承Thread类.实现Runnable接口 ② 实现Callable接

  • 详解Spring连接数据库的几种常用的方式

    本文简单的讲解使用Spring连接数据库的几种常用方法: 测试主类为: package myspring2; import java.sql.*; import javax.sql.DataSource; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MySp

随机推荐