选择排序(Selection Sort)
选择排序是一种排序算法,是一个占用常用内存(In-place)的排序方法。时间复杂度为O(n2)。通常情况下,在处理大型数据的时候,性能要比相似的插入排序低。选择排序因其简单性而著称,并且在某些情况下性能要优于更复杂的算法,尤其是在辅助存储空间有限的情况下。
原理
选择排序的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。
复杂度
算法 | 最好情况 | 平均情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 |
ES6实现
function SelectionSort(originalArray) { const array = [...originalArray]; let len = array.length; for (let i = 0; i < len - 1; i++) { let minIndex = i; for (let j = i + 1; j < len; j++) { if (array[j] < array[minIndex]) { minIndex = j; } } if (minIndex != i) { [array[minIndex], array[i]] = [array[i], array[minIndex]] } } return array; }
参考
相关阅读
JavaScript的排序算法——冒泡排序
JavaScript的排序算法——选择排序
JavaScript的排序算法——插入排序
JavaScript的排序算法——归并排序
JavaScript的排序算法——快速排序
冒泡排序(Bubble Sort)
冒泡排序,有时也被称做沉降排序,是一种比较简单的排序算法。这种算法的实现是通过遍历要排序的列表,把相邻两个不符合排列规则的数据项交换位置,然后重复遍历列表,直到不再出现需要交换的数据项。当没有数据项需要交换时,则表明该列表已排序。
复杂度
算法 | 最好情况 | 平均情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n) | O(n2) | O(n2) | O(1) | 稳定 |
ES6实现
- 普通版冒泡排序
function BubbleSort(array) { let len = array.length; for (let i = 0; i < len; i++) { for (let j = 0; j < len-i-1; j++) { if (array[j]> array[j+1]) { [array[j],array[j+1]] = [array[j+1],array[j]]; }
}
}
return array; }
- 优化版冒泡排序
function BubbleSort(originalArray) { const array = [...originalArray]; let swapped; for (let i = 0; i < array.length; i++) { swapped = true; for (let j = 0; j < array.length - i - 1; j++) { if (array[j] > array[j+1]) { [array[j],array[j+1]] = [array[j+1],array[j]] swapped = false; } } if (swapped) { break; } } return array; }
https://www.jianshu.com/p/2fd84fadab5f
在JavaScript里使用
typeof判断数据类型
,只能区分基本类型,即:number、string、undefined、boolean、object
。
对于null、array、function、object来说,使用typeof都会统一返回object字符串。
要想区分对象、数组、函数、单纯使用typeof是不行的。在JS中,可以通过Object.prototype.toString方法
,判断某个对象之属于哪种内置类型。
分为null、string、boolean、number、undefined、array、function、object、date、math。
1. 判断基本类型
Object.prototype.toString.call(null); // "[object Null]" Object.prototype.toString.call(undefined); // "[object Undefined]" Object.prototype.toString.call(“abc”);// "[object String]" Object.prototype.toString.call(123);// "[object Number]" Object.prototype.toString.call(true);// "[object Boolean]"
2. 判断原生引用类型
**函数类型** Function fn(){ console.log(“test”); } Object.prototype.toString.call(fn); // "[object Function]"
**日期类型** var date = new Date(); Object.prototype.toString.call(date); // "[object Date]"
**数组类型** var arr = [1,2,3]; Object.prototype.toString.call(arr); // "[object Array]"
**正则表达式** var reg = /[hbc]at/gi; Object.prototype.toString.call(reg); // "[object RegExp]"
**自定义类型** function Person(name, age) { this.name = name; this.age = age; } var person = new Person("Rose", 18); Object.prototype.toString.call(arr); // "[object Object]"
无法区分自定义对象类型,自定义类型可以采用instanceof区分
console.log(person instanceof Person); // true
3.判断原生JSON对象
var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON); console.log(isNativeJSON);//输出结果为”[object JSON]”说明JSON是原生的,否则不是;
什么是事件循环
事件循环允许Node.js执行一些异步非阻塞I/O操作的机制或者策略。
尽管JavaScript是单线程的,所以这些操作都尽量交给操作系统的内核去做。 大多数现代操作系统内核都是多线程的,所以它们可以处理在后台执行的多个操作。当其中一个操作完成时,内核会告诉Node.js,将适当的回调添加到轮询队列中,以便最终执行。
事件循环的6个阶段
当Node.js启动时,会初始化事件循环,然后处理在Node.js里运行的代码,包括各种API的调用,定时器,process.nextTick(),然后运行事件循环机制。
6阶段都分别做了什么?
- timers: setTimeout(),setInterval()在这个阶段被执行
- pending callbacks: 执行一些系统错误的处理,比如TCP,UDP,Socket错误等等。
- idle, prepare: only used internally.
- poll: 轮询阶段,向系统获取新的I/O事件,执行I/O回调。首先会先处理到期的定时器的回调,然后处理poll队列里的回调,直到队列里的回调全部被清空,或者达到一个处理的最大值,如果poll队列不为空且刚好有setImmediate,则停止当前的poll阶段,前往check阶段,执行setImmediate回调。如果没有setImmediate,Node.js会去查看有没有定时器任务到期了,如果有的话,就前往timers阶段,执到期行定时器的回调。
retrieve new I/O events; execute I/O related callbacks (almost all with the exception of close callbacks, the ones scheduled by timers, and setImmediate()); node will block here when appropriate. - check: 执行setImmediate() 回调,且setImmediate只能在check阶段执行。
- close callbacks: 执行结束的回调。
每个阶段都有一个FIFO回调队列要执行。虽然每个阶段都有其特殊的方式,但通常情况下,当事件循环进入给定阶段时,它将执行特定于该阶段的任何操作,然后在该阶段的队列中执行回调,直到队列耗尽或执行了最大数量的回调。当队列耗尽或达到回调限制时,事件循环将移动到下一个阶段,依此类推。
process.nextTick()
图中没有显示process.nextTick(),尽管它是异步API的一部分。这是因为process.nextTick()在技术上不是事件循环的一部分。 是在任意两个阶段中间,只要有process.nextTick()没有被执行,那么优先执行process.nextTick的回调。
测试代码
const { readFile } = require('fs');const EventEmitter = require('events');class EE extends EventEmitter{}const yy = new EE();
yy.on('event',() => {
console.log('出大事了!'); //2 })
setTimeout(() => { //5 console.log('0 毫秒到期执行的定时器回调')
},0)
setTimeout(() => { // console.log('100 毫秒到期执行的定时器回调')
},100)
setTimeout(() => { // console.log('200 毫秒到期执行的定时器回调')
},200)
readFile('./package.json','utf-8',data => {
console.log('完成文件1读操作的回调'); //7 })
readFile('./task.js','utf-8',data => {
console.log('完成文件2读操作的回调'); //8 })
setImmediate(() => {
console.log('immediate 立即回调'); //6 })
process.nextTick(() => { //1 console.log('process 的第一次回调');
})Promise.resolve()
.then(() => {
yy.emit('event');
process.nextTick(() => {
console.log('process 的第二次回调'); //5 })
console.log('Promise的第一次回调'); //3 }).then(() => {
console.log('Promise的第2次回调'); //4 })
执行结果是什么?
注:文件1,文件2的大小会影响操作结果。
process 的第一次回调
出大事了!Promise的第一次回调Promise的第2次回调
process 的第二次回调0 毫秒到期执行的定时器回调
immediate 立即回调
完成文件2读操作的回调
完成文件1读操作的回调100 毫秒到期执行的定时器回调200 毫秒到期执行的定时器回调
$project:包含、排除、重命名和显示字段
$match:查询,需要同find()一样的参数
$limit:限制结果数量
$skip:忽略结果的数量
$sort:按照给定的字段排序结果
$group:按照给定表达式组合结果
$unwind:分割嵌入数组到自己顶层文件