常见算法(JavaScript版)

持续更新中…

目录

  • 排序
    • 冒泡排序
    • 选择排序
    • 插入排序
    • 希尔排序
    • 快速排序(必须掌握)
      • 优化
        • 枢纽选择
    • 堆排序
    • 归并排序
  • 查找算法
    • 二分查找

排序

假设以下所有排序都是升序

快速排序在大部分情况下是效率最高的,所以笔试的时候要求写排序算法,能写快速排序就尽量写快速排序。

为后续讲解做铺垫,先封装ArrayList类如下。待排序的数组array是这个类的属性,里面存储了列表排序的方法(升序)、插入数组元素及打印数组的方法。

function ArrayList(){this.array=[]// 向数组插入元素ArrayList.prototype.insert=function(item){this.array.push(item)}// 打印数组元素ArrayList.prototype.toString=function(){return this.array.join()}// 因为该ArrayList类会多次使用交换的操作,故此处封装次操作ArrayList.prototype.swap=function(m,n){let temp=this.array[m]this.array[m]=this.array[n]this.array[n]=temp}
}

测试代码

let arr= new ArrayList()
arr.insert(11)
arr.insert(90)
arr.insert(0)
arr.insert(9)
arr.insert(19)
// alert默认会调用参数的toString方法
alert(arr)

在这里插入图片描述

冒泡排序

原理详见https://www.bilibili.com/video/BV1x7411L7Q7

// 冒泡排序
ArrayList.prototype.bubbleSort=function(){let length=this.array.length// 外层循环j变量控制每轮比较的终止位置for(let j=length-1;j>=1;j--){// 内层循环i表示当前正在比较的位置for(i=0;i<j;i++){if(this.array[i]>this.array[i+1]){this.swap(i,i+1)}}}
}

测试代码

let arr= new ArrayList()
arr.insert(11)
arr.insert(90)
arr.insert(0)
arr.insert(9)
arr.insert(19)
// alert默认会调用参数的toString方法
alert(arr)
arr.bubbleSort()
alert(arr)

在这里插入图片描述

冒泡排序的效率:
对于测试代码中的5个数组元素,比较次数为:4 + 3 + 2 + 1;
对于N个数据项,比较次数为:(N - 1) + (N - 2) + (N - 3) + … + 1 = N * (N - 1) / 2;
所以时间复杂度: O ( n 2 ) O(n^2) O(n2)
交换次数:每次比较有交换和不交换的情况,取平均,那交换次数就是比较次数的一半,所以交换次数= 1 / 2 ∗ n ( n − 1 ) / 2 = 1 / 4 ∗ n ( n − 1 ) 1/2 *n(n-1)/2=1/4 *n(n-1) 1/2n(n1)/2=1/4n(n1),也是 O ( n 2 ) O(n^2) O(n2)的复杂度

选择排序

// 选择排序
// 每轮查找最小的元素放到前面
ArrayList.prototype.selectSort=function(){let length=this.array.length// 外层循环j变量控制每轮查找的起始位置for(let j=0;j<length-1;j++){// 先假定起始位置的元素是本轮最小的值,min存储每轮最小元素的下标let min=j// 内层循环i表示当前正在比较的位置for(i=j;i<length;i++){if(this.array[i]<this.array[min]){min=i}}this.swap(j,min)}
}

测试代码

let arr= new ArrayList()
arr.insert(11)
arr.insert(90)
arr.insert(0)
arr.insert(9)
arr.insert(19)
// alert默认会调用参数的toString方法
alert(arr)
arr.selectSort()
alert(arr)

在这里插入图片描述
选择排序的效率:
选择排序的比较次数与冒泡排序一样,为:N * (N - 1) / 2,用大O表示法表示为: O ( N 2 ) O(N^2) ON2;
选择排序的交换次数最多为(N-1)次,平均为:(N - 1) / 2,用大O表示法表示为:O(N);
选择排序平均交换次数少于冒泡排序,所以效率高于冒泡排序;

插入排序

插入排序的思路:
插入排序思想的核心是局部有序。如图所示,X左边的人称为局部有序;
首先指定一数据X(从第一个数据开始),并将数据X的左边变成局部有序状态;
随后将X右移一位,再次达到局部有序之后,继续右移一位,重复前面的操作直至X移至最后一个元素。
插入排序的详细过程:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
代码实现

// 插入排序
ArrayList.prototype.insertSort=function(){let length=this.array.length// 外层循环j变量表示当前要插入元素所处的位置for(let j=1;j<length;j++){// 因为this.array[j]待会可能被覆盖,所以要用变量暂存下let temp=this.array[j]// 从j开始往左比较let i=j// 要插入的元素与左边的元素比较大小,寻找最终要插入的位置while(temp<this.array[i-1] && i>=1){// 在找到最终要插入的位置前,左边原有的元素右移一位this.array[i]=this.array[i-1]i--}// 找到最终要插入的位置后,将该元素值放入该位置this.array[i]=temp}
}

插入排序的效率:

比较次数:第一趟时,需要的最大次数为1;第二次最大为2;以此类推,最后一趟最大为N-1;所以,插入排序的总比较次数最多为N * (N - 1) / 2;平均比较次数大概为:N * (N - 1) / 4;
赋值次数:指定第一个数据为X时赋值0次,指定第二个数据为X最多需要赋值1次,以此类推,指定第N个数据为X时最多需要赋值N - 1次,总赋值次数最多为N * (N - 1) / 2次,平均赋值次数大概为:N * (N - 1) / 4;
虽然插入排序的效率也是 O ( N 2 ) O(N^2) ON2,但插入排序没有交换的操作,赋值消耗的性能比交换操作小,插入排序 比较和赋值 的次数总共才为N(N-1)/2,相当于冒泡排序和选择排序 比较 的次数,所以是简单排序中效率最高的算法

以上都是简单排序(冒泡,选择,插入),时间复杂度都是 O ( N 2 ) O(N^2) ON2。下面的高级排序时间复杂度能低于 O ( N 2 ) O(N^2) ON2

希尔排序

详见

  1. 某位网友的博客(本文没有对希尔排序做详细的说明,详见这篇博客)
  2. codewhy视频

插入排序的问题

  • 如果一个很小的数据项在很靠近右端的位置上(这里本应该是较大的数据项的位置),将这个小数据项移动到正确的位置,需要其左侧很多元素右移一位,这样效率非常低;
  • 如果通过某种方式,先让较小的元素整体靠近左侧,较大的元素整体靠近右侧,数组处于基本有序的状态,则元素做插入排序的时候就不会一个个移动其左侧的大量元素,那么算法的效率将有很大的提升。

希尔排序的思想:按照增量将排列分组,每一组用插入排序排好序,再逐渐减小增量,再排序,直至增量为1,这时整个排列较小的数基本靠左,较大的数基本靠右,这时再用插入排序,移位的次数会少很多,所以效率优于插入排序,是插入排序的改进版。

希尔排序的历史背景

  • 希尔排序按其设计者希尔(Donald Shell)的名字命名,该算法由1959年公布;
  • 希尔算法首次突破了计算机界一直认为的算法的时间复杂度都是O(N^2)的大关,为了纪念该算法里程碑式的意义,用Shell来命名该算法

排序过程:
假设要排序数组[81, 94,11, 96,12,35, 17, 95, 28, 58, 41, 75, 15]
设初始增量为5,分组如下,共分成了5组(一般增量是几就分成了几组)
在这里插入图片描述
将每组排好序(一般用插入排序排好序),再按增量3分组,如下
在这里插入图片描述
3间隔排好序如下
在这里插入图片描述
最后1间隔排好序如下
在这里插入图片描述

增量选择
在这里插入图片描述

复杂度:希尔排序的效率和增量的选择关系比较大,但复杂度都小于 O ( N 2 ) O(N^2) ON2

代码

            // 希尔排序ArrayList.prototype.shellSort = function () {let length = this.array.length// 这里增量采取的是希尔原稿中建议的,就是不断折半gap=Math.floor(gap/2),直至最后gap=1let gap = Math.floor(length / 2)//gap初始值是数组长度一半的整数while (gap >= 1) {/* 这一部分的for循环和插入排序差不多,就是步进值由1变成了gap。---start--- */// 由增量分组做插入排序,以使数组达到基本有序的状态(比如对于升序排列,较小的值基本靠左,较大的值基本靠右)// 外层循环j变量表示当前要插入元素所处的位置for (let j = gap; j < length; j++) {// 因为this.array[j]待会可能被覆盖,所以要用变量暂存下let temp = this.array[j]// 从j开始往左比较let i = j// 要插入的元素与左边同组的元素比较大小,寻找最终要插入的位置// i >= gap确保下标不为负数while (temp < this.array[i - gap] && i >= gap) {// 在找到最终要插入的位置前,左边原有的元素右移一位this.array[i] = this.array[i - gap] i -= gap}// 找到最终要插入的位置后,将该元素值放入该位置this.array[i] = temp}/* 这一部分的for循环和插入排序差不多,就是步进值由1变成了gap。---end--- */gap=Math.floor(gap/2)}}

测试代码

let arr = new ArrayList()
arr.insert(8)
arr.insert(9)
arr.insert(1)
arr.insert(7)
arr.insert(2)
arr.insert(3)
arr.insert(5)
arr.insert(4)
arr.insert(6)
arr.insert(0)
// alert默认会调用参数的toString方法
alert(arr)
arr.insertSort()
alert(arr)

在这里插入图片描述

快速排序(必须掌握)

参见书籍《大话数据结构》
思想:从排列中选取一个数作为枢纽,使得该排列所有小于枢纽的数都在枢纽的一边,所有大于枢纽的数都在枢纽的另一边。然后对子排列递归地这样操作,直到整个排列达到有序状态。
快速排序用了分治算法的思想,就是将一个大问题分成许多相同的小问题(解决思路一样,只是问题规模可能不一样),递归地去解决这些问题

代码(这是未优化的情况,即默认排列第一个数为枢纽)

// 测试数据
let arr=[5,1,9,3,7,4,8,6,2];// 交换操作
function swap(arr,m, n) {let temp = arr[m]arr[m] = arr[n]arr[n] = temp
}// 找到枢纽下标值(快速排序核心代码)
function partition(L, left, right) {// 假设排列第一个数为枢纽,该枢纽会随着之后的交换逐渐移动到排列的合适位置let pivotValue=L[left]// 跳出该循环时,left==right,这时swap(L,left,right)不会影响已经排好的数列while (left < right) {while (left < right && L[right] >=pivotValue) right--; // 从右侧下标递减,直到找到比枢纽小的数swap(L,left,right); // 交换下标为left和right的值while (left < right && L[left] <=pivotValue) left++; // 从左侧下标递增,直到找到比枢纽大的数swap(L,left,right); // 交换下标为left和right的值}return left // 最终left==right,所以return right也行
}function quickSort(L, left, right) {if (left < right) {let pivot = partition(L, left, right) // 获取枢纽下标quickSort(L, left, pivot - 1)quickSort(L, pivot + 1, right)return L}
}console.log( quickSort(arr, 0, arr.length - 1));

复杂度:时间复杂度O(nlogn)

优化

枢纽选择

三数取中法

堆排序

归并排序

查找算法

二分查找

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/602874.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SSR 服务器端渲染:提升用户体验的新趋势(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Java学习,一文掌握Java之SpringBoot框架学习文集(5)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

物联网的感知层、网络层与应用层分享

物联网的概念在很早以前就已经被提出&#xff0c;20世纪末期在美国召开的移动计算和网络国际会议就已经提出了物联网(Internet of Things)这个概念。 最先提出这个概念的是MIT Auto-ID中心的Ashton教授&#xff0c;他在研究RFID技术时&#xff0c;便提出了结合物品编码、互联网…

CMU15-445-Spring-2023-Project #1 - Buffer Pool

前置知识&#xff0c;参考上一篇博客&#xff1a;CMU15-445-Spring-2023-Project #1 - 前置知识&#xff08;lec01-06&#xff09; 在存储管理器中实现缓冲池。缓冲池负责将物理页从主内存来回移动到磁盘。它允许 DBMS 支持大于系统可用内存量的数据库。缓冲池的操作对系统中的…

常用的窗体控件

常用窗体控件 MenuStrip&#xff1a;一个标准的菜单栏控件&#xff0c;可以添加菜单单项和子菜单项 //双击子菜单项同样可以添加点击事件 //menu&#xff1a;菜单 item&#xff1a;菜单选项 //当点击新建选项的时候触发的事件 private void 新建ToolStripMenuItem_Click(ob…

QT+OSG/osgEarth编译之五十八:OpenEXRUtil+Qt编译(一套代码、一套框架,跨平台编译,版本:OpenEXRUtil-3.2.1)

Qt+OSG/osgEarth跨平台编译(用Qt Creator组装各个库,实现一套代码、一套框架,跨平台编译)_qt + osgearth安装-CSDN博客 目录 1、OpenEXRUtil介绍 2、文件下载 3、文件分析

【论文解读】基于神经辐射场NeRF的像素级交互式编辑(Seal-3D)

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/pdf/2307.15131 项目主页&#xff1a;https://windingwind.github.io/seal-3d/ 摘要&#xff1a; 随着隐式神经表征或神经辐射场&#xff08;NeRF&#xff09;的普及…

ElecardStreamEye使用教程(视频质量分析工具、视频分析)

文章目录 Elecard StreamEye 使用教程安装与设置下载安装 界面导航主菜单视频窗口分析窗口 文件操作打开视频文件 视频流分析帧类型识别码率分析分析报告 高级功能视觉表示比较模式自动化脚本 下载地址1&#xff1a;https://www.onlinedown.net/soft/58792.htm 下载地址2&…

Typora 编辑器 讲解 包括使用方式 快捷键 附带下载地址 (免费破解)

CSDN 成就一亿技术人&#xff01; 今天来讲一下很好用的编辑器 Typora CSDN 成就一亿技术人&#xff01; 什么是Typora&#xff1f; 它是一个 Markdown 编辑器和阅读器&#xff0c;这意味着您可以使用简单的格式代码 &#xff08;Markdown&#xff09;是一种轻量级标记语言&…

muduo网络库剖析——日志Log类

muduo网络库剖析——日志Log类 前情从muduo到my_muduo 概要日志日志级别 框架与细节成员函数 源码 前情 从muduo到my_muduo 作为一个宏大的、功能健全的muduo库&#xff0c;考虑的肯定是众多情况是否可以高效满足&#xff1b;而作为学习者&#xff0c;我们需要抽取其中的精华…

Java的二进制数据处理

在Java中&#xff0c;可以使用二进制运算来处理整数类型数据。 二进制基础知识 二进制&#xff08;binary&#xff09;在数学和数字电路中指以2为基数&#xff0c;由0和1组成的数字系统。 位运算符 位运算符是对二进制数进行操作的特殊运算符。在Java中&#xff0c;有以下几…

NCC基础开发技能培训

YonBuilder for NCC 是一个带插件的eclipse工具&#xff0c;跟eclipse没什么区别 NC Cloud2021.11版本开发环境搭建改动 https://nccdev.yonyou.com/article/detail/495 不管是NC Cloud 新手还是老NC开发&#xff0c;在开发NC Cloud时开发环境搭建必看&#xff01;&#xff…

python基础教程七(布尔类型,条件语句,断言)

1. 布尔类型 在前面&#xff0c;你遇到了很多真值&#xff0c;现在终于需要他们了。真值也称布尔值。 用做布尔表达式&#xff08;如用作if语句中的条件&#xff09;时&#xff0c;下面的值都将被解释器视为假: False None 0 "" () [] {} 换而言之&#xff0…

207课程表

题目 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示如果要学习课程 ai 则 必须 先学习课程 bi 。 …

Vue2商品规格选择

Vue2Element-ui Vu2仿写拼多多商家后台规则选择&#xff0c;为什么用Vue2呢&#xff0c;因为公司用的Vue2... 样式不是很好看&#xff0c;自己调一下就行。 <template><div ref"inputContainer"><div>{{ combinationsResult }}</div><…

深入浅出Nacos的原理

前言 本文来讲一讲nacos作为底层注册中心的实现原理。那么就有这几个问题&#xff1f; 临时实例和永久实例是什么&#xff1f;有什么区别&#xff1f; 服务实例是如何注册到服务端的&#xff1f; 服务实例和服务端之间是如何保活的&#xff1f; 服务订阅是如何实现的&#…

Adobe illustrator各版本安装指南

下载链接 https://pan.baidu.com/s/11sTpMUbQEXhyjpkBlixcLg?pwd0531 #2024版 1.鼠标右击【Ai2024(64bit)】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到 Ai2024(64bit)】。 2.打开解压后的文件夹&#xff0c;鼠标右击【Setup】选择【以…

【Pytorch】学习记录分享11——GAN对抗生成网络

PyTorch GAN对抗生成网络 0. 工程实现1. GAN对抗生成网络结构2. GAN 构造损失函数&#xff08;LOSS&#xff09;3. GAN对抗生成网络核心逻辑3.1 参数加载&#xff1a;3.2 生成器&#xff1a;3.3 判别器&#xff1a; 0. 工程实现 原理解析&#xff1a; 论文解析&#xff1a;GAN…

canvas如何自定义绘制图片

在Canvas中&#xff0c;可以使用drawImage()方法来绘制图片。该方法接受三个参数&#xff1a; 1. 图片对象&#xff1a;可以是<img>、<canvas>或<video>元素。 2. 图片左上角在Canvas中的X坐标。 3. 图片左上角在Canvas中的Y坐标。 如果要自定义绘制图片&a…

Postman接口测试实战

1.什么是接口测试 来自百度百科的解释&#xff1a; 接口测试是测试系统组件间接口的一种测试&#xff0c;主要用于测试系统与外部其他系统之间的接口&#xff0c;以及系统内部各个子模块之间的接口。测试的重点是要检查接口参数传递的正确性&#xff0c;接口功能实现的正确性&…