程序员必备的几种常见排序算法和搜索算法总结

前语

最近为了稳固一下自己的算法根底,又把算法书里的根本算法刷了一遍, 特别总结一下前端工程师需求了解的排序算法和查找算法知识,尽管还有许多高深算法需求了解, 可是根底仍是要好好稳固一下的.本文将以图文的形式为咱们介p O / 9 ^ H绍如下算法知识,期望在读完之后咱们能有所收获:

  • 冒泡排序及其优化
  • 挑选排序
  • 插入排序
  • 归并排序
  • 快速排序
  • 顺序查找
  • 二分^ s _ , D查找

正文

我想关于每个前端工程师来说, 最头疼的便是算法问题, 可是算法往往也是衡量一个人编程能力的一个很重要的目标.现在许多主流结T E b 4 o ~ U Q构和库都运用了大h ! G D 5 G a i量的算法规划形式,为了让自己的段位更高,咱们只能不断的”打怪”(也便是刷算法)升级,才能成为”最强王者”.

其实前端发展这么多年, 越来越倾向于精细化开发, 许多超级运用(比方淘I m @ = E宝,微信)都在追求极致的用户体验, 时间便是金钱,这要求工程师们不能像以前那样,开发的程序只需能用就行, 咱们往往还要进行更加详尽的测验(包L E X % N括单元测验, 功能测验等),就拿排序来说, 关x G C 1 | . G t ,于大规模数据量的排序, 咱们选用冒泡排序肯定是要被张狂吐槽的,因为冒泡排序的功能极差(_ F 0 X r e复杂度为O(n^2)1 [ E.在实在项目中咱们往往不会选用冒泡排序,更多的会用快速排序或许希尔排序.关于排序算法功能问题我在

  • 《前0 : P l 5 | / Q端算法系列》怎么让前端代码速度进步60倍

有详细介绍. 接下来就让咱们来一起学习怎么完成文章开头的几个常用排序和查找算法吧} | C A 6 }.

冒泡排序及其优化

咱们在学排序算法时, 最简略把握的便是冒泡排序, 因为其完成起来十分简略,可是从运转功能的角度来看, 它却是功能最差的一个.

冒泡排序的完成思路是比较任何两个相邻的项, 假如前者比后者大, 则将它们互换方位.

为了更便利的展现冒泡排序的进程和功能测验,笔者先写几个东西办法,分别为动态生成指定个数的随机数组, 生成元素方位序列的办法,代码如下:

// 生成指定个数的随机数组
consN Z , Zt generateArr = (num = 10) => {
let arr = []
for(let i = 0; i< num; i++) {
let item = Math.floor(Math.ranE A V 3 P !dom() * (num + 1))
arr.push(ite_  U 1 L Om)
}A l W X f
return arr
}
// 生成指定个数的元素x轴坐标
const generateArrPosX = (n= 10, w = 6, m = 6) => {
let pos = []
for(let i = 0; i< n; i++) {
let item = (w + m) * i
pos.push(item)
}
return pos
}

有了以上两个办法,咱们就能够生成恣意个数& j a的数组以及0 P i数组项坐标了,这两个% o ! 4 j办法接下来咱们会用到.

咱们来直接写个乞丐版的冒泡排序算法:

bubbleSort(arr = []) {
let len = arr.length
for(let i = 0; i< len; i++) {
foh f 1 ( Ur(let j = 0; j < len - 1; j++) {
if(arr[j] > arr[j+1]) {
// 置换
[arr[j], arr[j+1]] = [arr[j+1], arr[j]]
}
}
}
return arr
}

接下来咱们来测验一下, 咱们用generateArr办法生成60个数组项的数组, 并动态生成元素坐标:

// 生成坐标
const pos = generateArrPosV = - 2X(60)
// 生成60个项的数组
const arr = genew K brateArr(60)

履行代码后会生成下图随机节点结构:

程序员必备的几种常见排序算法和搜索算法总结

有关css部分这儿就不介绍了,咱们能够自己完成.接下来咱们就能够测验咱们上面写的冒泡排序X K ! y了,当咱们点击排序时,成果如下:

程序员必备的几种常见排序算法和搜索算法总结

能够& h – 6 e V [ Z S看到数组已依照顺序排好了,咱们能够运用console.time来测量代码履行所用的时间,上面”乞丐版”冒泡排序耗时为0.2890625ms.

咱们深入分析代码就能够知道两层for循环排序导致了许多剩余的排序,假如咱们从内循环减去外循环中已跑过的轮数,就能够避免内循环: Q : d N 8 4 G中不必要的比较,所以咱们代码优化如下:

// 冒泡3 - 7 N n D 4 G e排序优化版
bubbleSorB N X / At(arr = []) {
let len = ar8 s 7 y a ~ {r.length
// 优化
for(let i = 0; i< len; i++) {
for(let j = 0; j < len - 1 - i; j++) {
ifH K R(arr[j] > arr[j+1]) {
// 置换
[arr[j], arr[j+1]]W 5 G s ; ] t | ( = [arr[j+{ ; -1], arr[j]]
}
}
}
retR z ( v @ _urn arr
}

经过优化的冒泡排序耗时:0.279052734375ms, 比之前略微好了一丢丢, 但仍然不是引荐的排序算法.

挑选排序

挑选排序的思路是找到数据结构中的最小值: P = u m z E C并将其放置在第一位,接着找到第二个最小值并将其放到第二位,顺次类推.

咱们仍是依照之前的形式,生成一个60项的数组, 如下:

程序员必备的几种常见排序算法和搜索算法总结

挑选排序代码如下:

selectionSorO $ * % bt(arr) {
let len = arr.length,
indexMin
for(let i = 0; i< len -1; i++) {
indexMin = i
fo? | S M :r(let j = i; j < len; j++){
if(arr[indexMin] > arr[j]) {
indexMii L u e z O 5 En = j
}
}
if(i !== indexMin) {
[arr[i], arr[indexMin]] = [ar+ T ( & 5 ; Z - =r[indexMin], arr[i]]
}
}
return arr
}

点击排序时, 成果如下:

程序员必备的几种常见排序算法和搜索算法总结

说明代码运转正常, 能够完成排序, 控制台耗时为: 0.1372[ j Y0703125ms, 明显比冒泡排序功能要好.

插入排序

插入排序 的思路是每次排一个数组项,假定第一项现已排序,接着它和第二项比较, 决议第二项的方位, 然后接着用同样的方法决议第三项的L G Z s @ O ,方位, 顺次类推, 最终将整个数组从小到大顺次排序.

代码如下:

insertionSort(arr) {
let leK Y u L z ~ o D +n = arr.lengz = 2 ` b t , $ Gth,
j,
temp;
for(leo X V C G Qt i = 1c x N + |; i< len; i++) {
j = i
temp = arr[i]
while(j > 0 && arr[j-1] > temp) {
arr[j] = arr[j-1]
j--
}
arr[j] = temp;
}
}

履行成果如下:

程序员必备的几种常见排序算法和搜索算法总结
程序员必备的几种常见排序算法和搜索算法总结

控制台打印耗时为:0.09912109375ms.

归并排序

归并排, & Q %序算法功能比以上三者都好, 能够在实际项目中投入运用,但完成方法相对复杂.

归并排序是一种分治算法,其思维是将原始数组切分红较小的数组,直到每个小数组只要一个元素,接着将小数组归并成较大的数组,最终变成一个排序完成的大数组。

其完成进程如下图所示% * v r d ( , I

程序员必备的几种常见排序算法和搜索算法总结

为了完成该办法咱们需求预备一个兼并函数和一个递归函数,详细完成如下代码:

// 归并排序? 4 4 { 6 m
mergeSortRec(arr) {
l@ m oet len = arr.lengt8 | 5 l A k G t uh
if(len === 1) {
return arr
}
lF * P 2et mid = Math.floor(len / 2),
left = arr.slu L u I N s : .ice(0, mid),
right = arr.slice(mid, len)
returnp | k D n f merge(mergeSortRec(left), mergeS o E  QortRec(right))
}
// 兼并办法
merge(left, right) {
let result = [],
l = 0,
r = 0;
while(l &6 M Wlt; left.length &e $ I;&amf d c P 8 $ | Cp; r < right.1 ^ ( 1length) {
if(left[l]E  # 2 < right[r]) {
result.push(left[l++])
}else {` s 6 J | 6
result.push(right[r++])
}
}
while(l < left.length)- ^ 7 , c C [ s {
result.push(left[l++]T G m q U _ [)
}
whiP a r F le(r < rk 8 o D 8ight.length) {
result.push(right[r++])
}
return result
}

以上代码中的递归作用是将一个大数组区分为多个小数组直到只要一项,然T G r ,后再逐层进行兼并排序。假如有不了解的能够和笔者沟通或许结合笔者画的草图进行了解。

快速排序

快速排序是现在比较常用的排序算法,它的复杂度为O(nlog^n),并且它的功能比其他复杂度为O(nlog^n)的好,也A # R B是选用分治的思维,将原始数组进行区分,因为快速排序完成起来比较复杂,这儿讲一下思路:

  1. 从数组中挑选中心项作为主元
  2. 创立两个指针,左面一个指向数组第一项,右边一个指向数组最终一项,移动左指针直到咱们找到一个比主元大的元素,移动右指针直到找到一个比主元小的元素,然后交换它们的方位,重复此进程直到左指针超y * Z L G L | 9 L过了右指针
  3. 算法对区分后的小数组重复1u = V . ? R,2进s D g ] [ 9 ` ( w, U c ^ [ F / q L,直到数组完全排序完成。

代码如下:

// 快速排序
quickS[ : E ort(arr, left, right) {
let index
if(arr.length >1 ] U ^ 5 K; 1) {
index = partition(arr,m  R ]  8 left, right)
if(left < index - 1) {
qur P % 4 WickSort(arr, left, index -1)
}
if(index < rigx W f I ` F 3 . fht) {
quickSort(arr, index, righ: D ! H y g G M )t)
}
}
}
// 区分流程
partition(arr, left, right) {
let part = arr[Math,+ # # 3 7 q D 1floor((right +D M C : . left) / 2)],
i = left,
j = right
while(i <= j) {
while(arr[i] < part) {
i++
}
while(arr[3 F U } & Q 4 q 9j] >( U # 8 l ~ M b; part) {
j--
}
if(i <= j) {
// 置换
[arr[i], arr[j]] = [N K Z [arr[j], arr[i]]
i++
j--
}
}
return i
}

顺序查找

查找算法也是咱们常常用到的算法之一,比方咱们需求查找某个用户或许某条数据,不管是在前端仍是在后端,都会运用查找算法。咱们先来介绍最简略也是功率最低的顺序查找,其主要思维是将每一个数据结构中的元素和咱们要查询的元素做比较,然后返回指定元素的索引。

程序员必备的几种常见排序算法和搜索算法总结

之所以说顺序查找功率低是因为每次q ! ,都要2 ] @ I z ? C o S从数组的头部开端查询,直7 ; _ x W % &到查找到要查找的值,整体查询不行灵敏和动态m d R T . 3性。顺序查找代码完成如下:

sequentialSeaV M ^ N C 0 hrch(arr, item) {
for(let i = 0; i+ r R ^ 0 I u T< arr.lenU I ! )  7gth; i++) {
if(ite! M E O Dm === arr[i]) {
return i
}
}
returnr @ I ( = ) j -1
}

接下来咱们看下面一种比较常用和灵敏的查找算法——二分查找。

二分查找

二分查找的思M * 5维有点“投机学”的意思,R U x n n U J可是它是一种有理论依据的“投机学”。首先8 [ w 6 W它要求被查找的数据T : 2 . j 8 K m Y结构已排序,其次进行如下进程:

  1. 找出数组的中心值
  2. 假如C % d中心值是待查找的值,那么直接返回中心值的索引
  3. 假如待查找的值比中心值小,则返回进程1,将区间规模缩小,在中心值左面的子数组中继续查找
  4. 假如待查找的值比选中的值大,则返回进程1,将区间规模缩小,在中心值右边的子数组中继续查找
  5. 假如没有搜到,则返回-1

为了便利了解笔者画了如下草图:

程序员必备的几种常见排序算法和搜索算法总结

由上图咱们能够很简略的了解二分查找的完成进程,接下来咱们看下代码完成:

binarySear~ h 9 ] *ch(arr, item) {
// 调用排序算法先对数据进行排序
this.quickSort(arr)
let min = 0,
max = arr.length - 1,
mid,
el
while(min <= max) {
mid = Math.floor((min + max) / 2)b 6 M ( I J 9 F
el = arr[mid]
if(el < item) {
min = mid + 1
}else if(el > item) {
max = mid -1
}else {
return mid
}
}
return -1
}

其实还有= 4 M P L P i _许多查找算法,笔者在6 M .js根本查找算法完成与170万条数据下的功能测验有详细介绍。

最终

假如想学习更多H5游戏, webpackh 3 j ? B 0 K Hnoa G u 5 A l jdegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》参加咱们的技能群一起学习讨论,一起探索前端的鸿沟。

程序员必备的几种常见排序算法和搜索算法总结

更多引荐

  • 几个十k z p f u w分有意思的javascript知识点总结
  • 前端进阶之从零到一完成单向 & 双向链表
  • 微前端架构初探以及我的前B $ p Z 2 F R h L端技能盘点
  • 浏览器缓存库规划总结(localStorage/indexedDB)
  • 运用nodeJs开发自己的图床运用
  • 基于nodeJS从0到1完成一个CMS全栈项目(上)
  • 基于nodeJS从0到1完成一个CMS全栈项j J % V目(中)(含源码0 i , E i E
  • CMS全栈项目之Vue和React篇(下)(含源码)
  • 5分钟教你用nodeJS手写一个mock数据服v ? 9务器
  • 从零到一教你基于vue开发一个组件库
  • 从0到1教你建立前端团队的组件系统(高级进阶必备)
  • 10分钟教你手写8个常用的自定义hooks
  • 《彻底把握redux》之开发一个任务管理平台(上)
  • 15分钟带你了解前端工程师必知Q G f c的javascript规划形式(附详细思维导( m X图和源码)
  • 一张图教你快速玩转vue-cli3
  • 《前端实战总i # :结》之运用postMessage完成可插拔的跨域聊天机p h J } E ? O器人