├── 02 ├── find-index │ ├── README.md │ ├── find-index.js │ └── test.js ├── priority-queue │ ├── README.md │ ├── test.js │ └── priority-queue.js ├── shell-sort │ ├── test.js │ ├── README.md │ └── shell-sort.js ├── bubble-sort │ ├── test.js │ ├── bubble-sort.js │ └── README.md ├── heap-sort │ ├── test.js │ ├── README.md │ └── heap-sort.js ├── insertion-sort │ ├── test.js │ ├── README.md │ └── insertion-sort.js ├── selection-sort │ ├── test.js │ ├── README.md │ └── selection-sort.js ├── merge-sort │ ├── test.js │ ├── README.md │ └── merge-sort.js ├── quick-sort │ ├── test.js │ ├── README.md │ ├── quick3way.js │ └── quick-sort.js ├── compare-test.js └── README.md ├── imgs ├── 1-1.png ├── 1-2.png ├── 1-3.png ├── 2-1.png ├── 2-10.png ├── 2-11.png ├── 2-12.png ├── 2-13.png ├── 2-14.png ├── 2-15.png ├── 2-16.png ├── 2-17.png ├── 2-18.png ├── 2-19.png ├── 2-2.png ├── 2-3.png ├── 2-4.png ├── 2-5.png ├── 2-6.png ├── 2-7.png ├── 2-8.png ├── 2-9.png ├── 3-1.png ├── 3-2.png └── cover.jpg ├── 04 ├── undirected-graph │ ├── data │ │ ├── tiny.txt │ │ ├── data.txt │ │ └── routes.txt │ ├── test │ │ ├── test-symbol-graph.js │ │ ├── test-cc.js │ │ ├── test-dfs-path.js │ │ ├── test-bfs-path.js │ │ └── test-dfs-search.js │ ├── depth-first-search.js │ ├── depth-first-path.js │ ├── cc.js │ ├── breadth-first-path.js │ ├── graph.js │ ├── symbol-graph.js │ └── utils.js ├── directed-graph │ ├── data │ │ ├── data.txt │ │ └── tinyDG.txt │ ├── test │ │ ├── test-digraph.js │ │ ├── test-directed-cycle.js │ │ ├── test-topological.js │ │ ├── test-directed-dfs.js │ │ ├── test-transitive-closure.js │ │ ├── test-kosaraju-scc.js │ │ ├── test-dfs-path.js │ │ └── test-bfs-path.js │ ├── utils.js │ ├── transitive-closure.js │ ├── topological.js │ ├── directed-dfs.js │ ├── depth-first-directed-path.js │ ├── depth-first-order.js │ ├── kosaraju-scc.js │ ├── breadth-first-directed-path.js │ ├── directed-cycle.js │ └── digraph.js ├── spt │ ├── data │ │ └── tinyEWD.txt │ ├── directed-edge.js │ ├── utils.js │ ├── test │ │ └── test-edge-weighted-digraph.js │ ├── edge-weighted-digraph.js │ └── dijkstra-sp.js └── mst │ ├── data │ └── tinyEWG.txt │ ├── test │ └── test-edge-weighted-graph.js │ ├── utils.js │ ├── edge.js │ ├── lazy-prim-mst.js │ └── edge-weighted-graph.js ├── 01 ├── binary-search │ ├── README.md │ ├── binary-search.js │ └── test.js ├── bag │ └── bag.js ├── stack │ └── stack.js ├── queue │ └── queue.js └── README.md ├── 05 ├── string-sort │ ├── data │ │ ├── data-msd.txt │ │ ├── data.txt │ │ └── data-quick3string.txt │ ├── test │ │ ├── test-msd.js │ │ ├── test-lsd.js │ │ └── test-quick3string.js │ ├── utils.js │ ├── lsd.js │ ├── quick3string.js │ └── msd.js ├── sub-str-search │ ├── test │ │ ├── test-kmp.js │ │ ├── test-kmp2.js │ │ └── test-boyer-moore.js │ ├── boyer-moore.js │ ├── kmp.js │ └── kmp-2.js ├── string-search │ ├── test │ │ └── test-triest.js │ └── triest.js └── README.md ├── 03 ├── red-black-tree │ ├── README.md │ ├── test.js │ └── red-black-tree.js ├── hash-table │ ├── README.md │ ├── test.js │ └── hash-table.js ├── README.md └── binary-search-tree │ ├── test.js │ ├── README.md │ └── binary-search-tree.js ├── README.md └── utils.js /02/find-index/README.md: -------------------------------------------------------------------------------- 1 | ## 测试 2 | ```js 3 | node test.js 4 | ``` -------------------------------------------------------------------------------- /02/priority-queue/README.md: -------------------------------------------------------------------------------- 1 | ## 测试 2 | ```js 3 | node test.js 4 | ``` -------------------------------------------------------------------------------- /imgs/1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/1-1.png -------------------------------------------------------------------------------- /imgs/1-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/1-2.png -------------------------------------------------------------------------------- /imgs/1-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/1-3.png -------------------------------------------------------------------------------- /imgs/2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-1.png -------------------------------------------------------------------------------- /imgs/2-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-10.png -------------------------------------------------------------------------------- /imgs/2-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-11.png -------------------------------------------------------------------------------- /imgs/2-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-12.png -------------------------------------------------------------------------------- /imgs/2-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-13.png -------------------------------------------------------------------------------- /imgs/2-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-14.png -------------------------------------------------------------------------------- /imgs/2-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-15.png -------------------------------------------------------------------------------- /imgs/2-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-16.png -------------------------------------------------------------------------------- /imgs/2-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-17.png -------------------------------------------------------------------------------- /imgs/2-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-18.png -------------------------------------------------------------------------------- /imgs/2-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-19.png -------------------------------------------------------------------------------- /imgs/2-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-2.png -------------------------------------------------------------------------------- /imgs/2-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-3.png -------------------------------------------------------------------------------- /imgs/2-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-4.png -------------------------------------------------------------------------------- /imgs/2-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-5.png -------------------------------------------------------------------------------- /imgs/2-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-6.png -------------------------------------------------------------------------------- /imgs/2-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-7.png -------------------------------------------------------------------------------- /imgs/2-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-8.png -------------------------------------------------------------------------------- /imgs/2-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/2-9.png -------------------------------------------------------------------------------- /imgs/3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/3-1.png -------------------------------------------------------------------------------- /imgs/3-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/3-2.png -------------------------------------------------------------------------------- /imgs/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woai3c/Algorithm/HEAD/imgs/cover.jpg -------------------------------------------------------------------------------- /04/undirected-graph/data/tiny.txt: -------------------------------------------------------------------------------- 1 | 6 2 | 8 3 | 0 5 4 | 2 4 5 | 2 3 6 | 1 2 7 | 0 1 8 | 3 4 9 | 3 5 10 | 0 2 -------------------------------------------------------------------------------- /01/binary-search/README.md: -------------------------------------------------------------------------------- 1 | ## 测试 2 | ```js 3 | node test 生成值的上限 生成值的个数 4 | 5 | // example 6 | node test 100000 8888 7 | ``` -------------------------------------------------------------------------------- /04/undirected-graph/data/data.txt: -------------------------------------------------------------------------------- 1 | 13 2 | 13 3 | 0 5 4 | 4 3 5 | 0 1 6 | 9 12 7 | 6 4 8 | 5 4 9 | 0 2 10 | 11 12 11 | 9 10 12 | 0 6 13 | 7 8 14 | 9 11 15 | 5 3 -------------------------------------------------------------------------------- /05/string-sort/data/data-msd.txt: -------------------------------------------------------------------------------- 1 | she 2 | sells 3 | seashells 4 | by 5 | the 6 | seashore 7 | the 8 | shells 9 | she 10 | sells 11 | are 12 | surely 13 | seashells -------------------------------------------------------------------------------- /04/directed-graph/data/data.txt: -------------------------------------------------------------------------------- 1 | 13 2 | 15 3 | 0 5 4 | 0 1 5 | 0 6 6 | 2 0 7 | 2 3 8 | 3 5 9 | 5 4 10 | 6 4 11 | 6 9 12 | 7 6 13 | 8 7 14 | 9 10 15 | 9 11 16 | 9 12 17 | 11 12 -------------------------------------------------------------------------------- /05/string-sort/data/data.txt: -------------------------------------------------------------------------------- 1 | 4PGC938 2 | 2IYE230 3 | 3CIO720 4 | 1ICK750 5 | 1OHV845 6 | 4JZY524 7 | 1ICK750 8 | 3CIO720 9 | 1OHV845 10 | 1OHV845 11 | 2RLA629 12 | 2RLA629 13 | 3ATW723 -------------------------------------------------------------------------------- /05/string-sort/test/test-msd.js: -------------------------------------------------------------------------------- 1 | const msd = require('../msd') 2 | const { getData } = require('../utils') 3 | const data = getData('../data/data-msd.txt') 4 | 5 | msd(data) 6 | console.log(data) -------------------------------------------------------------------------------- /05/string-sort/test/test-lsd.js: -------------------------------------------------------------------------------- 1 | const lsd = require('../lsd') 2 | const { getData } = require('../utils') 3 | const data = getData('../data/data.txt') 4 | 5 | lsd(data, data[0].length) 6 | console.log(data) -------------------------------------------------------------------------------- /05/string-sort/utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | function getData(path) { 4 | const data = fs.readFileSync(path, 'utf-8') 5 | return data.split('\r\n') 6 | } 7 | 8 | module.exports = { 9 | getData 10 | } -------------------------------------------------------------------------------- /04/directed-graph/test/test-digraph.js: -------------------------------------------------------------------------------- 1 | const { createDigraph } = require('../utils') 2 | const graph = createDigraph('../data/tinyDG.txt') 3 | 4 | console.log(graph.toString()) 5 | console.log('反向图') 6 | console.log(graph.reverse().toString()) -------------------------------------------------------------------------------- /04/spt/data/tinyEWD.txt: -------------------------------------------------------------------------------- 1 | 8 2 | 15 3 | 4 5 0.35 4 | 5 4 0.35 5 | 4 7 0.37 6 | 5 7 0.28 7 | 7 5 0.28 8 | 5 1 0.32 9 | 0 4 0.38 10 | 0 2 0.26 11 | 7 3 0.39 12 | 1 3 0.29 13 | 2 7 0.34 14 | 6 2 0.40 15 | 3 6 0.52 16 | 6 0 0.58 17 | 6 4 0.93 -------------------------------------------------------------------------------- /05/string-sort/test/test-quick3string.js: -------------------------------------------------------------------------------- 1 | const quick3string = require('../quick3string') 2 | const { getData } = require('../utils') 3 | const data = getData('../data/data-quick3string.txt') 4 | 5 | quick3string(data) 6 | console.log(data) -------------------------------------------------------------------------------- /04/directed-graph/data/tinyDG.txt: -------------------------------------------------------------------------------- 1 | 13 2 | 22 3 | 4 2 4 | 2 3 5 | 3 2 6 | 6 0 7 | 0 1 8 | 2 0 9 | 11 12 10 | 12 9 11 | 9 10 12 | 9 11 13 | 8 9 14 | 10 12 15 | 11 4 16 | 4 3 17 | 3 5 18 | 7 8 19 | 8 7 20 | 5 4 21 | 0 5 22 | 6 4 23 | 6 9 24 | 7 6 -------------------------------------------------------------------------------- /04/mst/data/tinyEWG.txt: -------------------------------------------------------------------------------- 1 | 8 2 | 16 3 | 4 5 0.35 4 | 4 7 0.37 5 | 5 7 0.28 6 | 0 7 0.16 7 | 1 5 0.32 8 | 0 4 0.38 9 | 2 3 0.17 10 | 1 7 0.19 11 | 0 2 0.26 12 | 1 2 0.36 13 | 1 3 0.29 14 | 2 7 0.34 15 | 6 2 0.40 16 | 3 6 0.52 17 | 6 0 0.58 18 | 6 4 0.93 -------------------------------------------------------------------------------- /02/shell-sort/test.js: -------------------------------------------------------------------------------- 1 | const shellSort = require('./shell-sort') 2 | 3 | function test() { 4 | const arry = [10,9,5,1,2,3,4,7,8,95,100] 5 | console.log('排序前:' + arry) 6 | shellSort(arry) 7 | console.log('排序后:' + arry) 8 | } 9 | 10 | test() -------------------------------------------------------------------------------- /04/undirected-graph/data/routes.txt: -------------------------------------------------------------------------------- 1 | JFK MCO 2 | ORD DEN 3 | ORD HOU 4 | DFW PHX 5 | JFK ATL 6 | ORD DFW 7 | ORD PHX 8 | ATL HOU 9 | DEN PHX 10 | PHX LAX 11 | JFK ORD 12 | DEN LAS 13 | DFW HOU 14 | ORD ATL 15 | LAS LAX 16 | ATL MCO 17 | HOU MCO 18 | LAS PHX -------------------------------------------------------------------------------- /02/bubble-sort/test.js: -------------------------------------------------------------------------------- 1 | const bubbleSort = require('./bubble-sort') 2 | 3 | function test() { 4 | const arry = [10,9,5,1,2,3,4,7,8,95,100] 5 | console.log('排序前:' + arry) 6 | bubbleSort(arry) 7 | console.log('排序后:' + arry) 8 | } 9 | 10 | test() -------------------------------------------------------------------------------- /02/heap-sort/test.js: -------------------------------------------------------------------------------- 1 | const heapSort = require('./heap-sort') 2 | 3 | function test() { 4 | const arry = [10,9,5,1,2,88,3,9,15,17,4,7,8,95,100] 5 | console.log('排序前:' + arry) 6 | heapSort(arry) 7 | console.log('排序后:' + arry) 8 | } 9 | 10 | test() -------------------------------------------------------------------------------- /05/string-sort/data/data-quick3string.txt: -------------------------------------------------------------------------------- 1 | edu.princeton.cs 2 | com.apple 3 | edu.princeton.cs 4 | com.cnn 5 | com.google 6 | edu.uva.cs 7 | edu.princeton.cs 8 | edu.princeton.cs.www 9 | edu.uva.cs 10 | edu.uva.cs 11 | edu.uva.cs 12 | com.adobe 13 | edu.princeton.ee -------------------------------------------------------------------------------- /02/insertion-sort/test.js: -------------------------------------------------------------------------------- 1 | const insertionSort = require('./insertion-sort') 2 | 3 | function test() { 4 | const arry = [10,9,5,1,2,3,4,7,8,95,100] 5 | console.log('排序前:' + arry) 6 | insertionSort(arry) 7 | console.log('排序后:' + arry) 8 | } 9 | 10 | test() -------------------------------------------------------------------------------- /02/selection-sort/test.js: -------------------------------------------------------------------------------- 1 | const selectionSort = require('./selection-sort') 2 | 3 | function test() { 4 | const arry = [10,9,5,1,2,3,4,7,8,95,100] 5 | console.log('排序前:' + arry) 6 | selectionSort(arry) 7 | console.log('排序后:' + arry) 8 | } 9 | 10 | test() -------------------------------------------------------------------------------- /04/mst/test/test-edge-weighted-graph.js: -------------------------------------------------------------------------------- 1 | const { createGraph } = require('../utils') 2 | const LazyPrimMST = require('../lazy-prim-mst') 3 | const graph = createGraph('../data/tinyEWG.txt') 4 | const mst = new LazyPrimMST(graph) 5 | console.log(mst.getEdges()) 6 | console.log(mst.getWeight()) -------------------------------------------------------------------------------- /04/undirected-graph/test/test-symbol-graph.js: -------------------------------------------------------------------------------- 1 | const { createSymbolGraph } = require('../utils') 2 | const symbolGraph = createSymbolGraph('../data/routes.txt', ' ') 3 | 4 | console.log(symbolGraph.toString()) 5 | console.log(symbolGraph.getIndex('JFK')) 6 | console.log(symbolGraph.getName(0)) -------------------------------------------------------------------------------- /04/directed-graph/test/test-directed-cycle.js: -------------------------------------------------------------------------------- 1 | const { createDigraph } = require('../utils') 2 | const DirectedCycle = require('../directed-cycle') 3 | const graph = createDigraph('../data/data.txt') 4 | const cycle = new DirectedCycle(graph) 5 | console.log(cycle.getCycle()) 6 | console.log(cycle.hasCycle()) -------------------------------------------------------------------------------- /04/directed-graph/test/test-topological.js: -------------------------------------------------------------------------------- 1 | const { createDigraph } = require('../utils') 2 | const Topological = require('../topological') 3 | const graph = createDigraph('../data/data.txt') 4 | const topological = new Topological(graph) 5 | console.log(topological.getOrder()) 6 | console.log(topological.isDAG()) -------------------------------------------------------------------------------- /02/merge-sort/test.js: -------------------------------------------------------------------------------- 1 | const mergeSort = require('./merge-sort') 2 | 3 | function test() { 4 | const arry = [10,9,5,1,2,3,4,7,8,95,100,19,11,15,16,16,18,12,23,27,112,64,84,75,59,68,338,667,755,16,36,33,12] 5 | console.log('排序前:' + arry) 6 | mergeSort(arry) 7 | console.log('排序后:' + arry) 8 | } 9 | 10 | test() -------------------------------------------------------------------------------- /02/insertion-sort/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **插入排序**(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到 O(1) 的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。 3 | 4 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F) 5 | 6 | ## 测试 7 | ```js 8 | node test 9 | ``` -------------------------------------------------------------------------------- /03/red-black-tree/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **红黑树**(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为"对称二叉B树",它现代的名字源于Leo J. Guibas和Robert Sedgewick于1978年写的一篇论文。红黑树的结构复杂,但它的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在 O(log n) 时间内完成查找,插入和删除,这里的 n 是树中元素的数目。 3 | 4 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91) 5 | ## 测试 6 | ```js 7 | node test 8 | ``` 9 | -------------------------------------------------------------------------------- /05/sub-str-search/test/test-kmp.js: -------------------------------------------------------------------------------- 1 | const KMP = require('../kmp') 2 | const data = 'AABRAACADABRAACAADABRA' 3 | const pat = 'AACAA' 4 | const kmp = new KMP(pat) 5 | const index = kmp.search(data) 6 | 7 | let str = `text: ${data}\n` 8 | str += 'pattern:' 9 | for (let i = 0; i < index; i++) { 10 | str += ' ' 11 | } 12 | 13 | str += index < 0? '未命中' : pat 14 | console.log(str) -------------------------------------------------------------------------------- /02/shell-sort/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **希尔排序**,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。 3 | 4 | 希尔排序是基于插入排序的以下两点性质而提出改进方法的: 5 | 1. 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率 6 | 1. 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位 7 | 8 | ![](../../imgs/2-1.png) 9 | 10 | ![](../../imgs/2-2.png) 11 | 12 | ![](../../imgs/2-3.png) 13 | 14 | ![](../../imgs/2-4.png) 15 | ## 测试 16 | ```js 17 | node test 18 | ``` -------------------------------------------------------------------------------- /05/sub-str-search/test/test-kmp2.js: -------------------------------------------------------------------------------- 1 | const KMP = require('../kmp-2') 2 | const data = 'AABRAACADABRAACAADABRA' 3 | const pat = 'AACAA' 4 | const kmp = new KMP(pat) 5 | const index = kmp.search(data) 6 | 7 | let str = `text: ${data}\n` 8 | str += 'pattern:' 9 | for (let i = 0; i < index; i++) { 10 | str += ' ' 11 | } 12 | 13 | str += index < 0? '未命中' : pat 14 | console.log(str) -------------------------------------------------------------------------------- /04/directed-graph/test/test-directed-dfs.js: -------------------------------------------------------------------------------- 1 | const { createDigraph } = require('../utils') 2 | const DirectedDFS = require('../directed-dfs') 3 | const graph = createDigraph('../data/tinyDG.txt') 4 | const search = new DirectedDFS(graph, [1, 2, 6]) 5 | let str = '' 6 | for (let v = 0, V = graph.getV(); v < V; v++) { 7 | if (search.isMarked(v)) str += v + ' ' 8 | } 9 | 10 | console.log(str) -------------------------------------------------------------------------------- /02/merge-sort/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **归并排序**(英语:Merge sort,或mergesort),是创建在归并操作上的一种有效的排序算法,效率为 O(nlog n)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。归并排序算法依赖归并操作。 3 | 4 | ![](../../imgs/2-5.png) 5 | 6 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F) 7 | 8 | ## 测试 9 | ```js 10 | node test 11 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 目录 2 | 1. [基础](https://github.com/woai3c/Algorithm/tree/master/01) 3 | 2. [排序](https://github.com/woai3c/Algorithm/tree/master/02) 4 | 3. [查找](https://github.com/woai3c/Algorithm/tree/master/03) 5 | 4. [图](https://github.com/woai3c/Algorithm/tree/master/04) 6 | 5. [字符串](https://github.com/woai3c/Algorithm/tree/master/05) 7 | 8 | ![封面](https://github.com/woai3c/Algorithm/blob/master/imgs/cover.jpg) 9 | -------------------------------------------------------------------------------- /01/bag/bag.js: -------------------------------------------------------------------------------- 1 | function Bag() { 2 | this.arry = [] 3 | } 4 | 5 | Bag.prototype = { 6 | add(item) { 7 | this.arry.push(item) 8 | }, 9 | 10 | isEmpty() { 11 | return this.arry.length == 0 12 | }, 13 | 14 | size() { 15 | return this.arry.length 16 | }, 17 | 18 | getData() { 19 | return [...this.arry] 20 | } 21 | } 22 | 23 | module.exports = Bag -------------------------------------------------------------------------------- /04/directed-graph/test/test-transitive-closure.js: -------------------------------------------------------------------------------- 1 | const { createDigraph } = require('../utils') 2 | const TransitiveClosure = require('../transitive-closure') 3 | const graph = createDigraph('../data/tinyDG.txt') 4 | const closure = new TransitiveClosure(graph) 5 | 6 | for (let v = 0, V = graph.getV(); v < V; v++) { 7 | console.log(closure.isReachable(v, 0)) 8 | } 9 | 10 | console.log(graph.toString()) -------------------------------------------------------------------------------- /05/sub-str-search/test/test-boyer-moore.js: -------------------------------------------------------------------------------- 1 | const BoyerMoore = require('../boyer-moore') 2 | const data = 'AABRAACADABRAACAADABRA' 3 | const pat = 'AACAA' 4 | const bm = new BoyerMoore(pat) 5 | const index = bm.search(data) 6 | 7 | let str = `text: ${data}\n` 8 | str += 'pattern:' 9 | for (let i = 0; i < index; i++) { 10 | str += ' ' 11 | } 12 | 13 | str += index < 0? '未命中' : pat 14 | console.log(str) -------------------------------------------------------------------------------- /01/stack/stack.js: -------------------------------------------------------------------------------- 1 | function Stack() { 2 | this.arry = [] 3 | } 4 | 5 | Stack.prototype = { 6 | push(item) { 7 | this.arry.push(item) 8 | }, 9 | 10 | pop() { 11 | return this.arry.pop() 12 | }, 13 | 14 | isEmpty() { 15 | return this.arry.length == 0 16 | }, 17 | 18 | size() { 19 | return this.arry.length 20 | } 21 | } 22 | 23 | module.exports = Stack -------------------------------------------------------------------------------- /01/queue/queue.js: -------------------------------------------------------------------------------- 1 | function Queue() { 2 | this.arry = [] 3 | } 4 | 5 | Queue.prototype = { 6 | enqueue(item) { 7 | this.arry.push(item) 8 | }, 9 | 10 | dequeue() { 11 | return this.arry.shift(item) 12 | }, 13 | 14 | isEmpty() { 15 | return this.arry.length == 0 16 | }, 17 | 18 | size() { 19 | return this.arry.length 20 | } 21 | } 22 | 23 | module.exports = Queue -------------------------------------------------------------------------------- /02/selection-sort/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **选择排序**(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 3 | 4 | 选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对 n 个元素的表进行排序总共进行至多 n - 1 次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。 5 | 6 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F) 7 | 8 | ## 测试 9 | ```js 10 | node test 11 | ``` -------------------------------------------------------------------------------- /02/quick-sort/test.js: -------------------------------------------------------------------------------- 1 | const quickSort = require('./quick-sort') 2 | const quick3Way = require('./quick3way') 3 | 4 | function test() { 5 | let arry = [10,9,5,1,2,3,4,7,8,95,100] 6 | console.log('quickSort排序前:' + arry) 7 | quickSort(arry) 8 | console.log('quickSort排序后:' + arry) 9 | arry = [10,9,5,1,2,3,4,7,8,95,100] 10 | console.log('quick3Way排序前:' + arry) 11 | quick3Way(arry) 12 | console.log('quick3Way排序后:' + arry) 13 | } 14 | 15 | test() -------------------------------------------------------------------------------- /03/hash-table/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **散列表**(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。 3 | 4 | 一个通俗的例子是,为了查找电话簿中某人的号码,可以创建一个按照人名首字母顺序排列的表(即建立人名 x 到首字母 F(x) 的一个函数关系),在首字母为 W 的表中查找“王”姓的电话号码,显然比直接查找就要快得多。这里使用人名作为关键字,“取首字母”是这个例子中散列函数的函数法则 F(),存放首字母的表对应散列表。关键字和函数法则理论上可以任意确定。 5 | 6 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E5%93%88%E5%B8%8C%E8%A1%A8) 7 | ## 测试 8 | ```js 9 | node test 10 | ``` 11 | -------------------------------------------------------------------------------- /02/heap-sort/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | 堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。若以升序排序说明,把数组转换成最大堆积(Max-Heap Heap),这是一种满足最大堆积性质(Max-Heap Property)的二叉树:对于除了根之外的每个节点i, A[parent(i)] ≥ A[i]。 3 | 4 | 重复从最大堆积取出数值最大的节点(把根节点和最后一个节点交换,把交换后的最后一个节点移出堆),并让残余的堆积维持最大堆积性质。 5 | 6 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F) 7 | 8 | ![](../../imgs/2-13.png) 9 | 10 | ![](../../imgs/2-14.png) 11 | 12 | ## 测试 13 | ```js 14 | node test 15 | ``` -------------------------------------------------------------------------------- /04/spt/directed-edge.js: -------------------------------------------------------------------------------- 1 | function DirectedEdge(v, w, weight) { 2 | this.v = v 3 | this.w = w 4 | this.weight = weight 5 | } 6 | 7 | DirectedEdge.prototype = { 8 | getWeight() { 9 | return this.weight 10 | }, 11 | 12 | getFrom() { 13 | return this.v 14 | }, 15 | 16 | getTo() { 17 | return this.w 18 | }, 19 | 20 | toString() { 21 | return `${this.v}-${this.w} ${this.weight}` 22 | } 23 | } 24 | 25 | module.exports = DirectedEdge -------------------------------------------------------------------------------- /02/selection-sort/selection-sort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arry 3 | * @return {void} 4 | */ 5 | function selectionSort(arry) { 6 | const len = arry.length 7 | for (let i = 0, l = len - 1; i < l; i++) { 8 | let minIndex = i 9 | for (let j = i + 1; j < len; j++) { 10 | if (arry[j] < arry[minIndex]) { 11 | minIndex = j 12 | } 13 | } 14 | 15 | const temp = arry[i] 16 | arry[i] = arry[minIndex] 17 | arry[minIndex] = temp 18 | } 19 | } 20 | 21 | module.exports = selectionSort -------------------------------------------------------------------------------- /04/directed-graph/utils.js: -------------------------------------------------------------------------------- 1 | function createDigraph(path) { 2 | const Graph = require('./digraph') 3 | const fs = require('fs') 4 | let data = fs.readFileSync(path, 'utf-8') 5 | data = data.split('\r\n') 6 | const V = data[0] 7 | const E = data[1] 8 | const vData = [] 9 | for (let i = 2, len = data.length; i < len; i++) { 10 | vData.push(...data[i].split(' ')) 11 | } 12 | 13 | return new Graph(V, E, vData) 14 | } 15 | 16 | module.exports = { 17 | createDigraph, 18 | } -------------------------------------------------------------------------------- /04/directed-graph/transitive-closure.js: -------------------------------------------------------------------------------- 1 | const DirectedDFS = require('./directed-dfs') 2 | 3 | function TransitiveClosure(graph) { 4 | this.all = [] 5 | this.init(graph) 6 | } 7 | 8 | TransitiveClosure.prototype = { 9 | init(graph) { 10 | for (let v = 0, V = graph.getV(); v < V; v++) { 11 | this.all[v] = new DirectedDFS(graph, v) 12 | } 13 | }, 14 | 15 | isReachable(v, w) { 16 | return this.all[v].isMarked(w) 17 | } 18 | } 19 | 20 | module.exports = TransitiveClosure -------------------------------------------------------------------------------- /01/binary-search/binary-search.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arry 3 | * @param {number} val 4 | * @return {number} 5 | */ 6 | function binarySearch(arry, val) { 7 | let start = 0 8 | let end = arry.length - 1 9 | while (start <= end) { 10 | let mid = start + Math.floor((end - start) / 2) 11 | if (arry[mid] < val) { 12 | start = mid + 1 13 | } else if (arry[mid] > val) { 14 | end = mid - 1 15 | } else { 16 | return mid 17 | } 18 | } 19 | 20 | return -1 21 | } 22 | 23 | module.exports = binarySearch -------------------------------------------------------------------------------- /02/insertion-sort/insertion-sort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arry 3 | * @return {void} 4 | */ 5 | function insertionSort(arry) { 6 | for (let i = 1, len = arry.length; i < len; i++) { 7 | for (let j = i; j > 0; j--) { 8 | if (arry[j] < arry[j - 1]) { 9 | const temp = arry[j] 10 | arry[j] = arry[j - 1] 11 | arry[j - 1] = temp 12 | } else { 13 | break 14 | } 15 | } 16 | } 17 | } 18 | 19 | module.exports = insertionSort -------------------------------------------------------------------------------- /04/mst/utils.js: -------------------------------------------------------------------------------- 1 | function createGraph(path) { 2 | const EdgeWeightedGraph = require('./edge-weighted-graph') 3 | const fs = require('fs') 4 | let data = fs.readFileSync(path, 'utf-8') 5 | data = data.split('\r\n') 6 | const V = data[0] 7 | const E = data[1] 8 | const vData = [] 9 | for (let i = 2, len = data.length; i < len; i++) { 10 | vData.push(...data[i].split(' ')) 11 | } 12 | 13 | return new EdgeWeightedGraph(V, E, vData) 14 | } 15 | 16 | module.exports = { 17 | createGraph 18 | } -------------------------------------------------------------------------------- /04/spt/utils.js: -------------------------------------------------------------------------------- 1 | function createGraph(path) { 2 | const EdgeWeightedDigraph = require('./edge-weighted-digraph') 3 | const fs = require('fs') 4 | let data = fs.readFileSync(path, 'utf-8') 5 | data = data.split('\r\n') 6 | const V = data[0] 7 | const E = data[1] 8 | const vData = [] 9 | for (let i = 2, len = data.length; i < len; i++) { 10 | vData.push(...data[i].split(' ')) 11 | } 12 | 13 | return new EdgeWeightedDigraph(V, E, vData) 14 | } 15 | 16 | module.exports = { 17 | createGraph 18 | } -------------------------------------------------------------------------------- /02/priority-queue/test.js: -------------------------------------------------------------------------------- 1 | const MaxPQ = require('./priority-queue') 2 | const assert = require('assert') 3 | const pq = new MaxPQ([10, 1, 12, 100, 5, 7, 3, 99, 8, 2, 4, 201]) 4 | 5 | assert.strictEqual(201, pq.getVal()) 6 | assert.strictEqual(100, pq.getVal()) 7 | assert.strictEqual(99, pq.getVal()) 8 | pq.insert(999) 9 | pq.insert(8888) 10 | assert.strictEqual(8888, pq.getVal()) 11 | assert.strictEqual(999, pq.getVal()) 12 | pq.insert(1000) 13 | pq.insert(10000) 14 | assert.strictEqual(10000, pq.getVal()) 15 | assert.strictEqual(1000, pq.getVal()) 16 | console.log('测试通过') -------------------------------------------------------------------------------- /04/spt/test/test-edge-weighted-digraph.js: -------------------------------------------------------------------------------- 1 | const { createGraph } = require('../utils') 2 | const DijkstraSP = require('../dijkstra-sp') 3 | const graph = createGraph('../data/tinyEWD.txt') 4 | const s = 0 5 | const sp = new DijkstraSP(graph, s) 6 | 7 | let str = '' 8 | for (let t = 0, V = graph.getV(); t < V; t++) { 9 | str += s + ' to ' + t + ' (' + sp.getDistTo(t) + '): ' 10 | if (sp.hasPathTo(t)) { 11 | sp.getPathTo(t).forEach(edge => { 12 | str += edge.toString() + ' ' 13 | }) 14 | } 15 | 16 | str += '\n' 17 | } 18 | 19 | console.log(str) -------------------------------------------------------------------------------- /02/bubble-sort/bubble-sort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arry 3 | * @return {void} 4 | */ 5 | function bubbleSort(arry) { 6 | let len = arry.length - 1 7 | for (let i = 0; i < len; i++) { 8 | // 当一轮排序中没有发生交换时 即可判断数组已经有序 9 | let isChange = false 10 | // len - i 可以排除上一轮最大的数 不用再对比 减少对比次数 11 | for (let j = 0, l = len - i; j < l; j++) { 12 | if (arry[j] > arry[j + 1]) { 13 | const temp = arry[j] 14 | arry[j] = arry[j + 1] 15 | arry[j + 1] = temp 16 | isChange = true 17 | } 18 | } 19 | 20 | if (!isChange) break 21 | } 22 | } 23 | 24 | module.exports = bubbleSort -------------------------------------------------------------------------------- /03/README.md: -------------------------------------------------------------------------------- 1 | # 查找 2 | ## 符号表 3 | 符号表可以用链表或数组实现。基于链表的实现是非常低效的,顺序查找平均比较次数为 (n+1)/2 ~ n/2。 4 | 5 | ![](../imgs/3-1.png) 6 | 7 | ![](../imgs/3-2.png) 8 | 9 | ## 数据结构 10 | * [二叉查找树](https://github.com/woai3c/Algorithm/tree/master/03/binary-search-tree) 11 | * [红黑树](https://github.com/woai3c/Algorithm/tree/master/03/red-black-tree) 12 | * [散列表](https://github.com/woai3c/Algorithm/tree/master/03/hash-table) 13 | 14 | |算法(数据结构)|查找|插入|是否支持有序性相关操作| 15 | |-|-|-|-| 16 | |顺序查询|N|N|否| 17 | |二分查找|log N|N|是| 18 | |二叉查找树|N|N|是| 19 | |红黑树|2log N|2log N|是| 20 | |拉链法|< log N| < log N|| 21 | |线性探测法|clog N|clog N|| -------------------------------------------------------------------------------- /04/undirected-graph/depth-first-search.js: -------------------------------------------------------------------------------- 1 | function DepthFirstSearch(graph, s) { 2 | this.marked = [] 3 | this.count = 0 4 | this.dfs(graph, s) 5 | } 6 | 7 | DepthFirstSearch.prototype = { 8 | dfs(graph, v) { 9 | this.marked[v] = true 10 | this.count++ 11 | graph.getAdj(v).forEach(w => { 12 | if (!this.marked[w]) this.dfs(graph, w) 13 | }) 14 | }, 15 | 16 | getCount() { 17 | return this.count 18 | }, 19 | 20 | isMarked(v) { 21 | return this.marked[v] == true 22 | } 23 | } 24 | 25 | module.exports = DepthFirstSearch -------------------------------------------------------------------------------- /03/hash-table/test.js: -------------------------------------------------------------------------------- 1 | const HashTable = require('./hash-table') 2 | const assert = require('assert') 3 | 4 | const data = [] 5 | for (let i = 65; i <= 122; i++) { 6 | data.push({ 7 | key: String.fromCharCode(i), 8 | val: i 9 | }) 10 | } 11 | 12 | const hash = new HashTable(data) 13 | assert.strictEqual(58, hash.getSize()) 14 | hash.set('xx', 500) 15 | assert.strictEqual(59, hash.getSize()) 16 | assert.strictEqual(500, hash.get('xx')) 17 | hash.set('xx', 400) 18 | assert.strictEqual(59, hash.getSize()) 19 | assert.strictEqual(400, hash.get('xx')) 20 | hash.delete('xx') 21 | assert.strictEqual(58, hash.getSize()) 22 | console.log('测试通过') -------------------------------------------------------------------------------- /04/undirected-graph/test/test-cc.js: -------------------------------------------------------------------------------- 1 | const { createGraph } = require('../utils') 2 | const CC = require('../cc') 3 | const graph = createGraph('../data/data.txt') 4 | 5 | const cc = new CC(graph) 6 | const counts = cc.getCount() 7 | const components = {} 8 | let str = counts + ' components\n' 9 | for (let i = 1; i <= counts; i++) { 10 | components[i] = [] 11 | } 12 | 13 | for (let v = 0, V = graph.getV(); v < V; v++) { 14 | const component = components[cc.getID(v)] 15 | if (component) component.push(v) 16 | } 17 | 18 | 19 | for (const key in components) { 20 | str += components[key] + '\n' 21 | } 22 | 23 | console.log(graph.toString() + '\n') 24 | console.log(str) -------------------------------------------------------------------------------- /04/mst/edge.js: -------------------------------------------------------------------------------- 1 | function Edge(v, w, weight) { 2 | this.v = v 3 | this.w = w 4 | this.weight = weight 5 | } 6 | 7 | Edge.prototype = { 8 | getWeight() { 9 | return this.weight 10 | }, 11 | 12 | getEither() { 13 | return this.v 14 | }, 15 | 16 | getOther(v) { 17 | if (v == this.v) return this.w 18 | return this.v 19 | }, 20 | 21 | compareTo(that) { 22 | if (this.weight < that.weight) return -1 23 | if (this.weight > that.weight) return 1 24 | return 0 25 | }, 26 | 27 | toString() { 28 | return `${this.v}-${this.w} ${this.weight}` 29 | } 30 | } 31 | 32 | module.exports = Edge -------------------------------------------------------------------------------- /05/string-sort/lsd.js: -------------------------------------------------------------------------------- 1 | function lsd(arry, w) { 2 | const len = arry.length 3 | const aux = [] 4 | const R = 256 5 | 6 | for (let d = w - 1; d >= 0; d--) { 7 | const count = new Array(R + 1).fill(0) 8 | for (let i = 0; i < len; i++) { 9 | count[arry[i][d].charCodeAt() + 1]++ 10 | } 11 | 12 | for (let r = 0; r < R; r++) { 13 | count[r + 1] += count[r] 14 | } 15 | 16 | for (let i = 0; i < len; i++) { 17 | aux[count[arry[i][d].charCodeAt()]++] = arry[i] 18 | } 19 | 20 | for (let i = 0; i < len; i++) { 21 | arry[i] = aux[i] 22 | } 23 | } 24 | } 25 | 26 | module.exports = lsd -------------------------------------------------------------------------------- /04/directed-graph/topological.js: -------------------------------------------------------------------------------- 1 | const DirectedCycle = require('./directed-cycle') 2 | const DepthFirstOrder = require('./depth-first-order') 3 | 4 | function Topological(graph) { 5 | this.order = null 6 | this.init(graph) 7 | } 8 | 9 | Topological.prototype = { 10 | init(graph) { 11 | const cycle = new DirectedCycle(graph) 12 | if (!cycle.hasCycle()) { 13 | const dfs = new DepthFirstOrder(graph) 14 | this.order = dfs.getReversePost() 15 | } 16 | }, 17 | 18 | getOrder() { 19 | return this.order 20 | }, 21 | 22 | // 是有向无环图吗 23 | isDAG() { 24 | return this.order !== null 25 | } 26 | } 27 | 28 | module.exports = Topological -------------------------------------------------------------------------------- /04/directed-graph/directed-dfs.js: -------------------------------------------------------------------------------- 1 | function DirectedDFS(graph, data) { 2 | this.marked = [] 3 | this.init(graph, data) 4 | } 5 | 6 | DirectedDFS.prototype = { 7 | init(graph, data) { 8 | if (Array.isArray(data)) { 9 | data.forEach(v => { 10 | this.dfs(graph, v) 11 | }) 12 | } else { 13 | this.dfs(graph, data) 14 | } 15 | }, 16 | 17 | dfs(graph, v) { 18 | this.marked[v] = true 19 | graph.getAdj(v).forEach(w => { 20 | if (!this.marked[w]) this.dfs(graph, w) 21 | }) 22 | }, 23 | 24 | isMarked(v) { 25 | return this.marked[v] == true 26 | } 27 | } 28 | 29 | module.exports = DirectedDFS -------------------------------------------------------------------------------- /04/undirected-graph/test/test-dfs-path.js: -------------------------------------------------------------------------------- 1 | const { createGraph } = require('../utils') 2 | const DepthFirstPath = require('../depth-first-path') 3 | const graph = createGraph('../data/tiny.txt') 4 | const s = 0 5 | const search = new DepthFirstPath(graph, s) 6 | let str = '' 7 | for (let v = s, V = graph.getV(); v < V; v++) { 8 | str += s + ' to ' + v + ': ' 9 | if (search.hasPathTo(v)) { 10 | const paths = search.pathTo(v) 11 | for (let i = paths.length - 1; i >= 0; i--) { 12 | const w = paths[i] 13 | if (w != s) { 14 | str += '-' + w 15 | } else { 16 | str += w 17 | } 18 | } 19 | 20 | str += '\n' 21 | } 22 | } 23 | 24 | console.log(str) -------------------------------------------------------------------------------- /04/undirected-graph/test/test-bfs-path.js: -------------------------------------------------------------------------------- 1 | const { createGraph } = require('../utils') 2 | const BreadthFirstPath = require('../breadth-first-path') 3 | const graph = createGraph('../data/tiny.txt') 4 | const s = 0 5 | const search = new BreadthFirstPath(graph, s) 6 | let str = '' 7 | for (let v = s, V = graph.getV(); v < V; v++) { 8 | str += s + ' to ' + v + ': ' 9 | if (search.hasPathTo(v)) { 10 | const paths = search.pathTo(v) 11 | for (let i = paths.length - 1; i >= 0; i--) { 12 | const w = paths[i] 13 | if (w != s) { 14 | str += '-' + w 15 | } else { 16 | str += w 17 | } 18 | } 19 | 20 | str += '\n' 21 | } 22 | } 23 | 24 | console.log(str) -------------------------------------------------------------------------------- /04/directed-graph/test/test-kosaraju-scc.js: -------------------------------------------------------------------------------- 1 | const { createDigraph } = require('../utils') 2 | const KosarajuSCC = require('../kosaraju-SCC') 3 | const graph = createDigraph('../data/tinyDG.txt') 4 | 5 | const scc = new KosarajuSCC(graph) 6 | const counts = scc.getCount() 7 | const components = {} 8 | let str = counts + ' components\n' 9 | for (let i = 1; i <= counts; i++) { 10 | components[i] = [] 11 | } 12 | 13 | for (let v = 0, V = graph.getV(); v < V; v++) { 14 | const component = components[scc.getID(v)] 15 | if (component) component.push(v) 16 | } 17 | 18 | 19 | for (const key in components) { 20 | str += components[key] + '\n' 21 | } 22 | 23 | console.log(graph.toString() + '\n') 24 | console.log(graph.reverse().toString() + '\n') 25 | console.log(str) -------------------------------------------------------------------------------- /03/binary-search-tree/test.js: -------------------------------------------------------------------------------- 1 | const BinarySearchTree = require('./binary-search-tree') 2 | const assert = require('assert') 3 | const tree = new BinarySearchTree([6,7,8,1,2,5,15,10]) 4 | 5 | assert.strictEqual(8, tree.getSize()) 6 | assert.strictEqual('1,2,5,6,7,8,10,15', tree.sort().toString()) 7 | assert.strictEqual(15, tree.findMax().val) 8 | assert.strictEqual(1, tree.findMin().val) 9 | 10 | tree.delete(6) 11 | tree.delete(7) 12 | assert.strictEqual(6, tree.getSize()) 13 | assert.strictEqual('1,2,5,8,10,15', tree.sort().toString()) 14 | assert.strictEqual(10, tree.find(10).val) 15 | 16 | tree.insert(200) 17 | assert.strictEqual(200, tree.findMax().val) 18 | assert.strictEqual('1,2,5,8,10,15,200', tree.sort().toString()) 19 | assert.strictEqual(7, tree.getSize()) 20 | console.log('测试通过') -------------------------------------------------------------------------------- /04/directed-graph/test/test-dfs-path.js: -------------------------------------------------------------------------------- 1 | const { createDigraph } = require('../utils') 2 | const DepthFirstDirectedPath = require('../depth-first-directed-path') 3 | const graph = createDigraph('../data/tinyDG.txt') 4 | const s = 0 5 | const search = new DepthFirstDirectedPath(graph, s) 6 | let str = '' 7 | for (let v = s, V = graph.getV(); v < V; v++) { 8 | if (search.hasPathTo(v)) { 9 | str += s + ' to ' + v + ': ' 10 | const paths = search.pathTo(v) 11 | for (let i = paths.length - 1; i >= 0; i--) { 12 | const w = paths[i] 13 | if (w != s) { 14 | str += '-' + w 15 | } else { 16 | str += w 17 | } 18 | } 19 | 20 | str += '\n' 21 | } 22 | } 23 | 24 | console.log(str) -------------------------------------------------------------------------------- /04/directed-graph/test/test-bfs-path.js: -------------------------------------------------------------------------------- 1 | const { createDigraph } = require('../utils') 2 | const BreadthFirstDirectedPath = require('../breadth-first-directed-path') 3 | const graph = createDigraph('../data/tinyDG.txt') 4 | const s = 0 5 | const search = new BreadthFirstDirectedPath(graph, s) 6 | let str = '' 7 | for (let v = s, V = graph.getV(); v < V; v++) { 8 | if (search.hasPathTo(v)) { 9 | str += s + ' to ' + v + ': ' 10 | const paths = search.pathTo(v) 11 | for (let i = paths.length - 1; i >= 0; i--) { 12 | const w = paths[i] 13 | if (w != s) { 14 | str += '-' + w 15 | } else { 16 | str += w 17 | } 18 | } 19 | 20 | str += '\n' 21 | } 22 | } 23 | 24 | console.log(str) -------------------------------------------------------------------------------- /02/quick-sort/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **快速排序**(英语:Quicksort),又称划分交换排序(partition-exchange sort),简称快排,一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序 n 个项目要 O(nlog n) 次比较。在最坏状况下则需要 O(n^{2}) 次比较,但这种状况并不常见。事实上,快速排序 (nlog n) 通常明显比其他算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地达成。 3 | 4 | 快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。 5 | 6 | ![](../../imgs/2-6.png) 7 | 8 | 步骤为: 9 | 10 | 1. 挑选基准值:从数列中挑出一个元素,称为“基准”(pivot), 11 | 2. 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成, 12 | 3. 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。 13 | 14 | 递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。 15 | 16 | 选取基准值有数种具体方法,此选取方法对排序的时间性能有决定性影响。 17 | 18 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F) 19 | 20 | ![](../../imgs/2-7.png) 21 | 22 | ## 测试 23 | ```js 24 | node test 25 | ``` 26 | -------------------------------------------------------------------------------- /05/string-search/test/test-triest.js: -------------------------------------------------------------------------------- 1 | const TrieST = require('../triest') 2 | const st = new TrieST() 3 | const data = [ 4 | { 5 | abc: 1 6 | }, 7 | { 8 | ab: 2 9 | }, 10 | { 11 | abd: 3 12 | }, 13 | { 14 | good: 'j' 15 | } 16 | ] 17 | 18 | for (let i = 0, len = data.length; i < len; i++) { 19 | const obj = data[i] 20 | for (let key in obj) st.set(key, obj[key]) 21 | } 22 | 23 | console.log(st.keys()) 24 | console.log(st.keysWithPrefix('ab')) 25 | console.log(st.keysThatMatch('ab.')) 26 | console.log(st.longestPrefixOf('abcdefg')) 27 | console.log(st.size()) 28 | console.log(st.contains('ab')) 29 | console.log(st.get('ab')) 30 | console.log(st.delete('ab')) 31 | console.log(st.get('ab')) 32 | console.log(st.contains('ab')) 33 | console.log(st.keys()) -------------------------------------------------------------------------------- /04/undirected-graph/test/test-dfs-search.js: -------------------------------------------------------------------------------- 1 | const { createGraph } = require('../utils') 2 | const DepthFirstSearch = require('../depth-first-search') 3 | const graph = createGraph('../data/data.txt') 4 | 5 | // first test 6 | let search = new DepthFirstSearch(graph, 0) 7 | let str = '' 8 | for (let v = 0, V = graph.getV(); v < V; v++) { 9 | if (search.isMarked(v)) str += v + ' ' 10 | } 11 | 12 | str += '\n' 13 | if (search.getCount() != graph.getV()) str += 'NOT ' 14 | str += 'connected' 15 | console.log(str) 16 | 17 | // second test 18 | search = new DepthFirstSearch(graph, 7) 19 | str = '' 20 | for (let v = 0, V = graph.getV(); v < V; v++) { 21 | if (search.isMarked(v)) str += v + ' ' 22 | } 23 | 24 | str += '\n' 25 | if (search.getCount() != graph.getV()) str += 'NOT ' 26 | str += 'connected' 27 | console.log(str) 28 | -------------------------------------------------------------------------------- /02/quick-sort/quick3way.js: -------------------------------------------------------------------------------- 1 | const { exchange } = require('../../utils') 2 | 3 | /** 4 | * @param {number[]} arry 5 | * @return {void} 6 | */ 7 | function quick3Way(arry) { 8 | sort(arry, 0, arry.length - 1) 9 | } 10 | 11 | /** 12 | * @param {number[]} arry 13 | * @param {number} start 14 | * @param {number} end 15 | * @return {void} 16 | */ 17 | function sort(arry, start, end) { 18 | if (start >= end) return 19 | let lt = start, i = start + 1, gt = end 20 | const val = arry[start] 21 | while (i <= gt) { 22 | if (arry[i] < val) { 23 | exchange(arry, lt++, i++) 24 | } else if (arry[i] > val) { 25 | exchange(arry, i, gt--) 26 | } else { 27 | i++ 28 | } 29 | } 30 | 31 | sort(arry, start, lt - 1) 32 | sort(arry, gt + 1, end) 33 | } 34 | 35 | module.exports = quick3Way -------------------------------------------------------------------------------- /03/binary-search-tree/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **二叉查找树**(英语:Binary Search Tree),也称为二叉搜索树、有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树: 3 | 4 | 1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值; 5 | 2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值; 6 | 3. 任意节点的左、右子树也分别为二叉查找树; 7 | 4. 没有键值相等的节点。 8 | 9 | 二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为 O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、多重集、关联数组等。 10 | 11 | 二叉查找树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉查找树的存储结构。中序遍历二叉查找树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉查找树变成一个有序序列,构造树的过程即为对无序序列进行查找的过程。每次插入的新的节点都是二叉查找树上新的叶子节点,在进行插入操作时,不必移动其它节点,只需改动某个节点的指针,由空变为非空即可。搜索、插入、删除的复杂度等于树高,期望 O(log n),最坏 O(n)(数列有序,树退化成线性表)。 12 | 13 | 虽然二叉查找树的最坏效率是 O(n),但它支持动态查询,且有很多改进版的二叉查找树可以使树高为 O(log n),从而将最坏效率降至 O(log n),如AVL树、红黑树等。 14 | 15 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%85%83%E6%90%9C%E5%B0%8B%E6%A8%B9) 16 | ## 测试 17 | ```js 18 | node test 19 | ``` 20 | -------------------------------------------------------------------------------- /02/find-index/find-index.js: -------------------------------------------------------------------------------- 1 | const { exchange } = require('../../utils') 2 | 3 | // 寻找第 k 小的元素 4 | function findIndex(arr, k) { 5 | sort(arr, 0, arr.length - 1, k) 6 | return arr[k] 7 | } 8 | 9 | function sort(arr, start, end, k) { 10 | if (start >= end) return 11 | const segmentIndex = partition(arr, start, end) 12 | if (segmentIndex == k) return 13 | if (segmentIndex > k) return sort(arr, start, segmentIndex - 1, k) 14 | return sort(arr, segmentIndex + 1, end, k) 15 | } 16 | 17 | function partition(arr, start, end) { 18 | const val = arr[start] 19 | let i = start, j = end + 1 20 | while (true) { 21 | while (arr[++i] < val && i < end) {} 22 | 23 | while (arr[--j] > val && j > start) {} 24 | 25 | if (i >= j) break 26 | exchange(arr, i, j) 27 | } 28 | 29 | exchange(arr, start, j) 30 | return j 31 | } 32 | 33 | module.exports = findIndex -------------------------------------------------------------------------------- /01/binary-search/test.js: -------------------------------------------------------------------------------- 1 | const binarySearch = require('./binary-search') 2 | const { generateRandomIntegerArry, loopSearchFindVal } = require('../../utils') 3 | 4 | valLimit = ~~process.argv[2] 5 | valNum = ~~process.argv[3] 6 | if (!valLimit || !valNum) { 7 | console.log('请按照命令格式运行测试:node test 生成值的上限 生成值的个数') 8 | return 9 | } 10 | 11 | function test(valLimit, valNum) { 12 | const arry1 = generateRandomIntegerArry(valLimit, valNum).sort((a, b) => a - b) 13 | const arry2 = generateRandomIntegerArry(valLimit, valNum) 14 | 15 | console.time(`二分查找耗时(查找${valNum}次)`) 16 | arry2.forEach(val => { 17 | binarySearch(arry1, val) 18 | }) 19 | console.timeEnd(`二分查找耗时(查找${valNum}次)`) 20 | 21 | console.time(`循环查找耗时(查找${valNum}次)`) 22 | arry2.forEach(val => { 23 | loopSearchFindVal(arry1, val) 24 | }) 25 | console.timeEnd(`循环查找耗时(查找${valNum}次)`) 26 | } 27 | 28 | test(valLimit, valNum) -------------------------------------------------------------------------------- /05/string-sort/quick3string.js: -------------------------------------------------------------------------------- 1 | const { exchange } = require('../../utils') 2 | function quick3string(arry) { 3 | sort(arry, 0, arry.length - 1, 0) 4 | } 5 | 6 | 7 | function sort(arry, start, end, d) { 8 | if (end <= start) return 9 | let lt = start, gt = end, i 10 | const v = charAt(arry[start], d) 11 | i = start + 1 12 | 13 | while (i <= gt) { 14 | const t = charAt(arry[i], d) 15 | if (t < v) { 16 | exchange(arry, lt++, i++) 17 | } else if (t > v) { 18 | exchange(arry, i, gt--) 19 | } else { 20 | i++ 21 | } 22 | } 23 | 24 | sort(arry, start, lt - 1, d) 25 | // v < 0 代表已经到了字符串末尾 26 | if (v >= 0) sort(arry, lt, gt, d + 1) 27 | sort(arry, gt + 1, end, d) 28 | } 29 | 30 | function charAt(s, d) { 31 | return d < s.length? s[d].charCodeAt() : -1 32 | } 33 | 34 | module.exports = quick3string -------------------------------------------------------------------------------- /04/undirected-graph/depth-first-path.js: -------------------------------------------------------------------------------- 1 | function DepthFirstPath(graph, s) { 2 | this.marked = [] 3 | this.edgeTo = [] 4 | this.s = s 5 | this.dfs(graph, s) 6 | } 7 | 8 | DepthFirstPath.prototype = { 9 | dfs(graph, v) { 10 | this.marked[v] = true 11 | graph.getAdj(v).forEach(w => { 12 | if (!this.marked[w]) { 13 | this.edgeTo[w] = v 14 | this.dfs(graph, w) 15 | } 16 | }) 17 | }, 18 | 19 | pathTo(v) { 20 | if (!this.hasPathTo(v)) return null 21 | const s = this.s 22 | const paths = [] 23 | for (let w = v; w != s; w = this.edgeTo[w]) { 24 | paths.push(w) 25 | } 26 | 27 | paths.push(s) 28 | return paths 29 | }, 30 | 31 | hasPathTo(v) { 32 | return this.marked[v] == true 33 | }, 34 | } 35 | 36 | module.exports = DepthFirstPath -------------------------------------------------------------------------------- /05/string-sort/msd.js: -------------------------------------------------------------------------------- 1 | const aux = [] 2 | const R = 256 3 | 4 | function msd(arry) { 5 | sort(arry, 0, arry.length - 1, 0) 6 | } 7 | 8 | 9 | function sort(arry, start, end, d) { 10 | if (end <= start) return 11 | const count = new Array(R + 2).fill(0) 12 | 13 | for (let i = start; i <= end; i++) { 14 | count[charAt(arry[i], d) + 2]++ 15 | } 16 | 17 | for (let r = 0; r < R + 1; r++) { 18 | count[r + 1] += count[r] 19 | } 20 | 21 | for (let i = start; i <= end; i++) { 22 | aux[count[charAt(arry[i], d) + 1]++] = arry[i] 23 | } 24 | 25 | for (let i = start; i <= end; i++) { 26 | arry[i] = aux[i - start] 27 | } 28 | 29 | for (let r = 0; r < R; r++) { 30 | sort(arry, start + count[r], start + count[r + 1] - 1, d + 1) 31 | } 32 | } 33 | 34 | function charAt(s, d) { 35 | return d < s.length? s[d].charCodeAt() : -1 36 | } 37 | 38 | module.exports = msd -------------------------------------------------------------------------------- /03/red-black-tree/test.js: -------------------------------------------------------------------------------- 1 | const RedBlackTree = require('./red-black-tree') 2 | const assert = require('assert') 3 | const tree = new RedBlackTree([6,7,8,1,2,5,15,10]) 4 | 5 | assert.strictEqual(8, tree.getSize()) 6 | assert.strictEqual('1,2,5,6,7,8,10,15', tree.sort().toString()) 7 | assert.strictEqual(15, tree.findMax().val) 8 | assert.strictEqual(1, tree.findMin().val) 9 | assert.strictEqual(true, tree.isBalance()) 10 | 11 | tree.delete(6) 12 | assert.strictEqual(true, tree.isBalance()) 13 | tree.delete(7) 14 | assert.strictEqual(true, tree.isBalance()) 15 | assert.strictEqual(6, tree.getSize()) 16 | assert.strictEqual('1,2,5,8,10,15', tree.sort().toString()) 17 | assert.strictEqual(10, tree.find(10).val) 18 | 19 | tree.insert(200) 20 | assert.strictEqual(true, tree.isBalance()) 21 | assert.strictEqual(200, tree.findMax().val) 22 | assert.strictEqual('1,2,5,8,10,15,200', tree.sort().toString()) 23 | assert.strictEqual(7, tree.getSize()) 24 | console.log('测试通过') -------------------------------------------------------------------------------- /04/directed-graph/depth-first-directed-path.js: -------------------------------------------------------------------------------- 1 | // 和无向图一样 2 | function DepthFirstDirectedPath(graph, s) { 3 | this.marked = [] 4 | this.edgeTo = [] 5 | this.s = s 6 | this.dfs(graph, s) 7 | } 8 | 9 | DepthFirstDirectedPath.prototype = { 10 | dfs(graph, v) { 11 | this.marked[v] = true 12 | graph.getAdj(v).forEach(w => { 13 | if (!this.marked[w]) { 14 | this.edgeTo[w] = v 15 | this.dfs(graph, w) 16 | } 17 | }) 18 | }, 19 | 20 | pathTo(v) { 21 | if (!this.hasPathTo(v)) return null 22 | const s = this.s 23 | const paths = [] 24 | for (let w = v; w != s; w = this.edgeTo[w]) { 25 | paths.push(w) 26 | } 27 | 28 | paths.push(s) 29 | return paths 30 | }, 31 | 32 | hasPathTo(v) { 33 | return this.marked[v] == true 34 | }, 35 | } 36 | 37 | module.exports = DepthFirstDirectedPath -------------------------------------------------------------------------------- /02/bubble-sort/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | **冒泡排序**(英语:Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 3 | 4 | 冒泡排序对 n 个项目需要O(n^2)的比较次数,且可以原地排序。尽管这个算法是最简单了解和实现的排序算法之一,但它对于包含大量的元素的数列排序是很没有效率的。 5 | 6 | 冒泡排序是与插入排序拥有相等的运行时间,但是两种算法在需要的交换次数却很大地不同。在最坏的情况,冒泡排序需要 O(n^2)次交换,而插入排序只要最多 O(n) 交换。冒泡排序的实现(类似下面)通常会对已经排序好的数列拙劣地运行( O(n^2)),而插入排序在这个例子只需要 O(n)个运算。因此很多现代的算法教科书避免使用冒泡排序,而用插入排序取代之。冒泡排序如果能在内部循环第一次运行时,使用一个旗标来表示有无需要交换的可能,也可以把最优情况下的复杂度降低到 O(n)。在这个情况,已经排序好的数列就无交换的需要。若在每次走访数列时,把走访顺序反过来,也可以稍微地改进效率。有时候称为鸡尾酒排序,因为算法会从数列的一端到另一端之间穿梭往返。 7 | 8 | 冒泡排序算法的运作如下: 9 | 10 | 1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 11 | 2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 12 | 3. 针对所有的元素重复以上的步骤,除了最后一个。 13 | 4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 14 | 15 | 由于它的简洁,冒泡排序通常被用来对于程序设计入门的学生介绍算法的概念。 16 | 17 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F) 18 | 19 | ## 测试 20 | ```js 21 | node test 22 | ``` -------------------------------------------------------------------------------- /04/undirected-graph/cc.js: -------------------------------------------------------------------------------- 1 | function CC(graph) { 2 | this.marked = [] 3 | this.id = [] 4 | this.count = 0 5 | this.init(graph) 6 | } 7 | 8 | CC.prototype = { 9 | init(graph) { 10 | for (let v = 0, V = graph.getV(); v < V; v++) { 11 | if (!this.marked[v]) { 12 | this.count++ 13 | this.dfs(graph, v) 14 | } 15 | } 16 | }, 17 | 18 | dfs(graph, v) { 19 | this.marked[v] = true 20 | this.id[v] = this.count 21 | graph.getAdj(v).forEach(w => { 22 | if (!this.marked[w]) this.dfs(graph, w) 23 | }) 24 | }, 25 | 26 | getCount() { 27 | return this.count 28 | }, 29 | 30 | getID(v) { 31 | return this.id[v] 32 | }, 33 | 34 | isConnected(v, w) { 35 | if (this.id[v] && this.id[w]) { 36 | return this.id[v] == this.id[w] 37 | } 38 | 39 | return false 40 | } 41 | } 42 | 43 | module.exports = CC -------------------------------------------------------------------------------- /02/heap-sort/heap-sort.js: -------------------------------------------------------------------------------- 1 | const { exchange } = require('../../utils') 2 | 3 | /** 4 | * @param {number[]} arry 5 | * @return {void} 6 | */ 7 | function heapSort(arry) { 8 | const len = arry.length 9 | let maxIndex = len - 1 10 | for (let i = Math.floor(len / 2 - 1); i >= 0; i--) { 11 | sink(arry, i, maxIndex) 12 | } 13 | 14 | while (maxIndex) { 15 | exchange(arry, 0, maxIndex--) 16 | sink(arry, 0, maxIndex) 17 | } 18 | } 19 | 20 | /** 21 | * @param {number[]} arry 22 | * @param {number} i 23 | * @param {number} maxIndex 24 | * @return {void} 25 | */ 26 | function sink(arry, i, maxIndex) { 27 | while ((2 * i + 1) <= maxIndex) { 28 | let j = 2 * i + 1 29 | if (j < maxIndex && arry[j] < arry[j + 1]) { 30 | j++ 31 | } 32 | 33 | if (arry[i] >= arry[j]) { 34 | break 35 | } 36 | 37 | exchange(arry, i, j) 38 | i = j 39 | } 40 | } 41 | 42 | 43 | module.exports = heapSort -------------------------------------------------------------------------------- /04/directed-graph/depth-first-order.js: -------------------------------------------------------------------------------- 1 | function DepthFirstOrder(graph) { 2 | this.pre = [] 3 | this.post = [] 4 | this.reversePost = [] 5 | this.marked = [] 6 | this.init(graph) 7 | } 8 | 9 | DepthFirstOrder.prototype = { 10 | init(graph) { 11 | for (let v = 0, V = graph.getV(); v < V; v++) { 12 | if (!this.marked[v]) this.dfs(graph, v) 13 | } 14 | }, 15 | 16 | dfs(graph, v) { 17 | this.marked[v] = true 18 | this.pre.push(v) 19 | graph.getAdj(v).forEach(w => { 20 | if (!this.marked[w]) { 21 | this.dfs(graph, w) 22 | } 23 | }) 24 | 25 | this.post.push(v) 26 | this.reversePost.unshift(v) 27 | }, 28 | 29 | getPre() { 30 | return this.pre 31 | }, 32 | 33 | getPost() { 34 | return this.post 35 | }, 36 | 37 | getReversePost() { 38 | return this.reversePost 39 | }, 40 | } 41 | 42 | module.exports = DepthFirstOrder -------------------------------------------------------------------------------- /05/README.md: -------------------------------------------------------------------------------- 1 | ### 各种字符串排序算法的性能特点 2 | |算法|是否稳定|原地排序|运行时间|额外空间|优势领域| 3 | |-|-|-|-|-|-| 4 | |字符串的插入排序|是|是|N到N*N之间|1|小数组或是已经有序的数组| 5 | |快速排序|否|是|NlgN|lgN|通用排序算法,特别适合于空间不足情况| 6 | |归并排序|是|否|NlgN|N|稳定的通用排序算法| 7 | |三向快速排序|否|是|N到NlgN之间|lgN|大量重复键| 8 | |低位优先的字符串排序|是|否|NW|N|较短的定长字符串| 9 | |高位优先的字符串排序|是|否|N到NW之间|N+WR|随机字符串| 10 | |三向字符串快速排序|否|是|N到NW之间|W+lgN|通用排序算法,特别适合用于含有较长公共前缀的字符串| 11 | 12 | ### 各种字符串查找算法的性能特点 13 | |算法|未命中查找检查的字符数量|内存使用|优点| 14 | |-|-|-|-| 15 | |二叉查找树(BST)|c(lgN)**2|64N|适用于随机排列的键| 16 | |2-3树查找(红黑树)|c(lgN)**2|64N|有性能保证| 17 | |线性探测法(并行数组)|w|32N-128N|内置类型,缓存散列值| 18 | |字典树查找(R向单词查找树)|lgN|(8R+56)N-(8R+56)Nw|适用于较短的键和较小的字母表| 19 | |字典树查找(三向单词查找树)|1.39lgN|64N-64Nw|适用于非随机的键| 20 | 21 | ### 各种字符串查找算法的实现的成本总结 22 | |算法|版本|最坏情况|一般情况|在文本中回退|正确性|额外的空间需求| 23 | |-|-|-|-|-|-|-| 24 | |暴力算法|-|MN|1.1N|是|是|1| 25 | |KMP|完整的DFA|2N|1.1N|否|是|MR| 26 | |KMP|仅构造不匹配的状态转换|3N|1.1N|否|是|M| 27 | |KMP|完整版本|3N|N/M|是|是|R| 28 | |Boyer-Moore算法|启发式的查找不匹配的字符|MN|N/M|是|是|R| 29 | |Rabin-Karp算法|蒙特卡洛|7N|7N|否|是|1| 30 | |Rabin-Karp算法|拉斯维加斯|7N|7N|是|是|1| 31 | -------------------------------------------------------------------------------- /05/sub-str-search/boyer-moore.js: -------------------------------------------------------------------------------- 1 | function BoyerMoore(pat) { 2 | this.pat = pat 3 | this.R = 256 4 | this.right = new Array(this.R).fill(-1) 5 | this.init() 6 | } 7 | 8 | BoyerMoore.prototype = { 9 | init() { 10 | for (let i = 0, len = this.pat.length; i < len; i++) { 11 | this.right[this.pat.charCodeAt(i)] = i 12 | } 13 | }, 14 | 15 | search(txt) { 16 | const M = this.pat.length 17 | const N = txt.length - M 18 | let skip 19 | for (let i = 0; i <= N; i += skip) { 20 | skip = 0 21 | for (let j = M - 1; j >= 0; j--) { 22 | if (txt[i + j] != this.pat[j]) { 23 | skip = j - this.right[txt.charCodeAt(i + j)] 24 | if (skip < 0) skip = 1 25 | break 26 | } 27 | } 28 | 29 | if (skip == 0) return i 30 | } 31 | 32 | return -1 33 | } 34 | } 35 | 36 | module.exports = BoyerMoore -------------------------------------------------------------------------------- /02/find-index/test.js: -------------------------------------------------------------------------------- 1 | const findIndex = require('./find-index') 2 | const quickSort = require('../quick-sort/quick-sort') 3 | const assert = require('assert') 4 | 5 | assert.strictEqual(1, findIndex([6,5,4,6,3,1,2], 0)) 6 | assert.strictEqual(2, findIndex([6,5,4,6,3,1,2], 1)) 7 | assert.strictEqual(3, findIndex([6,5,4,6,3,1,2], 2)) 8 | assert.strictEqual(4, findIndex([6,5,4,6,3,1,2], 3)) 9 | assert.strictEqual(5, findIndex([6,5,4,6,3,1,2], 4)) 10 | assert.strictEqual(6, findIndex([6,5,4,6,3,1,2], 5)) 11 | assert.strictEqual(6, findIndex([6,5,4,6,3,1,2], 6)) 12 | 13 | const arr = [6,5,4,6,3,1,2,10,11,30,50,4,32,89,45,12636,778,112,5646556,6558,1,2,3,4,887,665,414,55,62,10,1,5,54,4,5,5,566,21,4,4,5,5,22] 14 | const arr1 = [...arr] 15 | const arr2 = [...arr] 16 | console.time('quicksort') 17 | quickSort(arr1) 18 | console.log(arr1[10]) 19 | console.timeEnd('quicksort') 20 | 21 | console.time('findIndex') 22 | console.log(findIndex(arr2, 10)) 23 | console.timeEnd('findIndex') 24 | // 很明显的可以看到,找到第 k 小的元素,findIndex 比排序后再找要快。 25 | console.log('测试通过') -------------------------------------------------------------------------------- /02/shell-sort/shell-sort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arry 3 | * @return {void} 4 | */ 5 | function shellSort(arry) { 6 | const len = arry.length 7 | const n = len / 3 8 | let step = 1 9 | // 以 step 为步长隔开数组,将每相隔一个 step 之间的数组项进行比较 10 | while (step < n) { 11 | step = step * 3 + 1 // 1, 4, 13, 40, 121, 364, 1093... 12 | } 13 | 14 | while (step >= 1) { 15 | // 将数组变为 step 有序 16 | for (let i = step; i < len; i++) { 17 | // 将 arry[i] 插入到 arry[i - step], arry[i - 2 * step], arry[i - 3 * step]... 中 18 | for (let j = i; j >= step; j -= step) { 19 | const tempIndex = j - step 20 | if (arry[j] < arry[tempIndex]) { 21 | const tempVal = arry[j] 22 | arry[j] = arry[tempIndex] 23 | arry[tempIndex] = tempVal 24 | } else { 25 | break 26 | } 27 | } 28 | } 29 | 30 | step = Math.floor(step / 3) 31 | } 32 | } 33 | 34 | module.exports = shellSort -------------------------------------------------------------------------------- /02/quick-sort/quick-sort.js: -------------------------------------------------------------------------------- 1 | const { exchange } = require('../../utils') 2 | 3 | /** 4 | * @param {number[]} arry 5 | * @return {void} 6 | */ 7 | function quickSort(arry) { 8 | sort(arry, 0, arry.length - 1) 9 | } 10 | 11 | /** 12 | * @param {number[]} arry 13 | * @param {number} start 14 | * @param {number} end 15 | * @return {void} 16 | */ 17 | function sort(arry, start, end) { 18 | if (start >= end) return 19 | const segmentIndex = partition(arry, start, end) 20 | sort(arry, start, segmentIndex - 1) 21 | sort(arry, segmentIndex + 1, end) 22 | } 23 | 24 | /** 25 | * @param {number[]} arry 26 | * @param {number} start 27 | * @param {number} end 28 | * @return {number} 29 | */ 30 | function partition(arry, start, end) { 31 | const val = arry[start] 32 | let i = start, j = end + 1 33 | while (true) { 34 | while (arry[++i] < val && i < end) {} 35 | 36 | while (arry[--j] > val && j > start) {} 37 | 38 | if (i >= j) break 39 | exchange(arry, i, j) 40 | } 41 | 42 | exchange(arry, start, j) 43 | return j 44 | } 45 | 46 | module.exports = quickSort -------------------------------------------------------------------------------- /04/undirected-graph/breadth-first-path.js: -------------------------------------------------------------------------------- 1 | function BreadthFirstPath(graph, s) { 2 | this.marked = [] 3 | this.edgeTo = [] 4 | this.queue = [] 5 | this.s = s 6 | this.bfs(graph, s) 7 | } 8 | 9 | BreadthFirstPath.prototype = { 10 | bfs(graph, v) { 11 | this.queue.push(v) 12 | this.marked[v] = true 13 | while (this.queue.length) { 14 | v = this.queue.shift() 15 | graph.getAdj(v).forEach(w => { 16 | if (!this.marked[w]) { 17 | this.edgeTo[w] = v 18 | this.marked[w] = true 19 | this.queue.push(w) 20 | } 21 | }) 22 | } 23 | }, 24 | 25 | pathTo(v) { 26 | if (!this.hasPathTo(v)) return null 27 | const s = this.s 28 | const paths = [] 29 | for (let w = v; w != s; w = this.edgeTo[w]) { 30 | paths.push(w) 31 | } 32 | 33 | paths.push(s) 34 | return paths 35 | }, 36 | 37 | hasPathTo(v) { 38 | return this.marked[v] == true 39 | }, 40 | } 41 | 42 | module.exports = BreadthFirstPath -------------------------------------------------------------------------------- /01/README.md: -------------------------------------------------------------------------------- 1 | ## 数据结构 2 | 数据结构与算法是紧密相连的,数据结构的性质将直接影响到算法的效率。 3 | ### 背包 4 | 背包是一种不支持删除元素的集合数据类型。 5 | ### 队列 6 | 先进先出队列是一种基于先进先出(FIFO)策略的集合类型。 7 | ### 栈 8 | 栈是一种基于后进先出(LIFO)策略的集合类型。 9 | ### 链表 10 | 链表是一种递归的数据结构,它指向一个 node 节点的引用,或者为 null。 11 | ## 算法 12 | 一个程序运行的总时间主要和两点相关: 13 | 1. 执行每条语句的耗时(取决于操作系统、计算机、编程语言) 14 | 2. 执行每条语句的频率(取决于程序本身和输入) 15 | 16 | 通过以上两点,可以算出程序的总运行时间。 17 | 18 | ![](../imgs/1-1.png) 19 | 20 | ![](../imgs/1-2.png) 21 | 22 | ![](../imgs/1-3.png) 23 | ### 二分查找 24 | 在计算机科学中,**二分搜索**(英语:binary search),也称**折半搜索**(英语:half-interval search)、**对数搜索**(英语:logarithmic search),是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。 25 | 26 | 二分搜索在情况下的复杂度是对数时间,进行 **O(log n)** 次比较操作(n在此处是数组的元素数量,O是大O记号,log 是对数)。二分搜索使用常数空间,无论对任何大小的输入数据,算法使用的空间都是一样的。除非输入数据数量很少,否则二分搜索比线性搜索更快,但数组必须事先被排序。尽管特定的、为了快速搜索而设计的数据结构更有效(比如哈希表),二分搜索应用面更广。 27 | 28 | 二分搜索有许多中变种。比如分散层叠可以提升在多个数组中对同一个数值的搜索。分散层叠有效的解决了计算几何学和其他领域的许多搜索问题。指数搜索将二分搜索拓宽到无边界的列表。二分搜索树和B树数据结构就是基于二分搜索的。 29 | 30 | 资料来源:[维基百科](https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%88%86%E6%90%9C%E7%B4%A2%E7%AE%97%E6%B3%95) 31 | 32 | -------------------------------------------------------------------------------- /04/directed-graph/kosaraju-scc.js: -------------------------------------------------------------------------------- 1 | const DepthFirstOrder = require('./depth-first-order') 2 | 3 | function KosarajuSCC(graph) { 4 | this.marked = [] 5 | this.id = [] 6 | this.count = 0 7 | this.init(graph) 8 | } 9 | 10 | KosarajuSCC.prototype = { 11 | init(graph) { 12 | const order = new DepthFirstOrder(graph.reverse()) 13 | order.getReversePost().forEach(v => { 14 | if (!this.marked[v]) { 15 | this.count++ 16 | this.dfs(graph, v) 17 | } 18 | }) 19 | }, 20 | 21 | dfs(graph, v) { 22 | this.marked[v] = true 23 | this.id[v] = this.count 24 | graph.getAdj(v).forEach(w => { 25 | if (!this.marked[w]) this.dfs(graph, w) 26 | }) 27 | }, 28 | 29 | getCount() { 30 | return this.count 31 | }, 32 | 33 | getID(v) { 34 | return this.id[v] 35 | }, 36 | 37 | isStronglyConnected(v, w) { 38 | if (this.id[v] && this.id[w]) { 39 | return this.id[v] == this.id[w] 40 | } 41 | 42 | return false 43 | } 44 | } 45 | 46 | module.exports = KosarajuSCC -------------------------------------------------------------------------------- /04/directed-graph/breadth-first-directed-path.js: -------------------------------------------------------------------------------- 1 | // 和无向图一样 2 | function BreadthFirstDirectedPath(graph, s) { 3 | this.marked = [] 4 | this.edgeTo = [] 5 | this.queue = [] 6 | this.s = s 7 | this.bfs(graph, s) 8 | } 9 | 10 | BreadthFirstDirectedPath.prototype = { 11 | bfs(graph, v) { 12 | this.queue.push(v) 13 | this.marked[v] = true 14 | while (this.queue.length) { 15 | v = this.queue.shift() 16 | graph.getAdj(v).forEach(w => { 17 | if (!this.marked[w]) { 18 | this.edgeTo[w] = v 19 | this.marked[w] = true 20 | this.queue.push(w) 21 | } 22 | }) 23 | } 24 | }, 25 | 26 | pathTo(v) { 27 | if (!this.hasPathTo(v)) return null 28 | const s = this.s 29 | const paths = [] 30 | for (let w = v; w != s; w = this.edgeTo[w]) { 31 | paths.push(w) 32 | } 33 | 34 | paths.push(s) 35 | return paths 36 | }, 37 | 38 | hasPathTo(v) { 39 | return this.marked[v] == true 40 | }, 41 | } 42 | 43 | module.exports = BreadthFirstDirectedPath -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} valLimit 3 | * @param {number} valNum 4 | * @return {number[]} 5 | */ 6 | function generateRandomIntegerArry(valLimit, valNum) { 7 | if (valNum > valLimit) { 8 | console.log('生成数数量不能大于上限值') 9 | return [] 10 | } 11 | 12 | const arry = [] 13 | for (let i = 0; i < valNum; i++) { 14 | let tempVal = 0 15 | do { 16 | tempVal = ~~(Math.random() * valLimit) 17 | } while (arry.includes(tempVal)) 18 | 19 | arry.push(tempVal) 20 | } 21 | 22 | return arry 23 | } 24 | 25 | /** 26 | * @param {number[]} arry 27 | * @param {number} val 28 | * @return {number} 29 | */ 30 | function loopSearchFindVal(arry, val) { 31 | for (let i = 0, len = arry.length; i < len; i++) { 32 | if (arry[i] === val) { 33 | return i 34 | } 35 | } 36 | 37 | return -1 38 | } 39 | 40 | /** 41 | * @param {number[]} arry 42 | * @param {number} i 43 | * @param {number} j 44 | * @return {void} 45 | */ 46 | function exchange(arry, i, j) { 47 | const temp = arry[i] 48 | arry[i] = arry[j] 49 | arry[j] = temp 50 | } 51 | 52 | module.exports = { 53 | generateRandomIntegerArry, 54 | loopSearchFindVal, 55 | exchange, 56 | } -------------------------------------------------------------------------------- /04/directed-graph/directed-cycle.js: -------------------------------------------------------------------------------- 1 | function DirectedCycle(graph) { 2 | this.marked = [] 3 | this.edgeTo = [] 4 | this.onStack = [] 5 | this.cycle = null 6 | this.init(graph) 7 | } 8 | 9 | DirectedCycle.prototype = { 10 | init(graph) { 11 | for (let v = 0, V = graph.getV(); v < V; v++) { 12 | if (!this.marked[v]) this.dfs(graph, v) 13 | } 14 | }, 15 | 16 | dfs(graph, v) { 17 | this.marked[v] = true 18 | this.onStack[v] = true 19 | graph.getAdj(v).forEach(w => { 20 | if (this.hasCycle()) return 21 | if (!this.marked[w]) { 22 | this.edgeTo[w] = v 23 | this.dfs(graph, w) 24 | } else if (this.onStack[w]) { 25 | this.cycle = [] 26 | for (let x = v; x != w; x = this.edgeTo[x]) { 27 | this.cycle.push(x) 28 | } 29 | 30 | this.cycle.push(w, v) 31 | } 32 | }) 33 | 34 | this.onStack[v] = false 35 | }, 36 | 37 | getCycle() { 38 | return this.cycle 39 | }, 40 | 41 | hasCycle() { 42 | return this.cycle !== null 43 | } 44 | } 45 | 46 | module.exports = DirectedCycle -------------------------------------------------------------------------------- /05/sub-str-search/kmp.js: -------------------------------------------------------------------------------- 1 | function KMP(pat) { 2 | this.pat = pat 3 | this.R = 256 4 | this.dfa = createTwoDimensionalArray(256, pat.length, 0) 5 | this.init() 6 | } 7 | 8 | KMP.prototype = { 9 | init() { 10 | this.dfa[this.pat.charCodeAt(0)][0] = 1 11 | for (let i = 0, j = 1, len = this.pat.length; j < len; j++) { 12 | for (let c = 0, R = this.R; c < R; c++) { 13 | this.dfa[c][j] = this.dfa[c][i] // 复制匹配失败情况下的值 14 | } 15 | 16 | this.dfa[this.pat.charCodeAt(j)][j] = j + 1 // 设置匹配成功情况下的值 17 | i = this.dfa[this.pat.charCodeAt(j)][i] // 更新重启状态 18 | } 19 | }, 20 | 21 | search(txt) { 22 | let i, j 23 | const N = txt.length 24 | const M = this.pat.length 25 | for (i = 0, j = 0; i < N && j < M; i++) { 26 | j = this.dfa[txt.charCodeAt(i)][j] 27 | } 28 | 29 | if (j == M) { 30 | return i - M 31 | } 32 | 33 | return -1 34 | } 35 | } 36 | 37 | function createTwoDimensionalArray(rows, cols, val) { 38 | const result = [] 39 | for (let i = 0; i < rows; i++) { 40 | const temp = new Array(cols).fill(val) 41 | result.push(temp) 42 | } 43 | 44 | return result 45 | } 46 | 47 | module.exports = KMP -------------------------------------------------------------------------------- /04/undirected-graph/graph.js: -------------------------------------------------------------------------------- 1 | const Bag = require('../../01/bag/bag') 2 | function Graph(V = 0, E = 0, data = []) { 3 | this.data = data 4 | this.V = V 5 | this.E = E 6 | this.adj = [] 7 | this.init() 8 | } 9 | 10 | Graph.prototype = { 11 | init() { 12 | for (let v = 0; v < this.V; v++) { 13 | this.adj[v] = new Bag() 14 | } 15 | 16 | let start = 0 17 | for (let i = 0; i < this.E; i++) { 18 | const v = this.data[start++] 19 | const w = this.data[start++] 20 | this.addEdge(v, w) 21 | } 22 | }, 23 | 24 | getV() { 25 | return this.V 26 | }, 27 | 28 | getE() { 29 | return this.E 30 | }, 31 | 32 | // 添加 v-w 的一条边 33 | addEdge(v, w) { 34 | this.adj[v].add(w) 35 | this.adj[w].add(v) 36 | }, 37 | 38 | // 和 v 相邻的所有顶点 39 | getAdj(v) { 40 | return this.adj[v].getData().reverse() 41 | }, 42 | 43 | toString() { 44 | let str = this.V + ' 顶点, ' + this.E + ' 边\n' 45 | for (let v = 0; v < this.V; v++) { 46 | str += v + ': ' 47 | this.getAdj(v).forEach(w => str += w + ' ') 48 | str += '\n' 49 | } 50 | 51 | return str 52 | } 53 | } 54 | 55 | module.exports = Graph -------------------------------------------------------------------------------- /04/undirected-graph/symbol-graph.js: -------------------------------------------------------------------------------- 1 | const Graph = require('./graph') 2 | 3 | function SymbolGraph(data, delim) { 4 | this.names = {} 5 | this.indexs = [] 6 | this.G = null 7 | this.init(data, delim) 8 | } 9 | 10 | SymbolGraph.prototype = { 11 | init(data, delim) { 12 | data = data.split('\r\n') 13 | const E = data.length 14 | const newData = [] 15 | let index = 0 16 | for (let i = 0; i < E; i++) { 17 | data[i].split(delim).forEach(key => { 18 | newData.push(key) 19 | if (this.names[key] === undefined) { 20 | this.names[key] = index 21 | this.indexs[index] = key 22 | index++ 23 | } 24 | }) 25 | } 26 | 27 | this.G = new Graph(index, E, this.translateData(newData)) 28 | }, 29 | 30 | translateData(data) { 31 | for (let i = 0, len = data.length; i < len; i++) { 32 | data[i] = this.getIndex(data[i]) 33 | } 34 | 35 | return data 36 | }, 37 | 38 | getIndex(key) { 39 | return this.names[key] 40 | }, 41 | 42 | getName(index) { 43 | return this.indexs[index] 44 | }, 45 | 46 | contains(key) { 47 | return this.names[key] !== undefined 48 | }, 49 | 50 | getG() { 51 | return this.G 52 | }, 53 | 54 | toString() { 55 | return this.G.toString() 56 | }, 57 | } 58 | 59 | module.exports = SymbolGraph -------------------------------------------------------------------------------- /04/mst/lazy-prim-mst.js: -------------------------------------------------------------------------------- 1 | function LazyPrimMST(graph) { 2 | this.mst = [] 3 | this.marked = [] 4 | // 横切边 5 | this.pq = [] 6 | this.init(graph) 7 | } 8 | 9 | LazyPrimMST.prototype = { 10 | init(graph) { 11 | this.visit(graph, 0) 12 | while (this.pq.length) { 13 | const e = getMin(this.pq) 14 | const v = e.getEither() 15 | const w = e.getOther(v) 16 | if (this.marked[v] && this.marked[w]) continue 17 | this.mst.push(e) 18 | if (!this.marked[v]) this.visit(graph, v) 19 | if (!this.marked[w]) this.visit(graph, w) 20 | } 21 | }, 22 | 23 | visit(graph, v) { 24 | this.marked[v] = true 25 | graph.getAdj(v).forEach(edge => { 26 | if (!this.marked[edge.getOther(v)]) this.pq.push(edge) 27 | }) 28 | }, 29 | 30 | getEdges() { 31 | return this.mst 32 | }, 33 | 34 | getWeight() { 35 | const mst = this.mst 36 | if (!mst.length) return null 37 | let sum = 0 38 | for (let i = 0, len = mst.length; i < len; i++) { 39 | sum += parseFloat(mst[i].getWeight()) 40 | } 41 | 42 | return sum 43 | } 44 | } 45 | 46 | function getMin(data) { 47 | let min = 100 48 | let minIndex = 0 49 | for (let i = 0, len = data.length; i < len; i++) { 50 | if (min > data[i].getWeight()) { 51 | min = data[i].getWeight() 52 | minIndex = i 53 | } 54 | } 55 | 56 | return data.splice(minIndex, 1)[0] 57 | } 58 | 59 | module.exports = LazyPrimMST -------------------------------------------------------------------------------- /04/directed-graph/digraph.js: -------------------------------------------------------------------------------- 1 | const Bag = require('../../01/bag/bag') 2 | function Digraph(V = 0, E = 0, data = []) { 3 | this.data = data 4 | this.V = V 5 | this.E = E 6 | this.adj = [] 7 | this.init() 8 | } 9 | 10 | Digraph.prototype = { 11 | init() { 12 | for (let v = 0; v < this.V; v++) { 13 | this.adj[v] = new Bag() 14 | } 15 | 16 | if (!this.data.length) return 17 | let start = 0 18 | for (let i = 0; i < this.E; i++) { 19 | const v = this.data[start++] 20 | const w = this.data[start++] 21 | this.addEdge(v, w) 22 | } 23 | }, 24 | 25 | getV() { 26 | return this.V 27 | }, 28 | 29 | getE() { 30 | return this.E 31 | }, 32 | 33 | // 添加 v-w 的一条边 34 | addEdge(v, w) { 35 | this.adj[v].add(w) 36 | }, 37 | 38 | // 和 v 相邻的所有顶点 39 | getAdj(v) { 40 | return this.adj[v].getData().reverse() 41 | }, 42 | 43 | reverse() { 44 | const V = this.V 45 | const R = new Digraph(V, this.E) 46 | for (let v = 0; v < V; v++) { 47 | this.getAdj(v).forEach(w => { 48 | R.addEdge(w, v) 49 | }) 50 | } 51 | 52 | return R 53 | }, 54 | 55 | toString() { 56 | let str = this.V + ' 顶点, ' + this.E + ' 边\n' 57 | for (let v = 0; v < this.V; v++) { 58 | str += v + ': ' 59 | this.getAdj(v).forEach(w => str += w + ' ') 60 | str += '\n' 61 | } 62 | 63 | return str 64 | } 65 | } 66 | 67 | module.exports = Digraph -------------------------------------------------------------------------------- /04/undirected-graph/utils.js: -------------------------------------------------------------------------------- 1 | function createGraph(path) { 2 | const Graph = require('./graph') 3 | const fs = require('fs') 4 | let data = fs.readFileSync(path, 'utf-8') 5 | data = data.split('\r\n') 6 | const V = data[0] 7 | const E = data[1] 8 | const vData = [] 9 | for (let i = 2, len = data.length; i < len; i++) { 10 | vData.push(...data[i].split(' ')) 11 | } 12 | 13 | return new Graph(V, E, vData) 14 | } 15 | 16 | function createSymbolGraph(path, delim = ' ') { 17 | const SymbolGraph = require('./symbol-graph') 18 | const fs = require('fs') 19 | let data = fs.readFileSync(path, 'utf-8') 20 | 21 | return new SymbolGraph(data, delim) 22 | } 23 | 24 | function getDegree(graph, v = 0) { 25 | let degree = 0 26 | graph.getAdj(v).forEach(() => { 27 | degree++ 28 | }) 29 | 30 | return degree 31 | } 32 | 33 | function getMaxDegree(graph) { 34 | let max = 0 35 | for (let v = 0, V = graph.getV(); v < V; v++) { 36 | let degree = getDegree(graph, v) 37 | if (degree > max) max = degree 38 | } 39 | 40 | return max 41 | } 42 | 43 | function getAveDegree(graph) { 44 | return graph.getE() * 2 / graph.getV() 45 | } 46 | 47 | function getNumberOfSelfLoops(graph) { 48 | let count = 0 49 | for (let v = 0, V = graph.getV(); v < V; v++) { 50 | graph.getAdj(v).forEach(w => { 51 | if (v == w) count++ 52 | }) 53 | } 54 | 55 | return count / 2 56 | } 57 | 58 | module.exports = { 59 | getDegree, 60 | getMaxDegree, 61 | getNumberOfSelfLoops, 62 | getAveDegree, 63 | createGraph, 64 | createSymbolGraph, 65 | } -------------------------------------------------------------------------------- /04/spt/edge-weighted-digraph.js: -------------------------------------------------------------------------------- 1 | const Bag = require('../../01/bag/bag') 2 | const Edge = require('./directed-edge') 3 | function EdgeWeightedDigraph(V = 0, E = 0, data = []) { 4 | this.data = data 5 | this.V = V 6 | this.E = E 7 | this.adj = [] 8 | this.init() 9 | } 10 | 11 | EdgeWeightedDigraph.prototype = { 12 | init() { 13 | for (let v = 0; v < this.V; v++) { 14 | this.adj[v] = new Bag() 15 | } 16 | 17 | let start = 0 18 | for (let i = 0; i < this.E; i++) { 19 | const v = parseFloat(this.data[start++]) 20 | const w = parseFloat(this.data[start++]) 21 | const weight = parseFloat(this.data[start++]) 22 | this.addEdge(new Edge(v, w, weight)) 23 | } 24 | }, 25 | 26 | getV() { 27 | return this.V 28 | }, 29 | 30 | getE() { 31 | return this.E 32 | }, 33 | 34 | // 添加 v-w 的一条边 35 | addEdge(e) { 36 | this.adj[e.getFrom()].add(e) 37 | }, 38 | 39 | // 和 v 相邻的所有顶点 40 | getAdj(v) { 41 | return this.adj[v].getData().reverse() 42 | }, 43 | 44 | toString() { 45 | let str = this.V + ' 顶点, ' + this.E + ' 边\n' 46 | for (let v = 0; v < this.V; v++) { 47 | str += v + ': ' 48 | this.getAdj(v).forEach(w => str += w + ' ') 49 | str += '\n' 50 | } 51 | 52 | return str 53 | }, 54 | 55 | getEdges() { 56 | const edges = [] 57 | for (let v = 0; v < this.V; v++) { 58 | this.getAdj(v).forEach(edge => { 59 | edges.push(edge) 60 | }) 61 | } 62 | 63 | return edges 64 | } 65 | } 66 | 67 | module.exports = EdgeWeightedDigraph -------------------------------------------------------------------------------- /02/merge-sort/merge-sort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arry 3 | * @return {void} 4 | */ 5 | function mergeSort(arry) { 6 | sort(arry, 0, arry.length - 1) 7 | } 8 | 9 | // 另一种写法 循环 10 | // function mergeSort(arry) { 11 | // const len = arry.length 12 | // for (let step = 1; step < len; step += step) { 13 | // for (let start = 0; start < len - step; start += step + step) { 14 | // merge(arry, start, start + step - 1, Math.min(start + step + step - 1, len - 1)) 15 | // } 16 | // } 17 | // } 18 | 19 | /** 20 | * @param {number[]} arry 21 | * @param {number} start 22 | * @param {number} end 23 | * @return {void} 24 | */ 25 | function sort(arry, start, end) { 26 | if (start >= end) return 27 | const mid = start + Math.floor((end - start) / 2) 28 | sort(arry, start, mid) 29 | sort(arry, mid + 1, end) 30 | // 如果a[mid] 小于 a[mid + 1] 就可以认为数组已经有序了,路过 merge 方法 31 | if (arry[mid] < arry[mid + 1]) return 32 | merge(arry, start, mid, end) 33 | } 34 | 35 | /** 36 | * @param {number[]} arry 37 | * @param {number} start 38 | * @param {number} mid 39 | * @param {number} end 40 | * @return {void} 41 | */ 42 | function merge(arry, start, mid, end) { 43 | const tempArry = arry.slice(start, end + 1) 44 | let i = 0, j = mid - start + 1 45 | let leftEnd = mid - start, rightEnd = end - start 46 | for (let k = start; k <= end; k++) { 47 | if (j > rightEnd) { 48 | arry[k] = tempArry[i++] 49 | } else if (i > leftEnd) { 50 | arry[k] = tempArry[j++] 51 | } else if (tempArry[i] < tempArry[j]) { 52 | arry[k] = tempArry[i++] 53 | } else { 54 | arry[k] = tempArry[j++] 55 | } 56 | } 57 | } 58 | 59 | module.exports = mergeSort -------------------------------------------------------------------------------- /02/priority-queue/priority-queue.js: -------------------------------------------------------------------------------- 1 | const { exchange } = require('../../utils') 2 | 3 | // 最大优先队列 4 | class MaxPQ { 5 | constructor(arr = []) { 6 | this.pq = [...arr] 7 | const len = this.pq.length 8 | // 从当前数组的中间节点开始,构造最大优先队列 9 | for (let i = Math.floor(len / 2 - 1); i >= 0; i--) { 10 | this.sink(i) 11 | } 12 | } 13 | 14 | isEmpty() { 15 | return this.pq.length === 0 16 | } 17 | 18 | getSize() { 19 | return this.pq.length 20 | } 21 | 22 | getVal() { 23 | // 将最小的值放到最前面,然后再执行 sink(),这样就可以把比它大的值逐个换到前面 24 | exchange(this.pq, 0, this.pq.length - 1) 25 | const val = this.pq.pop() 26 | this.sink(0) 27 | return val 28 | } 29 | 30 | insert(val) { 31 | this.pq.push(val) 32 | this.swim() 33 | } 34 | 35 | swim() { 36 | const pq = this.pq 37 | let i = pq.length - 1 38 | // 添加到最后面的元素可能比它的父节点要大,所以要逐个上浮比较替换 39 | while (i > 0) { 40 | // 将索引转换为从 1 开始计算,也就是将当前索引加 1,然后除以 2,再将这个数减 1,就能得到父节点的索引 41 | let j = ~~((i + 1) / 2) - 1 42 | if (pq[i] < pq[j]) break 43 | exchange(pq, i, j) 44 | i = j 45 | } 46 | } 47 | 48 | sink(i) { 49 | const pq = this.pq 50 | const maxIndex = pq.length - 1 51 | // 当前节点的值可能比较小,所以要与它的子节点比较,将大的值换到上面,如此直到末尾 52 | while ((2 * i + 1) <= maxIndex) { 53 | let j = 2 * i + 1 54 | if (j < maxIndex && pq[j] < pq[j + 1]) { 55 | j++ 56 | } 57 | 58 | if (pq[i] >= pq[j]) { 59 | break 60 | } 61 | 62 | exchange(pq, i, j) 63 | i = j 64 | } 65 | } 66 | } 67 | 68 | module.exports = MaxPQ -------------------------------------------------------------------------------- /02/compare-test.js: -------------------------------------------------------------------------------- 1 | const bubbleSort = require('./bubble-sort/bubble-sort') 2 | const heapSort = require('./heap-sort/heap-sort') 3 | const insertionSort = require('./insertion-sort/insertion-sort') 4 | const mergeSort = require('./merge-sort/merge-sort') 5 | const quickSort = require('./quick-sort/quick-sort') 6 | const quick3Way = require('./quick-sort/quick3way') 7 | const selectionSort = require('./selection-sort/selection-sort') 8 | const shellSort = require('./shell-sort/shell-sort') 9 | const { generateRandomIntegerArry } = require('../utils') 10 | 11 | valLimit = ~~process.argv[2] 12 | valNum = ~~process.argv[3] 13 | if (!valLimit || !valNum) { 14 | console.log('请按照命令格式运行测试:node test 生成值的上限 生成值的个数') 15 | return 16 | } 17 | 18 | function test(valLimit, valNum) { 19 | const arry = generateRandomIntegerArry(valLimit, valNum) 20 | const sortFunc = { 21 | bubbleSort, 22 | selectionSort, 23 | insertionSort, 24 | shellSort, 25 | quickSort, 26 | quick3Way, 27 | mergeSort, 28 | heapSort, 29 | } 30 | 31 | const map = { 32 | bubbleSort: '冒泡排序', 33 | selectionSort: '选择排序', 34 | insertionSort: '插入排序', 35 | shellSort: '希尔排序', 36 | quickSort: '快速排序', 37 | quick3Way: '三向切分快速排序', 38 | mergeSort: '归并排序', 39 | heapSort: '堆排序', 40 | } 41 | 42 | console.log(`包含${valNum}个数的数组排序时间:`) 43 | for (const key in sortFunc) { 44 | const tempArry = [...arry] 45 | console.time(`${map[key]}耗时`) 46 | sortFunc[key](tempArry) 47 | console.timeEnd(`${map[key]}耗时`) 48 | } 49 | 50 | console.time(`JavaScript数组原生方法sort排序耗时`) 51 | arry.sort((a, b) => a - b) 52 | console.timeEnd(`JavaScript数组原生方法sort排序耗时`) 53 | } 54 | 55 | test(valLimit, valNum) -------------------------------------------------------------------------------- /04/mst/edge-weighted-graph.js: -------------------------------------------------------------------------------- 1 | const Bag = require('../../01/bag/bag') 2 | const Edge = require('./edge') 3 | function EdgeWeightedGraph(V = 0, E = 0, data = []) { 4 | this.data = data 5 | this.V = V 6 | this.E = E 7 | this.adj = [] 8 | this.init() 9 | } 10 | 11 | EdgeWeightedGraph.prototype = { 12 | init() { 13 | for (let v = 0; v < this.V; v++) { 14 | this.adj[v] = new Bag() 15 | } 16 | 17 | let start = 0 18 | for (let i = 0; i < this.E; i++) { 19 | const v = this.data[start++] 20 | const w = this.data[start++] 21 | const weight = this.data[start++] 22 | this.addEdge(new Edge(v, w, weight)) 23 | } 24 | }, 25 | 26 | getV() { 27 | return this.V 28 | }, 29 | 30 | getE() { 31 | return this.E 32 | }, 33 | 34 | // 添加 v-w 的一条边 35 | addEdge(e) { 36 | const v = e.getEither() 37 | const w = e.getOther(v) 38 | this.adj[v].add(e) 39 | this.adj[w].add(e) 40 | }, 41 | 42 | // 和 v 相邻的所有顶点 43 | getAdj(v) { 44 | return this.adj[v].getData().reverse() 45 | }, 46 | 47 | toString() { 48 | let str = this.V + ' 顶点, ' + this.E + ' 边\n' 49 | for (let v = 0; v < this.V; v++) { 50 | str += v + ': ' 51 | this.getAdj(v).forEach(w => str += w + ' ') 52 | str += '\n' 53 | } 54 | 55 | return str 56 | }, 57 | 58 | getEdges() { 59 | const edges = [] 60 | for (let v = 0; v < this.V; v++) { 61 | this.getAdj(v).forEach(edge => { 62 | if (v < edge.getOther(v)) { 63 | edges.push(edge) 64 | } 65 | }) 66 | } 67 | 68 | return edges 69 | } 70 | } 71 | 72 | module.exports = EdgeWeightedGraph -------------------------------------------------------------------------------- /02/README.md: -------------------------------------------------------------------------------- 1 | # 排序 2 | 排序就是将一组对象按照某种逻辑顺序重新排列的过程。排序算法的额外内存开销和运行时间是同等重要的。 3 | 排序算法可分为两类:除了函数调用所需的栈和固定数目的实例变量之外无需额外内存的原地排序算法,以及需要额外内存空间来存储另一份数组副本的其他排序算法。 4 | 5 | 通过提升速度来解决其他方式无法解决的问题是研究算法的设计和性能的主要原因之一。 6 | 7 | ## 算法 8 | |算法|是否稳定|是否为原地排序|时间复杂度|空间复杂度|备注| 9 | |-|-|-|-|-|-| 10 | |[冒泡排序](https://github.com/woai3c/Algorithm/tree/master/02/bubble-sort)|是|是|N^2|1|| 11 | |[选择排序](https://github.com/woai3c/Algorithm/tree/master/02/selection-sort)|否|是|N^2|1|当数据规模较小时,选择排序性能较好| 12 | |[插入排序](https://github.com/woai3c/Algorithm/tree/master/02/insertion-sort)|是|是|介于N和N^2之间|1|若数组基本有序且数据规模较小时,选用插入排序较好(在本人电脑做实验得出,数组长度为 600 左右时,选择和插入排序差不多,数组长度越小,选择排序越快,数组长度越大,插入排序越快)。| 13 | |[希尔排序](https://github.com/woai3c/Algorithm/tree/master/02/shell-sort)|否|是|NlogN - N^2|1|数据量较小且基本有序时(比插入排序更快的排序算法)| 14 | |[快速排序](https://github.com/woai3c/Algorithm/tree/master/02/quick-sort)|否|是|NlogN|logN|快速排序适合处理大量数据排序时的场景| 15 | |[三向快速排序](https://github.com/woai3c/Algorithm/tree/master/02/quick-sort)|否|是|介于N和NlogN之间|logN|运行效率由概率提供保证,同时也取决于输入元素的分布情况| 16 | |[归并排序](https://github.com/woai3c/Algorithm/tree/master/02/merge-sort)|是|否|NlogN|N|数据量较大且要求排序稳定时| 17 | |[堆排序](https://github.com/woai3c/Algorithm/tree/master/02/heap-sort)|否|是|NlogN|1|堆排序适合处理数据量大的情况,数据呈流式输入时用堆排序也很方便| 18 | 19 | ![](../imgs/2-15.png) 20 | 21 | ![](../imgs/2-16.png) 22 | 23 | ![](../imgs/2-17.png) 24 | 25 | ## 数据结构 26 | ### 优先队列 27 | [优先队列实现](./priority-queue) 28 | 29 | ![](../imgs/2-8.png) 30 | 31 | #### 二叉堆 32 | 当一棵二叉树的每个节点都大于等于它的两个子节点时,它被称为堆有序。 33 | 34 | ![](../imgs/2-9.png) 35 | 36 | 堆有序化过程中又分为上浮和下沉两种情况: 37 | 38 | ![](../imgs/2-10.png) 39 | 40 | ![](../imgs/2-11.png) 41 | 42 | #### 多叉堆 43 | ![](../imgs/2-12.png) 44 | 45 | ## 测试本章所有排序方法 46 | ```js 47 | node compare-test.js 生成值的上限 生成值的个数 48 | 49 | // example 50 | node compare-test.js 100000 8888 51 | ``` 52 | 53 | ## 排序的应用 54 | #### 归约 55 | 归约指的是为解决某个问题而发明的算法正好可以用来解决另一个问题。 56 | 57 | ### 找出中位数 58 | [程序实现](./find-index) 59 | 60 | 使用快速排序的切分法可以在线性级别的时间内找到中位数。 61 | 62 | ![](../imgs/2-18.png) 63 | 64 | ![](../imgs/2-19.png) -------------------------------------------------------------------------------- /05/sub-str-search/kmp-2.js: -------------------------------------------------------------------------------- 1 | function KMP(pat) { 2 | this.pat = pat 3 | // 部分匹配值表 4 | this.matchTable = createMatchTable(pat) 5 | } 6 | 7 | KMP.prototype = { 8 | search(txt) { 9 | const pat = this.pat 10 | const M = pat.length 11 | const N = txt.length 12 | // 是否已经命中首字母,即pat[0] 13 | let isInitialMatch = false 14 | // 字符串索引 匹配模式索引 txt[i] == pat[0]时 i的缓存 cacheMatchIndex = i 15 | let i = 0, j = 0, cacheMatchIndex = 0 16 | while (i < N && j < M) { 17 | if (txt[i] == pat[j]) { 18 | if (!isInitialMatch) { 19 | isInitialMatch = true 20 | cacheMatchIndex = i 21 | } 22 | 23 | j++ 24 | i++ 25 | } else if (isInitialMatch) { 26 | i = cacheMatchIndex + j - this.matchTable[j - 1] 27 | j = 0 28 | isInitialMatch = false 29 | } else { 30 | i++ 31 | } 32 | } 33 | 34 | if (j == M) { 35 | return i - M 36 | } 37 | 38 | return -1 39 | } 40 | } 41 | 42 | // 算法参考 43 | // http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 44 | function createMatchTable(pat) { 45 | const matchTable = [] 46 | for (let i = 0, len = pat.length; i < len; i++) { 47 | matchTable[i] = segmentation(pat.slice(0, i + 1)) 48 | } 49 | 50 | return matchTable 51 | } 52 | 53 | function segmentation(pat) { 54 | const prefix = [] 55 | const suffix = [] 56 | let pre = '' 57 | let suf = '' 58 | for (let i = 0, len = pat.length; i < len - 1; i++) { 59 | pre += pat[i] 60 | suf = pat[len - i - 1] + suf 61 | prefix.push(pre) 62 | suffix.push(suf) 63 | } 64 | 65 | let max = 0 66 | for (let i = 0, len = prefix.length; i < len; i++) { 67 | // "部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度 68 | if (suffix.includes(prefix[i]) && prefix[i].length > max) { 69 | max = prefix[i].length 70 | } 71 | } 72 | 73 | return max 74 | } 75 | 76 | module.exports = KMP -------------------------------------------------------------------------------- /04/spt/dijkstra-sp.js: -------------------------------------------------------------------------------- 1 | function DijkstraSP(graph, s) { 2 | this.distTo = [] 3 | this.edgeTo = [] 4 | // 横切边 5 | this.pq = [] 6 | this.init(graph, s) 7 | } 8 | 9 | DijkstraSP.prototype = { 10 | init(graph, s) { 11 | for (let v = 0, V = graph.getV(); v < V; v++) { 12 | this.distTo[v] = Infinity 13 | } 14 | 15 | this.edgeTo[s] = null 16 | this.distTo[s] = 0 17 | this.pq.push({ 0: 0 }) 18 | while (this.pq.length) { 19 | this.relax(graph, getMin(this.pq)) 20 | } 21 | }, 22 | 23 | relax(graph, v) { 24 | graph.getAdj(v).forEach(edge => { 25 | const w = edge.getTo() 26 | if (this.distTo[w] > this.distTo[v] + edge.getWeight()) { 27 | this.distTo[w] = this.distTo[v] + edge.getWeight() 28 | this.edgeTo[w] = edge 29 | if (includes(this.pq, w)) { 30 | change(this.pq, w, this.distTo[w]) 31 | } else { 32 | const obj = {} 33 | obj[w] = this.distTo[w] 34 | this.pq.push(obj) 35 | } 36 | } 37 | }) 38 | }, 39 | 40 | getDistTo(v) { 41 | return this.distTo[v] 42 | }, 43 | 44 | hasPathTo(v) { 45 | return this.distTo[v] < Infinity 46 | }, 47 | 48 | getPathTo(v) { 49 | if (!this.hasPathTo(v)) return null 50 | const paths = [] 51 | for (let e = this.edgeTo[v]; e != null; e = this.edgeTo[e.getFrom()]) { 52 | paths.unshift(e) 53 | } 54 | 55 | return paths 56 | } 57 | } 58 | 59 | function getMin(data) { 60 | let min = 100 61 | let minIndex = 0 62 | let minV = 0 63 | for (let i = 0, len = data.length; i < len; i++) { 64 | const obj = data[i] 65 | const v = Object.keys(obj)[0] 66 | const weight = obj[v] 67 | if (min > weight) { 68 | minIndex = i 69 | minV = v 70 | min = weight 71 | } 72 | } 73 | 74 | data.splice(minIndex, 1) 75 | return minV 76 | } 77 | 78 | function includes(data, v) { 79 | for (let i = 0, len = data.length; i < len; i++) { 80 | const obj = data[i] 81 | const w = Object.keys(obj)[0] 82 | if (v == w) return true 83 | } 84 | 85 | return false 86 | } 87 | 88 | function change(data, v, weight) { 89 | for (let i = 0, len = data.length; i < len; i++) { 90 | const obj = data[i] 91 | const w = Object.keys(obj)[0] 92 | if (v == w) { 93 | data[i][w] = weight 94 | return 95 | } 96 | } 97 | } 98 | 99 | module.exports = DijkstraSP -------------------------------------------------------------------------------- /03/hash-table/hash-table.js: -------------------------------------------------------------------------------- 1 | function HashTable(data = []) { 2 | this.size = data.length 3 | this.keys = new Array(this.size) 4 | this.vals = new Array(this.size) 5 | this.n = 0 6 | this.init(data) 7 | } 8 | 9 | HashTable.prototype = { 10 | init(data) { 11 | data.forEach(item => { 12 | this.set(item.key, item.val) 13 | }) 14 | }, 15 | // 返回哈希表键的数量 非哈希表大小 16 | getSize() { 17 | return this.n 18 | }, 19 | 20 | get(key) { 21 | for (let i = this.hashCode(key); this.keys[i] !== undefined; i = (i + 1) % this.size) { 22 | if (this.keys[i] == key) return this.vals[i] 23 | } 24 | 25 | return undefined 26 | }, 27 | 28 | delete(key) { 29 | if (!this.contains(key)) return 30 | let i = this.hashCode(key) 31 | while (this.keys[i] !== key) { 32 | i = (i + 1) % this.size 33 | } 34 | 35 | this.keys[i] = undefined 36 | this.vals[i] = undefined 37 | this.n-- 38 | i = (i + 1) % this.size 39 | while (this.keys[i] !== undefined) { 40 | const tempKey = this.keys[i] 41 | const tempVal = this.vals[i] 42 | this.keys[i] = undefined 43 | this.vals[i] = undefined 44 | this.n-- 45 | this.set(tempKey, tempVal) 46 | i = (i + 1) % this.size 47 | } 48 | 49 | if (this.n > 0 && this.n == this.size / 8) this.resize(this.resize / 2) 50 | }, 51 | 52 | contains(key) { 53 | const { size, keys } = this 54 | const index = this.hashCode(key) 55 | for (let i = index; i < size; i++) { 56 | if (keys[i] == key) return true 57 | } 58 | 59 | for (let i = 0; i < index; i++) { 60 | if (keys[i] == key) return true 61 | } 62 | 63 | return false 64 | }, 65 | 66 | set(key, val) { 67 | if (this.n >= this.size / 2) this.resize(2 * this.size) 68 | let i = this.hashCode(key) 69 | for (; this.keys[i] !== undefined; i = (i + 1) % this.size) { 70 | if (this.keys[i] == key) { 71 | this.vals[i] = val 72 | return 73 | } 74 | } 75 | 76 | this.n++ 77 | this.keys[i] = key 78 | this.vals[i] = val 79 | }, 80 | 81 | hashCode(key) { 82 | return (keyToHash(key) & 0x7fffffff) % this.size 83 | }, 84 | 85 | resize(newSize) { 86 | const arry = new Array(newSize) 87 | const tempTable = new HashTable(arry) 88 | for (let i = 0; i < this.size; i++) { 89 | if (this.keys[i] !== undefined) { 90 | tempTable.set(this.keys[i], this.vals[i]) 91 | } 92 | } 93 | 94 | this.keys = tempTable.keys 95 | this.vals = tempTable.vals 96 | this.size = tempTable.size 97 | } 98 | } 99 | 100 | function keyToHash(key) { 101 | let hash = 0 102 | key += '' 103 | if (key.length == 0) return hash 104 | for (let i = 0, len = key.length; i < len; i++) { 105 | char = key.charCodeAt(i) 106 | hash = ((hash << 5) - hash) + char 107 | hash = hash & hash // Convert to 32bit integer 108 | } 109 | 110 | return hash 111 | } 112 | 113 | module.exports = HashTable -------------------------------------------------------------------------------- /05/string-search/triest.js: -------------------------------------------------------------------------------- 1 | function Node() { 2 | this.next = [] 3 | this.val = null 4 | } 5 | 6 | let R 7 | function TrieST() { 8 | this.root = null 9 | R = 256 10 | } 11 | 12 | TrieST.prototype = { 13 | get(key) { 14 | const node = get(this.root, key, 0) 15 | if (!node) return null 16 | return node.val 17 | }, 18 | 19 | set(key, val) { 20 | this.root = set(this.root, key, val, 0) 21 | }, 22 | 23 | delete(key) { 24 | this.root = deleteNode(this.root, key, 0) 25 | }, 26 | 27 | contains(key) { 28 | const node = get(this.root, key, 0) 29 | if (!node || node.val === null) return false 30 | return true 31 | }, 32 | 33 | size() { 34 | return this.keys().length 35 | }, 36 | 37 | keys() { 38 | return this.keysWithPrefix('') 39 | }, 40 | 41 | isEmpty() { 42 | return this.root == null 43 | }, 44 | 45 | longestPrefixOf(s) { 46 | const length = search(this.root, s, 0, 0) 47 | return s.substr(0, length) 48 | }, 49 | 50 | keysWithPrefix(pre) { 51 | const keys = [] 52 | collect(get(this.root, pre, 0), pre, keys) 53 | return keys 54 | }, 55 | 56 | keysThatMatch(re) { 57 | const keys = [] 58 | collectMatch(this.root, '', re, keys) 59 | return keys 60 | } 61 | } 62 | 63 | function set(node, key, val, d) { 64 | if (!node) node = new Node() 65 | if (d == key.length) { 66 | node.val = val 67 | return node 68 | } 69 | 70 | const c = key.charCodeAt(d) 71 | node.next[c] = set(node.next[c], key, val, d + 1) 72 | return node 73 | } 74 | 75 | function get(node, key, d) { 76 | if (!node) return null 77 | if (d == key.length) { 78 | return node 79 | } 80 | 81 | const c = key.charCodeAt(d) 82 | return get(node.next[c], key, d + 1) 83 | } 84 | 85 | function deleteNode(node, key, d) { 86 | if (!node) return null 87 | if (d != key.length) { 88 | const c = key.charCodeAt(d) 89 | node.next[c] = deleteNode(node.next[c], key, d + 1) 90 | } else { 91 | node.val = null 92 | } 93 | 94 | for (let c = 0; c < R; c++) { 95 | if (node.next[c]) return node 96 | } 97 | 98 | return null 99 | } 100 | 101 | const fromCharCode = String.fromCharCode 102 | function collect(node, pre, keys) { 103 | if (!node) return 104 | if (node.val !== null) keys.push(pre) 105 | for (let c = 0; c < R; c++) { 106 | collect(node.next[c], pre + fromCharCode(c), keys) 107 | } 108 | } 109 | 110 | function collectMatch(node, pre, re, keys) { 111 | const d = pre.length 112 | if (!node) return 113 | if (d == re.length && node.val !== null) keys.push(pre) 114 | if (d == re.length) return 115 | 116 | const next = re[d] 117 | for (let c = 0; c < R; c++) { 118 | if (next == fromCharCode(c) || next == '.') { 119 | collectMatch(node.next[c], pre + fromCharCode(c), re, keys) 120 | } 121 | } 122 | } 123 | 124 | function search(node, s, d, length) { 125 | if (!node) return length 126 | if (node.val !== null) length = d 127 | if (d == s.length) return length 128 | const c = s.charCodeAt(d) 129 | return search(node.next[c], s, d + 1, length) 130 | } 131 | 132 | module.exports = TrieST -------------------------------------------------------------------------------- /03/binary-search-tree/binary-search-tree.js: -------------------------------------------------------------------------------- 1 | function Node(val) { 2 | this.val = val 3 | this.left = null 4 | this.right = null 5 | this.size = 1 6 | } 7 | 8 | function BinarySearchTree(data) { 9 | this.root = null 10 | this.arry = [] 11 | this.init(data) 12 | } 13 | 14 | // 生成二叉树 15 | BinarySearchTree.prototype.init = function(data) { 16 | if (!data || !data.length) return 17 | data.forEach(val => { 18 | this.insert(val) 19 | }) 20 | } 21 | 22 | // 返回节点数 默认返回根节点 即整棵树的节点数 23 | BinarySearchTree.prototype.getSize = function(node) { 24 | node = node || this.root 25 | return subGetSize(node) 26 | } 27 | 28 | function subGetSize(node) { 29 | return node? node.size : 0 30 | } 31 | 32 | // 查找val对应的节点 33 | BinarySearchTree.prototype.find = function(val) { 34 | return subFind(val, this.root) 35 | } 36 | 37 | function subFind(val, node) { 38 | if (!node) return null 39 | if (val == node.val) return node 40 | if (val > node.val) return subFind(val, node.right) 41 | if (val < node.val) return subFind(val, node.left) 42 | } 43 | 44 | // 查找包含最大值的节点 45 | BinarySearchTree.prototype.findMax = function(node) { 46 | node = node || this.root 47 | if (!node) return null 48 | return subFindMax(node) 49 | } 50 | 51 | function subFindMax(node) { 52 | if (node.right) { 53 | return subFindMax(node.right) 54 | } 55 | 56 | return node 57 | } 58 | 59 | // 查找包含最小值的节点 60 | BinarySearchTree.prototype.findMin = function(node) { 61 | node = node || this.root 62 | if (!node) return null 63 | return subFindMin(node) 64 | } 65 | 66 | function subFindMin(node) { 67 | if (node.left) { 68 | return subFindMin(node.left) 69 | } 70 | 71 | return node 72 | } 73 | 74 | // 删除节点 75 | BinarySearchTree.prototype.delete = function(val, node) { 76 | if (!this.find(val)) return null 77 | node = node || this.root 78 | return subDelete(val, node) 79 | } 80 | 81 | function subDelete(val, node) { 82 | if (!node) return null 83 | if (val < node.val) { 84 | node.left = subDelete(val, node.left) 85 | } else if (val > node.val) { 86 | node.right = subDelete(val, node.right) 87 | } else { 88 | if (!node.left) return node.right 89 | if (!node.right) return node.left 90 | const tempNode = subFindMin(node.right) 91 | node.val = tempNode.val 92 | node.right = subDelete(tempNode.val, node.right) 93 | } 94 | 95 | node.size = subGetSize(node.left) + subGetSize(node.right) + 1 96 | return node 97 | } 98 | 99 | // 插入节点 100 | BinarySearchTree.prototype.insert = function(val) { 101 | if (!this.root) { 102 | this.root = new Node(val) 103 | return 104 | } 105 | 106 | subInsert(this.root, val) 107 | } 108 | 109 | function subInsert(node, val) { 110 | if (val > node.val) { 111 | if (node.right) { 112 | subInsert(node.right, val) 113 | } else { 114 | node.right = new Node(val) 115 | } 116 | 117 | node.size = node.right.size + subGetSize(node.left) + 1 118 | return 119 | } 120 | 121 | if (val < node.val) { 122 | if (node.left) { 123 | subInsert(node.left, val) 124 | } else { 125 | node.left = new Node(val) 126 | } 127 | 128 | node.size = subGetSize(node.right) + node.left.size + 1 129 | } 130 | } 131 | 132 | // 升序排序其实就是中序遍历一遍二叉树 133 | BinarySearchTree.prototype.sort = function() { 134 | this.arry = [] 135 | if (!this.root) return this.arry 136 | subSort(this.root.left, this.arry) 137 | this.arry.push(this.root.val) 138 | subSort(this.root.right, this.arry) 139 | return [...this.arry] 140 | } 141 | 142 | function subSort(node, arry) { 143 | if (!node) return 144 | subSort(node.left, arry) 145 | arry.push(node.val) 146 | subSort(node.right, arry) 147 | } 148 | 149 | module.exports = BinarySearchTree -------------------------------------------------------------------------------- /03/red-black-tree/red-black-tree.js: -------------------------------------------------------------------------------- 1 | // red: true black: false 2 | const RED = true 3 | const BLACK = false 4 | 5 | function Node(val, color = false) { 6 | this.val = val 7 | this.left = null 8 | this.right = null 9 | // 节点总数 10 | this.size = 1 11 | this.color = color 12 | } 13 | 14 | function RedBlackTree(data) { 15 | this.root = null 16 | this.arry = [] 17 | this.init(data) 18 | } 19 | 20 | // 生成红黑书 21 | RedBlackTree.prototype.init = function(data) { 22 | if (!data || !data.length) return 23 | data.forEach(val => { 24 | this.insert(val) 25 | }) 26 | } 27 | 28 | // 返回节点数 默认返回根节点 即整棵树的节点数 29 | RedBlackTree.prototype.getSize = function(node) { 30 | node = node || this.root 31 | return subGetSize(node) 32 | } 33 | 34 | function subGetSize(node) { 35 | return node? node.size : 0 36 | } 37 | 38 | // 查找val对应的节点 39 | RedBlackTree.prototype.find = function(val) { 40 | return subFind(val, this.root) 41 | } 42 | 43 | function subFind(val, node) { 44 | if (!node) return null 45 | if (val == node.val) return node 46 | if (val > node.val) return subFind(val, node.right) 47 | if (val < node.val) return subFind(val, node.left) 48 | } 49 | 50 | // 查找包含最大值的节点 51 | RedBlackTree.prototype.findMax = function(node) { 52 | node = node || this.root 53 | if (!node) return null 54 | return subFindMax(node) 55 | } 56 | 57 | function subFindMax(node) { 58 | if (node.right) { 59 | return subFindMax(node.right) 60 | } 61 | 62 | return node 63 | } 64 | 65 | // 查找包含最小值的节点 66 | RedBlackTree.prototype.findMin = function(node) { 67 | node = node || this.root 68 | if (!node) return null 69 | return subFindMin(node) 70 | } 71 | 72 | function subFindMin(node) { 73 | if (node.left) { 74 | return subFindMin(node.left) 75 | } 76 | 77 | return node 78 | } 79 | 80 | // 删除节点 81 | RedBlackTree.prototype.delete = function(val) { 82 | if (!this.find(val)) return null 83 | const node = this.root 84 | if (!isRed(node.left) && !isRed(node.right)) node.color = RED 85 | this.root = subDelete(val, node) 86 | if (this.root) this.root.color = BLACK 87 | } 88 | 89 | function subDelete(val, node) { 90 | if (!node) return null 91 | if (val < node.val) { 92 | if (!isRed(node.left) && !isRed(node.left.left)) node = moveRedLeft(node) 93 | node.left = subDelete(val, node.left) 94 | } else { 95 | if (isRed(node.left)) node = rotateRight(node) 96 | if (val == node.val && !node.right) return null 97 | if (!isRed(node.right) && !isRed(node.right.left)) node = moveRedRight(node) 98 | if (val == node.val) { 99 | const tempNode = subFindMin(node.right) 100 | node.val = tempNode.val 101 | node.right = subDelete(tempNode.val, node.right) 102 | } else { 103 | node.right = subDelete(val, node.right) 104 | } 105 | } 106 | 107 | return balance(node) 108 | } 109 | 110 | function moveRedLeft(node) { 111 | flipColors(node) 112 | // 如果出现两个连续红节点 113 | if (isRed(node.right.left)) { 114 | node.right = rotateRight(node.right) 115 | node = rotateLeft(node) 116 | flipColors(node) 117 | } 118 | 119 | return node 120 | } 121 | 122 | function moveRedRight(node) { 123 | flipColors(node) 124 | // 如果出现两个连续红节点 125 | if (isRed(node.left.left)) { 126 | node = rotateRight(node) 127 | flipColors(node) 128 | } 129 | 130 | return node 131 | } 132 | 133 | // 删除节点后重新开始平衡树 134 | function balance(node) { 135 | if (isRed(node.right)) node = rotateLeft(node) 136 | if (isRed(node.left) && isRed(node.left.left)) node = rotateRight(node) 137 | if (isRed(node.left) && isRed(node.right)) flipColors(node) 138 | 139 | node.size = subGetSize(node.left) + subGetSize(node.right) + 1 140 | return node 141 | } 142 | 143 | // 插入节点 144 | RedBlackTree.prototype.insert = function(val) { 145 | if (!this.root) { 146 | this.root = new Node(val) 147 | return 148 | } 149 | 150 | this.root = subInsert(this.root, val) 151 | this.root.color = BLACK 152 | } 153 | 154 | function subInsert(node, val) { 155 | if (val > node.val) { 156 | if (node.right) { 157 | node.right = subInsert(node.right, val) 158 | } else { 159 | node.right = new Node(val, RED) 160 | } 161 | } else if (val < node.val) { 162 | if (node.left) { 163 | node.left = subInsert(node.left, val) 164 | } else { 165 | node.left = new Node(val, RED) 166 | } 167 | } 168 | 169 | // 插入节点后开始平衡树 170 | if (!isRed(node.left) && isRed(node.right)) node = rotateLeft(node) 171 | if (isRed(node.left) && isRed(node.left.left)) node = rotateRight(node) 172 | if (isRed(node.left) && isRed(node.right)) flipColors(node) 173 | 174 | node.size = subGetSize(node.right) + subGetSize(node.left) + 1 175 | return node 176 | } 177 | 178 | function rotateLeft(node) { 179 | const x = node.right 180 | node.right = x.left 181 | x.left = node 182 | x.color = node.color 183 | node.color = RED 184 | x.size = node.size 185 | node.size = subGetSize(node.right) + subGetSize(node.left) + 1 186 | return x 187 | } 188 | 189 | function rotateRight(node) { 190 | const x = node.left 191 | node.left = x.right 192 | x.right = node 193 | x.color = node.color 194 | node.color = RED 195 | x.size = node.size 196 | node.size = subGetSize(node.right) + subGetSize(node.left) + 1 197 | return x 198 | } 199 | 200 | function isRed(node) { 201 | if (!node) return false 202 | return node.color == RED 203 | } 204 | 205 | function flipColors(node) { 206 | node.color = !node.color 207 | node.left.color = !node.left.color 208 | node.right.color = !node.right.color 209 | } 210 | 211 | // 升序排序其实就是中序遍历一遍红黑书 212 | RedBlackTree.prototype.sort = function() { 213 | this.arry = [] 214 | if (!this.root) return this.arry 215 | subSort(this.root.left, this.arry) 216 | this.arry.push(this.root.val) 217 | subSort(this.root.right, this.arry) 218 | return [...this.arry] 219 | } 220 | 221 | function subSort(node, arry) { 222 | if (!node) return 223 | subSort(node.left, arry) 224 | arry.push(node.val) 225 | subSort(node.right, arry) 226 | } 227 | 228 | RedBlackTree.prototype.isBalance = function() { 229 | let black = 0 230 | let node = this.root 231 | while (node) { 232 | if (!isRed(node)) black++ 233 | node = node.left 234 | } 235 | 236 | return subIsBalance(this.root, black) 237 | } 238 | 239 | function subIsBalance(node, black) { 240 | if (!node) return black == 0 241 | if (!isRed(node)) black-- 242 | return subIsBalance(node.left, black) && subIsBalance(node.right, black) 243 | } 244 | 245 | module.exports = RedBlackTree --------------------------------------------------------------------------------