├── 递归.md ├── DFS深度优先遍历.md ├── compose.md ├── 冒泡排序.md ├── 归并排序.md ├── 快速排序.md ├── 插入排序.md ├── 数组从0开始.md ├── 栈操作.md ├── 流操作.md ├── 淘汰算法.md ├── 获得素数.md ├── 链表算法.md └── 队列操作.md /递归.md: -------------------------------------------------------------------------------- 1 | # 递归 2 | ## 基本分析 3 | 1. 一个问题的解可以分解为几个子问题的解 4 | 2. 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样 5 | 3. 存在递归终止条件 6 | 4. 递归代码要警惕堆栈溢出 7 | 5. 递归代码要警惕重复计算 8 | ## 需求 9 | * 数组扁平化 10 | 11 | ### 递归的实现方式 12 | ``` javascript 13 | class Flattening{ 14 | constructor(arr){ 15 | this.res=[] 16 | this.flattening(arr) 17 | } 18 | 19 | flattening(obj){ 20 | obj.forEach(e=>{ 21 | if(Array.isArray(e)){ 22 | this.flattening(e) 23 | }else{ 24 | this.res.push(e) 25 | } 26 | }) 27 | } 28 | getRes(){ 29 | return this.res 30 | } 31 | } 32 | ``` 33 |  34 | ### 非递归的实现方式 35 | ``` javascript 36 | function getAll(arr=[1,2,[3,4,[5],[6,7,8,[9,10,11,[12,13,14,15,[16,17,18]]]]]]){ 37 | var contain =arr 38 | for(let i=0;i { 18 | const nextCoordinate = this.getNext(this.currentCoordinate, this.contain).filter(item => { 19 | const isRepeat = this.coordinateRes.some(isHas => this.U_eqArr(isHas, item)) // 坐标是否重复 20 | const isNextChars = this.currentChars == Path.findCharsFromContain(item, this.contain) // 该坐标是否是下一个字符的点 21 | return !isRepeat && isNextChars 22 | }) 23 | const copy = { // 需要存下来,否则,下面forEach的时候,会改变 coordinateRes 的值 24 | coordinateRes: [...this.coordinateRes] 25 | } 26 | nextCoordinate.forEach((coordinate, index) => { // 隐藏bug,如果需要新建Path对象,需要放在push操作之前 27 | if (index >= 1) { 28 | Path.callBackPush(new Path([...copy.coordinateRes], coordinate, this.charsArr, this.contain)) //创建一个新对象开启下一轮自举 29 | } else { 30 | this.coordinateRes.push(coordinate) // 修改当前点的容器 31 | this.currentCoordinate = coordinate // 修改当前点 32 | this.currentChars = this.charsArr[this.coordinateRes.length] // 当前字符 33 | if (this.currentChars) { 34 | loop() 35 | } 36 | } 37 | }) 38 | } 39 | 40 | loop() 41 | } 42 | U_eqArr(a, b) { 43 | if (Object.prototype.toString.call(a) === '[object Array]' && Object.prototype.toString.call(b) === '[object Array]' && a.length === b.length) { 44 | for (let index in a) { 45 | if (a[index] !== b[index]) { 46 | return false 47 | } 48 | } 49 | return true 50 | } 51 | return false 52 | } 53 | // 根据当前的坐标,查找下一个坐标 54 | getNext(coordinate, contain) { 55 | const length = coordinate.length 56 | const computedArr = ['add', 'subtract', 'same'] 57 | const res = [] 58 | function checkCoordinate(coordinate) { // 检查边界 和是否回退 59 | let chileContain = contain //存下引用 60 | for (let index in coordinate) { 61 | if (coordinate[index] < 0 || coordinate[index] > chileContain.length - 1) { //如果坐标小于0,或者大于最大坐标 62 | return false 63 | } 64 | chileContain = chileContain[0] 65 | } 66 | return true 67 | } 68 | function checkRepeat(status, type) { //只允许上下左右运动,不允许运算重复 69 | return !status.some(item => { 70 | return item == type 71 | }) 72 | } 73 | function loopGetNext(value, arr = [], status = []) { 74 | let currentLength = length - arr.length - 1 75 | 76 | for (let type of computedArr) { 77 | switch (type) { 78 | case 'add': 79 | const valueAdd = Number(value + 1) 80 | if (currentLength > 0) { 81 | loopGetNext(coordinate[arr.length + 1], [ 82 | ...arr, 83 | valueAdd 84 | ], [ 85 | ...status, 86 | 'add' 87 | ]) 88 | } else { 89 | const resArr = [ 90 | ...arr, 91 | valueAdd 92 | ] 93 | 94 | if (checkCoordinate(resArr) && checkRepeat(status, 'add')) { 95 | res.push(resArr) 96 | } 97 | 98 | } 99 | break; 100 | case 'subtract': 101 | const valueSubtract = Number(value - 1) 102 | if (currentLength > 0) { 103 | loopGetNext(coordinate[arr.length + 1], [ 104 | ...arr, 105 | valueSubtract 106 | ], [ 107 | ...status, 108 | 'subtract' 109 | ]) 110 | } else { 111 | const resArr = [ 112 | ...arr, 113 | valueSubtract 114 | ] 115 | 116 | if (checkCoordinate(resArr) && checkRepeat(status, 'subtract')) { 117 | res.push(resArr) 118 | } 119 | } 120 | break; 121 | case 'same': 122 | const valueSame = Number(value) 123 | if (currentLength > 0) { 124 | loopGetNext(coordinate[arr.length + 1], [ 125 | ...arr, 126 | valueSame 127 | ], [ 128 | ...status, 129 | 'same' 130 | ]) 131 | } else { 132 | const resArr = [ 133 | ...arr, 134 | valueSame 135 | ] 136 | 137 | if (checkCoordinate(resArr) && checkRepeat(status, 'same')) { 138 | res.push(resArr) 139 | } 140 | } 141 | break; 142 | } 143 | } 144 | } 145 | loopGetNext(coordinate[0]) 146 | //还需要滤过已经走过的点 147 | return res 148 | } 149 | // 根据字符找到坐标 150 | static findCoordinateFromContain(chars, contain = contain) { 151 | const res = [] 152 | for (let i in contain) { 153 | for (let ii in contain[i]) { 154 | if (contain[i][ii] == chars) { 155 | res.push([Number(i), Number(ii)]) 156 | } 157 | } 158 | } 159 | return res 160 | } 161 | // 根据坐标找到字符 162 | static findCharsFromContain(coordinate, contain = contain) { 163 | try { 164 | return coordinate.reduce((a, b) => { 165 | return a[b] 166 | }, contain) 167 | } catch (err) { 168 | return null 169 | } 170 | } 171 | } 172 | function test(contain, str) { 173 | //1. 找到开始的点 174 | //2. 设置容器回调装 Path 对象 175 | const resPath = [] 176 | const strArr = str.split('') 177 | const coordinateArr = Path.findCoordinateFromContain(strArr[0], contain) 178 | Path.callBackPush = function (item) { 179 | resPath.push(item) 180 | } 181 | for (let value of coordinateArr) { 182 | resPath.push(new Path([], value, strArr, contain)) 183 | } 184 | const res = resPath.filter(item => { 185 | return item.coordinateRes && item.coordinateRes.length == strArr.length 186 | }).map(item => item.coordinateRes) 187 | console.log(JSON.stringify(res, null)) 188 | } 189 | var contain = [ 190 | [1, 2, 3], 191 | [6, 7, 8], 192 | [1, 2, 3], 193 | [6, 7, 8], 194 | [1, 2, 3], 195 | [6, 7, 8], 196 | ] 197 | test(contain, '173') 198 | ``` 199 | -------------------------------------------------------------------------------- /compose.md: -------------------------------------------------------------------------------- 1 | # redux中间件 2 | ## 源码实现 3 | ```js 4 | const fucArr = [ 5 | function f1(next) { 6 | debugger 7 | return function f11(action) { 8 | debugger 9 | setTimeout(() => { 10 | console.log(action++); 11 | next(action) 12 | }, 300) 13 | } 14 | }, 15 | function f2(next) { 16 | debugger 17 | return function f22(action) { 18 | debugger 19 | setTimeout(() => { 20 | console.log(action++); 21 | next(action) 22 | }, 200) 23 | } 24 | }, 25 | function f3(next) { 26 | debugger 27 | return function f33(action) { 28 | debugger 29 | setTimeout(() => { 30 | console.log(action++); 31 | next(action) 32 | }, 100) 33 | } 34 | }, 35 | ] 36 | 37 | var run = arr => { 38 | var reduceResult = arr.reduce((pre, next) => { 39 | debugger 40 | return (...arg) => pre(next(...arg)) 41 | }); 42 | debugger 43 | return reduceResult(function space(){}); 44 | } 45 | // reduceResult 包装之后 执行顺序 f3(space)=>f2=>f1=>f11=>f22=>f33 46 | run(fucArr)(1); 47 | ``` -------------------------------------------------------------------------------- /冒泡排序.md: -------------------------------------------------------------------------------- 1 | # Bubble 2 | * 冒泡排序是原地排序算法。每次操作只涉及相邻的操作 3 | * 冒泡是稳定排序,只有大于的时候才做交换,相等的时候不改变顺序 4 | * 时间复杂度,最小 O(n),最大O(n²) 5 | ```javascript 6 | class Bubble{ 7 | constructor(arr){ 8 | this.sort(arr) 9 | } 10 | sort(arr){ 11 | const length=arr.length 12 | for(let i =0;iarr[ii+1]){ 16 | var t =arr[ii] 17 | arr[ii]=arr[ii+1] 18 | arr[ii+1]=t 19 | flag=true 20 | } 21 | } 22 | if(!flag)break 23 | } 24 | this.res=arr 25 | } 26 | getRes(){ 27 | return this.res 28 | } 29 | } 30 | ``` -------------------------------------------------------------------------------- /归并排序.md: -------------------------------------------------------------------------------- 1 | # Merge Sort 2 | * 分而治之 3 | * 使用递归的方式解决 4 | * 子问题解决了,在合并在一起 5 | * 过程:先分解,组内排序,合并分组,从中间开始插排 6 | ``` javascript 7 | class Merge { 8 | constructor(arr) { 9 | this.res = this.sort(arr) 10 | } 11 | sort(arr) { 12 | if (arr.length > 1) { 13 | //切割 14 | var center = this.getCenter(arr.length) //中间的下标 15 | var tmp1 = arr.slice(0, center) 16 | var tmp2 = arr.slice(center) 17 | if (arr.length < 4) { 18 | //递归排序 19 | tmp1 = this.insertion(tmp1, 1) 20 | tmp2 = this.insertion(tmp2, 1) 21 | //合并,需要优化起始排序位置外层 22 | return this.insertion([...tmp1, ...tmp2], tmp1.length) 23 | } else { 24 | return [...this.sort(tmp1),...this.sort(tmp2)] 25 | } 26 | } else { 27 | return arr 28 | } 29 | } 30 | getRes() { 31 | return this.res 32 | } 33 | insertion(arr, start) { 34 | const length = arr.length 35 | for (let i = start; i < length; i++) { 36 | let value = arr[i] //保存当前值 37 | //let j =i-1 获得当前未排序的元素,前面有的元素个数 38 | for (var j = i - 1; j >= 0; j--) { //当i=1的时候,也要循环一次,因此有>= 39 | if (arr[j] > value) { 40 | arr[j + 1] = arr[j] //移动 41 | } else { 42 | break; //如果小于,则跳出,进入插入赋值 43 | } 44 | } 45 | arr[j + 1] = value // 插入赋值,由于上面运算之后--了,所以需要+1 46 | } 47 | return arr 48 | } 49 | getCenter(length) { 50 | if (length % 2 == 0) { 51 | return length / 2 52 | } else { 53 | return (length - 1) / 2 54 | } 55 | } 56 | } 57 | ``` -------------------------------------------------------------------------------- /快速排序.md: -------------------------------------------------------------------------------- 1 | # Quicksort 2 | * 获得中间某个值,将整个数组切割为大小两组 3 | * 关键是如何原地排序 4 | * 通过交换位置 5 | * 递归排序 6 | ```javascript 7 | class Quicksort{ 8 | constructor(arr){ 9 | this.arr=arr 10 | this.res=this.sort(arr,0,arr.length-1) 11 | console.log(this.res) 12 | } 13 | sort(arr,start,end){ 14 | debugger 15 | var length =end-start+1 16 | var centerKey=this.getCenter(length)+start 17 | var centerValue=this.arr[centerKey] 18 | var indexTmp2=centerKey+1 //[...tmp1] ,centerValue ,[...tmp2] 19 | for(let i =0;icenterValue){//与tmp2进行交换 21 | var value =arr[i] 22 | let isBreak=false 23 | for(let ii=indexTmp2;ii=centerKey;ii--){ //移动数组 48 | arr[ii]=arr[i-1] 49 | } 50 | arr[centerKey]=value 51 | centerKey++ //centerKey右挪一次 52 | } 53 | } 54 | } 55 | if(centerKey-start>2) { 56 | this.sort(arr,start,centerKey) 57 | } 58 | if(centerKey-end>2){ 59 | this.sort(arr,centerKey,end) 60 | } 61 | return arr 62 | } 63 | getCenter(length){ 64 | if(length%2 == 0 ){ 65 | return length/2 66 | }else{ 67 | return (length-1)/2 68 | } 69 | } 70 | } 71 | var a =new Quicksort([3,1,2,5,6,4,9,7]) 72 | ``` -------------------------------------------------------------------------------- /插入排序.md: -------------------------------------------------------------------------------- 1 | # Insertion 2 | * 分为已排序区域和未排序区 3 | * 每次选择未排序区的第一位与前面的比较 4 | * 将位置后挪 5 | # 实现 6 | 3,4,5,2,1 7 | ```javascript 8 | class Insertion{ 9 | constructor(arr){ 10 | this.arr=arr 11 | this.sort() 12 | } 13 | sort(){ 14 | const length=this.arr.length 15 | for (let i=1;i=0;j--){ //当i=1的时候,也要循环一次,因此有>= 19 | if(this.arr[j]>value){ 20 | this.arr[j+1]=this.arr[j]//移动 21 | }else{ 22 | break; //如果小于,则跳出,进入插入赋值 23 | } 24 | } 25 | this.arr[j+1]=value // 插入赋值,由于上面运算之后--了,所以需要+1 26 | } 27 | } 28 | getRes(){ 29 | return this.arr 30 | } 31 | } 32 | ··· -------------------------------------------------------------------------------- /数组从0开始.md: -------------------------------------------------------------------------------- 1 | # 数组 2 | 一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据 3 | 4 | ## 线性表 5 | 线性表就是数据排成像一条线一样的结构 6 | * 队列(先进先出) 7 | * 栈(先进后出,后进先出) 8 | * 注:非线性表(连续的内存空间和相同类型的数据) 9 | * 二叉树 10 | * 堆 11 | * 图 12 | ## 连续的内存空间和相同类型的数据 13 | * 优势:随机访问 14 | * 劣势:数组操作很低效(删除,插入) 15 | ## 为什么数组的下标从0开始 16 | * 节省了一次cpu运算 17 | ``` 18 | a[k]_address = base_address + k * type_size 19 | a[k]_address = base_address + (k-1)*type_size 20 | 21 | ``` 22 | * 23 | 24 | -------------------------------------------------------------------------------- /栈操作.md: -------------------------------------------------------------------------------- 1 | # 实现浏览器的前进和后退 2 | ## 需求 3 | * 当你依次访问完一串页面 a-b-c 之后,点击浏览器的后退按钮,就可以查看之前浏览过的页面 b 和 a。当你后退到页面 a,点击前进按钮,就可以重新查看页面 b 和 c。但是,如果你后退到页面 b 后,点击了新的页面 d,那就无法再通过前进、后退功能查看页面 c 了。 4 | ## 方案 5 | * 实现一个类,只有进栈和出栈 6 | ``` javascript 7 | class ArrayStack { 8 | constructor(arr){ 9 | var length=arr.length 10 | this.arr=arr 11 | this.count=length-1//当前指针,默认指向最后一个 12 | } 13 | push(e){ 14 | if(this.arr[this.count+1]){ //下一个路由存在 15 | if(e!=this.arr[this.count+1]){ //下一个路由不相等 16 | this.arr.splice(this.count) //切掉之后的路由 17 | this.arr.push(e) 18 | this.count=this.arr.length-1 19 | }else{ 20 | this.count++ 21 | } 22 | }else{//不存在下一个路由 23 | this.arr.push(e) 24 | this.count++ 25 | } 26 | return this.arr[this.count] 27 | } 28 | pop(){ 29 | if(this.arr.length>0&&this.this.count>0){ 30 | this.count-- 31 | return this.arr[this.count]//返回最后一个 32 | } 33 | }else{ 34 | console.log('已没有元素') 35 | return false 36 | } 37 | } 38 | } 39 | ``` -------------------------------------------------------------------------------- /流操作.md: -------------------------------------------------------------------------------- 1 | # 流操作 2 | ## 流的概念 3 | **把集合的任务,当做一件事情来做,是一种响应式变成** 4 | ## 理解 5 | 1. 比如说input事件,每次input都要对值进行格式化,通过流这种,可以更直观的链式操作 6 | 2. 所谓的链式,每一个操作符都将当前流的对象return回去 7 | 3. 内置参数的话,是在update的时候,内置参数或者 bind 当前的this 8 | 4. 9 | ## 实现 10 | ```js 11 | class Task{ //流的处理 12 | constructor(){ 13 | this.taskList=[] 14 | this.current=0 15 | this.value='' 16 | } 17 | next(){ 18 | if(this.current { 7 | let value = arr[arr.length - 1] 8 | /** 9 | * 10 | * @param {number} no 10 需要获得的素数位数 11 | * @returns {number} 12 | */ 13 | return (no = 10) => { 14 | if (no <= arr.length) { 15 | console.log(arr[no - 1]) 16 | return arr[no - 1] 17 | } 18 | const cycle = () => { 19 | if (arr.length === no) { // 20 | console.log(arr, value) 21 | return value 22 | } 23 | value += 2 24 | var targe = true // 标记是否是素数 25 | for (let i = 0; i < arr.length; i++) { // 遍历判断已经存在的素数 26 | if(arr[i]>Math.sqrt(value)){ //计算1到开根号之间的值,开根号之后的值的另外一个因子肯定是开根号之前的,如果那个因子还可以分解,那最后肯定也是更小的素数 27 | break 28 | } 29 | if (value % arr[i] === 0) { 30 | targe = false 31 | break 32 | } 33 | } 34 | if (targe) { 35 | arr.push(value) 36 | } 37 | setTimeout(cycle, 0) 38 | } 39 | cycle() 40 | } 41 | } 42 | ``` -------------------------------------------------------------------------------- /链表算法.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zheNeng/leanArithmetic/88174ba13faf1defd1252131cc43ddc2cdcec26d/链表算法.md -------------------------------------------------------------------------------- /队列操作.md: -------------------------------------------------------------------------------- 1 | # 实现一个中间件 2 | ## 需求 3 | * 可以手动添加task,并在完成的时候,自动执行下一个task,并且只能从头开始执行 4 | ## 实现 5 | ``` javascript 6 | class ArrayEnqueue { 7 | constructor() { 8 | this.arr = [] 9 | this.count = 1 //指针 10 | } 11 | push(fn) { 12 | var next = this.next 13 | var that = this 14 | this.arr.push(function () { 15 | fn.call(that, next.bind(that)) 16 | }) 17 | } 18 | next() { 19 | if (this.count < this.arr.length) { 20 | this.arr[this.count++]() 21 | } else { 22 | console.log('执行完毕') 23 | } 24 | } 25 | go() { 26 | this.count = 1 27 | this.arr[0]() 28 | } 29 | } 30 | var a = new ArrayEnqueue() 31 | a.push(function (next) { 32 | console.log('test1', this); 33 | next() 34 | }) 35 | a.push(function (next) { 36 | console.log('test2', this); 37 | next() 38 | }) 39 | a.push(function (next) { 40 | console.log('test3', this); 41 | next() 42 | }) 43 | a.go() 44 | ``` 45 | --------------------------------------------------------------------------------