一 : 冒泡排序

人们开始学习排序算法时,通常都先学冒泡算法,因为它在所有排序算法中最简单。然而, 从运行时间的角度来看,冒泡排序是最差的一个,接下来你会知晓原因

冒泡排序比较所有相邻的两个项,如果第一个比第二个大,则交换它们。元素项向上移动至 正确的顺序,就好像气泡升至表面一样,冒泡排序因此得名。

export function bubbleSort(arr: number[]) {

for (let i = 0; i < arr.length - 1; i++) {

for (let j = 0; j < arr.length - i - 1; j++) {

if (arr[j] > arr[j + 1]) {

;[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]

}

}

}

return arr

}

冒泡排序每一轮(外层循环) 会选出一个最小 或者最大的数组 放到数组最后

所以N 个 数字 只用 选N -1 次 =>外层循环 i

内存循环 两两相比, 只用 比较 N - 1次 ,但是 外层循环已经找到的最值不用比较

所以 => 内层循环 j < arr.length - i -1

一幅图来描述 冒泡的工作流程

二 :选择排序

选择排序算法是一种原址比较排序算法。 他解决了冒泡 交换次数过多的毛病,在冒泡排序中 需要交换 O(N^2) 次 但 选择排序中 只用交换 O(N)次

选择排序大致的思路是找到数据结构中的最小值并 将其放置在第一位,接着找到第二小的值并将其放在第二位,以此类推。

export function selectionSort(arr: number[]) {

let min = 0

for (let i = 0; i < arr.length - 1; i++) {

min = i

for (let j = i + 1; j < arr.length; j++) {

if (arr[j] < arr[min]) {

min = j

}

}

if (i !== min) {

;[arr[i], arr[min]] = [arr[min], arr[i]]

}

}

return arr

}

选择排序的代码执行过程 如下图

三 :插入排序

插入排序的思想十分的重要, 学会了他你才能学习 希尔排序,而 希尔排序 又是排序算法 历史上的一个转折点 他打破了 排序算法 时间复杂度平均不会低于 O(N^2) 的理论。

插入排序每次排一个数组项,以此方式构建最后的排序数组。假定第一项已经排序了。接着, 它和第二项进行比较——第二项是应该待在原位还是插到第一项之前呢?这样,头两项就已正确 排序,接着和第三项比较(它是该插入到第一、第二还是第三的位置呢),以此类推

export function insertionSort(arr: number[]) {

for (let i = 1; i < arr.length; i++) {

let j = i

let temp = arr[i]

//插入操作 while (j > 0 && arr[j - 1] > temp) {

arr[j] = arr[j - 1]

j--

}

arr[j] = temp

}

return arr

}

插入排序的 过程 假设数组 [ 3 ,5 , 1, 4, 2]

一开始 把 3 当作局部有序 , 把 5 拎出来 和 局部有序 数列比较 并插入

// 第一次 : 5 > 3 合理 进入下一次循环

// 第二次 : 把 1 拎出来 插入 , 局部有序变为 : [ 3,5 ] 并比较 数组变为[1,3,5,4,2]

//第三次 : 把 4拎出来 插入 , 局部有序 变为 [1,3,5 ] 数组变为 [ 1,3,4,5, 2 ]

// 第四次 : 把2拎出来插入 局部有序 变为 [1,3,4,5 ] 数组变为 [ 1,2,3,4,5

//结束循环

四 : 希尔排序

希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”。

他是 第一个 将 排序算法 复杂度降低 到 O (N^2)之下的 . 但是他的复杂度 至今未被证明 猜测 效率 为 O (N^1.3)

他就是插入排序的一种进阶。只要你会了插入排序 你就能写出希尔排序

//复杂度为被证明,猜测 为 O (N ^1.3)export function shellSort(arr: number[]) {

//获取增量 let gap = Math.floor(arr.length / 2)

//增量等于1 即为 插入排序 原始。 一定会将数组排好 这个时候结束循环 while (gap >= 1) {

//进行插入排序 for (let i = gap; i < arr.length; i++) {

let j = i

let temp = arr[i]

while (j > gap - 1 && arr[j - gap] > temp) {

arr[j] = arr[j - gap]

j -= gap

}

arr[j] = temp

}

//缩小增量 gap = Math.floor(gap / 2)

}

return arr

}

注意: 插入排序 就是 进行 gap(增量) 为 1 时候的操作 ,

在希尔排序 中 初始增量 为数组长度一半 , 并依次缩小 .这是原版的做法

事实上 这个 增量 有别的计算方式,可以让 希尔排序 效率更高 接近O(N^4/5) 但是他不是我们学习 希尔排序 的重点 , 关于增量的证明 与改进 更多是数学方面的知识 我们基于面试 能够实现基本的希尔排序 就可以了 .

五 : 快速排序

快速排序也许是最常用的排序算法了。它的复杂度为 O(nlog(n)),且性能通常比其他复杂度 为 O(nlog(n))的排序算法要好。 快速排序 使用分而治之的方法 .

快速排序也是二十世纪 十大算法之一,感兴趣的朋友可以了解一下

我们重点学习 快速排序。

我们先看下阮一峰老师给出的快速排序算法:

function quickSort(arr) {

if (arr.length <= 1) return arr

let left = [],

right = []

//将中间值 取除 并在arr中移除 let middle = arr.splice(Math.floor(arr.length / 2), 1)[0]

arr.forEach((el) => (el >= middle ? right.push(el) : left.push(el)))

return quickSort1(left).concat(middle, quickSort1(right))

}

例如 数组 [ 5, 4 ,3, 2 ,1 ] 先取出 中间值 3 => [5,4,2,1]

然后 将数组一分为二 => 小于 3 的在左边 大于 3 的在右边

=> left [ 2 , 1 ] right[ 5, 4]

=>然后将 左右 数组 递归

=> [2,1] => middle :2 left[ 1 ] right [ ]

=> [5,4 ] => middle :5 left[ 4 ] right [ ]

=>最后 依次拼接数组 left+middle +right => [1,2,3,4,5]

注意: 上面的算法 使用了 splice 效率 非常的差,只是基于快速排序的思想, 写出的 比较容易理解的代码 我们来看下效率 更好 的快排 是如何做的

export function quickSort(arr: number[]) {

quick(arr, 0, arr.length - 1)

return arr

}

function quick(arr: number[], left: number, right: number) {

if (arr.length > 1) {

let index = partition(arr, left, right)

// 两半局部有序分开 递归排序 分而治之 if (left < index - 1) {

quick(arr, left, index - 1)

}

if (index < right) {

quick(arr, index, right)

}

}

}

function partition(arr: number[], left: number, right: number) {

//将数组 以pivot 为标准 划分为 两半 局部有序的 let pivot = arr[Math.floor((left + right) / 2)]

let i = left

let j = right

while (i <= j) {

while (arr[i] < pivot) {

i++

}

while (arr[j] > pivot) {

j--

}

if (i <= j) {

swap(arr, i, j)

i++

j--

}

}

return i

}

function swap(arr: number[], i: number, j: number) {

;[arr[i], arr[j]] = [arr[j], arr[i]]

}

这种实现思路 是利用 i j 两个指针 , 将 数组 以 pivot(枢纽) 分割成两半

左边 小于 pivot 右边大于pivot ,然后递归 分而治之 .

例如 数组 [ 3 , 6 , 5 , 7 , 4 , 1 , 8 , 2 , 9]

选取pivot : 4 , left : 3 right : 9

//left 会一直找到 >= 4 时停住

//right 会一直找到<= 4 停住

=> left =>6 , right => 2 这个时候 swap

[3,2,5,7,4,1,8,6,9] 继续循环

=>left =>5 , right =>1 调用 swap

[ 3,2,1,7,4,5,8,6,9]

=>left => 7 , right =>4 调用 swap

[3,2,1,4,7,5,8,6,9] 退出循环 ( i > j )

你会发现 在 4 的左边 都比 4 小

在 4 的右边都比 4 大 ,这个时候以4 为枢纽分开 递归即可

六 归并排序

归并排序也是一个可以实际使用的排序算法。 归并排序性能不错,其复杂度为 O(nlog(n))。

// JavaScript 的 Array 类定义了一个 sort 函数(Array.prototype.sort)用以 排序 JavaScript 数组(我们不必自己实现这个算法)。ECMAScript 没有定义用哪 个排序算法,所以浏览器厂商可以自行去实现算法。例如,Mozilla Firefox 使用归并排序作为 Array.prototype.sort 的实现,而 Chrome(V8 引擎)使用了 一个快速排序的变体

一张图 足以 说明 归并排序 的流程

export function mergeSort(arr: number[]) {

//分而治之 //先分 if (arr.length > 1) {

//将数组 分成两半 递归进行 直到 数组长度 小于等于 1 let middle = Math.floor(arr.length / 2)

let left = mergeSort(arr.slice(0, middle))

let right = mergeSort(arr.slice(middle, arr.length))

//然后 合并排序 arr = merge(left, right)

}

//将结果返回 return arr

}

function merge(left: number[], right: number[]) {

//将左右两个数组 合并 排序 //i 指向 左数组 j 指向右数组 let i = 0

let j = 0

//将结果有序的push 进 result 中 let result = []

while (i < left.length && j < right.length) {

//排序 result.push(left[i] < right[j] ? left[i++] : right[j++])

}

//合并 =>将 左右 数组 剩余的部分 concat return result.concat(i < left.length ? left.slice(i) : right.slice(j))

}

七 : 计数排序

计数排序是一个分布式排序 , 它是用来排序整数的优秀算法(它是一个整数排序算法),时间复杂度为 O(n+k),其中 k 是 临时计数数组的大小;但是,它确实需要更多的内存来存放临时数组。

这个排序 算法 的思路 就是 计数

=> 将每个数 当作索引 存进 数组(count)中,如果重复出现 则 索引对应的值 ++ ,

这个时候遍历我们的 的数组(count) ,他就排好序了。非常的简单

export function countingSort(arr: number[]) {

let count = [] //计数 let result = [] //结果 arr.forEach((el) => {

//将每个数字 以索引 存入 count中 if (!count[el]) {

count[el] = 0

}

count[el]++

})

count.forEach((el, i) => {

//将count 取出 if (el && el > 0) {

for (let j = 0; j < el; j++) {

result.push(i)

}

}

})

return result

}

你会发现 这个算法 只能存储 整数 且 需要消耗大量的空间, 但是在 有些时候利用此算法排序 非常的 好用 , 比如 按照年龄排序, 按照 成绩排序 .

即 量大 范围小(年龄 0 岁~ 150岁) 时 非常好用的算法 ,因为他的效率高 , 因为范围小 空间消耗也是 可以接受的。

八 : 基数排序

基数排序也是一个分布式排序算法,它是根据关键字排序的。

什么是关键字排序 ? 我们就按数组排序 为例 ,他先比较 每个数组的个位 按 计数排序处理, 再比较数组的 十位. 计数排序 处理 ..... 直到比较完最高位 就排好序了.

其实也就是 重复 进行我们的计数排序 ,来节省 空间的浪费

export function radixSort(arr: number[]) {

if (arr.length < 2) {

return arr

}

//找到最大值 let max = -Infinity

arr.forEach((el) => (el > max ? (max = el) : null))

//求他的位数 let digit = (max + '').length

//循环计数排序 let count = []

for (let i = 0; i < digit; i++) {

//按 个位排序, 十位排序 ,百位排序 .... arr.forEach((el) => {

let str = el + ''

let temp = +str[str.length - 1 - i]

if (isNaN(temp)) {

temp = 0

}

if (Array.isArray(count[temp])) {

count[temp].push(el)

} else {

count[temp] = [el]

}

})

arr = []

count.forEach((el) => {

if (Array.isArray(el)) {

el.forEach((e) => {

arr.push(e)

})

}

})

count = []

}

return arr

}

九 :堆排序

这个我在二叉堆里介绍过了。

十 : 桶排序

桶排序(也被称为箱排序)也是分布式排序算法,它将元素分为不同的桶(较小的数组), 再使用一个简单的排序算法,例如插入排序(用来排序小数组的不错的算法),来对每个桶进行 排序。然后,它将所有的桶合并为结果数组。

其实 上面的计数排序 基数排序 应该都属于 我们的桶排序,

但是桶排序 实际用的并不多 , 我们掌握 计数 和基数 两个变体 就好了.

感兴趣的可以去单独了解桶 排序 , 看看 他为什么 用的不多 。

十一 : 总结

然后我又基于 随机的数组 (十万个随机数)

,分别进行了上面的 排序算法 我们来看下他的耗时

你可以发现 冒泡 简直不能用, 等了24秒

最快 的计数排序 6 ms 就搞定了。。 但是耗费了大量的空间

官方自带的sort 在大部分情况下 速度还是慢于 我们的 快速排序

最后贴上单元测试。

import { createRandomArray } from '../util'

import { bubbleSort } from './01 冒泡排序'

import { selectionSort } from './02 选择排序'

import { insertionSort } from './03 插入排序'

import { shellSort } from './04 希尔排序'

import { quickSort } from './05 快速排序'

import { mergeSort } from './06 归并排序'

import { countingSort } from './07 计数排序'

import { radixSort } from './08 基数排序'

describe('排序算法测试', () => {

test('冒泡排序测试', () => {

const arr = createRandomArray(10)

const sortArr = Array.from(arr).sort((a, b) => a - b)

expect(bubbleSort(arr)).toEqual(sortArr)

})

test('选择排序测试', () => {

const arr = createRandomArray(10)

const sortArr = Array.from(arr).sort((a, b) => a - b)

expect(selectionSort(arr)).toEqual(sortArr)

})

test('插入排序测试', () => {

const arr = createRandomArray(10)

const sortArr = Array.from(arr).sort((a, b) => a - b)

expect(insertionSort(arr)).toEqual(sortArr)

})

test('希尔排序测试', () => {

const arr = createRandomArray(10)

const sortArr = Array.from(arr).sort((a, b) => a - b)

expect(shellSort(arr)).toEqual(sortArr)

})

test('快速排序', () => {

const arr = createRandomArray(10)

const sortArr = Array.from(arr).sort((a, b) => a - b)

expect(quickSort(arr)).toEqual(sortArr)

})

test('归并排序', () => {

const arr = createRandomArray(10)

const sortArr = Array.from(arr).sort((a, b) => a - b)

expect(mergeSort(arr)).toEqual(sortArr)

})

test('计数排序', () => {

const arr = createRandomArray(10)

const sortArr = Array.from(arr).sort((a, b) => a - b)

expect(countingSort(arr)).toEqual(sortArr)

})

test('基数排序', () => {

const arr = createRandomArray(10)

const sortArr = Array.from(arr).sort((a, b) => a - b)

expect(radixSort(arr)).toEqual(sortArr)

})

})

创建随机数组

'../util'

export function createRandomArray(size: number) {

const array = []

for (let i = 0; i < size; i++) {

array.push(Math.floor(Math.random() * 100))

}

return array

}

js排序的时间复杂度_JavaScript实现十大排序算法相关推荐

  1. JS的十大经典算法排序

    更正(2018/5/31):如果看的是原文章的话,注意希尔排序少一个等号,在本博中,我已经添加上了,希尔排序(更正后)及以前的经测试都是正确的,后面的我正找时间持续研究验证中--(过程可能有点慢) 更 ...

  2. JS 实现十大排序算法

    文章目录 前言 零.十大排序 一.冒泡排序(bubbleSort) 二.选择排序(selectionSort) 三.插入排序(insertSort) 四.希尔排序(shellSort) 五.归并排序( ...

  3. 十大排序算法JS实现以及复杂度分析

    文章目录 十大排序算法概述 应用场景 代码实现 一.冒泡排序 二.选择排序 三.插入排序 四.希尔排序 五.归并排序 六.快速排序 七.堆排序 八.计数排序 九.桶排序 十.基数排序 十大排序算法概述 ...

  4. 十大排序算法(Java)

    文章目录 十大排序算法(Java) 一.冒泡排序(Bubble Sort) 二.选择排序(Selection Sort) 三.堆排序(Heap Sort) 四.插入排序(Insertion Sort) ...

  5. 这或许是东半球分析十大排序算法最好的一篇文章

    作者 | 不该相遇在秋天 转载自五分钟学算法(ID:CXYxiaowu) 前言 本文全长 14237 字,配有 70 张图片和动画,和你一起一步步看懂排序算法的运行过程. 预计阅读时间 47 分钟,强 ...

  6. 「干货总结」程序员必知必会的十大排序算法

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 绪论 身 ...

  7. 「归纳|总结」程序员必知必会的十大排序算法

    微信搜一搜「bigsai」关注这个有趣的程序员 新人原创公众号,求支持一下!你的点赞三连肯定对我至关重要! 文章已收录在 我的Github bigsai-algorithm 欢迎star 本文目录 绪 ...

  8. 归并排序执行次数_十大排序算法,看这篇就够了

    排序算法分类[1][2] 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序. 非比较类排序:不通过比较来决定元素间的相对次序,它可以 ...

  9. C 数据结构之十大排序

    C 数据结构之十大排序 排序算法是<数据结构与算法>中最基本的算法之一. 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳 ...

最新文章

  1. CPU 是如何理解 01 二进制的?
  2. 安装多个java后,java版本不对
  3. 【CodeForces - 151D】Quantity of Strings (字符串问题,思维推导,有坑)
  4. java 二叉树特点_疯狂java笔记之树和二叉树
  5. php-fpm的pool php-fpm慢执行日志 open_basedir php-fpm进程管理
  6. java对mysql的简单操作——增删改查的总结
  7. python2.7更新python3.6_python2.7升级到python3.6注意事项
  8. Jmeter查看结果树
  9. iPhone/iPad各种文件路径详解 帮助了解自己的iphone和ipad
  10. 电池SOH仿真系列-基于LSTM神经网络的电池SOH估算方法
  11. 超越宝典汽配汽修管理系统——“维修业务”模块功能实现
  12. Ubuntu20.04安装搜狗拼音
  13. 2022-2027年中国玩偶行业市场全景评估及发展战略规划报告
  14. Repeated DNA
  15. Caj文件怎么转换成pdf?Caj转pdf在线转换器推荐
  16. 关于三通道彩色图像的存储方式理解
  17. 【吴刚】个人网站设计初级入门标准视频教程-吴刚-专题视频课程
  18. 关于node启动:File exists: node_modules\.bin\babel-doctor不成功
  19. Connectify中文版必不可少的wifi软件
  20. Puppet应用配置的工作原理

热门文章

  1. Spring第三天,详解Bean的生命周期,学会后让面试官无话可说!
  2. Python时间转换:X秒 --> 时:分:秒
  3. 让Excel输入内容后自动加边框的方法,及其原理深度解析
  4. 基于百度云人脸融合API的python实现视频人像换脸
  5. 微信小程序集合2(外卖+商城半成品+教育商城+外卖商城+婚庆商店+简易抽奖+外卖购物车+小天气+打卡签到+游戏社区攻略)
  6. 市场营销书籍推荐,这些书帮你学好营销
  7. MATLAB绘制泰勒图(10个以上model)
  8. 如何判断你用的是左脑还是右脑!
  9. iOS Swift 判断手机机型 已更新 至iPhone12
  10. mysql 取消外键关联约束