├── .coveralls.yml ├── .eslintrc.js ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .nyc_output ├── 05fa095d199037102caa7251fdf5e3f9.json └── 63fc08531b5c6a68aa5328ce109023a8.json ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── binary-heap ├── README.md └── binaryHeap.js ├── bitwise ├── README.md ├── bitwise-basics.js └── bitwise-rgb-hex-binary.js ├── complex-array ├── README.md └── complex-array.js ├── fast-fourier-transforms ├── README.md └── fft.js ├── graph-traversing ├── README.md ├── breadth-first-search.js ├── depth-first-search-imperative.js └── depth-first-search-recursive.js ├── graph ├── README.md ├── graphNode.js ├── gridGraph.js └── tree.js ├── hash-table ├── README.md └── hash-table.js ├── linked-list ├── README.md └── linkedList.js ├── list ├── README.md └── list.js ├── memoization ├── README.md └── memoize.js ├── package.json ├── queue ├── README.md ├── priority-queue.js └── queue.js ├── rabin-karp ├── README.md └── rk.js ├── search ├── README.md ├── binary-search-tree.js └── linearSearch.js ├── shortest-path ├── README.md ├── aStar.js └── dijkstra.js ├── sorting ├── README.md ├── bubble-recursive.js ├── bubblesort.js ├── insertionsort.js ├── mergesort.js ├── quick-recursive.js ├── quicksort.js └── selectionsort.js ├── stack ├── README.md └── stack.js ├── test ├── binary-heap.spec.js ├── bitwise.spec.js ├── complex-array.spec.js ├── fft-test-helper.js ├── fft.spec.js ├── graph-traversing.spec.js ├── graph.spec.js ├── hash-table.spec.js ├── linked-list.spec.js ├── list.spec.js ├── queue.spec.js ├── rabin-karp.spec.js ├── search.spec.js ├── shortest-path.spec.js ├── sorting.spec.js ├── stack.spec.js └── trie.spec.js └── trie ├── README.md └── trie.js /.coveralls.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JoeKarlsson/data-structures/14112c25e95ea926d66682c7a436d9f75cf78207/.coveralls.yml -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb-base", 3 | 'rules': { 4 | 'no-underscore-dangle': 'off', 5 | "no-console": 0, 6 | "space-in-parens": 0, 7 | "no-plusplus": 0, 8 | "no-use-before-define": 0, 9 | "padded-blocks": 0, 10 | "no-param-reassign": 0, 11 | "consistent-return": 0, 12 | "no-bitwise": 0, 13 | "no-shadow": 0, 14 | }, 15 | "env": { 16 | "browser": false, // browser global variables. 17 | "node": true, // Node.js global variables and Node.js-specific rules. 18 | "mocha": true, // adds all of the Mocha testing global variables. 19 | }, 20 | "plugins": [ 21 | "import", 22 | ], 23 | }; -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.nyc_output/05fa095d199037102caa7251fdf5e3f9.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "9.4.0" 4 | notifications: 5 | email: false 6 | after_success: npm run coverage 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at joekarlsson1@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | ## Code of Conduct 4 | 5 | This project is intended to be a safe, welcoming space for collaboration. All contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. Thank you for being kind to each other! 6 | 7 | ## Contributions welcome! 8 | 9 | **Before spending lots of time on something, ask for feedback on your idea first!** 10 | 11 | Please search [issues](../../issues/) and [pull requests](../../pulls/) before adding something new! This helps avoid duplicating efforts and conversations. 12 | 13 | This project welcomes any kind of contribution! Here are a few suggestions: 14 | 15 | - **Ideas**: participate in an issue thread or start your own to have your voice heard. 16 | - **Writing**: contribute your expertise in an area by helping expand the included content. 17 | - **Copy editing**: fix typos, clarify language, and generally improve the quality of the content. 18 | - **Formatting**: help keep content easy to read with consistent formatting. 19 | - **Code**: help maintain and improve the project codebase. 20 | 21 | ## Code Style 22 | 23 | [![standard][standard-image]][standard-url] 24 | 25 | This repository uses [`standard`][standard-url] to maintain code style and consistency, and to avoid style arguments. 26 | 27 | [standard-image]: https://cdn.rawgit.com/feross/standard/master/badge.svg 28 | [standard-url]: https://github.com/feross/standard 29 | [semistandard-image]: https://cdn.rawgit.com/flet/semistandard/master/badge.svg 30 | [semistandard-url]: https://github.com/Flet/semistandard 31 | 32 | ## Project Governance 33 | 34 | **This is an [OPEN Open Source Project](http://openopensource.org/).** 35 | 36 | Individuals making significant and valuable contributions are given commit access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. 37 | 38 | ### Rules 39 | 40 | There are a few basic ground rules for collaborators: 41 | 42 | 1. **No `--force` pushes** or modifying the Git history in any way. 43 | 1. **Non-master branches** ought to be used for ongoing work. 44 | 1. **External API changes and significant modifications** ought to be subject to an **internal pull request** to solicit feedback from other contributors. 45 | 1. Internal pull requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor. 46 | 1. Contributors should attempt to adhere to the prevailing code style. 47 | 48 | ### Releases 49 | 50 | Declaring formal releases remains the prerogative of the project maintainer. 51 | 52 | ### Changes to this arrangement 53 | 54 | This is an experiment and feedback is welcome! This document may also be subject to pull requests or changes by contributors where you believe you have something valuable to add or change. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 Joe Karlsson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Data Structures and Algorithms 2 | 3 | 4 | [![deps][deps]][deps-url] 5 | [![Coverage Status][cover]][cover-url] 6 | [![Build Status][tests]][tests-url] 7 | [![Maintainability][maintainability]][maintainability-url] 8 | [![stars][stars]][stars-url] 9 | [![pr][pr]][pr-url] 10 | [![license][license]][license-url] 11 | [![twitter][twitter]][twitter-url] 12 | [![BCH-compliance][BCH-compliance]][BCH-compliance-url] 13 | [![Greenkeeper badge](https://badges.greenkeeper.io/JoeKarlsson/data-structures.svg)](https://greenkeeper.io/) 14 | 15 | 16 | I put this repository together to get ready for technical interviews. I hope it helps you get ready for your next big technical interview. If you like what you see plz give it a star. Also, feel free to contribute :D 17 | 18 | ## What is an algorithm? 19 | 20 | In simple terms, it is possible to say that an algorithm is a sequence of steps which allow to solve a certain task ( Yes, not just computers use algorithms, humans also use them). Now, an algorithm should have three important characteristics to be considered valid: 21 | 22 | 1. **It should be finite:** If your algorithm never ends trying to solve the problem it was designed to solve then it is useless 23 | 24 | 1. **It should have well defined instructions:** Each step of the algorithm has to be precisely defined; the instructions should be unambiguously specified for each case. 25 | 26 | 1. **It should be effective:** The algorithm should solve the problem it was designed to solve. And it should be possible to demonstrate that the algorithm converges with just a paper and pencil. 27 | 28 | ## Getting Started 29 | 30 | 1. Run `npm install` 31 | 1. Run `npm test` to run all of the algorithms 32 | 33 | 34 | This repo covers the following topics: 35 | 36 | * [Binary Heap](https://github.com/JoeKarlsson/data-structures/tree/master/binary-heap) 37 | * [Bitwise](https://github.com/JoeKarlsson/data-structures/tree/master/bitwise) 38 | * [Complex Arrays](https://github.com/JoeKarlsson/data-structures/tree/master/complex-array) 39 | * [Graph Traversing](https://github.com/JoeKarlsson/data-structures/tree/master/graph-traversing) 40 | * [Breadth First Search](https://github.com/JoeKarlsson/data-structures/blob/master/graph-traversing/breadth-first-search.js) 41 | * [Depth First Search](https://github.com/JoeKarlsson/data-structures/blob/master/graph-traversing/depth-first-search-recursive.js) 42 | * [Graph](https://github.com/JoeKarlsson/data-structures/tree/master/graph) 43 | * [Graph Node](https://github.com/JoeKarlsson/data-structures/blob/master/graph/graphNode.js) 44 | * [Grid Graph](https://github.com/JoeKarlsson/data-structures/blob/master/graph/gridGraph.js) 45 | * [Tree Node](https://github.com/JoeKarlsson/data-structures/blob/master/graph/tree.js) 46 | * [Hash Table](https://github.com/JoeKarlsson/data-structures/tree/master/hash-table) 47 | * [Fast Fourier Transformations](https://github.com/JoeKarlsson/data-structures/tree/master/fast-fourier-transforms) 48 | * [Linked Lists](https://github.com/JoeKarlsson/data-structures/tree/master/linked-list) 49 | * [List](https://github.com/JoeKarlsson/data-structures/tree/master/list) 50 | * [Memoization](https://github.com/JoeKarlsson/data-structures/tree/master/memoization) 51 | * [Queue](https://github.com/JoeKarlsson/data-structures/tree/master/queue) 52 | * [Priority Queue](https://github.com/JoeKarlsson/data-structures/blob/master/queue/priorityQueue.js) 53 | * [Queue](https://github.com/JoeKarlsson/data-structures/blob/master/queue/queue.js) 54 | * [Rabin Karp](https://github.com/JoeKarlsson/data-structures/tree/master/rabin-karp) 55 | * [Search](https://github.com/JoeKarlsson/data-structures/tree/master/search) 56 | * [Binary Search Trees](https://github.com/JoeKarlsson/data-structures/tree/master/search) 57 | * [Linear](https://github.com/JoeKarlsson/data-structures/blob/master/search/linearSearch.js) 58 | * [Shortest Path](https://github.com/JoeKarlsson/data-structures/tree/master/shortest-path) 59 | * [A*](https://github.com/JoeKarlsson/data-structures/blob/master/shortest-path/aStar.js) 60 | * [Dijkstra](https://github.com/JoeKarlsson/data-structures/blob/master/shortest-path/dijkstra.js) 61 | * [Sorting](https://github.com/JoeKarlsson/data-structures/tree/master/sorting-algorithms) 62 | * [Bubble Sort](https://github.com/JoeKarlsson/data-structures/blob/master/sorting/bubblesort.js) 63 | * [Insertion Sort](https://github.com/JoeKarlsson/data-structures/blob/master/sorting/insertionsort.js) 64 | * [Merge Sort](https://github.com/JoeKarlsson/data-structures/blob/master/sorting/mergesort.js) 65 | * [Quick Sort (duh)](https://github.com/JoeKarlsson/data-structures/blob/master/sorting/quicksort.js) 66 | * [Selection Sort](https://github.com/JoeKarlsson/data-structures/blob/master/sorting/selectionsort.js) 67 | * [Stack](https://github.com/JoeKarlsson/data-structures/tree/master/stack) 68 | * [Trie](https://github.com/JoeKarlsson/data-structures/tree/master/trie) 69 | 70 | 71 | An algorithm is a self-contained step-by-step set of operations to be performed. Algorithms perform calculation, data processing, and/or automated reasoning tasks. 72 | 73 | "Elegant" (compact) programs, "good" (fast) programs : The notion of "simplicity and elegance" appears informally in Knuth and precisely in Chaitin: 74 | 75 | Knuth: ". . .we want good algorithms in some loosely defined aesthetic sense. One criterion . . . is the length of time taken to perform the algorithm . . .. Other criteria are adaptability of the algorithm to computers, its simplicity and elegance, etc" 76 | 77 | Chaitin: " . . . a program is 'elegant,' by which I mean that it's the smallest possible program for producing the output that it does" 78 | 79 | ### TODO: 80 | - Timsort 81 | - Floyd-Warshall 82 | - Traveling Salesman 83 | - k-way merge 84 | - Matching users to servers, using Gayle-Shapely Algorithm for Stable Marriage problem 85 | - This is a beautiful algorithm for fair matching. Simple, elegant and effective. In its core form, it’s also straightforward to implement. Has numerous applications. See: Stable marriage problem - Wikipedia 86 | - A toy implementation of Viterbi algorithm 87 | - Ubiquitous in cell phone technology, and many other applications, Viterbi algorithm is a Dynamic Programming based algorithm that finds the most likely sequence of states. 88 | - SSL transport, is the bane of safe existence on Internet these days. One of the most well-known algorithms in secure transport, is RSA, named by the first initials of its inventors. Implementing RSA is fun and instructive e.g. C code to implement RSA Algorithm(Encryption and Decryption) 89 | - Safe Browsing (or similar) using Bloom filters 90 | - Bloom filters found very rare usage until the world got more online and we hit scale. But these days, we see new applications very frequently.Chrome browser uses Bloom filters to make preliminary decision on safe browsing. See some novel applications here. 91 | - Implement an LALR parser 92 | - As a CS student, you may have already implemented it as part of your compiler’s class. But if not, then you should. LALR parsing makes syntactic sense of source code, whichever language you use. Many implementations of LALR exist. e.g. Where can I find a _simple_, easy to understand implementation of an LR(1) parser generator? Also, use YACC to understand LALR parsing better. 93 | - Treemap using Red Black Trees! 94 | - RB Trees are not algorithms, but they are famed enough, that no discussion of tantalizing DS/Algorithms is complete without discussing them. The smoothest way to see/implement RB Trees, is to look at Treemap implementation in Java. 95 | - Circle Drawing using Bresenham’s algorithm 96 | - Ever wondered, how circles are drawn on the screen, with minimal jaggedness (aliasing)? Bresenham’s elegant algorithm is at play here. See a version here: Circle Generation Algorithm . A refreshing use of a similar algorithm, is to make properly sized tabs in Chrome. Something we see almost every day. Such hidden gems! 97 | - Implement PageRank 98 | - Can’t miss this. This transformed our lives in ways we never thought possible. Get started here: Pagerank Explained Correctly with Examples 99 | 100 | ## Contributing 101 | Don't hesitate to create a pull request. Every contribution is appreciated. In development you can start the tests by calling `npm test`. Checkout our [contribution README](https://github.com/JoeKarlsson/data-structures/blob/master/CONTRIBUTING.md) for more info. 102 | 103 | 1. Fork it! 104 | 2. Create your feature branch: ```git checkout -b my-new-feature``` 105 | 3. Commit your changes: ```git commit -am 'Add some feature'``` 106 | 4. Push to the branch: ````git push origin my-new-feature```` 107 | 5. Submit a pull request :D 108 | 109 |

Maintainers

110 | 111 | 112 | 113 | 114 | 120 | 121 | 122 |
115 | 117 |
118 | Joe Karlsson 119 |
123 | 124 |

LICENSE

125 | 126 | #### [MIT](./LICENSE) 127 | 128 | [deps]: https://david-dm.org/JoeKarlsson/data-structures/status.svg 129 | [deps-url]: https://david-dm.org/JoeKarlsson/data-structures 130 | 131 | [tests]: https://travis-ci.org/JoeKarlsson/data-structures.svg?branch=master 132 | [tests-url]: https://travis-ci.org/JoeKarlsson/data-structures 133 | 134 | [maintainability]: https://api.codeclimate.com/v1/badges/462bdda833cde7f72e01/maintainability 135 | [maintainability-url]: https://codeclimate.com/github/JoeKarlsson/data-structures/maintainability 136 | 137 | [pr]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg 138 | [pr-url]: CONTRIBUTING.md 139 | 140 | [cover]: https://coveralls.io/repos/github/JoeKarlsson/data-structures/badge.svg?branch=master 141 | [cover-url]: https://coveralls.io/github/JoeKarlsson/data-structures?branch=master 142 | 143 | [stars]: https://img.shields.io/github/stars/JoeKarlsson/data-structures.svg?style=flat-square 144 | [stars-url]: https://github.com/JoeKarlsson/data-structures/stargazers 145 | 146 | [license]: https://img.shields.io/github/license/JoeKarlsson/data-structures.svg 147 | [license-url]: https://github.com/JoeKarlsson/data-structures/blob/master/LICENSE 148 | 149 | [twitter]: https://img.shields.io/twitter/url/https/github.com/JoeKarlsson/data-structures.svg?style=social&style=flat-square 150 | [twitter-url]: https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2FJoeKarlsson%2Fdata-structures 151 | 152 | [BCH-compliance]: https://bettercodehub.com/edge/badge/JoeKarlsson/data-structures?branch=master 153 | [BCH-compliance-url]: https://bettercodehub.com/ 154 | -------------------------------------------------------------------------------- /binary-heap/README.md: -------------------------------------------------------------------------------- 1 | http://eloquentjavascript.net/1st_edition/appendix2.html 2 | # Binary Heap 3 | 4 | A binary heap is a heap data structure that takes the form of a binary tree. Binary heaps are a common way of implementing priority queues. 5 | 6 | A binary heap is defined as a binary tree with two additional constraints. 7 | 8 | Shape property: a binary heap is a complete binary tree; that is, all levels of the tree, except possibly the last one (deepest) are fully filled, and, if the last level of the tree is not complete, the nodes of that level are filled from left to right. 9 | 10 | Heap property: the key stored in each node is either greater than or equal to or less than or equal to the keys in the node's children, according to some total order. 11 | 12 | |____ |Average |Worst Case | 13 | |----------|------------|------------| 14 | |Space |`O(n)` |`O(n)` | 15 | |Search |`O(n)` |`O(n)` | 16 | |Insert |`O(1)` |`O(log n)` | 17 | |Delete |`O(log n)` |`O(log n)` | 18 | |Peek |`O(1)` |`O(1)` | 19 | 20 | ### Pseudocode 21 | 22 | ``` 23 | Max-Heapify (A, i): 24 | left ← 2*i // ← means "assignment" 25 | right ← 2*i + 1 26 | largest ← i 27 | 28 | if left ≤ heap_length[A] and A[left] > A[largest] then: 29 | largest ← left 30 | if right ≤ heap_length[A] and A[right] > A[largest] then: 31 | largest ← right 32 | 33 | if largest ≠ i then: 34 | swap A[i] and A[largest] 35 | Max-Heapify(A, largest) 36 | ``` 37 | 38 | # Additional Resources 39 | 40 | #### Binary Heap - Wikipedia 41 | - Link: [Binary heap - Wikipedia](https://en.wikipedia.org/wiki/Binary_heap) 42 | 43 | #### Binary Heap - Eloquent JavaScript 44 | - Link: [Binary heap - Eloquent JavaScript](http://eloquentjavascript.net/1st_edition/appendix2.html) -------------------------------------------------------------------------------- /binary-heap/binaryHeap.js: -------------------------------------------------------------------------------- 1 | function BinaryHeap(scoreFunction){ 2 | this.content = []; 3 | this.scoreFunction = scoreFunction; 4 | } 5 | 6 | BinaryHeap.prototype = { 7 | push: function(element) { 8 | // Add the new element to the end of the array. 9 | this.content.push(element); 10 | // Allow it to bubble up. 11 | this.bubbleUp(this.content.length - 1); 12 | }, 13 | 14 | pop: function() { 15 | // Store the first element so we can return it later. 16 | let result = this.content[0]; 17 | // Get the element at the end of the array. 18 | let end = this.content.pop(); 19 | // If there are any elements left, put the end element at the 20 | // start, and let it sink down. 21 | if (this.content.length > 0) { 22 | this.content[0] = end; 23 | this.sinkDown(0); 24 | } 25 | return result; 26 | }, 27 | 28 | remove: function(node) { 29 | const length = this.content.length; 30 | // To remove a value, we must search through the array to find 31 | // it. 32 | for (let i = 0; i < length; i++) { 33 | if (this.content[i] != node) continue; 34 | // When it is found, the process seen in 'pop' is repeated 35 | // to fill up the hole. 36 | const end = this.content.pop(); 37 | // If the element we popped was the one we needed to remove, 38 | // we're done. 39 | if (i == length - 1) break; 40 | // Otherwise, we replace the removed element with the popped 41 | // one, and allow it to float up or sink down as appropriate. 42 | this.content[i] = end; 43 | this.bubbleUp(i); 44 | this.sinkDown(i); 45 | break; 46 | } 47 | }, 48 | 49 | size: function() { 50 | return this.content.length; 51 | }, 52 | 53 | bubbleUp: function(n) { 54 | // Fetch the element that has to be moved. 55 | const element = this.content[n], score = this.scoreFunction(element); 56 | // When at 0, an element can not go up any further. 57 | while (n > 0) { 58 | // Compute the parent element's index, and fetch it. 59 | const parentN = Math.floor((n + 1) / 2) - 1, 60 | parent = this.content[parentN]; 61 | // If the parent has a lesser score, things are in order and we 62 | // are done. 63 | if (score >= this.scoreFunction(parent)) 64 | break; 65 | 66 | // Otherwise, swap the parent with the current element and 67 | // continue. 68 | this.content[parentN] = element; 69 | this.content[n] = parent; 70 | n = parentN; 71 | } 72 | }, 73 | 74 | sinkDown: function(n) { 75 | // Look up the target element and its score. 76 | const length = this.content.length, 77 | element = this.content[n], 78 | elemScore = this.scoreFunction(element); 79 | 80 | while(true) { 81 | let child1; 82 | // Compute the indices of the child elements. 83 | const child2N = (n + 1) * 2, child1N = child2N - 1; 84 | // This is used to store the new position of the element, 85 | // if any. 86 | let swap = null; 87 | // If the first child exists (is inside the array)... 88 | if (child1N < length) { 89 | // Look it up and compute its score. 90 | child1 = this.content[child1N], 91 | child1Score = this.scoreFunction(child1); 92 | // If the score is less than our element's, we need to swap. 93 | if (child1Score < elemScore) 94 | swap = child1N; 95 | } 96 | // Do the same checks for the other child. 97 | if (child2N < length) { 98 | let child2 = this.content[child2N], 99 | child2Score = this.scoreFunction(child2); 100 | if (child2Score < (swap == null ? elemScore : child1Score)) 101 | swap = child2N; 102 | } 103 | 104 | // No need to swap further, we are done. 105 | if (swap == null) break; 106 | 107 | // Otherwise, swap and continue. 108 | this.content[n] = this.content[swap]; 109 | this.content[swap] = element; 110 | n = swap; 111 | } 112 | } 113 | }; 114 | 115 | module.exports = BinaryHeap; 116 | 117 | // Test 118 | // const heap = new BinaryHeap((x) => x); 119 | // const data = [10, 3, 4, 8, 2, 9, 7, 1, 2, 6, 5]; 120 | 121 | // data.forEach((el) => { 122 | // heap.push(el) 123 | // }); 124 | 125 | // heap.remove(2); 126 | // while (heap.size() > 0) { 127 | // console.log(heap.pop()); 128 | // }; 129 | -------------------------------------------------------------------------------- /bitwise/README.md: -------------------------------------------------------------------------------- 1 | # BitWise 2 | 3 | Bitwise AND a & b Returns a one in each bit position for which the corresponding bits of both operands are ones. 4 | Bitwise OR a | b Returns a one in each bit position for which the corresponding bits of either or both operands are ones. 5 | Bitwise XOR a ^ b Returns a one in each bit position for which the corresponding bits of either but not both operands are ones. 6 | Bitwise NOT ~ a Inverts the bits of its operand. 7 | Left shift a << b Shifts a in binary representation b (< 32) bits to the left, shifting in zeroes from the right. 8 | Sign-propagating right shift a >> b Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off. 9 | Zero-fill right shift a >>> b Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off, and shifting in zeroes from the left. 10 | 11 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#>>>_(Zero-fill_right_shift) 12 | - http://michalbe.blogspot.com.br/2013/03/javascript-less-known-parts-bitwise.html 13 | - http://jsperf.com/bitwise-vs-math-object 14 | - http://united-coders.com/christian-harms/results-for-game-for-forfeits-and-the-winner-is/ 15 | - https://mudcu.be/journal/2011/11/bitwise-gems-and-other-optimizations/ 16 | - https://dreaminginjavascript.wordpress.com/2009/02/09/bitwise-byte-foolish/ 17 | - http://jsperf.com/math-min-max-vs-ternary-vs-if/24 18 | -------------------------------------------------------------------------------- /bitwise/bitwise-basics.js: -------------------------------------------------------------------------------- 1 | const bitwiseAND = (a, b) => { 2 | return (a & b).toString(2); 3 | }; 4 | 5 | const bitwiseOR = (a, b) => { 6 | return (a | b).toString(2); 7 | }; 8 | 9 | const bitwiseXOR = (a, b) => { 10 | return (a ^ b).toString(2); 11 | }; 12 | 13 | const bitwiseNOT = (a) => { 14 | return (~a >>> 0).toString(2); 15 | }; 16 | 17 | const bitwiseLeftShift = (a, bits) => { 18 | return (a << bits).toString(2); 19 | }; 20 | 21 | const bitwiseSignPropagatingRightShift = (a, bits) => { 22 | return (a >> bits).toString(2); 23 | }; 24 | 25 | const bitwiseZeroFillRightShift = (a, bits) => { 26 | return (a >>> bits).toString(2); 27 | }; 28 | 29 | const isEven = (n) => { 30 | return !(n & 1); 31 | }; 32 | 33 | const isOdd = (n) => { 34 | return ((n & 1) == 1); 35 | }; 36 | 37 | const dec2bin = (dec) => { 38 | return (dec >>> 0).toString(2); 39 | }; 40 | // Source: http://stackoverflow.com/questions/9939760/how-do-i-convert-an-integer-to-binary-in-javascript 41 | 42 | const avgInt = (a, b) => { // a + b / 2 43 | return (a + b) >> 1; 44 | } 45 | 46 | const plusOneInt = (n) => { // slower than ++ 47 | return -~n; 48 | }; 49 | 50 | module.exports = { 51 | bitwiseAND, 52 | bitwiseOR, 53 | bitwiseXOR, 54 | bitwiseNOT, 55 | bitwiseLeftShift, 56 | bitwiseSignPropagatingRightShift, 57 | bitwiseZeroFillRightShift, 58 | isEven, 59 | isOdd, 60 | dec2bin, 61 | avgInt, 62 | plusOneInt, 63 | }; 64 | -------------------------------------------------------------------------------- /bitwise/bitwise-rgb-hex-binary.js: -------------------------------------------------------------------------------- 1 | // convert 0..255 R,G,B values to binary string 2 | const RGBToBin = ( r, g, b ) => { 3 | const bin = r << 16 | g << 8 | b; 4 | return ((h) => { 5 | return new Array(25 - h.length).join( '0' ) + h; 6 | })(bin.toString(2)); 7 | }; 8 | 9 | // convert 0..255 R,G,B values to a hexidecimal color string 10 | const RGBToHex = ( r, g, b ) => { 11 | const bin = r << 16 | g << 8 | b; 12 | return (( h ) => { 13 | return new Array(7 - h.length).join( '0' ) + h; 14 | })( bin.toString( 16 ).toUpperCase() ); 15 | }; 16 | 17 | // convert a 24 bit binary color to 0..255 R,G,B 18 | const binToRGB = ( bin ) => { 19 | const pbin = parseInt( bin, 2 ); 20 | const r = pbin >> 16; 21 | const g = pbin >> 8 & 0xFF; 22 | const b = pbin & 0xFF; 23 | return [r, g, b]; 24 | }; 25 | 26 | // convert a hexidecimal color string to 0..255 R,G,B 27 | const hexToRGB = ( hex ) => { 28 | const r = hex >> 16; 29 | const g = hex >> 8 & 0xFF; 30 | const b = hex & 0xFF; 31 | return [r, g, b]; 32 | }; 33 | 34 | module.exports = { 35 | RGBToBin, 36 | RGBToHex, 37 | binToRGB, 38 | hexToRGB, 39 | }; 40 | 41 | // source: https://gist.github.com/lrvick/2080648 42 | -------------------------------------------------------------------------------- /complex-array/README.md: -------------------------------------------------------------------------------- 1 | # Complex Arrays 2 | 3 | A complex variable or value is usually represented as a pair of floating point numbers. Languages that support a complex data type usually provide special syntax for building such values, and extend the basic arithmetic operations ('+', '−', '×', '÷') to act on them. These operations are usually translated by the compiler into a sequence of floating-point machine instructions or into library calls. Those languages may also provide support for other operations, such as formatting, equality testing, etc. As in mathematics, those languages often interpret a floating-point value as equivalent to a complex value with a zero imaginary part. 4 | 5 | -------------------------------------------------------------------------------- /complex-array/complex-array.js: -------------------------------------------------------------------------------- 1 | class ComplexArray { 2 | constructor(other, arrayType = Float32Array) { 3 | if (other instanceof ComplexArray) { 4 | // Copy constuctor. 5 | this.ArrayType = other.ArrayType; 6 | this.real = new this.ArrayType(other.real); 7 | this.imag = new this.ArrayType(other.imag); 8 | } else { 9 | this.ArrayType = arrayType; 10 | // other can be either an array or a number. 11 | this.real = new this.ArrayType(other); 12 | this.imag = new this.ArrayType(this.real.length); 13 | } 14 | 15 | this.length = this.real.length; 16 | } 17 | 18 | toString() { 19 | const components = []; 20 | 21 | this.forEach((value, i) => { 22 | components.push( 23 | `(${value.real.toFixed(2)}, ${value.imag.toFixed(2)})` 24 | ); 25 | }); 26 | 27 | return `[${components.join(', ')}]`; 28 | } 29 | 30 | forEach(iterator) { 31 | const n = this.length; 32 | // For gc efficiency, re-use a single object in the iterator. 33 | const value = Object.seal(Object.defineProperties({}, { 34 | real: {writable: true}, imag: {writable: true}, 35 | })); 36 | 37 | for (let i = 0; i < n; i++) { 38 | value.real = this.real[i]; 39 | value.imag = this.imag[i]; 40 | iterator(value, i, n); 41 | } 42 | } 43 | 44 | // In-place mapper. 45 | map(mapper) { 46 | this.forEach((value, i, n) => { 47 | mapper(value, i, n); 48 | this.real[i] = value.real; 49 | this.imag[i] = value.imag; 50 | }); 51 | 52 | return this; 53 | } 54 | 55 | conjugate() { 56 | return new ComplexArray(this).map((value) => { 57 | value.imag *= -1; 58 | }); 59 | } 60 | 61 | magnitude() { 62 | const mags = new this.ArrayType(this.length); 63 | 64 | this.forEach((value, i) => { 65 | mags[i] = Math.sqrt(value.real*value.real + value.imag*value.imag); 66 | }) 67 | 68 | return mags; 69 | } 70 | } 71 | 72 | module.exports = ComplexArray; 73 | -------------------------------------------------------------------------------- /fast-fourier-transforms/README.md: -------------------------------------------------------------------------------- 1 | # Fast Fourier Transforms (FFT) 2 | 3 | ### Music Search using Fast Fourier Transforms (FFT) 4 | 5 | Music recognition is done by converting it into frequency domain using FFT. FFT has implementations in number of languages. See this article for a great start: Shazam It! Music Recognition Algorithms, Fingerprinting, and Processing. -------------------------------------------------------------------------------- /fast-fourier-transforms/fft.js: -------------------------------------------------------------------------------- 1 | const baseComplexArray = require('../complex-array/complex-array'); 2 | 3 | // Math constants and functions we need. 4 | const PI = Math.PI; 5 | const SQRT1_2 = Math.SQRT1_2; 6 | 7 | function FFT(input) { 8 | return ensureComplexArray(input).FFT(); 9 | }; 10 | 11 | function InvFFT(input) { 12 | return ensureComplexArray(input).InvFFT(); 13 | }; 14 | 15 | function frequencyMap(input, filterer) { 16 | return ensureComplexArray(input).frequencyMap(filterer); 17 | }; 18 | 19 | class ComplexArray extends baseComplexArray { 20 | FFT() { 21 | return fft(this, false); 22 | } 23 | 24 | InvFFT() { 25 | return fft(this, true); 26 | } 27 | 28 | // Applies a frequency-space filter to input, and returns the real-space 29 | // filtered input. 30 | // filterer accepts freq, i, n and modifies freq.real and freq.imag. 31 | frequencyMap(filterer) { 32 | return this.FFT().map(filterer).InvFFT(); 33 | } 34 | } 35 | 36 | function ensureComplexArray(input) { 37 | return input instanceof ComplexArray && input || new ComplexArray(input); 38 | } 39 | 40 | function fft(input, inverse) { 41 | const n = input.length; 42 | 43 | if (n & (n - 1)) { 44 | return FFT_Recursive(input, inverse); 45 | } else { 46 | return FFT_2_Iterative(input, inverse); 47 | } 48 | } 49 | 50 | function FFT_Recursive(input, inverse) { 51 | const n = input.length; 52 | 53 | if (n === 1) { 54 | return input; 55 | } 56 | 57 | const output = new ComplexArray(n, input.ArrayType); 58 | 59 | // Use the lowest odd factor, so we are able to use FFT_2_Iterative in the 60 | // recursive transforms optimally. 61 | const p = LowestOddFactor(n); 62 | const m = n / p; 63 | const normalisation = 1 / Math.sqrt(p); 64 | let recursive_result = new ComplexArray(m, input.ArrayType); 65 | 66 | // Loops go like O(n Σ p_i), where p_i are the prime factors of n. 67 | // for a power of a prime, p, this reduces to O(n p log_p n) 68 | for(let j = 0; j < p; j++) { 69 | for(let i = 0; i < m; i++) { 70 | recursive_result.real[i] = input.real[i * p + j]; 71 | recursive_result.imag[i] = input.imag[i * p + j]; 72 | } 73 | // Don't go deeper unless necessary to save allocs. 74 | if (m > 1) { 75 | recursive_result = fft(recursive_result, inverse); 76 | } 77 | 78 | const del_f_r = Math.cos(2*PI*j/n); 79 | const del_f_i = (inverse ? -1 : 1) * Math.sin(2*PI*j/n); 80 | let f_r = 1; 81 | let f_i = 0; 82 | 83 | for(let i = 0; i < n; i++) { 84 | const _real = recursive_result.real[i % m]; 85 | const _imag = recursive_result.imag[i % m]; 86 | 87 | output.real[i] += f_r * _real - f_i * _imag; 88 | output.imag[i] += f_r * _imag + f_i * _real; 89 | 90 | [f_r, f_i] = [ 91 | f_r * del_f_r - f_i * del_f_i, 92 | f_i = f_r * del_f_i + f_i * del_f_r, 93 | ]; 94 | } 95 | } 96 | 97 | // Copy back to input to match FFT_2_Iterative in-placeness 98 | // TODO: faster way of making this in-place? 99 | for(let i = 0; i < n; i++) { 100 | input.real[i] = normalisation * output.real[i]; 101 | input.imag[i] = normalisation * output.imag[i]; 102 | } 103 | 104 | return input; 105 | } 106 | 107 | function FFT_2_Iterative(input, inverse) { 108 | const n = input.length; 109 | 110 | const output = BitReverseComplexArray(input); 111 | const output_r = output.real; 112 | const output_i = output.imag; 113 | // Loops go like O(n log n): 114 | // width ~ log n; i,j ~ n 115 | let width = 1; 116 | while (width < n) { 117 | const del_f_r = Math.cos(PI/width); 118 | const del_f_i = (inverse ? -1 : 1) * Math.sin(PI/width); 119 | for (let i = 0; i < n/(2*width); i++) { 120 | let f_r = 1; 121 | let f_i = 0; 122 | for (let j = 0; j < width; j++) { 123 | const l_index = 2*i*width + j; 124 | const r_index = l_index + width; 125 | 126 | const left_r = output_r[l_index]; 127 | const left_i = output_i[l_index]; 128 | const right_r = f_r * output_r[r_index] - f_i * output_i[r_index]; 129 | const right_i = f_i * output_r[r_index] + f_r * output_i[r_index]; 130 | 131 | output_r[l_index] = SQRT1_2 * (left_r + right_r); 132 | output_i[l_index] = SQRT1_2 * (left_i + right_i); 133 | output_r[r_index] = SQRT1_2 * (left_r - right_r); 134 | output_i[r_index] = SQRT1_2 * (left_i - right_i); 135 | 136 | [f_r, f_i] = [ 137 | f_r * del_f_r - f_i * del_f_i, 138 | f_r * del_f_i + f_i * del_f_r, 139 | ]; 140 | } 141 | } 142 | width <<= 1; 143 | } 144 | 145 | return output; 146 | } 147 | 148 | function BitReverseIndex(index, n) { 149 | let bitreversed_index = 0; 150 | 151 | while (n > 1) { 152 | bitreversed_index <<= 1; 153 | bitreversed_index += index & 1; 154 | index >>= 1; 155 | n >>= 1; 156 | } 157 | return bitreversed_index; 158 | } 159 | 160 | function BitReverseComplexArray(array) { 161 | const n = array.length; 162 | const flips = new Set(); 163 | 164 | for(let i = 0; i < n; i++) { 165 | const r_i = BitReverseIndex(i, n); 166 | 167 | if (flips.has(i)) continue; 168 | 169 | [array.real[i], array.real[r_i]] = [array.real[r_i], array.real[i]]; 170 | [array.imag[i], array.imag[r_i]] = [array.imag[r_i], array.imag[i]]; 171 | 172 | flips.add(r_i); 173 | } 174 | 175 | return array; 176 | } 177 | 178 | function LowestOddFactor(n) { 179 | const sqrt_n = Math.sqrt(n); 180 | let factor = 3; 181 | 182 | while(factor <= sqrt_n) { 183 | if (n % factor === 0) return factor; 184 | factor += 2; 185 | } 186 | return n; 187 | } 188 | 189 | module.exports = { 190 | FFT, 191 | InvFFT, 192 | frequencyMap, 193 | ComplexArray, 194 | } -------------------------------------------------------------------------------- /graph-traversing/README.md: -------------------------------------------------------------------------------- 1 | # Graph Traversing/Searching Algorithms 2 | 3 | ## Breadth-first search (BFS) 4 | 5 | Breadth-first search (BFS) is an algorithm for traversing or searching tree or graph data structures. It starts at the tree root (or some arbitrary node of a graph, sometimes referred to as a 'search key'[1]) and explores the neighbor nodes first, before moving to the next level neighbors. 6 | 7 | |____ |____ | 8 | |-----------------------------|-----------------| 9 | |Class |Search algorithms| 10 | |Data structure |Graph | 11 | |Worst case performance |`O(|E|)=O(b^{d})`| 12 | |Worst case space complexity |`O(|V|)=O(b^{d})`| 13 | 14 | ![Animated example of a breadth-first search](https://upload.wikimedia.org/wikipedia/commons/4/46/Animated_BFS.gif) 15 | 16 | ### Pseudocode 17 | 18 | *Input:* A graph Graph and a starting vertex root of Graph 19 | *Output:* All vertices reachable from root labeled as explored. 20 | A non-recursive implementation of breadth-first search: 21 | 22 | ``` 23 | Breadth-First-Search(Graph, root): 24 | 25 | for each node n in Graph: 26 | n.distance = INFINITY 27 | n.parent = NIL 28 | 29 | create empty queue Q 30 | 31 | root.distance = 0 32 | Q.enqueue(root) 33 | 34 | while Q is not empty: 35 | 36 | current = Q.dequeue() 37 | 38 | for each node n that is adjacent to current: 39 | if n.distance == INFINITY: 40 | n.distance = current.distance + 1 41 | n.parent = current 42 | Q.enqueue(n) 43 | ``` 44 | 45 | ## Depth-first search 46 | 47 | Depth-first search (DFS) is an algorithm for traversing or searching tree or graph data structures. One starts at the root (selecting some arbitrary node as the root in the case of a graph) and explores as far as possible along each branch before backtracking. 48 | 49 | |____ |____ | 50 | |----------------------------|-----------------| 51 | |Class |Search algorithm | 52 | |Data structure |Graph | 53 | |Worst case performance |`O(|E|)` | 54 | |Worst case space complexity |`O(|V|)` | 55 | 56 | ![Order in which the nodes are visited](https://upload.wikimedia.org/wikipedia/commons/1/1f/Depth-first-tree.svg) 57 | 58 | ### Pseudocode 59 | *Input:* A graph G and a vertex v of G 60 | *Output:* All vertices reachable from v labeled as discovered 61 | A recursive implementation of DFS: 62 | 63 | ``` 64 | procedure DFS(G,v): 65 | label v as discovered 66 | for all edges from v to w in G.adjacentEdges(v) do 67 | if vertex w is not labeled as discovered then 68 | recursively call DFS(G,w) 69 | ``` 70 | 71 | # Additional Resources 72 | 73 | #### Graph - Wikipedia 74 | - Link: [Graph (Abstract Data Type) - Wikipedia](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)) 75 | - Concepts: *Graph Node*, *Graph theory*, *search* and *depth first search* 76 | 77 | #### Depth First Search - Wikipedia 78 | - Link: [Depth First Search - Wikipedia](https://en.wikipedia.org/wiki/Depth-first_search) 79 | - Concepts: *Graph Node*, *Graph theory*, *search* and *depth first search* 80 | 81 | #### Breadth First Search - Wikipedia 82 | - Link: [Breadth First Search - Wikipedia](https://en.wikipedia.org/wiki/Breadth-first_search) 83 | - Concepts: *Graph Node*, *Graph theory*, *search* and *breadth first search* 84 | 85 | #### The breadth-first search algorithm - Khan Academy 86 | - Link: [The breadth-first search algorithm - Khan Academy](https://www.khanacademy.org/computing/computer-science/algorithms/breadth-first-search/a/the-breadth-first-search-algorithm) 87 | - Concepts: *Trees*, *Data Structures* 88 | 89 | #### Labyrinth Algorithms DFS and BFS Visulaization 90 | - Link: [Labyrinth Algorithms DFS and BFS Visulaization](http://bryukh.com/labyrinth-algorithms/) 91 | - Concepts: *Graph Node*, *Graph theory*, *search* and *depth first search* 92 | - Notes: Great site that visualizes BFS and DFS -------------------------------------------------------------------------------- /graph-traversing/breadth-first-search.js: -------------------------------------------------------------------------------- 1 | /* 2 | In a breadth first search you will start at the root node. 3 | You will then search all their children nodes moving from left to right. 4 | Once all the children nodes have been searched, the process is repeated 5 | on the level below the root node. 6 | 7 | This process is repeated on each level until you reach the end of the 8 | tree or you reach the node that you were searching for initially. 9 | The image below shows you the order that you will search a tree in a breadth first search. 10 | */ 11 | 12 | const bfs = (start) => { 13 | // initailize the open to be the nodes we are currently exploring 14 | const open = []; 15 | open.push(start); 16 | 17 | // Keep track of the nodes we have already visited, so we don't repeat nodes 18 | const visitedNode = [start]; 19 | const searchPath = []; 20 | 21 | // Keep checking nodes until our open array is empty 22 | while (open.length > 0) { 23 | 24 | // Pull the first item off of our queue 25 | const current = open.shift(); 26 | 27 | // Add the current node to our search path stack 28 | searchPath.push(current.name); 29 | 30 | // Iterate through all of the neighbors of the current node 31 | current.neighbors.forEach((next) => { 32 | // If we haven't already visisted a node, add it to our 33 | // visisted stack, and add it to our open queue 34 | if (visitedNode.indexOf(next) < 0) { 35 | visitedNode.push(next); 36 | open.push(next); 37 | } 38 | }); 39 | } 40 | 41 | // Once we have traversed the whole graph - return the search path 42 | return searchPath; 43 | }; 44 | 45 | module.exports = bfs; 46 | -------------------------------------------------------------------------------- /graph-traversing/depth-first-search-imperative.js: -------------------------------------------------------------------------------- 1 | const DFS = (start, searchFor) => { 2 | const stack = [start]; 3 | const visited = []; 4 | let currNode = start; 5 | 6 | while ( stack.length !== 0) { 7 | currNode = stack.pop(); 8 | if (currNode.value === searchFor) { 9 | return currNode; 10 | } 11 | if (visited.indexOf(currNode) === -1) { 12 | visited.push(currNode); 13 | currNode.neighbors.forEach((w) => { 14 | stack.push(w); 15 | }); 16 | } 17 | } 18 | 19 | if (stack.length === 0) { 20 | return false; 21 | } 22 | }; 23 | 24 | module.exports = DFS; 25 | -------------------------------------------------------------------------------- /graph-traversing/depth-first-search-recursive.js: -------------------------------------------------------------------------------- 1 | /* 2 | 1 procedure DFS(G,v): 3 | 2 label v as discovered 4 | 3 for all edges from v to w in G.adjacentEdges(v) do 5 | 4 if vertex w is not labeled as discovered then 6 | 5 recursively call DFS(G,w) 7 | */ 8 | const dfs = (start, searchFor) => { 9 | if (!searchFor || !start) { 10 | throw new Error('Invalid input'); 11 | } 12 | 13 | // If the node we are searching for 14 | if (searchFor === start.getValue()) { 15 | return start; 16 | } 17 | let child; 18 | let found; 19 | const neighbors = start.getNeighbors(); 20 | 21 | // iterate through all of the starting nodes neighbors 22 | for (let i = 0; i < neighbors.length; i++) { 23 | child = neighbors[i]; 24 | 25 | // Recursviely call the child nodes until we find 26 | found = dfs(child, searchFor); 27 | 28 | // If we find the item we are searching for - return the node 29 | if (found) { 30 | return found; 31 | } 32 | } 33 | // If we cannot find the node - return false; 34 | return false; 35 | }; 36 | 37 | module.exports = dfs; 38 | -------------------------------------------------------------------------------- /graph/README.md: -------------------------------------------------------------------------------- 1 | # Graph Node 2 | 3 | A graph is a representation of a set of objects where some pairs of objects are connected by links. The interconnected objects are represented by mathematical abstractions called vertices (also called nodes or points), and the links that connect some pairs of vertices are called edges (also called arcs or lines).[1] Typically, a graph is depicted in diagrammatic form as a set of dots for the vertices, joined by lines or curves for the edges. Graphs are one of the objects of study in discrete mathematics. 4 | 5 | - [ ] Skiena Lectures - great intro: 6 | - [ ] [CSE373 2012 - Lecture 11 - Graph Data Structures (video)](https://www.youtube.com/watch?v=OiXxhDrFruw&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b&index=11) 7 | - [ ] [CSE373 2012 - Lecture 12 - Breadth-First Search (video)](https://www.youtube.com/watch?v=g5vF8jscteo&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b&index=12) 8 | - [ ] [CSE373 2012 - Lecture 13 - Graph Algorithms (video)](https://www.youtube.com/watch?v=S23W6eTcqdY&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b&index=13) 9 | - [ ] [CSE373 2012 - Lecture 14 - Graph Algorithms (con't) (video)](https://www.youtube.com/watch?v=WitPBKGV0HY&index=14&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b) 10 | - [ ] [CSE373 2012 - Lecture 15 - Graph Algorithms (con't 2) (video)](https://www.youtube.com/watch?v=ia1L30l7OIg&index=15&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b) 11 | - [ ] [CSE373 2012 - Lecture 16 - Graph Algorithms (con't 3) (video)](https://www.youtube.com/watch?v=jgDOQq6iWy8&index=16&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b) 12 | 13 | - [ ] Graphs (review and more): 14 | 15 | - [ ] [6.006 Single-Source Shortest Paths Problem (video)](https://www.youtube.com/watch?v=Aa2sqUhIn-E&index=15&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb) 16 | - [ ] [6.006 Dijkstra (video)](https://www.youtube.com/watch?v=2E7MmKv0Y24&index=16&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb) 17 | - [ ] [6.006 Bellman-Ford (video)](https://www.youtube.com/watch?v=ozsuci5pIso&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=17) 18 | - [ ] [6.006 Speeding Up Dijkstra (video)](https://www.youtube.com/watch?v=CHvQ3q_gJ7E&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=18) 19 | - [ ] [Aduni: Graph Algorithms I - Topological Sorting, Minimum Spanning Trees, Prim's Algorithm - Lecture 6 (video)]( https://www.youtube.com/watch?v=i_AQT_XfvD8&index=6&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm) 20 | - [ ] [Aduni: Graph Algorithms II - DFS, BFS, Kruskal's Algorithm, Union Find Data Structure - Lecture 7 (video)]( https://www.youtube.com/watch?v=ufj5_bppBsA&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=7) 21 | - [ ] [Aduni: Graph Algorithms III: Shortest Path - Lecture 8 (video)](https://www.youtube.com/watch?v=DiedsPsMKXc&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=8) 22 | - [ ] [Aduni: Graph Alg. IV: Intro to geometric algorithms - Lecture 9 (video)](https://www.youtube.com/watch?v=XIAQRlNkJAw&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=9) 23 | - [ ] [CS 61B 2014 (starting at 58:09) (video)](https://youtu.be/dgjX4HdMI-Q?list=PL-XXv-cvA_iAlnI-BQr9hjqADPBtujFJd&t=3489) 24 | - [ ] [CS 61B 2014: Weighted graphs (video)](https://www.youtube.com/watch?v=aJjlQCFwylA&list=PL-XXv-cvA_iAlnI-BQr9hjqADPBtujFJd&index=19) 25 | - [ ] [Greedy Algorithms: Minimum Spanning Tree (video)](https://www.youtube.com/watch?v=tKwnms5iRBU&index=16&list=PLUl4u3cNGP6317WaSNfmCvGym2ucw3oGp) 26 | - [ ] [Strongly Connected Components Kosaraju's Algorithm Graph Algorithm (video)](https://www.youtube.com/watch?v=RpgcYiky7uw) 27 | 28 | - Full Coursera Course: 29 | - [ ] [Algorithms on Graphs (video)](https://www.coursera.org/learn/algorithms-on-graphs/home/welcome) 30 | 31 | - Yegge: If you get a chance, try to study up on fancier algorithms: 32 | - [ ] Dijkstra's algorithm - see above - 6.006 33 | - [ ] A* 34 | - [ ] [A Search Algorithm](https://en.wikipedia.org/wiki/A*_search_algorithm) 35 | - [ ] [A* Pathfinding Tutorial (video)](https://www.youtube.com/watch?v=KNXfSOx4eEE) 36 | - [ ] [A* Pathfinding (E01: algorithm explanation) (video)](https://www.youtube.com/watch?v=-L-WgKMFuhE) 37 | 38 | 39 | #Tree (Graph Theory) 40 | 41 | A tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any acyclic connected graph is a tree. A forest is a disjoint union of trees. -------------------------------------------------------------------------------- /graph/graphNode.js: -------------------------------------------------------------------------------- 1 | class Node { 2 | constructor(name, value) { 3 | this.name = name; 4 | this.value = value; 5 | this.neighbors = []; 6 | } 7 | 8 | addNeighbors(arr) { 9 | if (this.neighbors.length !== 0 && arr !== undefined) { 10 | this.neighbors = this.neighbors.concat(arr); 11 | } else if (this.neighbors.length === 0 && arr !== undefined) { 12 | this.neighbors = arr; 13 | return this.neighbors; 14 | } 15 | return this.neighbors; 16 | } 17 | 18 | getNeighbors() { 19 | return this.neighbors; 20 | } 21 | 22 | getValue() { 23 | return this.value; 24 | } 25 | 26 | toString() { 27 | return this.name; 28 | } 29 | } 30 | 31 | module.exports = Node; 32 | -------------------------------------------------------------------------------- /graph/gridGraph.js: -------------------------------------------------------------------------------- 1 | const astar = require('../shortest-path/aStar'); 2 | /** 3 | * A graph memory structure 4 | * @param {Array} gridIn 2D array of input weights 5 | * @param {Object} [options] 6 | * @param {bool} [options.diagonal] Specifies whether diagonal moves are allowed 7 | */ 8 | class GridGraph { 9 | constructor(gridIn, options) { 10 | options = options || {}; 11 | this.nodes = []; 12 | this.diagonal = !!options.diagonal; 13 | this.grid = []; 14 | for (let x = 0; x < gridIn.length; x++) { 15 | this.grid[x] = []; 16 | for (let y = 0, row = gridIn[x]; y < row.length; y++) { 17 | const node = new GridNode(x, y, row[y]); 18 | this.grid[x][y] = node; 19 | this.nodes.push(node); 20 | } 21 | } 22 | this.init(); 23 | } 24 | 25 | init() { 26 | this.dirtyNodes = []; 27 | for (let i = 0; i < this.nodes.length; i++) { 28 | astar.cleanNode(this.nodes[i]); 29 | } 30 | } 31 | 32 | cleanDirty() { 33 | for (let i = 0; i < this.dirtyNodes.length; i++) { 34 | astar.cleanNode(this.dirtyNodes[i]); 35 | } 36 | this.dirtyNodes = []; 37 | } 38 | 39 | markDirty(node) { 40 | this.dirtyNodes.push(node); 41 | } 42 | 43 | neighbors(node) { 44 | const ret = []; 45 | const x = node.x; 46 | const y = node.y; 47 | const grid = this.grid; 48 | 49 | // West 50 | if (grid[x - 1] && grid[x - 1][y]) { 51 | ret.push(grid[x - 1][y]); 52 | } 53 | 54 | // East 55 | if (grid[x + 1] && grid[x + 1][y]) { 56 | ret.push(grid[x + 1][y]); 57 | } 58 | 59 | // South 60 | if (grid[x] && grid[x][y - 1]) { 61 | ret.push(grid[x][y - 1]); 62 | } 63 | 64 | // North 65 | if (grid[x] && grid[x][y + 1]) { 66 | ret.push(grid[x][y + 1]); 67 | } 68 | 69 | if (this.diagonal) { 70 | // Southwest 71 | if (grid[x - 1] && grid[x - 1][y - 1]) { 72 | ret.push(grid[x - 1][y - 1]); 73 | } 74 | 75 | // Southeast 76 | if (grid[x + 1] && grid[x + 1][y - 1]) { 77 | ret.push(grid[x + 1][y - 1]); 78 | } 79 | 80 | // Northwest 81 | if (grid[x - 1] && grid[x - 1][y + 1]) { 82 | ret.push(grid[x - 1][y + 1]); 83 | } 84 | 85 | // Northeast 86 | if (grid[x + 1] && grid[x + 1][y + 1]) { 87 | ret.push(grid[x + 1][y + 1]); 88 | } 89 | } 90 | return ret; 91 | } 92 | 93 | toString() { 94 | const graphString = []; 95 | const nodes = this.grid; 96 | for (let x = 0; x < nodes.length; x++) { 97 | const rowDebug = []; 98 | const row = nodes[x]; 99 | for (let y = 0; y < row.length; y++) { 100 | rowDebug.push(row[y].weight); 101 | } 102 | graphString.push(rowDebug.join(' ')); 103 | } 104 | return graphString.join('\n'); 105 | } 106 | 107 | } 108 | 109 | 110 | class GridNode { 111 | constructor(x, y, weight) { 112 | this.x = x; 113 | this.y = y; 114 | this.weight = weight; 115 | } 116 | 117 | toString() { 118 | return '[' + this.x + ' ' + this.y + ']'; 119 | } 120 | 121 | getCost(fromNeighbor) { 122 | // Take diagonal weight into consideration. 123 | if (fromNeighbor && fromNeighbor.x !== this.x && fromNeighbor.y !== this.y) { 124 | return this.weight * 1.41421; 125 | } 126 | return this.weight; 127 | } 128 | 129 | isWall() { 130 | return this.weight === 0; 131 | } 132 | } 133 | 134 | module.exports = GridGraph; 135 | -------------------------------------------------------------------------------- /graph/tree.js: -------------------------------------------------------------------------------- 1 | class Node { 2 | constructor( value ) { 3 | this.value = value; 4 | this.children = []; 5 | } 6 | 7 | getChildren() { 8 | return this.children; 9 | } 10 | 11 | addChild( child ) { 12 | this.children.push( child ); 13 | } 14 | 15 | hasChildren() { 16 | return this.children.length > 0; 17 | } 18 | 19 | getValue() { 20 | return this.value; 21 | } 22 | 23 | toString() { 24 | return this.name; 25 | } 26 | } 27 | 28 | module.exports = Node; 29 | -------------------------------------------------------------------------------- /hash-table/README.md: -------------------------------------------------------------------------------- 1 | # Hash Tables 2 | 3 | Hash tables optimize storage for key-value pairs. In best case scenarios hash table insertion, retrieval and deletion are constant time. Hash tables are used to store large amounts of quickly accessible information like passwords. 4 | 5 | Anatomy of a Hash Table: 6 | 7 | This is a basic Javascript hash table implementation. A hash table can be conceptualized as an array holding a series of tuples stored in sub-arrays inside of an object: 8 | 9 | ``` 10 | { [[ [‘a’, 9], [‘b’, 88] ],[ [‘e’, 7], [‘q’, 8] ],[ [‘j’, 7], [‘l’, 8] ]] }; 11 | ``` 12 | 13 | The outer array holds a number of buckets (sub-arrays) equal to the max length of the array. Inside the buckets, tuples or two element arrays hold key-value pairs. 14 | 15 | When key-value pairs are inserted into the hash table, the key is hashed with a hashing function. The key and the maximum length of the array are passed to the hashing function which returns an index used to identify the bucket. 16 | 17 | - hash table 18 | - hash(k, m) - m is size of hash table 19 | - add(key, value) - if key already exists, update value 20 | - exists(key) 21 | - get(key) 22 | - remove(key) -------------------------------------------------------------------------------- /hash-table/hash-table.js: -------------------------------------------------------------------------------- 1 | class Hashtable { 2 | constructor() { 3 | this._storage = []; 4 | this._storageLimit = 8; 5 | } 6 | 7 | insert(key, value) { 8 | const hash = getHash(key, this._storageLimit); 9 | 10 | // check if the hash index exists in HashTables 11 | if ( this._storage[hash] ) { 12 | this._storage[hash] = this._storage[hash]; 13 | } else { 14 | this._storage[hash] = []; 15 | } 16 | 17 | for (let i = 0; i < this._storage[hash].length; i++) { 18 | const tupple = this._storage[hash][i]; 19 | if (tupple[0] === key) { 20 | return 'key already exists; keys must be unique'; 21 | } 22 | } 23 | this._storage[hash].push([key, value]); 24 | return 'inserted'; 25 | } 26 | 27 | retrieve(key) { 28 | const hash = getHash(key, this._storageLimit); 29 | if (!this._storage[hash]) { 30 | return 'key does not exist'; 31 | } 32 | for (let i = 0; i < this._storage[hash].length; i++) { 33 | const tupple = this._storage[hash][i]; 34 | if (tupple[0] === key) { 35 | return tupple; 36 | } 37 | } 38 | } 39 | 40 | remove(key) { 41 | const hash = getHash(key, this._storageLimit); 42 | if (!this._storage[hash]) { 43 | return 'key does not exist'; 44 | } 45 | for (let i = 0; i < this._storage[hash].length; i++) { 46 | if (this._storage[hash][i][0] === key) { 47 | this._storage[hash][i].splice(i, 2); 48 | return `${key} removed`; 49 | } 50 | } 51 | } 52 | } 53 | 54 | // helper function that generates hash 55 | const getHash = (str, max) => { 56 | let hash = 0; 57 | for (let i = 0; i < str.length; i++) { 58 | hash = ( hash << 5 ) + hash + str.charCodeAt(i); 59 | hash &= hash; // 60 | hash = Math.abs(hash); 61 | } 62 | return hash % max; 63 | }; 64 | 65 | module.exports = Hashtable; 66 | // Source: https://medium.com/@jenwong/hash-tables-a-simple-javascript-example-237f92d36459#.khe8iijr8 67 | -------------------------------------------------------------------------------- /linked-list/README.md: -------------------------------------------------------------------------------- 1 | # Linked List - An Abstract Data Type 2 | 3 | ## Linked List Example 4 | 5 | { 6 | value: 'Ready Player One', 7 | next: { 8 | value: '1982', 9 | next: { 10 | value: 'Neuromancer', 11 | next: { 12 | value: 'Snow Crash', 13 | next: null 14 | } 15 | } 16 | } 17 | } 18 | 19 | ## Methods 20 | 21 | ### getHead() 22 | Returns the value of the first node of the list 23 | 24 | linkedListExample.getHead(); // returns a node object... 25 | { 26 | value: 'Ready Player One' 27 | next: { ... } 28 | } 29 | 30 | ### getTail() 31 | Returns the value of the last node of a list. 32 | 33 | linkedListExample.getTail(); // returns a node object... 34 | { 35 | value: 'Snow Crash', 36 | next: null 37 | } 38 | 39 | ### add(Value) 40 | Takes in any data value and adds a new node to the end of a list. Returns the new node that was created. 41 | 42 | linkedListExample.add('The Stranger'); // returns the newly created and appended node... 43 | { 44 | value: 'The Stranger', 45 | next: null 46 | } 47 | 48 | ### get(Number) 49 | Takes in a Number value and searches for the **Nth node** in a list and returns that node 50 | 51 | linkedListExample.get(2); // returns a node object... 52 | { 53 | value: 'Neuromancer', 54 | next: { ... } 55 | } 56 | 57 | ### remove(Number) 58 | Takes in a Number value and searches for the Nth node removes it from the list. Should return `false` if the the position is outside the length of the list. 59 | 60 | linkedListExample.remove(3); 61 | 62 | ### insert(Value, Number) 63 | Inserts the specified element at the specified position in this list. Shifts the element currently at that position (if any) and any subsequent elements to the right. Cannot be used to append a node to the end of a list, if attempted, should return `false`. 64 | -------------------------------------------------------------------------------- /linked-list/linkedList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @name linkedListGenerator 3 | * @description Main Module 4 | * @return {Object} an object exposing methods to be used to manipulate a linked list 5 | */ 6 | class LinkedList { 7 | constructor() { 8 | this.tail = null; 9 | this.head = null; 10 | } 11 | 12 | // points to our head 13 | getHead() { 14 | return this.head; 15 | } 16 | 17 | // points to our tail 18 | getTail() { 19 | return this.tail; 20 | } 21 | 22 | // Create a new node 23 | newNode( value ) { 24 | return { 25 | value, 26 | next: null, 27 | }; 28 | } 29 | 30 | // Takes a new node and adds it to our linked list 31 | add( value ) { 32 | const node = this.newNode( value ); 33 | 34 | // init empty LL 35 | if ( this.getHead() === null ) { 36 | this.head = node; 37 | } else { // if it's not empty 38 | this.getTail().next = node; 39 | } 40 | // Happy Path 41 | this.tail = node; 42 | return node; 43 | } 44 | 45 | /** 46 | * Reads through our list and returns the node we are looking for 47 | * @param {[type]} index [description] 48 | * @return {[type]} [description] 49 | */ 50 | get( index ) { 51 | let currNode = this.getHead(); 52 | let postion = 0; 53 | 54 | // If index is less than 0, return false 55 | if ( index <= -1 ) { 56 | return false; 57 | } 58 | 59 | // Loop through all the nodes 60 | while ( postion < index ) { 61 | 62 | // Check if we hit the end of the LL 63 | if ( currNode.next === null ) { 64 | return false; 65 | } 66 | 67 | // If node exists go to next node 68 | currNode = currNode.next; 69 | postion++; 70 | } 71 | 72 | return currNode; 73 | } 74 | 75 | /** 76 | * reads through our list and removes desired node 77 | * @param {[type]} index [description] 78 | * @return {[type]} [description] 79 | */ 80 | remove( index ) { 81 | const currNode = this.get( index ); 82 | const prevNode = this.get( index - 1 ); 83 | 84 | // If index not in LL, return false 85 | if ( currNode === false ) { 86 | return false; 87 | } 88 | 89 | // If removing the head, reassign the head to the next node 90 | if ( index === 0 ) { 91 | this.head = currNode.next; 92 | 93 | // If removing the tail, reassign the tail to the prevNode 94 | } else if ( currNode.next === null ) { 95 | this.tail = prevNode; 96 | prevNode.next = currNode.next; 97 | 98 | // Happy Path 99 | } else { 100 | prevNode.next = currNode.next; 101 | } 102 | } 103 | 104 | /** 105 | * Inserts a new node at the deisred index 106 | * @param {[Num]} index 107 | * @param {[*]} value 108 | * @return {[Node]} node 109 | */ 110 | insert( value, index ) { 111 | const currNode = this.get( index ); 112 | const prevNode = this.get( index - 1 ); 113 | const node = this.newNode( value ); 114 | 115 | // If the index is not in the LL, return false 116 | if ( currNode === false ) { 117 | return false; 118 | } 119 | // If inserting at the head, reassign the head to the new node 120 | if ( index === 0 ) { 121 | this.head = node; 122 | node.next = currNode; 123 | } else { 124 | // If inserting at the tail, reassign the tail 125 | if (currNode.next === null) { 126 | this.tail = node; 127 | } 128 | node.next = currNode; 129 | prevNode.next = node; 130 | } 131 | return node; 132 | } 133 | } 134 | 135 | module.exports = LinkedList; 136 | -------------------------------------------------------------------------------- /list/README.md: -------------------------------------------------------------------------------- 1 | # Array 2 | 3 | - implement arrays methods 4 | - size() - number of items 5 | - capacity() - number of items it can hold 6 | - is_empty() 7 | - at(index) - returns item at given index, blows up if index out of bounds 8 | - push(item) 9 | - insert(index, item) - inserts item at index, shifts that index's value and trailing elements to the right 10 | - prepend(item) - can use insert above at index 0 11 | - pop() - remove from end, return value 12 | - delete(index) - delete item at index, shifting all trailing elements left 13 | - remove(item) - looks for value and removes index holding it (even if in multiple places) 14 | - find(item) - looks for value and returns first index with that value, -1 if not found 15 | - resize(new_capacity) // private function -------------------------------------------------------------------------------- /list/list.js: -------------------------------------------------------------------------------- 1 | class Node { 2 | /** 3 | * Creates an instance of Node. 4 | * 5 | * @param {any} value Node's value 6 | * @param {Node} [next] The next Node in the list 7 | */ 8 | constructor(value, next = null) { 9 | this.value = value; 10 | this.next = next; 11 | } 12 | } 13 | 14 | /** 15 | * Javascript list implementation 16 | * 17 | * @export 18 | * @class List 19 | */ 20 | class List { 21 | /** 22 | * Creates an instance of List. 23 | * 24 | * @param {any} initialValue Value to initiate the list with. 25 | */ 26 | constructor(initialValue) { 27 | if (initialValue) { 28 | this.head = new Node(initialValue); 29 | } 30 | } 31 | 32 | /** 33 | * Computes the length of the list 34 | * 35 | * @return {number} The length of the list. 36 | */ 37 | getLength() { 38 | let current = this.head; 39 | let length = 0; 40 | 41 | while (current) { 42 | current = current.next; 43 | length += 1; 44 | } 45 | 46 | return length; 47 | } 48 | 49 | /** 50 | * Returns the head of the list (the first element) 51 | * 52 | * @return {Node} The head node (the first node in the list) 53 | */ 54 | getHead() { 55 | return this.head; 56 | } 57 | 58 | /** 59 | * Returns the tail of the list (the last element) 60 | * 61 | * @return {Node} The tail node (the last node in the list) 62 | */ 63 | getTail() { 64 | return this.reduce((_, node) => node, undefined, false); 65 | } 66 | 67 | /** 68 | * @callback reduceCallbackFn 69 | * @param {any} accumulated 70 | * @param {any} current 71 | * @return {any} 72 | */ 73 | 74 | /** 75 | * Reduces the list to a single value 76 | * 77 | * @param {reduceCallbackFN} callbackFn Callback which reduces the list. 78 | * @param {any} [startingValue] Value to initiate the reducing with. 79 | * @param {boolean} [extractValues=true] Decides on what will be passed to the callbackFn, 80 | * either values or whole nodes. 81 | * @return {any} Reduced value 82 | */ 83 | reduce(callbackFn, startingValue, extractValues = true) { 84 | let currentNode; 85 | let accumulated; 86 | 87 | let extractorFn; 88 | if (extractValues) { 89 | extractorFn = node => node.value; 90 | } else { 91 | extractorFn = node => node; 92 | } 93 | 94 | if (!this.head) { 95 | return startingValue; 96 | } 97 | 98 | if (startingValue === undefined) { 99 | currentNode = this.head; 100 | accumulated = startingValue; 101 | } else { 102 | currentNode = this.head.next; 103 | accumulated = extractorFn(this.head); 104 | } 105 | 106 | while (currentNode) { 107 | accumulated = callbackFn(accumulated, extractorFn(currentNode)); 108 | currentNode = currentNode.next; 109 | } 110 | 111 | return accumulated; 112 | } 113 | 114 | /** 115 | * @callback valueCallbackFn 116 | * @param {any} value 117 | * @param {number} index 118 | */ 119 | 120 | /** 121 | * Traverses the list and executes the callback function for each element in the list. 122 | * The first argument of the callback function is the node's value, the second one is the index. 123 | * 124 | * @param {valueCallbackFn} callbackFn Function invoked for each element in the list. 125 | */ 126 | forEach(callbackFn) { 127 | let index = 0; 128 | 129 | this.reduce((_, value) => { 130 | callbackFn(value, index); 131 | index += 1; 132 | return null; 133 | }); 134 | } 135 | 136 | /** 137 | * Executes callbackFn on each item from the list and returns the results as another list. 138 | * 139 | * @param {valueCallbackFn} callbackFn Function invoked for each element in the list 140 | * @return {List} Transformed list 141 | */ 142 | map(callbackFn) { 143 | const outputList = new List(); 144 | 145 | this.forEach((value, index) => { 146 | const transformedValue = callbackFn(value, index); 147 | outputList.pushBack(transformedValue); 148 | }); 149 | 150 | return outputList; 151 | } 152 | 153 | /** 154 | * Retrieves the Node with a given index 155 | * 156 | * @param {number} targetIndex Wanted node's index 157 | * @return {Node} Found node 158 | */ 159 | get(targetIndex) { 160 | let currentIndex = 0; 161 | let currentNode = this.head; 162 | 163 | if (targetIndex < 0 ) { 164 | throw new Error('Index exceeds list\'s size'); 165 | } 166 | 167 | while (currentIndex < targetIndex && currentNode) { 168 | if (currentNode.next === null) { 169 | throw new Error('Index exceeds list\'s size'); 170 | } 171 | currentIndex += 1; 172 | currentNode = currentNode.next; 173 | } 174 | 175 | return currentNode; 176 | } 177 | 178 | /** 179 | * Adds a value to the end of the list 180 | * 181 | * @param {any} newValue A value to be added 182 | * @return {List} This list. Allows for chainability 183 | */ 184 | pushBack(newValue) { 185 | const newNode = new Node(newValue); 186 | const tail = this.getTail(); 187 | 188 | if (tail) { 189 | tail.next = newNode; 190 | } else { 191 | this.head = newNode; 192 | } 193 | 194 | return this; 195 | } 196 | 197 | /** 198 | * Adds a value at a given index 199 | * 200 | * @param {any} value Value to be added. 201 | * @param {number} index Index at which the value should be added. 202 | * @throws {Error} Index must not exceed list size. 203 | * @return {List} This list. Allows for chainability. 204 | */ 205 | push(value, index) { 206 | if (index === 0) { 207 | // Replace head 208 | this.head = new Node(value, this.head); 209 | } else { 210 | const previousNode = this.get(index - 1); 211 | if (!previousNode) { 212 | throw new Error('Index exceeds list\'s size'); 213 | } 214 | 215 | const nextNode = previousNode.next; 216 | previousNode.next = new Node(value, nextNode); 217 | } 218 | 219 | return this.head; 220 | } 221 | 222 | /** 223 | * Removes nodes with specific values from the list 224 | * 225 | * @param {any} valueToRemove A value to be removed 226 | * @return {List} This list. Allows for chainability. 227 | */ 228 | remove(valueToRemove) { 229 | if (this.head.next === null) { 230 | this.head = {}; 231 | return this.head; 232 | } 233 | if (this.head.value === valueToRemove) { 234 | this.head = this.head.next; 235 | return this.remove(valueToRemove); 236 | } 237 | 238 | this._removeRecursive(this.head, valueToRemove); 239 | return this; 240 | } 241 | 242 | /** 243 | * Used internally by remove. Replaces the current node with a next one if the value matches. 244 | * 245 | * @private 246 | * @param {Node} previousNode Reference to the previous node. 247 | * @param {any} valueToRemove Value to be removed. 248 | */ 249 | _removeRecursive(previousNode, valueToRemove) { 250 | if (!previousNode.next) { 251 | return; 252 | } 253 | const currentNode = previousNode.next; 254 | 255 | if (currentNode.value === valueToRemove) { 256 | previousNode.next = currentNode.next; 257 | this._removeRecursive(previousNode, valueToRemove); 258 | } else { 259 | this._removeRecursive(currentNode, valueToRemove); 260 | } 261 | } 262 | 263 | /** 264 | * Returns the first node that value matches. 265 | * 266 | * @param {any} value Value to be found 267 | * @return {Node} Node with that value 268 | */ 269 | find(value) { 270 | const findRecursive = (node, value) => { 271 | if (!node) { 272 | return null; 273 | } else if (node.value === value) { 274 | return node; 275 | } 276 | return findRecursive(node.next, value); 277 | }; 278 | 279 | return findRecursive(this.head, value); 280 | } 281 | 282 | /** 283 | * Converts the list to an array 284 | * 285 | * @return {any[]} Array of values from the list 286 | */ 287 | getValues() { 288 | const valuesArray = []; 289 | this.forEach(value => valuesArray.push(value)); 290 | return valuesArray; 291 | } 292 | 293 | } 294 | 295 | module.exports = List; 296 | 297 | // Source: https://github.com/Gelio/js-list 298 | -------------------------------------------------------------------------------- /memoization/README.md: -------------------------------------------------------------------------------- 1 | # Memoization 2 | 3 | In computing, memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again. 4 | 5 | The term "memoization" was coined by Donald Michie in 1968 and is derived from the Latin word "memorandum" ("to be remembered"), usually truncated as "memo" in the English language, and thus carries the meaning of "turning [the results of] a function into something to be remembered." 6 | -------------------------------------------------------------------------------- /memoization/memoize.js: -------------------------------------------------------------------------------- 1 | /* 2 | Memoization is a useful optimization technique for caching the results of function calls such that 3 | lengthy lookups or expensive recursive computations can be minimized where possible. 4 | */ 5 | 6 | Function.prototype.memoize = () => { 7 | const cache = {}; 8 | 9 | return ( arg ) => { 10 | if (arg in cache) { 11 | // Cache Hit 12 | return cache[arg]; 13 | } 14 | // Chache Miss 15 | cache[arg] = this( arg ); 16 | return cache[arg]; 17 | }; 18 | }; 19 | 20 | // Test 21 | function fooBar( x ) { 22 | return x; 23 | } 24 | 25 | const memoFooBar = fooBar.memoize(); 26 | console.log('memoFooBar(1): ', memoFooBar(1)); 27 | memoFooBar(1); // Cache miss 28 | memoFooBar(1); // Cache hit :D 29 | memoFooBar(2); // Cache miss 30 | 31 | // Source: https://addyosmani.com/blog/faster-javascript-memoization/ 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "data-structure", 3 | "version": "1.0.0", 4 | "description": "Various data structures and algorithms implemented in JavaScript.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "nyc ./node_modules/.bin/mocha", 8 | "test-watch": "./node_modules/.bin/mocha --watch", 9 | "coverage": "nyc report --reporter=text-lcov | coveralls" 10 | }, 11 | "author": "Joe Karlsson", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "chai": "^4.1.2", 15 | "coveralls": "^3.0.0", 16 | "eslint": "^5.0.1", 17 | "eslint-config-airbnb-base": "^13.0.0", 18 | "eslint-plugin-import": "^2.2.0", 19 | "istanbul": "^0.4.5", 20 | "mocha": "^5.0.0", 21 | "mocha-lcov-reporter": "^1.3.0", 22 | "nyc": "^12.0.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /queue/README.md: -------------------------------------------------------------------------------- 1 | # Queues 2 | 3 | # Queue (abstract data type) 4 | 5 | A queue is a particular kind of abstract data type or collection in which the entities in the collection are kept in order and the principal (or only) operations on the collection are the addition of entities to the rear terminal position, known as enqueue, and removal of entities from the front terminal position, known as dequeue. This makes the queue a First-In-First-Out (FIFO) data structure. In a FIFO data structure, the first element added to the queue will be the first one to be removed. 6 | 7 | ![Representation of a FIFO (first in, first out) queue](https://upload.wikimedia.org/wikipedia/commons/5/52/Data_Queue.svg) 8 | 9 | ### A simple queue implemented in Ruby: 10 | 11 | ```ruby 12 | class Queue 13 | def initialize 14 | @list = Array.new 15 | end 16 | 17 | def enqueue(element) 18 | @list << element 19 | end 20 | 21 | def dequeue 22 | @list.shift 23 | end 24 | end 25 | ``` 26 | 27 | ## Priority Queue (abstract data type) 28 | 29 | In computer science, a priority queue is an abstract data type which is like a regular queue or stack data structure, but where additionally each element has a "priority" associated with it. In a priority queue, an element with high priority is served before an element with low priority. If two elements have the same priority, they are served according to their order in the queue. 30 | 31 | While priority queues are often implemented with heaps, they are conceptually distinct from heaps. A priority queue is an abstract concept like "a list" or "a map"; just as a list can be implemented with a linked list or an array, a priority queue can be implemented with a heap or a variety of other methods such as an unordered array. 32 | 33 | One can imagine a priority queue as a modified queue, but when one would get the next element off the queue, the highest-priority element is retrieved first. 34 | 35 | Stacks and queues may be modeled as particular kinds of priority queues. As a reminder, here is how stacks and queues behave: 36 | 37 | * stack – elements are pulled in last-in first-out-order (e.g., a stack of papers) 38 | * queue – elements are pulled in first-in first-out-order (e.g., a line in a cafeteria) 39 | 40 | In a stack, the priority of each inserted element is monotonically increasing; thus, the last element inserted is always the first retrieved. In a queue, the priority of each inserted element is monotonically decreasing; thus, the first element inserted is always the first retrieved. -------------------------------------------------------------------------------- /queue/priority-queue.js: -------------------------------------------------------------------------------- 1 | /** 2 | Basic priority queue implementation. 3 | If a better priority queue is wanted/needed, 4 | */ 5 | 6 | class PriorityQueue { 7 | constructor() { 8 | this._nodes = []; 9 | } 10 | 11 | enqueue(priority, key) { 12 | this._nodes.push({ 13 | key, 14 | priority, 15 | }); 16 | this.sort(); 17 | } 18 | 19 | dequeue() { 20 | if (this._nodes.length <= 0) { 21 | return null; 22 | } 23 | return this._nodes.shift().key; 24 | } 25 | 26 | sort() { 27 | this._nodes.sort((a, b) => { 28 | return a.priority - b.priority; 29 | }); 30 | } 31 | 32 | isEmpty() { 33 | return !this._nodes.length; 34 | } 35 | } 36 | 37 | module.exports = PriorityQueue; 38 | -------------------------------------------------------------------------------- /queue/queue.js: -------------------------------------------------------------------------------- 1 | class Queue { 2 | constructor() { 3 | this.front = null; 4 | this.back = null; 5 | } 6 | 7 | enqueue( value ) { 8 | const newNode = { 9 | value, 10 | next: null, 11 | }; 12 | if ( !this.front ) { 13 | this.back = newNode; 14 | this.front = this.back; 15 | } else { 16 | this.back.next = newNode; 17 | this.back = newNode; 18 | } 19 | } 20 | 21 | dequeue() { 22 | if ( this.front ) { 23 | const value = this.front.value; 24 | this.front = this.front.next; 25 | return value; 26 | } 27 | return null; 28 | } 29 | 30 | isEmpty() { 31 | if ( this.front === null ) { 32 | return true; 33 | } 34 | return false; 35 | } 36 | } 37 | 38 | module.exports = Queue; 39 | -------------------------------------------------------------------------------- /rabin-karp/README.md: -------------------------------------------------------------------------------- 1 | # Rabin- Karp 2 | 3 | ## String Searching Algorithm 4 | 5 | String matching algorithms are pervasive in software. One particularly fun one, is Rabin Karp, which is used in Plagiarism detection. As a student in CS (or in any major), plagiarism detection should be of interest ;-) 6 | 7 | ![rabin-karp-basic-principles](https://cloud.githubusercontent.com/assets/4650739/25076866/79d441d6-22c0-11e7-8cf1-6af40e7cecff.png) 8 | 9 | Rabin Karp is relatively easy to implement. See this: 10 | 11 | * [Rabin–Karp algorithm - Wikipedia](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) 12 | 13 | Rabin Karp has also inspired a string matching routine in Zlib (one of the most popular un/zip libraries ever). See this, directly into the source code. -------------------------------------------------------------------------------- /rabin-karp/rk.js: -------------------------------------------------------------------------------- 1 | const primeBase = 101; // Any large prime number 2 | 3 | // Rolling Hash Function using The Rabin fingerprint method 4 | // I.E - The hash of the first substring, "abr", using 101 as a base is: 5 | // 6 | // ASCII a = 97, b = 98, r = 114. 7 | // hash("abr") = (97 × 1012) + (98 × 1011) + (114 × 1010) = 999,509 8 | const hashFromTo = ( str, from, to ) => { 9 | let hash = 0; // init hash 10 | for (let i = from; i < to && i < str.length; i++) { 11 | const charASCII = str.charCodeAt(i); 12 | hash = primeBase * hash + charASCII; 13 | } 14 | return hash; 15 | }; 16 | 17 | const matchesAtIndex = (index, text, str) => { 18 | let matches = true; 19 | 20 | for (let j = 0; j < str.length; j++) { 21 | if (text[index + j] !== str[j]) { 22 | matches = false; 23 | break; 24 | } 25 | } 26 | return matches; 27 | }; 28 | 29 | /** 30 | * Returns an array of indexes of the places where the str is found within text 31 | * @param {String} text 32 | * @param {String} str 33 | * @return {Array[num]} 34 | */ 35 | const searchRabinKarp = ( text, str ) => { 36 | const matches = []; 37 | 38 | const hashStr = hashFromTo(str, 0, str.length); // hash the substring 39 | const primeToPower = Math.pow(primeBase, str.length); 40 | const maxIndexForPotentialMatch = text.length - str.length; 41 | // init text has of the length of the substring 42 | let hashTextPart = hashFromTo(text, 0, str.length); 43 | 44 | for (let i = 0; i <= maxIndexForPotentialMatch; i++) { 45 | if ( hashTextPart === hashStr ) { 46 | // if there is a hash match - we need to verify this is 47 | // a valid match by manually checking it 48 | if (matchesAtIndex(i, text, str)) { 49 | matches.push(i); 50 | } 51 | } 52 | // Update rolling hash 53 | // s[i+1..i+m] = s[i..i+m-1] - s[i] + s[i+m] 54 | const charASCII = text.charCodeAt(i + str.length); 55 | hashTextPart = primeBase * hashTextPart - primeToPower * text.charCodeAt(i) + charASCII; 56 | } 57 | 58 | return matches; 59 | }; 60 | 61 | const str = 'abaacadabra'; 62 | const subStr = 'abaaa'; 63 | const hashStr = searchRabinKarp(str, subStr); 64 | console.log(hashStr); 65 | 66 | module.exports = searchRabinKarp; -------------------------------------------------------------------------------- /search/README.md: -------------------------------------------------------------------------------- 1 | # Search 2 | 3 | ## Binary Search Tree 4 | 5 | A binary search tree (BST), sometimes also called an ordered or sorted binary tree, is a node-based binary tree data structure where each node has a comparable key (and an associated value) and satisfies the restriction that the key in any node is larger than the keys in all nodes in that node's left subtree and smaller than the keys in all nodes in that node's right sub-tree. Each node has no more than two child nodes. Each child must either be a leaf node or the root of another binary search tree. The left sub-tree contains only nodes with keys less than the parent node; the right sub-tree contains only nodes with keys greater than the parent node. BSTs are also dynamic data structures, and the size of a BST is only limited by the amount of free memory in the operating system. The main advantage of binary search trees is that it remains ordered, which provides quicker search times than many other data structures. 6 | 7 | !(A binary search tree of size 9 and depth 3, with 8 at the root. The leaves are not drawn)[https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Binary_search_tree.svg/200px-Binary_search_tree.svg.png] 8 | 9 | ## Time Complexity 10 | 11 | | |Average |Worst case| 12 | |--------|----------|----------| 13 | |Space |Θ(n) |O(n) | 14 | |Search |Θ(log n) |O(n) | 15 | |Insert |Θ(log n) |O(n) | 16 | |Delete |Θ(log n) |O(n) | 17 | 18 | 19 | ## Linear Search 20 | 21 | Linear search or sequential search is a method for finding a target value within a list. It sequentially checks each element of the list for the target value until a match is found or until all the elements have been searched. 22 | 23 | Linear search runs in at worst linear time and makes at most n comparisons, where n is the length of the list. If each element is equally likely to be searched, then linear search has an average case of `n/2` comparisons, but the average case can be affected if the search probabilities for each element vary. Linear search is rarely practical because other search algorithms and schemes, such as the binary search algorithm and hash tables, allow significantly faster searching for all but short lists. -------------------------------------------------------------------------------- /search/binary-search-tree.js: -------------------------------------------------------------------------------- 1 | const LinkedList = require('../linked-list/linkedList'); 2 | 3 | // Private Helper functions 4 | const makeNode = ( value ) => { 5 | const node = {}; 6 | node.value = value; 7 | node.left = null; 8 | node.right = null; 9 | return node; 10 | }; 11 | 12 | class BinarySearchTree { 13 | constructor() { 14 | this.root = null; 15 | } 16 | 17 | add( value ) { 18 | const currentNode = makeNode( value ); 19 | if ( !this.root ) { 20 | this.root = currentNode; 21 | } else { 22 | this.insert( currentNode ); 23 | } 24 | return this.root; 25 | } 26 | 27 | // Find and insert a new node in the BST 28 | insert(currentNode) { 29 | const value = currentNode.value; 30 | 31 | const traverse = (node) => { 32 | if (value > node.value) { 33 | if (!node.right) { 34 | node.right = currentNode; 35 | return; 36 | } 37 | traverse(node.right); 38 | } else if (value < node.value) { 39 | if (!node.left) { 40 | node.left = currentNode; 41 | return; 42 | } 43 | traverse(node.left); 44 | } 45 | }; 46 | traverse(this.root); 47 | } 48 | 49 | // Find and return the node based on it's value 50 | get(start, searchFor, parent = null, isLeft = true) { 51 | if (!start) { 52 | return null; // key not found 53 | } 54 | if ( searchFor < start.value ) { 55 | return this.get( start.left, searchFor, start, true ); 56 | } else if ( searchFor > start.value ) { 57 | return this.get( start.right, searchFor, start, false ); 58 | } 59 | // key is equal to node key 60 | return { 61 | current: start, 62 | parent, 63 | isLeft, 64 | }; 65 | } 66 | 67 | remove(value) { 68 | let found = false; 69 | let childCount; 70 | let replacement; 71 | let replacementParent; 72 | 73 | // find the node 74 | let { 75 | current, 76 | parent, 77 | isLeft, 78 | } = this.get(this.root, value); 79 | 80 | if (current) { 81 | found = true; 82 | } 83 | // only proceed if the node was found 84 | if (found) { 85 | // Figure out how many children 86 | childCount = (current.left !== null ? 1 : 0) + 87 | (current.right !== null ? 1 : 0); 88 | // special case: the value is at the root 89 | if (current === this.root) { 90 | switch (childCount) { 91 | // two children, little work to do 92 | case 2: 93 | // new root will be the old root's left child 94 | // ...maybe 95 | replacement = this.root.left; 96 | // find the right-most leaf node to be 97 | // the real new root 98 | while (replacement.right !== null) { 99 | replacementParent = replacement; 100 | replacement = replacement.right; 101 | } 102 | // it's not the first node on the left 103 | if (replacementParent !== null) { 104 | // remove the new root from it's 105 | // previous position 106 | replacementParent.right = replacement.left; 107 | // give the new root all of the old 108 | // root's children 109 | replacement.right = this.root.right; 110 | replacement.left = this.root.left; 111 | } else { 112 | // just assign the children 113 | replacement.right = this.root.right; 114 | } 115 | // officially assign new root 116 | this.root = replacement; 117 | break; 118 | // no default 119 | } 120 | 121 | // non-root values 122 | } else { 123 | switch (childCount) { 124 | // Leaf Node 125 | case 0: 126 | if (isLeft) { 127 | parent.left = null; 128 | } else { 129 | parent.right = null; 130 | } 131 | break; 132 | // 1 child 133 | case 1: 134 | if ( isLeft ) { 135 | parent.left = current.left; 136 | } else { 137 | parent.right = current.left; 138 | } 139 | break; 140 | 141 | // two children, a bit more complicated - we need to determine 142 | // replacement node 143 | case 2: 144 | 145 | // find the min node on the right side of curr node 146 | const traverse = node => !node.left ? node : traverse( node.left ); 147 | const minNode = traverse(current.right); 148 | 149 | const result = this.get(this.root, minNode.value); 150 | 151 | // copy the value in targetted node 152 | current.value = minNode.value; 153 | 154 | // Delete duplicate node 155 | if ( result.isLeft ) { 156 | result.parent.left = minNode.left; 157 | } else { 158 | result.parent.right = minNode.left; 159 | } 160 | 161 | // no default 162 | } 163 | } 164 | } 165 | } 166 | 167 | contains(value) { 168 | const node = this.root; 169 | const traverse = ( node ) => { 170 | if (!node) { 171 | return false; 172 | } 173 | if ( value === node.value ) { 174 | return true; 175 | } else if ( value > node.value ) { 176 | return traverse(node.right); 177 | } else if ( value < node.value ) { 178 | return traverse( node.left ); 179 | } 180 | }; 181 | return traverse( node ); 182 | } 183 | 184 | // find the left most node to find the min value of a binary tree; 185 | findMin() { 186 | const node = this.root; 187 | const traverse = ( node ) => { 188 | return !node.left ? node.value : traverse( node.left ); 189 | }; 190 | return traverse(node); 191 | } 192 | 193 | // find the right most node to find the max value of a binary tree; 194 | findMax() { 195 | const node = this.root; 196 | const traverse = ( node ) => { 197 | return !node.right ? node.value : traverse( node.right ); 198 | }; 199 | return traverse( node ); 200 | } 201 | 202 | getDepth() { 203 | let maxDepth = 0; 204 | const node = this.root; 205 | const traverse = (node, depth) => { 206 | if ( !node ) return null; 207 | if ( node ) { 208 | maxDepth = depth > maxDepth ? depth : maxDepth; 209 | traverse( node.left, depth + 1 ); 210 | traverse( node.right, depth + 1 ); 211 | } 212 | }; 213 | traverse( node, 0 ); 214 | return maxDepth; 215 | } 216 | 217 | countLeaves() { 218 | let count = 0; 219 | const node = this.root; 220 | const traverse = ( node ) => { 221 | if ( !node) { 222 | return null; 223 | } 224 | if ( !node.left && !node.right ) { 225 | count++; 226 | } else { 227 | traverse(node.left) + traverse(node.right); 228 | } 229 | }; 230 | traverse(node); 231 | return count; 232 | } 233 | 234 | // Find the averages of all nodes on at each depth 235 | nodeAverages() { 236 | const node = this.root; 237 | const result = {}; 238 | const depthAverages = []; 239 | 240 | const traverse = ( node, depth ) => { 241 | if ( !node ) { 242 | return null; 243 | } 244 | if ( node ) { 245 | if ( !result[depth] ) { 246 | result[depth] = [node.value]; 247 | } else { 248 | result[depth].push( node.value ); 249 | } 250 | } 251 | // check to see if node is a leaf, depth stays the same if it is 252 | // otherwise increment depth for possible right and left nodes 253 | if ( node.right || node.left ) { 254 | traverse(node.left, depth + 1); 255 | traverse(node.right, depth + 1); 256 | } 257 | }; 258 | traverse(node, 0); 259 | 260 | // get averages and breadthFirst 261 | for ( let key in result ) { 262 | const len = result[key].length; 263 | let depthAvg = 0; 264 | for ( let i = 0; i < len; i++ ) { 265 | depthAvg += result[key][i]; 266 | } 267 | depthAverages.push( Number( ( depthAvg / len ).toFixed( 2 ) ) ); 268 | } 269 | return depthAverages; 270 | } 271 | 272 | /* BREADTH FIRST TREE TRAVERSAL */ 273 | 274 | /* Breadth First Search finds all the siblings at each level 275 | in order from left to right or from right to left. */ 276 | breadthFirstLTR() { 277 | let node = this.root; 278 | const queue = [node]; 279 | const result = []; 280 | while (node) { 281 | result.push(node.value); 282 | node.left && queue.push(node.left); 283 | node.right && queue.push(node.right); 284 | node = queue.shift(); 285 | } 286 | return result; 287 | } 288 | 289 | breadthFirstRTL() { 290 | let node = this.root; 291 | const queue = [node]; 292 | const result = []; 293 | while (node) { 294 | result.push(node.value); 295 | node.right && queue.push(node.right); 296 | node.left && queue.push(node.left); 297 | node = queue.shift(); 298 | } 299 | return result; 300 | } 301 | 302 | /* DEPTH FIRST TRAVERSALS */ 303 | 304 | /* preOrder is a type of depth-first traversal that tries 305 | togo deeper in the tree before exploring siblings. It 306 | returns the shallowest descendants first. 307 | 308 | 1) Display the data part of root element (or current element) 309 | 2) Traverse the left subtree by recursively calling the pre-order function. 310 | 3) Traverse the right subtree by recursively calling the pre-order function. */ 311 | preOrder() { 312 | const result = []; 313 | const node = this.root; 314 | const traverse = ( node ) => { 315 | result.push(node.value); 316 | node.left && traverse(node.left); 317 | node.right && traverse(node.right); 318 | }; 319 | traverse(node); 320 | return result; 321 | } 322 | 323 | /* inOrder traversal is a type of depth-first traversal 324 | that also tries to go deeper in the tree before exploring siblings. 325 | however, it returns the deepest descendents first 326 | 327 | 1) Traverse the left subtree by recursively calling the pre-order function. 328 | 2) Display the data part of root element (or current element) 329 | 3) Traverse the right subtree by recursively calling the pre-order function. */ 330 | inOrder() { 331 | const result = []; 332 | const node = this.root; 333 | const traverse = ( node ) => { 334 | node.left && traverse(node.left); 335 | result.push(node.value); 336 | node.right && traverse(node.right); 337 | }; 338 | traverse(node); 339 | return result; 340 | } 341 | 342 | /* postOrder traversal is a type of depth-first traversal 343 | that also tries to go deeper in the tree before exploring siblings. 344 | however, it returns the deepest descendents first 345 | 346 | 1) Traverse the left subtree by recursively calling the pre-order function. 347 | 2) Display the data part of root element (or current element) 348 | 3) Traverse the right subtree by recursively calling the pre-order function. */ 349 | postOrder() { 350 | const result = []; 351 | const node = this.root; 352 | const traverse = (node) => { 353 | node.left && traverse(node.left); 354 | node.right && traverse(node.right); 355 | result.push(node.value); 356 | }; 357 | traverse(node); 358 | return result; 359 | } 360 | 361 | // Convert a binary search tree to a linked-list in place. 362 | convertToLinkedList() { 363 | const node = this.root; 364 | if ( !node ) { 365 | return null; 366 | } 367 | const list = new LinkedList(); 368 | const result = this.inOrder( node ); 369 | 370 | for ( let i = 0; i < result.length; i++) { 371 | list.add( result[i] ); 372 | } 373 | return list; 374 | } 375 | } 376 | 377 | module.exports = BinarySearchTree; 378 | -------------------------------------------------------------------------------- /search/linearSearch.js: -------------------------------------------------------------------------------- 1 | class LinearSearch { 2 | constructor(arr) { 3 | this.arr = arr; 4 | } 5 | 6 | search(element) { 7 | for ( let i = 0; i < this.arr.length; i++) { 8 | if (element === this.arr[i]) { 9 | return i; 10 | } 11 | } 12 | return -1; 13 | } 14 | } 15 | 16 | module.exports = LinearSearch; 17 | -------------------------------------------------------------------------------- /shortest-path/README.md: -------------------------------------------------------------------------------- 1 | # Shortest Path 2 | 3 | ## Dijkstra's 4 | Dijkstra's algorithm is an algorithm for finding the shortest paths between nodes in a graph, which may represent, for example, road networks. It was conceived by computer scientist Edsger W. Dijkstra in 1956 and published three years later 5 | 6 | ![dijkstra_animation](https://cloud.githubusercontent.com/assets/4650739/19616580/b6f12870-97b2-11e6-9bda-a7967f60cf2b.gif) 7 | 8 | ### Alogrithm: 9 | Let the node at which we are starting be called the initial node. Let the distance of node Y be the distance from the initial node to Y. Dijkstra's algorithm will assign some initial distance values and will try to improve them step by step. 10 | 11 | 1. Assign to every node a tentative distance value: set it to zero for our initial node and to infinity for all other nodes. 12 | 1. Set the initial node as current. Mark all other nodes unvisited. Create a set of all the unvisited nodes called the unvisited set. 13 | 1. For the current node, consider all of its unvisited neighbors and calculate their tentative distances. Compare the newly calculated tentative distance to the current assigned value and assign the smaller one. For example, if the current node A is marked with a distance of 6, and the edge connecting it with a neighbor B has length 2, then the distance to B (through A) will be 6 + 2 = 8. If B was previously marked with a distance greater than 8 then change it to 8. Otherwise, keep the current value. 14 | 1. When we are done considering all of the neighbors of the current node, mark the current node as visited and remove it from the unvisited set. A visited node will never be checked again. 15 | 1. If the destination node has been marked visited (when planning a route between two specific nodes) or if the smallest tentative distance among the nodes in the unvisited set is infinity (when planning a complete traversal; occurs when there is no connection between the initial node and remaining unvisited nodes), then stop. The algorithm has finished. 16 | 1. Otherwise, select the unvisited node that is marked with the smallest tentative distance, set it as the new "current node", and go back to step 3. 17 | 18 | ### Pseudocode 19 | ``` 20 | function Dijkstra(Graph, source): 21 | create vertex set Q 22 | 23 | for each vertex v in Graph: // Initialization 24 | dist[v] ← INFINITY // Unknown distance from source to v 25 | prev[v] ← UNDEFINED // Previous node in optimal path from source 26 | add v to Q // All nodes initially in Q (unvisited nodes) 27 | 28 | dist[source] ← 0 // Distance from source to source 29 | 30 | while Q is not empty: 31 | u ← vertex in Q with min dist[u] // Source node will be selected first 32 | remove u from Q 33 | 34 | for each neighbor v of u: // where v is still in Q. 35 | alt ← dist[u] + length(u, v) 36 | if alt < dist[v]: // A shorter path to v has been found 37 | dist[v] ← alt 38 | prev[v] ← u 39 | 40 | return dist[], prev[] 41 | ``` 42 | 43 | ## A* 44 | In computer science, A* (pronounced as "A star") is a computer algorithm that is widely used in pathfinding and graph traversal, the process of plotting an efficiently traversable path between multiple points, called nodes. Noted for its performance and accuracy, it enjoys widespread use. However, in practical travel-routing systems, it is generally outperformed by algorithms which can pre-process the graph to attain better performance, although other work has found A* to be superior to other approaches. 45 | 46 | ### Pseudocode 47 | ``` 48 | push startNode onto openList 49 | while(openList is not empty) { 50 | currentNode = find lowest f in openList 51 | if currentNode is final, return the successful path 52 | push currentNode onto closedList and remove from openList 53 | foreach neighbor of currentNode { 54 | if neighbor is not in openList { 55 | save g, h, and f then save the current parent 56 | add neighbor to openList 57 | } 58 | if neighbor is in openList but the current g is better than previous g { 59 | save g and f, then save the current parent 60 | } 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /shortest-path/aStar.js: -------------------------------------------------------------------------------- 1 | const BinaryHeap = require('../binary-heap/binaryHeap'); 2 | 3 | const pathTo = (node) => { 4 | const path = []; 5 | let curr = node; 6 | 7 | while (curr.parent) { 8 | path.unshift(curr); 9 | curr = curr.parent; 10 | } 11 | return path; 12 | }; 13 | 14 | const getHeap = () => new BinaryHeap( node => node.f ); 15 | 16 | const astar = { 17 | /** 18 | * Perform an A* Search on a graph given a start and end node. 19 | * @param {Graph} graph 20 | * @param {GridNode} start 21 | * @param {GridNode} end 22 | * @param {Object} [options] 23 | * @param {bool} [options.closest] Specifies whether to return the 24 | path to the closest node if the target is unreachable. 25 | * @param {Function} [options.heuristic] Heuristic function (see 26 | * astar.heuristics). 27 | */ 28 | search: (graph, start, end, options) => { 29 | graph.cleanDirty(); 30 | options = options || {}; 31 | const heuristic = options.heuristic || astar.heuristics.manhattan; 32 | const closest = options.closest || false; 33 | 34 | const openHeap = getHeap(); 35 | let closestNode = start; // set the start node to be the closest if required 36 | 37 | start.h = heuristic(start, end); 38 | graph.markDirty(start); 39 | 40 | openHeap.push(start); 41 | 42 | while (openHeap.size() > 0) { 43 | // Grab the lowest f(x) to process next. Heap keeps this sorted for us. 44 | const currentNode = openHeap.pop(); 45 | 46 | // End case -- result has been found, return the traced path. 47 | if (currentNode === end) { 48 | return pathTo(currentNode); 49 | } 50 | 51 | // Normal case -- move currentNode from open to closed, process each of its neighbors. 52 | currentNode.closed = true; 53 | 54 | // Find all neighbors for the current node. 55 | const neighbors = graph.neighbors(currentNode); 56 | 57 | for (let i = 0, il = neighbors.length; i < il; ++i) { 58 | const neighbor = neighbors[i]; 59 | 60 | if (neighbor.closed || neighbor.isWall()) { 61 | // Not a valid node to process, skip to next neighbor. 62 | continue; 63 | } 64 | 65 | // The g score is the shortest distance from start to current node. 66 | // We need to check if the path we have arrived at 67 | // this neighbor is the shortest one we have seen yet. 68 | const gScore = currentNode.g + neighbor.getCost(currentNode); 69 | const beenVisited = neighbor.visited; 70 | 71 | if (!beenVisited || gScore < neighbor.g) { 72 | 73 | // Found an optimal (so far) path to this node. 74 | // Take score for node to see how good it is. 75 | neighbor.visited = true; 76 | neighbor.parent = currentNode; 77 | neighbor.h = neighbor.h || heuristic(neighbor, end); 78 | neighbor.g = gScore; 79 | neighbor.f = neighbor.g + neighbor.h; 80 | graph.markDirty(neighbor); 81 | if (closest) { 82 | // If the neighbour is closer than the current closestNode 83 | // or if it's equally close but has a cheaper path than 84 | // the current closest node then it becomes the closest node 85 | if ( 86 | neighbor.h < closestNode.h || 87 | (neighbor.h === closestNode.h && neighbor.g < closestNode.g) 88 | ) { 89 | closestNode = neighbor; 90 | } 91 | } 92 | 93 | if (!beenVisited) { 94 | // Pushing to heap will put it in proper place based on the 'f' value. 95 | openHeap.push(neighbor); 96 | } else { 97 | // Already seen the node, but since it has been 98 | // rescored we need to reorder it in the heap 99 | openHeap.rescoreElement(neighbor); 100 | } 101 | } 102 | } 103 | } 104 | 105 | if (closest) { 106 | return pathTo(closestNode); 107 | } 108 | 109 | // No result was found - empty array signifies failure to find path. 110 | return []; 111 | }, 112 | // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html 113 | heuristics: { 114 | manhattan: (pos0, pos1) => { 115 | const d1 = Math.abs(pos1.x - pos0.x); 116 | const d2 = Math.abs(pos1.y - pos0.y); 117 | return d1 + d2; 118 | }, 119 | diagonal: (pos0, pos1) => { 120 | const D = 1; 121 | const D2 = Math.sqrt(2); 122 | const d1 = Math.abs(pos1.x - pos0.x); 123 | const d2 = Math.abs(pos1.y - pos0.y); 124 | return (D * (d1 + d2)) + ((D2 - (2 * D)) * Math.min(d1, d2)); 125 | }, 126 | }, 127 | cleanNode: (node) => { 128 | node.f = 0; 129 | node.g = 0; 130 | node.h = 0; 131 | node.visited = false; 132 | node.closed = false; 133 | node.parent = null; 134 | }, 135 | }; 136 | 137 | module.exports = astar; 138 | 139 | // http://theory.stanford.edu/~amitp/GameProgramming/ImplementationNotes.html 140 | // http://www.briangrinstead.com/blog/astar-search-algorithm-in-javascript/ 141 | // https://github.com/bgrins/javascript-astar/blob/master/astar.js 142 | // https://en.wikipedia.org/wiki/A*_search_algorithm 143 | -------------------------------------------------------------------------------- /shortest-path/dijkstra.js: -------------------------------------------------------------------------------- 1 | const PriorityQueue = require('../queue/priority-queue'); 2 | 3 | /** 4 | * Pathfinding starts here 5 | */ 6 | class Graph { 7 | constructor() { 8 | this.INFINITY = 1 / 0; 9 | this.vertices = {}; 10 | } 11 | 12 | addVertex(name, edges) { 13 | this.vertices[name] = edges; 14 | } 15 | 16 | shortestPath(start, finish) { 17 | const nodes = new PriorityQueue(); 18 | const distances = {}; 19 | const previous = {}; 20 | const path = []; 21 | let smallest; 22 | let vertex; 23 | let neighbor; 24 | let alt; 25 | 26 | // Assign to every node a tentative distance value: 27 | // set it to zero for our initial node and to infinity 28 | // for all other nodes. 29 | for (vertex in this.vertices) { 30 | if (vertex === start) { 31 | // Set the initial node as current. 32 | distances[vertex] = 0; 33 | nodes.enqueue(0, vertex); 34 | } else { 35 | // Mark all other nodes unvisited. 36 | // Create a set of all the unvisited nodes 37 | // called the unvisited set. 38 | distances[vertex] = this.INFINITY; 39 | nodes.enqueue(this.INFINITY, vertex); 40 | } 41 | 42 | previous[vertex] = null; 43 | } 44 | 45 | // Loop through all of the nodes until we run out of nodes, 46 | // or we find the node we were looking for 47 | while (!nodes.isEmpty()) { 48 | smallest = nodes.dequeue(); 49 | 50 | // If the current smallest node is the node we are looking for 51 | if (smallest === finish) { 52 | while (previous[smallest]) { 53 | path.push(smallest); 54 | smallest = previous[smallest]; 55 | } 56 | break; 57 | } 58 | 59 | if (!smallest || distances[smallest] === this.INFINITY) { 60 | continue; 61 | } 62 | 63 | // For the current node, consider all of its unvisited 64 | // neighbors and calculate their tentative distances. 65 | for (neighbor in this.vertices[smallest]) { 66 | 67 | // Compare the newly calculated tentative distance to the 68 | // current assigned value and assign the smaller one. 69 | // For example, if the current node A is marked with a 70 | // distance of 6, and the edge connecting it with a 71 | // neighbor B has length 2, 72 | // then the distance to B (through A) will be 6 + 2 = 8. 73 | // If B was previously marked with a distance greater than 8 then change it to 8. 74 | // Otherwise, keep the current value. 75 | alt = distances[smallest] + this.vertices[smallest][neighbor]; 76 | 77 | // If the newly calculated tentative distance is smaller 78 | // than the currently assigned value then assign the smaller one. 79 | if (alt < distances[neighbor]) { 80 | distances[neighbor] = alt; 81 | previous[neighbor] = smallest; 82 | nodes.enqueue(alt, neighbor); 83 | } 84 | } 85 | } 86 | 87 | return path; 88 | } 89 | } 90 | 91 | // Source: https://github.com/mburst/dijkstras-algorithm 92 | 93 | module.exports = Graph; 94 | -------------------------------------------------------------------------------- /sorting/README.md: -------------------------------------------------------------------------------- 1 | # Sorting Algorithms 2 | 3 | 5 popular sorting algorithms implemented manually and visualized with DOM manipulation. 4 | The algorithms included are : bubble sort, merge sort, insertion sort, selection sort and quick sort. In addition to visualizations, this readme also includes high-level explanations and psuedo code for each sorting implementation, and big Θ worst case/ best case scenarios. 5 | 6 | You can check out a live version of these sorting algorithms [here](https://joekarlsson.github.io/sorting-alogorithms/): 7 | 8 | 9 | ## Visualizations 10 | 11 | Click on the different sorting algorithms to see visualizations for each different algorithm implementation. Before each sort, click on reset to randomize the order of the array. You are also able to adjust the speed of the sorting intervals at the top. 12 | 13 | ## bubble sort 14 | 15 | ### [javascript implementation](public/js/bubblesort.js) 16 | 17 | Bubble sort works in a nature similar to its name, the lesser or lighter values will 'bubble' to the beginning of the array, and the heavier values will sink. 18 | ``` 19 | procedure bubbleSort( A : list ) 20 | do 21 | swapped = false 22 | for i < A.length - 1 23 | if A[i-1] > A[i] 24 | swap ( A[i-1] > A[i] ) 25 | swapped = true 26 | end if 27 | end for 28 | while swapped == true 29 | end procedure 30 | ``` 31 | This process is done by making a do while loop, where the condition will exit if the array is sorted. Then we iterate over the array, and compare each element to the next one. If the element is lighter, then you swap their positions in the array, repeat until you hit the end of the array while always setting a conditional value to true. If you loop through the array, and the conditional value is never set, meaning there are no swaps, then you exit out of the array. 32 | 33 | ### Best Case 34 | Linearly increases time spent to sort with size of array. O(n) 35 | 36 | ### Worst Case 37 | Exponentially increases time spent to sort. O(n^2) 38 | 39 | ## merge sort 40 | 41 | ### [javascript implementation](public/js/mergesort.js) 42 | 43 | The process for this method takes two procedures, first splitting the array into smaller lists, then merging those lists back together, comparing the values of the two lists. Recursion is used to split the array into the smallest possible lists. 44 | ``` psuedocode 45 | procedure mergeSort ( A : list ) 46 | if A.length < 2 47 | return A 48 | 49 | middle = A.length / 2 50 | left = A.slice 0, middle 51 | right = A.slice middle, A.length 52 | 53 | return merge(mergeSort(left), mergeSort(right); 54 | 55 | procedure merge (left, right) 56 | result = [] 57 | while left.length && right.length both exist 58 | if left[0] <= right[0] 59 | push left to result 60 | else 61 | push right to result 62 | end while 63 | 64 | while only left exists 65 | push left 66 | 67 | while only right exists 68 | push right 69 | 70 | return result 71 | ``` 72 | If the list is 0 or 1 length, then its already sorted and we can return the array. Else, we have to find the mid point of the aray, then split it into a left and right array. Our second piece of logic takes in a left and right array, which returns a merged array. If both left and right arrays have a length, then we compare the first value of both, and push the lower value to the return array. Our first procedure utilizes recursion to get the smallest available arrays. 73 | 74 | ### Best & Worst Case 75 | Logarithimically and linearlly increases time spent to sort with size array. It's more efficient the larger the size of the array, not great for small arrays. O(n log(n)) 76 | 77 | ## insertion sort 78 | 79 | ### [javascript implementation](public/js/insertionsort.js) 80 | 81 | For each iteration in our insertion sort method, a single element is taken to find its location in a new sorted list. This pattern repeats until the old array has no elements left. 82 | 83 | ``` 84 | for i = 1 to A.length - 1 85 | j = i 86 | while j > 0 and A[j-1] > A[j] 87 | swap A[j-1] and A[j] 88 | j = j - 1 89 | ``` 90 | 91 | First, we have to iterate over our array, then we set a variable to our position in the array, j. While j has a length, we compare our values between our position and the next position in the array. If the next position is bigger, then we swap the two, and decrement j by 1; 92 | 93 | ### Best Case 94 | Linerally increases time with size. O(n) 95 | 96 | ### Worst Case 97 | Exponentially increases time with size. O(n^2) 98 | 99 | ## selection sort 100 | 101 | ### [javascript implementation](public/js/selectionsort.js) 102 | 103 | Searches the array to find the smallest value, then loops again to find the next smallest value. Assuming minimum is the first element, we compare our minimums then swap it with the position it should be in from the beginning. 104 | 105 | ``` 106 | for i = 0 to A.length - 1 107 | 108 | min = i; 109 | 110 | for j = i + 1 to A.length 111 | 112 | if A[j] < A[min] 113 | 114 | min = j 115 | 116 | if min != i 117 | swap A[i] and A[min] 118 | ``` 119 | We loop through our array, and set our min position variable to our first positon. We then loop again through the array with our variable j, and check if our position is our new minimum. If it is, then we set our min position to j. If min is not i, then we swap our new minimum with our current position. 120 | 121 | ### Best & Worst Case 122 | Exponentially increases time with size. O(n^2) 123 | 124 | ## quicksort 125 | 126 | ### [javascript implementation](public/js/quicksort.js) 127 | 128 | Otherwise known as partition-exchange sort, quicksort picks a pivot from the array, then reorders the array with values lower than the pivot before the pivot, and higher values after the pivot. After this is done, pivot belongs in this position, and we recursively apply the same steps to the other partitions. 129 | 130 | ``` 131 | quicksort(A, left, right) 132 | 133 | pivot = null 134 | 135 | if left does not exist 136 | left = 0 137 | 138 | if right does not exist 139 | right = A.length - 1 140 | 141 | if left < right 142 | pivot = position between left and right 143 | newPart = partition(A, pivot, left, right) 144 | quicksort(A, left, newPart - 1) 145 | quicksort(A, newPart + 1, right) 146 | 147 | partition(A, pivot, left, right) 148 | 149 | pivotValue = A[pivot] 150 | index = left 151 | 152 | swap pivot and right 153 | 154 | for i = left i <= right i++ 155 | 156 | if A[i] < pivotValue 157 | swap i and index 158 | index++ 159 | 160 | swap right and index 161 | 162 | return index 163 | ``` 164 | First we determine a pivot and loop through our array, if the value at our position is less than or equal to our pivot, then swap our position of i in the array to our position of j. 165 | Return j. We recursively do this until we have no more partitions. 166 | 167 | ### Best Case 168 | 169 | Logarithimically and linearlly increases time spent to sort with size array. It's more efficient the larger the size of the array, not great for small arrays. O(n log(n)) 170 | 171 | ### Worst Case 172 | 173 | Exponentially increase the time spent to sort with size array. 174 | 175 | - [ ] [Bubble Sort (video)](https://www.youtube.com/watch?v=P00xJgWzz2c&index=1&list=PL89B61F78B552C1AB) 176 | - [ ] [Analyzing Bubble Sort (video)](https://www.youtube.com/watch?v=ni_zk257Nqo&index=7&list=PL89B61F78B552C1AB) 177 | - [ ] [Insertion Sort, Merge Sort (video)](https://www.youtube.com/watch?v=Kg4bqzAqRBM&index=3&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb) 178 | - [ ] [Insertion Sort (video)](https://www.youtube.com/watch?v=c4BRHC7kTaQ&index=2&list=PL89B61F78B552C1AB) 179 | - [ ] [Merge Sort (video)](https://www.youtube.com/watch?v=GCae1WNvnZM&index=3&list=PL89B61F78B552C1AB) 180 | - [ ] [Quicksort (video)](https://www.youtube.com/watch?v=y_G9BkAm6B8&index=4&list=PL89B61F78B552C1AB) 181 | - [ ] [Selection Sort (video)](https://www.youtube.com/watch?v=6nDMgr0-Yyo&index=8&list=PL89B61F78B552C1AB) 182 | 183 | - [ ] Stanford lectures on sorting: 184 | - [ ] [Lecture 15 | Programming Abstractions (video)](https://www.youtube.com/watch?v=ENp00xylP7c&index=15&list=PLFE6E58F856038C69) 185 | - [ ] [Lecture 16 | Programming Abstractions (video)](https://www.youtube.com/watch?v=y4M9IVgrVKo&index=16&list=PLFE6E58F856038C69) 186 | 187 | - [ ] Shai Simonson, [Aduni.org](http://www.aduni.org/): 188 | - [ ] [Algorithms - Sorting - Lecture 2 (video)](https://www.youtube.com/watch?v=odNJmw5TOEE&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=2) 189 | - [ ] [Algorithms - Sorting II - Lecture 3 (video)](https://www.youtube.com/watch?v=hj8YKFTFKEE&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=3) 190 | 191 | - [ ] Steven Skiena lectures on sorting: 192 | - [ ] [lecture begins at 26:46 (video)](https://youtu.be/ute-pmMkyuk?list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b&t=1600) 193 | - [ ] [lecture begins at 27:40 (video)](https://www.youtube.com/watch?v=yLvp-pB8mak&index=8&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b) 194 | - [ ] [lecture begins at 35:00 (video)](https://www.youtube.com/watch?v=q7K9otnzlfE&index=9&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b) 195 | - [ ] [lecture begins at 23:50 (video)](https://www.youtube.com/watch?v=TvqIGu9Iupw&list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b&index=10) 196 | 197 | - [ ] UC Berkeley: 198 | - [ ] [CS 61B Lecture 29: Sorting I (video)](https://www.youtube.com/watch?v=EiUvYS2DT6I&list=PL4BBB74C7D2A1049C&index=29) 199 | - [ ] [CS 61B Lecture 30: Sorting II (video)](https://www.youtube.com/watch?v=2hTY3t80Qsk&list=PL4BBB74C7D2A1049C&index=30) 200 | - [ ] [CS 61B Lecture 32: Sorting III (video)](https://www.youtube.com/watch?v=Y6LOLpxg6Dc&index=32&list=PL4BBB74C7D2A1049C) 201 | - [ ] [CS 61B Lecture 33: Sorting V (video)](https://www.youtube.com/watch?v=qNMQ4ly43p4&index=33&list=PL4BBB74C7D2A1049C) 202 | 203 | - [ ] - Merge sort code: 204 | - [ ] [Using output array](http://www.cs.yale.edu/homes/aspnes/classes/223/examples/sorting/mergesort.c) 205 | - [ ] [In-place](https://github.com/jwasham/practice-cpp/blob/master/merge_sort/merge_sort.cc) 206 | - [ ] - Quick sort code: 207 | - [ ] [Implementation](http://www.cs.yale.edu/homes/aspnes/classes/223/examples/randomization/quick.c) 208 | - [ ] [Implementation](https://github.com/jwasham/practice-c/blob/master/quick_sort/quick_sort.c) 209 | 210 | - [ ] Implement: 211 | - [ ] Mergesort: O(n log n) average and worst case 212 | - [ ] Quicksort O(n log n) average case 213 | - Selection sort and insertion sort are both O(n^2) average and worst case 214 | - For heapsort, see Heap data structure above. 215 | 216 | - [ ] For curiosity - not required: 217 | - [ ] [Radix Sort](http://www.cs.yale.edu/homes/aspnes/classes/223/notes.html#radixSort) 218 | - [ ] [Radix Sort (video)](https://www.youtube.com/watch?v=xhr26ia4k38) 219 | - [ ] [Radix Sort, Counting Sort (linear time given constraints) (video)](https://www.youtube.com/watch?v=Nz1KZXbghj8&index=7&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb) 220 | - [ ] [Randomization: Matrix Multiply, Quicksort, Freivalds' algorithm (video)](https://www.youtube.com/watch?v=cNB2lADK3_s&index=8&list=PLUl4u3cNGP6317WaSNfmCvGym2ucw3oGp) 221 | - [ ] [Sorting in Linear Time (video)](https://www.youtube.com/watch?v=pOKy3RZbSws&list=PLUl4u3cNGP61hsJNdULdudlRL493b-XZf&index=14) 222 | -------------------------------------------------------------------------------- /sorting/bubble-recursive.js: -------------------------------------------------------------------------------- 1 | const swap = ( arr, i1, i2 ) => { 2 | const tmp = arr[i1]; 3 | arr[i1] = arr[i2]; 4 | arr[i2] = tmp; 5 | return arr; 6 | }; 7 | 8 | // Recursive implementation of Bubble Sort 9 | const bubbleSort = ( arr ) => { 10 | let swapped = false; 11 | 12 | for ( let i = 0; i < arr.length; i++ ) { 13 | if ( arr[i] > arr[i + 1] ) { 14 | swap( arr, i, i + 1 ); 15 | swapped = true; 16 | } 17 | } 18 | 19 | if ( swapped === true ) { 20 | return bubbleSort( arr ); 21 | } 22 | return arr; 23 | }; 24 | 25 | module.exports = bubbleSort; 26 | -------------------------------------------------------------------------------- /sorting/bubblesort.js: -------------------------------------------------------------------------------- 1 | const bubbleModule = (() => { 2 | const swap = ( arr, i1, i2) => { 3 | const tmp = arr[i1]; 4 | arr[i1] = arr[i2]; 5 | arr[i2] = tmp; 6 | return arr; 7 | }; 8 | 9 | /* 10 | * Bubble sort works in a nature similar to its name, 11 | * the lesser - or lighter - values 12 | * will 'bubble' to the beginning of the array, 13 | * and the heavier values will 'sink' 14 | * to the bottom. 15 | */ 16 | return { 17 | bubbleSort: ( array ) => { 18 | // create variables for swapping and our while loop condition 19 | let swapped = true; 20 | 21 | // Continue making passes until we have a clean pass with no swaps. 22 | while ( swapped ) { 23 | // init swapped to false at the top of the while loop 24 | swapped = false; 25 | 26 | // loop through our array 27 | for ( let i = 0; i < array.length; i++ ) { 28 | // at each position, compare this element with the previous 29 | // if this one is greater than our previous one swap it and 30 | // flag our conditional to loop through our array again 31 | if ( array[i - 1] > array[i] ) { 32 | // swap the two numbers 33 | swap( array, i - 1, i ); 34 | 35 | // flag our conditional to continue looping 36 | swapped = true; 37 | } 38 | } 39 | } 40 | // return our sorted array 41 | return array; 42 | }, 43 | }; 44 | }); 45 | 46 | module.exports = bubbleModule; 47 | -------------------------------------------------------------------------------- /sorting/insertionsort.js: -------------------------------------------------------------------------------- 1 | const insertionModule = (() => { 2 | // swap method because its used multiple times 3 | const swap = ( array, index1, index2 ) => { 4 | // store a tmp variable at pos index2 5 | const tmp = array[index2]; 6 | 7 | // set value of index2 to our value at index 8 | array[index2] = array[index1]; 9 | 10 | // set our value of index1 to our stored variable 11 | array[index1] = tmp; 12 | }; 13 | 14 | return { 15 | /** 16 | * Over each iteration insertion sort removes one element 17 | * from the input array, finds the location it belongs to 18 | * and inserts it at this point. 19 | * 20 | * @param array unsorted array that will be sort 21 | * @return array sorted array 22 | */ 23 | insertionSort: ( a ) => { 24 | // Iterate over each element in the array 25 | // for each element we will be finding the 26 | // correct place to put this element 27 | for ( let i = 1; i < a.length; i++ ) { 28 | // init j to i 29 | let j = i; 30 | // while our previous number is greater than 0, 31 | // and the number we're comparing is less than 32 | // our previous number enter our loop 33 | while ( j > 0 && ( a[j - 1] > a[j] ) ) { 34 | // shift the number down the array and give us a space to insert our current value 35 | swap( a, j, j - 1 ); 36 | // decrement j to go through our entire array 37 | j--; 38 | } 39 | } 40 | return a; 41 | }, 42 | }; 43 | }); 44 | 45 | module.exports = insertionModule; 46 | -------------------------------------------------------------------------------- /sorting/mergesort.js: -------------------------------------------------------------------------------- 1 | const mergeModule = ( () => { 2 | // used to merge all of our pieces back together after recursively separating the array 3 | const merge = ( left, right ) => { 4 | // initialize array to return 5 | const result = []; 6 | 7 | // if both of our split arrays have items inside go through this while loop 8 | while ( left.length > 0 && right.length > 0 ) { 9 | // compare the first element of each array 10 | if ( left[0] <= right[0] ) { 11 | // if the left element is smaller, push it 12 | // to our return array 13 | result.push( left.shift() ); 14 | } else { 15 | // if the right element is smaller, push it 16 | // to our return array 17 | result.push( right.shift() ); 18 | } 19 | } 20 | 21 | // if only our left array has an element left, push that 22 | while ( left.length > 0 ) { 23 | result.push( left.shift() ); 24 | } 25 | 26 | // if only our right array has an element left, push that 27 | while ( right.length > 0 ) { 28 | result.push( right.shift() ); 29 | } 30 | 31 | // return the sorted array 32 | return result; 33 | }; 34 | 35 | return { 36 | mergeSort: function ( arr ) { 37 | // Base Case - if the array is length 0 or 1, 38 | // then we can assume it is already sorted and return it 39 | if (arr.length < 2) { 40 | return arr; 41 | } 42 | 43 | // pick a pivot at our the middle of our arr 44 | const pivot = ( Math.floor(arr.length / 2) ); 45 | 46 | // separate the arr into two places, everything before it 47 | const pLeft = arr.slice( 0, pivot ); 48 | 49 | // and everything after 50 | const pRight = arr.slice( pivot, arr.length ); 51 | 52 | // call our mergeSort recursively on this array, 53 | // splitting it further and further until it hits 54 | // our base case and the array is split into lengths less than 2 55 | return merge( this.mergeSort( pLeft ), this.mergeSort( pRight )); 56 | }, 57 | }; 58 | }); 59 | 60 | module.exports = mergeModule; 61 | -------------------------------------------------------------------------------- /sorting/quick-recursive.js: -------------------------------------------------------------------------------- 1 | const quickSort = ( arr ) => { 2 | // Base case 3 | if ( arr.length <= 1 ) { 4 | return arr; 5 | } 6 | 7 | // 1) Pick a pivot 8 | const pivot = arr[0]; 9 | 10 | // 2) Partition 11 | const { left, right } = partition( arr, pivot ); 12 | 13 | // 3) Call quick sort recursively 14 | const leftArr = quickSort( left ); 15 | const rightArr = quickSort( right ); 16 | 17 | // 4) Concat after calling quicksort recursively 18 | return leftArr.concat( pivot, rightArr ); 19 | }; 20 | 21 | const partition = ( arr, pivot ) => { 22 | const left = []; 23 | const right = []; 24 | 25 | // Loop through the array and split it into left and right arrays 26 | for ( let i = 1; i < arr.length; i++ ) { 27 | // If value is less than the pivot - push into the left array, else push it into the right 28 | if ( arr[i] < pivot ) { 29 | left.push( arr[i] ); 30 | } else { 31 | right.push( arr[i] ); 32 | } 33 | } 34 | 35 | return { 36 | left, 37 | right, 38 | }; 39 | }; 40 | 41 | module.exports = quickSort; 42 | -------------------------------------------------------------------------------- /sorting/quicksort.js: -------------------------------------------------------------------------------- 1 | const quickModule = (() => { 2 | // Private Methods and variables 3 | 4 | // swap method because its used multiple times 5 | const swap = ( array, index1, index2 ) => { 6 | 7 | // store a tmp variable at pos index2 8 | const tmp = array[index2]; 9 | 10 | // set value of index2 to our value at index 11 | array[index2] = array[index1]; 12 | 13 | // Let our value of index1 to our stored letiable 14 | array[index1] = tmp; 15 | }; 16 | 17 | // function for creating our partitions and swapping 18 | const partition = ( arr, pivot, lo, hi ) => { 19 | 20 | // the value of our pivot, where pivot is the index 21 | const pivotValue = arr[pivot]; 22 | 23 | // our new pivot to be, and our comparison 24 | let index = lo; 25 | 26 | // swap our pivot to the end, because we want it in the hi partition 27 | swap(arr, hi, pivot); 28 | 29 | // loop through our array, from our lo value, to our hi value 30 | for ( let i = lo; i < hi; i++ ) { 31 | 32 | // if the value at this position is less than our pivot value, then it needs to be sorted 33 | // to the left 34 | if ( arr[i] < pivotValue ) { 35 | 36 | // swap it and the index, and now that we know it should be sorted 37 | // increment the index because its been sorted 38 | swap( arr, i, index ); 39 | index++; 40 | } 41 | } 42 | 43 | // swap our hi value back with the index value, this is putting our pivot value 44 | // back where it rightfully belongs 45 | swap( arr, index, hi ); 46 | 47 | // return the index for a new pivot in recursively calling quickSort 48 | return index; 49 | }; 50 | 51 | // Public methods 52 | return { 53 | /* Known as partition-exchange sort, quicksort picks a pivot from a partition 54 | * (assuming the first partition is our array). Reorders our array into lower 55 | * higher value partitions, then recursively creates partitions until it cannot. 56 | * takes in the array and optionally low and high parameters for recursion 57 | */ 58 | quickSort: function( array, low, high ) { 59 | 60 | // reset our pivot for recursive use 61 | let pivot = null; 62 | 63 | // used for initialization, begin on the end 64 | if ( typeof low !== 'number' ) { 65 | low = 0; 66 | } 67 | 68 | // used for initialization, begin on the end 69 | if ( typeof high !== 'number' ) { 70 | high = array.length - 1; 71 | } 72 | 73 | // base case for recursion, if low is >= high, then its already sorted 74 | if ( low < high ) { 75 | 76 | // create a point between our low and high values 77 | pivot = low + ( Math.ceil( ( high - low ) * 0.5) ); 78 | 79 | // create the positions and partitions to be recursively sorted 80 | const nextPivot = partition( array, pivot, low, high ); 81 | 82 | // sort from low, to the pivot - 1, because nextPivot belongs where it is 83 | this.quickSort( array, low, nextPivot - 1 ); 84 | 85 | // sort from pivot + 1 to high 86 | this.quickSort( array, nextPivot + 1, high ); 87 | } 88 | 89 | // return the sorted array 90 | return array; 91 | }, 92 | }; 93 | }); 94 | 95 | module.exports = quickModule; 96 | -------------------------------------------------------------------------------- /sorting/selectionsort.js: -------------------------------------------------------------------------------- 1 | const selectionModule = (() => { 2 | // swap method because its used multiple times 3 | const swap = (array, index1, index2) => { 4 | // store a tmp variable at pos index2 5 | const tmp = array[index2]; 6 | 7 | // set value of index2 to our value at index 8 | array[index2] = array[index1]; 9 | 10 | // set our value of index1 to our stored variable 11 | array[index1] = tmp; 12 | }; 13 | 14 | // Everything after the return statement is public 15 | return { 16 | selectionSort: ( array ) => { 17 | for ( let i = 0; i < array.length - 1; i++ ) { 18 | let min = i; 19 | for ( let j = i + 1; j < array.length; j++ ) { 20 | if ( array[j] < array[min] ) { 21 | min = j; 22 | } 23 | } 24 | if (min !== i) { 25 | swap(array, i, min); 26 | } 27 | } 28 | return array; 29 | }, 30 | }; 31 | }); 32 | 33 | module.exports = selectionModule; 34 | -------------------------------------------------------------------------------- /stack/README.md: -------------------------------------------------------------------------------- 1 | # Stack (abstract data type) 2 | 3 | A stack is an abstract data type that serves as a collection of elements, with two principal operations: push, which adds an element to the collection, and pop, which removes the most recently added element that was not yet removed. The order in which elements come off a stack gives rise to its alternative name, LIFO (for last in, first out). Additionally, a peek operation may give access to the top without modifying the stack. 4 | 5 | The name "stack" for this type of structure comes from the analogy to a set of physical items stacked on top of each other, which makes it easy to take an item off the top of the stack, while getting to an item deeper in the stack may require taking off multiple other items first. 6 | 7 | ![Simple representation of a stack runtime with push and pop operations.](https://upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Lifo_stack.png/350px-Lifo_stack.png) -------------------------------------------------------------------------------- /stack/stack.js: -------------------------------------------------------------------------------- 1 | class Stack { 2 | constructor() { 3 | this.top = null; 4 | } 5 | 6 | push( value ) { 7 | const newNode = { 8 | value, 9 | next: this.top, 10 | }; 11 | this.top = newNode; 12 | } 13 | 14 | pop() { 15 | if ( this.top !== null ) { 16 | const value = this.top.value; 17 | this.top = this.top.next; 18 | return value; 19 | } 20 | return null; 21 | } 22 | } 23 | 24 | module.exports = Stack; 25 | -------------------------------------------------------------------------------- /test/binary-heap.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const BinaryHeap = require('../binary-heap/binaryHeap'); 3 | 4 | const { expect } = chai; 5 | 6 | describe( 'BinaryHeap', () => { 7 | let heap; 8 | 9 | beforeEach(() => { 10 | heap = new BinaryHeap((x) => x); 11 | const data = [10, 3, 4, 8, 2, 9, 7, 1, 2, 6, 5]; 12 | 13 | data.forEach((el) => { 14 | heap.push(el); 15 | }); 16 | }); 17 | 18 | // describe( 'Constructor', () => { 19 | // it( 'should return a new empty binary heap', () => { 20 | // const fn = ((x) => { 21 | // return x; 22 | // }); 23 | // const newHeap = new BinaryHeap(fn); 24 | 25 | // console.log(newHeap); 26 | // expect(newHeap).to.deep.equal({ 27 | // content: [], 28 | // scoreFunction: fn, 29 | // }); 30 | 31 | // }); 32 | // }); 33 | 34 | describe( 'push', () => { 35 | it( 'should return a new empty binary heap', () => { 36 | // console.log(heap.pop()) 37 | // expect.(heap.content).to.deep.equal([1, 2, 4, 2, 5, 9, 7, 10, 3, 8, 6]) 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/bitwise.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const bitwise_basics = require('../bitwise/bitwise-basics'); 3 | const bitwise_rgb_hex_binary = require('../bitwise/bitwise-rgb-hex-binary'); 4 | 5 | const { expect } = chai; 6 | 7 | describe( 'Bitwise', () => { 8 | describe( 'Bitwise Basics', () => { 9 | describe( 'bitwise AND', () => { 10 | it( 'should return the binary & of two integers', () => { 11 | // 1001 & 1110 = 1000 12 | expect(bitwise_basics.bitwiseAND(9, 14)).to.equal('1000'); 13 | }); 14 | }); 15 | describe( 'bitwise OR', () => { 16 | it( 'should return the binary | of two integers', () => { 17 | // 1001 | 1110 = 1111 18 | expect(bitwise_basics.bitwiseOR(9, 14)).to.equal('1111'); 19 | }); 20 | }); 21 | describe( 'bitwise XOR', () => { 22 | it( 'should return the binary ^ of two integers', () => { 23 | // 1001 ^ 1110 = 0111 24 | expect(bitwise_basics.bitwiseXOR(9, 14)).to.equal('111'); 25 | }); 26 | }); 27 | describe( 'bitwise NOT', () => { 28 | it( 'should return the binary NOT of two integers', () => { 29 | // ~1001 = 0110 30 | expect(bitwise_basics.bitwiseNOT(9)).to.equal('11111111111111111111111111110110'); 31 | }); 32 | }); 33 | describe( 'bitwise Left Shift', () => { 34 | it( 'should return the binary left shift of a integer by n bits', () => { 35 | // 1001 << 2 = 100100 36 | expect(bitwise_basics.bitwiseLeftShift(9, 2)).to.equal('100100'); 37 | }); 38 | }); 39 | describe( 'bitwise Sign Propagating Right Shift', () => { 40 | it( 'should return the binary right of a integers by n bits', () => { 41 | // 1001 >> 2 = 0010 42 | expect(bitwise_basics.bitwiseSignPropagatingRightShift(9, 2)).to.equal('10'); 43 | }); 44 | }); 45 | describe( 'bitwise Zero Fill Right Shift', () => { 46 | it( 'should return the binary right shift of a integer by n bits', () => { 47 | // 1001 << 2 = 100100 48 | expect(bitwise_basics.bitwiseZeroFillRightShift(9, 2)).to.equal('10'); 49 | // 11111111111111111111111111110111 >>> 2 = 1073741821 50 | expect(bitwise_basics.bitwiseZeroFillRightShift(-9, 2)).to.equal('111111111111111111111111111101'); 51 | }); 52 | }); 53 | describe( 'bitwise is Even', () => { 54 | it( 'should return a boolean depending on whether n is even', () => { 55 | expect(bitwise_basics.isEven(9)).to.equal(false); 56 | expect(bitwise_basics.isEven(2)).to.equal(true); 57 | }); 58 | }); 59 | describe( 'bitwise is Odd', () => { 60 | it( 'should return a boolean depending on whether n is odd', () => { 61 | expect(bitwise_basics.isOdd(9)).to.equal(true); 62 | expect(bitwise_basics.isOdd(2)).to.equal(false); 63 | }); 64 | }); 65 | describe( 'bitwise dec2bin', () => { 66 | it( 'should return a binary equivent of n', () => { 67 | expect(bitwise_basics.dec2bin(9)).to.equal('1001'); 68 | expect(bitwise_basics.dec2bin(2)).to.equal('10'); 69 | expect(bitwise_basics.dec2bin(11)).to.equal('1011'); 70 | expect(bitwise_basics.dec2bin(-1)).to.equal('11111111111111111111111111111111'); 71 | }); 72 | }); 73 | describe( 'bitwise avgInt', () => { 74 | it( 'should return a the average of two ints', () => { 75 | // a + b / 2 76 | expect(bitwise_basics.avgInt(6, 12)).to.equal(9); 77 | expect(bitwise_basics.avgInt(2, 80)).to.equal(41); 78 | expect(bitwise_basics.avgInt(6, 1233)).to.equal(619); 79 | expect(bitwise_basics.avgInt(345345, 3234)).to.equal(174289); 80 | expect(bitwise_basics.avgInt(12643, 32222)).to.equal(22432); 81 | expect(bitwise_basics.avgInt(34444, 9999)).to.equal(22221); 82 | }); 83 | }); 84 | describe( 'bitwise plus One Int', () => { 85 | it( 'should return a int plus 1', () => { 86 | expect(bitwise_basics.plusOneInt(9)).to.equal(10); 87 | expect(bitwise_basics.plusOneInt(2)).to.equal(3); 88 | }); 89 | }); 90 | }); 91 | describe( 'Bitwise RGB HEX Binary', () => { 92 | describe( 'RGB', () => { 93 | let RGBarr; 94 | 95 | beforeEach(() => { 96 | RGBarr = new Array( 97 | [000,000,000], // black 98 | [000,255,000], // green 99 | [255,255,255] // white 100 | ); 101 | }); 102 | describe( 'To Bin', () => { 103 | it( 'should convert a RGB value to Binary', () => { 104 | let bin = []; 105 | // convert some RGB color values to hex and to binary 106 | RGBarr.forEach((rgb) => { 107 | bin.push(bitwise_rgb_hex_binary.RGBToBin(rgb[0],rgb[1],rgb[2])); 108 | }); 109 | // black 110 | expect(bin[0]).to.equal('000000000000000000000000'); 111 | // green 112 | expect(bin[1]).to.equal('000000001111111100000000'); 113 | // white 114 | expect(bin[2]).to.equal('111111111111111111111111'); 115 | 116 | }); 117 | }); 118 | describe( 'To Hex', () => { 119 | it( 'should convert a RGB value to Hex', () => { 120 | let bin = []; 121 | // convert some RGB color values to hex and to hex 122 | RGBarr.forEach((rgb) => { 123 | bin.push(bitwise_rgb_hex_binary.RGBToHex(rgb[0],rgb[1],rgb[2])); 124 | }); 125 | // black 126 | expect(bin[0]).to.equal('000000'); 127 | // green 128 | expect(bin[1]).to.equal('00FF00'); 129 | // white 130 | expect(bin[2]).to.equal('FFFFFF'); 131 | }); 132 | }); 133 | }); 134 | describe( 'Hex', () => { 135 | let HexArr; 136 | 137 | beforeEach(() => { 138 | HexArr = new Array( 139 | '000000', // black 140 | '00FF00', // green 141 | 'FFFFFF' // white 142 | ); 143 | }); 144 | describe( 'To RGB', () => { 145 | it( 'should convert a HEX value to RGB', () => { 146 | let rgb = []; 147 | // convert a hexidecimal color string to 0..255 R,G,B 148 | HexArr.forEach((hex) => { 149 | rgb.push(bitwise_rgb_hex_binary.hexToRGB(parseInt(hex,16))); 150 | }); 151 | // black 152 | expect(rgb[0]).to.deep.equal([0, 0, 0]); 153 | // green 154 | expect(rgb[1]).to.deep.equal([0, 255, 0]); 155 | // white 156 | expect(rgb[2]).to.deep.equal([255, 255, 255]); 157 | }); 158 | }); 159 | }); 160 | describe( 'Binart', () => { 161 | let binArr; 162 | 163 | beforeEach(() => { 164 | binArr = new Array( 165 | '000000000000000000000000', // black 166 | '000000001111111100000000', // green 167 | '111111111111111111111111' // white 168 | ); 169 | }); 170 | describe( 'To RGB', () => { 171 | it( 'should convert a binary value to RGB', () => { 172 | let rgb = []; 173 | // convert a hexidecimal color string to 0..255 R,G,B 174 | binArr.forEach((bin) => { 175 | rgb.push(bitwise_rgb_hex_binary.binToRGB(bin)); 176 | }); 177 | // black 178 | expect(rgb[0]).to.deep.equal([0, 0, 0]); 179 | // green 180 | expect(rgb[1]).to.deep.equal([0, 255, 0]); 181 | // white 182 | expect(rgb[2]).to.deep.equal([255, 255, 255]); 183 | 184 | }); 185 | }); 186 | }); 187 | }); 188 | }); 189 | -------------------------------------------------------------------------------- /test/complex-array.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | 3 | const expect = chai.expect; 4 | 5 | const ComplexArray = require('../complex-array/complex-array'); 6 | 7 | function assertArrayEquals(first, second) { 8 | const message = `${first} != ${second}`; 9 | 10 | first.forEach((item, i) => { 11 | expect(item).to.equal(second[i], message); 12 | }); 13 | } 14 | 15 | describe('Complex Array', () => { 16 | describe('Consructor', () => { 17 | it('should construct from a number', () => { 18 | const a = new ComplexArray(10); 19 | 20 | expect(a).to.exist; 21 | expect(a.real.length).to.equal(10); 22 | expect(a.imag.length).to.equal(10); 23 | expect(a.real[0]).to.equal(0); 24 | expect(a.imag[0]).to.equal(0); 25 | }); 26 | 27 | it('should construct from a number with a type', () => { 28 | const a = new ComplexArray(10, Int32Array); 29 | 30 | expect(a.ArrayType).to.equal(Int32Array); 31 | expect(a.real.length).to.equal(10); 32 | expect(a.imag.length).to.equal(10); 33 | expect(a.real[0]).to.equal(0); 34 | expect(a.imag[0]).to.equal(0); 35 | }); 36 | 37 | it('should contruct from a real array', () => { 38 | const a = new ComplexArray([1, 2]); 39 | 40 | assertArrayEquals([1, 2], a.real); 41 | assertArrayEquals([0, 0], a.imag); 42 | }); 43 | 44 | it('should contruct from a real array with a type', () => { 45 | const a = new ComplexArray([1, 2], Int32Array); 46 | 47 | expect(a.ArrayType).to.equal(Int32Array) 48 | assertArrayEquals([1, 2], a.real); 49 | assertArrayEquals([0, 0], a.imag); 50 | }); 51 | 52 | it('should contruct from another complex array', () => { 53 | const a = new ComplexArray(new ComplexArray([1, 2])); 54 | 55 | assertArrayEquals([1, 2], a.real); 56 | assertArrayEquals([0, 0], a.imag); 57 | }); 58 | }); 59 | 60 | describe('`map` method', () => { 61 | it('should alter all values', () => { 62 | const a = new ComplexArray([1, 2]).map((value, i) => { 63 | value.real *= 10; 64 | value.imag = i; 65 | }); 66 | 67 | assertArrayEquals([10, 20], a.real); 68 | assertArrayEquals([0, 1], a.imag); 69 | }); 70 | }); 71 | 72 | describe('`forEach` method', () => { 73 | it('should touch every value', () => { 74 | const a = new ComplexArray([1, 2]); 75 | a.imag[0] = 4; 76 | a.imag[1] = 8; 77 | 78 | let sum = 0; 79 | a.forEach((value, i) => { 80 | sum += value.real; 81 | sum += value.imag; 82 | }); 83 | 84 | expect(sum).to.equal(15); 85 | }); 86 | }); 87 | 88 | describe('`conjugate` method', () => { 89 | it('should multiply a number', () => { 90 | const a = new ComplexArray([1, 2]); 91 | a.imag[0] = 1; 92 | a.imag[1] = -2; 93 | 94 | const b = a.conjugate(); 95 | 96 | assertArrayEquals([1, 2], b.real); 97 | assertArrayEquals([-1, 2], b.imag); 98 | }); 99 | }); 100 | 101 | describe('`magnitude` method', () => { 102 | it('should give the an array of magnitudes', () => { 103 | const a = new ComplexArray([1, 3]); 104 | a.imag[0] = 0; 105 | a.imag[1] = 4; 106 | 107 | assertArrayEquals([1, 5], a.magnitude()); 108 | }); 109 | 110 | it('should return an iterable ArrayType object', () => { 111 | const a = new ComplexArray([1, 2]); 112 | 113 | let sum = 0; 114 | a.magnitude().forEach((value, i) => { 115 | sum += value; 116 | }); 117 | 118 | expect(sum).to.equal(3); 119 | }); 120 | }); 121 | 122 | }); 123 | -------------------------------------------------------------------------------- /test/fft-test-helper.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const fftMod = require('../fast-fourier-transforms/fft'); 3 | const ComplexArray = require('../complex-array/complex-array'); 4 | const {FFT, InvFFT} = fftMod; 5 | const expect = chai.expect; 6 | 7 | const EPSILON = 1e-4; 8 | const PI = Math.PI; 9 | 10 | function assertComplexArraysAlmostEqual(first, second) { 11 | const message = `${second} != ${first}`; 12 | 13 | expect(first.length).to.equal(second.length, message); 14 | 15 | first.forEach((value, i) => { 16 | assertApproximatelyEqual(value.real, second.real[i], message); 17 | assertApproximatelyEqual(value.imag, second.imag[i], message); 18 | }); 19 | } 20 | 21 | function assertFFTMatches(original, expected) { 22 | if (!(expected instanceof ComplexArray)) { 23 | throw TypeError('expected match should be a ComplexArray'); 24 | } 25 | 26 | const copy = new ComplexArray(original); 27 | const transformed = FFT(original); 28 | assertComplexArraysAlmostEqual(expected, transformed); 29 | assertComplexArraysAlmostEqual(copy, InvFFT(transformed)); 30 | } 31 | 32 | function assertFFTMatchesDFT(input) { 33 | input = new ComplexArray(input); 34 | 35 | assertComplexArraysAlmostEqual(DFT(input), FFT(input)); 36 | } 37 | 38 | function DFT(input) { 39 | const n = input.length; 40 | const amplitude = 1 / Math.sqrt(n); 41 | 42 | if (!(input instanceof ComplexArray)) { 43 | input = new ComplexArray(input); 44 | } 45 | const output = new ComplexArray(input); 46 | 47 | for(let i = 0; i < n; i++) { 48 | output.real[i] = 0, output.imag[i] = 0; 49 | const phase = {real: 1, imag: 0}; 50 | const delta = {real: Math.cos(2*PI*i/n), imag: Math.sin(2*PI*i/n)}; 51 | 52 | for(let j = 0; j < n; j++) { 53 | output.real[i] += phase.real * input.real[j] - phase.imag * input.imag[j]; 54 | output.imag[i] += phase.real * input.imag[j] + phase.imag * input.real[j]; 55 | [phase.real, phase.imag] = [ 56 | phase.real * delta.real - phase.imag * delta.imag, 57 | phase.real * delta.imag + phase.imag * delta.real, 58 | ]; 59 | } 60 | output.real[i] *= amplitude; 61 | output.imag[i] *= amplitude; 62 | } 63 | 64 | return output; 65 | } 66 | 67 | function assertApproximatelyEqual(first, second, message) { 68 | const delta = Math.abs(first - second); 69 | 70 | expect(delta < EPSILON).to.be.true; 71 | } 72 | 73 | module.exports = { 74 | assertComplexArraysAlmostEqual, 75 | assertFFTMatches, 76 | assertFFTMatchesDFT, 77 | DFT, 78 | } 79 | -------------------------------------------------------------------------------- /test/fft.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const fftMod = require('../fast-fourier-transforms/fft'); 3 | const testHelper = require('./fft-test-helper'); 4 | 5 | const {ComplexArray} = fftMod; 6 | const { 7 | assertComplexArraysAlmostEqual, 8 | assertFFTMatches, 9 | assertFFTMatchesDFT 10 | } = testHelper; 11 | const expect = chai.expect; 12 | 13 | describe('Fast Fourier Transformations', () => { 14 | describe('`FFT` method', () => { 15 | describe('on N=4 Arrays', () => { 16 | it('should return a single frequency given a constant array', () => { 17 | assertFFTMatches([1, 1, 1, 1], new ComplexArray([2, 0, 0, 0])); 18 | }); 19 | 20 | it('should return flat with a delta function input', () => { 21 | assertFFTMatches([1, 0, 0, 0], new ComplexArray([0.5, 0.5, 0.5, 0.5])); 22 | }); 23 | 24 | it('should return a single high freq', () => { 25 | assertFFTMatches([1, -1, 1, -1], new ComplexArray([0, 0, 2, 0])); 26 | }); 27 | 28 | it('should return a single low freq', () => { 29 | assertFFTMatches([1, 0, -1, -0], new ComplexArray([0, 1, 0, 1])); 30 | }); 31 | 32 | it('should return a high freq and DC', () => { 33 | assertFFTMatches([1, 0, 1, 0], new ComplexArray([1, 0, 1, 0])); 34 | }); 35 | }); 36 | 37 | describe('on N=6 Arrays', () => { 38 | it('should return a single frequency given a constant array', () => { 39 | assertFFTMatches( 40 | [1, 1, 1, 1, 1, 1], 41 | new ComplexArray([Math.sqrt(6), 0, 0, 0, 0, 0]) 42 | ); 43 | }); 44 | 45 | it('should return flat with a delta function input', () => { 46 | const a = 1 / Math.sqrt(6); 47 | 48 | assertFFTMatches( 49 | [1, 0, 0, 0, 0, 0], 50 | new ComplexArray([a, a, a, a, a, a]) 51 | ); 52 | }); 53 | }); 54 | 55 | describe('on N=`prime` Arrays', () => { 56 | it('should match the DFT', () => { 57 | const a = new ComplexArray(13).map((value) => { 58 | value.real = Math.random(); 59 | value.imag = Math.random(); 60 | }); 61 | 62 | assertFFTMatchesDFT(a); 63 | }); 64 | }); 65 | 66 | describe('on N=512 Arrays', () => { 67 | it('should match the DFT', () => { 68 | const a = new ComplexArray(512).map((value) => { 69 | value.real = Math.random(); 70 | value.imag = Math.random(); 71 | }); 72 | 73 | assertFFTMatchesDFT(a); 74 | }); 75 | }); 76 | 77 | describe('on N=900 Arrays', () => { 78 | it('should match the DFT', () => { 79 | const a = new ComplexArray(900).map((value) => { 80 | value.real = Math.random(); 81 | value.imag = Math.random(); 82 | }); 83 | 84 | assertFFTMatchesDFT(a); 85 | }); 86 | }); 87 | }); 88 | 89 | describe('`frequencyMap` method', () => { 90 | it('should not modify the original', () => { 91 | const original = new ComplexArray([1, 2, 3, 4]); 92 | const filtered = original.frequencyMap(() => {}); 93 | 94 | assertComplexArraysAlmostEqual(original, filtered); 95 | }); 96 | 97 | it('should halve the original', () => { 98 | const original = new ComplexArray([1, 2, 3, 4]); 99 | const filtered = original.frequencyMap((value, i) => { 100 | value.real /= 2; 101 | value.imag /= 2; 102 | }); 103 | 104 | assertComplexArraysAlmostEqual( 105 | new ComplexArray([0.5, 1, 1.5, 2]), filtered); 106 | }); 107 | 108 | it('should return zeroed ComplexArray', () => { 109 | const original = new ComplexArray([1, 2, 3, 4]); 110 | const filtered = original.frequencyMap((value, i) => { 111 | value.real = value.imag = 0; 112 | }); 113 | 114 | assertComplexArraysAlmostEqual(new ComplexArray([0, 0, 0, 0]), filtered); 115 | }); 116 | 117 | it('should shift the original', () => { 118 | const original = new ComplexArray([1, 2, 3, 4]); 119 | const filtered = original.frequencyMap((value, i) => { 120 | // Multiply by a phase to shift the original. 121 | const phase = {real: i % 2 ? 0 : (1 - i), imag: i % 2 ? (2 - i) : 0}; 122 | 123 | [value.real, value.imag] = [ 124 | phase.real * value.real - phase.imag * value.imag, 125 | phase.real * value.imag + phase.imag * value.real, 126 | ]; 127 | }); 128 | 129 | assertComplexArraysAlmostEqual(new ComplexArray([4, 1, 2, 3]), filtered); 130 | }); 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /test/graph-traversing.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | 3 | const expect = chai.expect; 4 | 5 | const Node = require('../graph/graphNode'); 6 | const breadthFirstSearch = require('../graph-traversing/breadth-first-search'); 7 | const depthFirstSearch = require('../graph-traversing/depth-first-search-imperative'); 8 | const depthFirstSearchRecursive = require('../graph-traversing/depth-first-search-recursive'); 9 | 10 | describe('Graph Traversing', () => { 11 | describe('Depth First Search - Recursive', () => { 12 | let A; 13 | let B; 14 | let C; 15 | let D; 16 | let E; 17 | let F; 18 | 19 | beforeEach(() => { 20 | A = new Node('A', 'Joe'); 21 | B = new Node('B', 'Jon'); 22 | C = new Node('C', 'Ray'); 23 | D = new Node('D', 'JSON'); 24 | E = new Node('E', 'Marifel'); 25 | F = new Node('F', 'Nigel'); 26 | A.addNeighbors([B, C]); 27 | B.addNeighbors([D, E]); 28 | C.addNeighbors([F]); 29 | }); 30 | 31 | it('should be a function that exists', () => { 32 | expect(depthFirstSearchRecursive).to.exist; 33 | expect(depthFirstSearchRecursive).to.be.a('function'); 34 | }); 35 | 36 | it('should return the node with the value of `searchFor` stored in its value property', () => { 37 | expect(depthFirstSearchRecursive(A, 'Joe').value).to.equal('Joe'); 38 | expect(depthFirstSearchRecursive(A, 'JSON').value).to.equal('JSON'); 39 | expect(depthFirstSearchRecursive(A, 'JSON').name).to.equal('D'); 40 | expect(depthFirstSearchRecursive(A, 'Nigel').value).to.equal('Nigel'); 41 | expect(depthFirstSearchRecursive(A, 'Nigel').name).to.equal('F'); 42 | expect(depthFirstSearchRecursive(B, 'Marifel').value).to.equal('Marifel'); 43 | expect(depthFirstSearchRecursive(A, 'Marifel').name).to.equal('E'); 44 | }); 45 | 46 | it('should return false if it cant find the value in the graph', () => { 47 | expect(depthFirstSearchRecursive(F, 'Joe')).to.equal(false); 48 | expect(depthFirstSearchRecursive(E, 'Joe')).to.equal(false); 49 | }); 50 | }); 51 | 52 | describe('Depth First Search - Imperative', () => { 53 | let A; 54 | let B; 55 | let C; 56 | let D; 57 | let E; 58 | let F; 59 | 60 | beforeEach(() => { 61 | A = new Node('A', 'Joe'); 62 | B = new Node('B', 'Jon'); 63 | C = new Node('C', 'Ray'); 64 | D = new Node('D', 'JSON'); 65 | E = new Node('E', 'Marifel'); 66 | F = new Node('F', 'Nigel'); 67 | A.addNeighbors([B, C]); 68 | B.addNeighbors([D, E]); 69 | C.addNeighbors([F]); 70 | }); 71 | 72 | it('should be a function that exists', () => { 73 | expect(depthFirstSearch).to.exist; 74 | expect(depthFirstSearch).to.be.a('function'); 75 | }); 76 | 77 | it('should return the node with the value of `searchFor` stored in its value property', () => { 78 | expect(depthFirstSearch(A, 'Joe').value).to.equal('Joe'); 79 | expect(depthFirstSearch(A, 'JSON').value).to.equal('JSON'); 80 | expect(depthFirstSearch(A, 'JSON').name).to.equal('D'); 81 | expect(depthFirstSearch(A, 'Nigel').value).to.equal('Nigel'); 82 | expect(depthFirstSearch(A, 'Nigel').name).to.equal('F'); 83 | expect(depthFirstSearch(B, 'Marifel').value).to.equal('Marifel'); 84 | expect(depthFirstSearch(A, 'Marifel').name).to.equal('E'); 85 | }); 86 | 87 | it('should return false if it cant find the value in the graph', () => { 88 | expect(depthFirstSearch(F, 'Joe')).to.equal(false); 89 | expect(depthFirstSearch(E, 'Joe')).to.equal(false); 90 | }); 91 | }); 92 | 93 | describe('Breadth First Search - Imperative', () => { 94 | let A; 95 | let B; 96 | let C; 97 | let D; 98 | let E; 99 | let F; 100 | 101 | beforeEach(() => { 102 | A = new Node('A', 'Joe'); 103 | B = new Node('B', 'Jon'); 104 | C = new Node('C', 'Ray'); 105 | D = new Node('D', 'JSON'); 106 | E = new Node('E', 'Marifel'); 107 | F = new Node('F', 'Nigel'); 108 | A.addNeighbors([B, C]); 109 | B.addNeighbors([D, E]); 110 | C.addNeighbors([F]); 111 | }); 112 | 113 | it('should be a function that exists', () => { 114 | expect(breadthFirstSearch).to.exist; 115 | expect(breadthFirstSearch).to.be.a('function'); 116 | }); 117 | 118 | it('should return the traversal path from the starting point all the way to the end', () => { 119 | expect(breadthFirstSearch(A)).to.deep.equal(['A', 'B', 'C', 'D', 'E', 'F']); 120 | expect(breadthFirstSearch(B)).to.deep.equal(['B', 'D', 'E']); 121 | expect(breadthFirstSearch(C)).to.deep.equal(['C', 'F']); 122 | expect(breadthFirstSearch(D)).to.deep.equal(['D']); 123 | expect(breadthFirstSearch(E)).to.deep.equal(['E']); 124 | expect(breadthFirstSearch(F)).to.deep.equal(['F']); 125 | }); 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /test/graph.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const expect = chai.expect; 3 | const Node = require('../graph/graphNode'); 4 | 5 | describe('Graphs', () => { 6 | let A; 7 | let B; 8 | let C; 9 | let D; 10 | let E; 11 | let F; 12 | 13 | beforeEach(() => { 14 | A = new Node('A', 'Joe'); 15 | B = new Node('B', 'Jon'); 16 | C = new Node('C', 'Ray'); 17 | D = new Node('D', 'JSON'); 18 | E = new Node('E', 'Marifel'); 19 | F = new Node('F', 'Nigel'); 20 | }); 21 | 22 | it('should be a function that exists', () => { 23 | expect(Node).to.exist; 24 | expect(Node).to.be.a('function'); 25 | }); 26 | 27 | it('Creating a new Node should return an object', () => { 28 | expect(A).to.exist; 29 | expect(A).to.be.an('object'); 30 | }); 31 | 32 | it('Node should have a property `name`', () => { 33 | expect(A.name).to.exist; 34 | expect(B.name).to.exist; 35 | expect(C.name).to.exist; 36 | expect(A.name).to.be.an('String'); 37 | expect(B.name).to.be.an('String'); 38 | expect(C.name).to.be.an('String'); 39 | expect(A.name).to.equal('A'); 40 | expect(B.name).to.equal('B'); 41 | expect(C.name).to.equal('C'); 42 | }); 43 | 44 | it('Node should have a property `value`', () => { 45 | expect(A.value).to.exist; 46 | expect(B.value).to.exist; 47 | expect(C.value).to.exist; 48 | expect(A.value).to.equal('Joe'); 49 | expect(B.value).to.equal('Jon'); 50 | expect(C.value).to.equal('Ray'); 51 | }); 52 | 53 | it('Node should have a property `neighbors`', () => { 54 | expect(A.neighbors).to.exist; 55 | expect(B.neighbors).to.exist; 56 | expect(C.neighbors).to.exist; 57 | expect(D.neighbors).to.exist; 58 | expect(E.neighbors).to.exist; 59 | expect(F.neighbors).to.exist; 60 | }); 61 | 62 | it('Node property `neighbors` should be initialized to an empty array', () => { 63 | expect(A.neighbors).to.be.an('Array'); 64 | expect(B.neighbors).to.be.an('Array'); 65 | expect(C.neighbors).to.be.an('Array'); 66 | expect(A.neighbors).to.deep.equal([]); 67 | expect(B.neighbors).to.deep.equal([]); 68 | expect(C.neighbors).to.deep.equal([]); 69 | }); 70 | 71 | it('Node should have a method `addNeighbors`', () => { 72 | expect(A.addNeighbors()).to.exist; 73 | expect(B.addNeighbors()).to.exist; 74 | expect(C.addNeighbors()).to.exist; 75 | }); 76 | 77 | it('Node method `addNeighbors` should return an array', () => { 78 | expect(A.addNeighbors([])).to.be.an('Array'); 79 | expect(B.addNeighbors([])).to.be.an('Array'); 80 | expect(C.addNeighbors([])).to.be.an('Array'); 81 | }); 82 | 83 | it('Node method `addNeighbors` should return an array of Nodes', () => { 84 | A.addNeighbors([B,C]) 85 | expect(A.neighbors[0].name).to.equal('B'); 86 | expect(A.neighbors[0].value).to.equal('Jon'); 87 | expect(A.neighbors[1].name).to.equal('C'); 88 | expect(A.neighbors[1].value).to.equal('Ray'); 89 | 90 | A.addNeighbors([D,E]) 91 | expect(A.neighbors[0].name).to.equal('B'); 92 | expect(A.neighbors[0].value).to.equal('Jon'); 93 | expect(A.neighbors[1].name).to.equal('C'); 94 | expect(A.neighbors[1].value).to.equal('Ray'); 95 | expect(A.neighbors[2].name).to.equal('D'); 96 | expect(A.neighbors[2].value).to.equal('JSON'); 97 | expect(A.neighbors[3].name).to.equal('E'); 98 | expect(A.neighbors[3].value).to.equal('Marifel'); 99 | }); 100 | 101 | it('Node should have a method `getNeighbors`', () => { 102 | expect(A.getNeighbors()).to.exist; 103 | expect(B.getNeighbors()).to.exist; 104 | expect(C.getNeighbors()).to.exist; 105 | expect(A.getNeighbors()).to.be.an('Array'); 106 | expect(B.getNeighbors()).to.be.an('Array'); 107 | expect(C.getNeighbors()).to.be.an('Array'); 108 | expect(A.getNeighbors()).to.deep.equal([]); 109 | expect(B.getNeighbors()).to.deep.equal([]); 110 | expect(B.getNeighbors()).to.deep.equal([]); 111 | }); 112 | 113 | it('Node `neighbors` should refernce other neighbors', () => { 114 | A.addNeighbors([B, C]); 115 | B.addNeighbors([D, E]); 116 | C.addNeighbors([F]); 117 | expect(A.neighbors[0].name).to.equal('B'); 118 | expect(A.neighbors[0].value).to.equal('Jon'); 119 | expect(A.neighbors[0].neighbors[0].name).to.equal('D'); 120 | expect(A.neighbors[1].name).to.equal('C'); 121 | expect(A.neighbors[1].value).to.equal('Ray'); 122 | expect(A.neighbors[1].neighbors[0].name).to.equal('F'); 123 | expect(B.neighbors[0].name).to.equal('D'); 124 | expect(B.neighbors[0].value).to.equal('JSON'); 125 | expect(B.neighbors[0].neighbors).to.deep.equal([]); 126 | expect(B.neighbors[1].name).to.equal('E'); 127 | expect(B.neighbors[1].value).to.equal('Marifel'); 128 | expect(B.neighbors[1].neighbors).to.deep.equal([]); 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /test/hash-table.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const HashTable = require('../hash-table/hash-table'); 3 | 4 | const { expect } = chai; 5 | 6 | describe( 'Hash Table', () => { 7 | let hashTable; 8 | 9 | beforeEach(() => { 10 | hashTable = new HashTable(); 11 | }); 12 | 13 | describe( '`insert` method', () => { 14 | it( 'should return `inserted` if successful', () => { 15 | expect(hashTable.insert('Cat', 'Elvis')).to.equal('inserted'); 16 | expect(hashTable.insert('Cat2', 'BMO')).to.equal('inserted'); 17 | expect(hashTable.insert('Cat4', 'Merrie')).to.equal('inserted'); 18 | expect(hashTable.insert('Dog', 'Rover')).to.equal('inserted'); 19 | }); 20 | it( 'should `key already exists; keys must be unique` if duplicate key', () => { 21 | hashTable.insert('Cat', 'Elvis'); 22 | hashTable.insert('Cat2', 'BMO'); 23 | hashTable.insert('Cat4', 'Merrie'); 24 | hashTable.insert('Dog', 'Rover'); 25 | expect(hashTable.insert('Cat', 'Elvis')).to.equal('key already exists; keys must be unique'); 26 | expect(hashTable.insert('Cat2', 'BMO')).to.equal('key already exists; keys must be unique'); 27 | expect(hashTable.insert('Cat4', 'Merrie')).to.equal('key already exists; keys must be unique'); 28 | expect(hashTable.insert('Dog', 'Rover')).to.equal('key already exists; keys must be unique'); 29 | }); 30 | }); 31 | 32 | describe( '`retrieve` method', () => { 33 | it( 'should return `key does not exist` if the key has not been added', () => { 34 | expect(hashTable.retrieve('Cat')).to.equal('key does not exist'); 35 | expect(hashTable.retrieve('Dog')).to.equal('key does not exist'); 36 | expect(hashTable.retrieve('Human')).to.equal('key does not exist'); 37 | }); 38 | it( 'should return the tupple if successful', () => { 39 | hashTable.insert('Cat', 'Elvis'); 40 | hashTable.insert('Cat2', 'BMO'); 41 | hashTable.insert('Cat4', 'Merrie'); 42 | hashTable.insert('Dog', 'Rover'); 43 | expect(hashTable.retrieve('Cat')).to.deep.equal(['Cat', 'Elvis']); 44 | expect(hashTable.retrieve('Cat2')).to.deep.equal(['Cat2', 'BMO']); 45 | expect(hashTable.retrieve('Cat4')).to.deep.equal(['Cat4', 'Merrie']); 46 | expect(hashTable.retrieve('Dog')).to.deep.equal(['Dog', 'Rover']); 47 | }); 48 | }); 49 | 50 | describe( '`remove` method', () => { 51 | it( 'should return `key does not exist` if the key does not exist', () => { 52 | expect(hashTable.remove('Cat')).to.equal('key does not exist'); 53 | expect(hashTable.remove('Dog')).to.equal('key does not exist'); 54 | expect(hashTable.remove('Human')).to.equal('key does not exist'); 55 | }); 56 | it( 'should return `[key name] removed` if successful', () => { 57 | hashTable.insert('Cat', 'Elvis'); 58 | hashTable.insert('Cat2', 'BMO'); 59 | hashTable.insert('Cat4', 'Merrie'); 60 | hashTable.insert('Dog', 'Rover'); 61 | expect(hashTable.remove('Cat')).to.equal('Cat removed'); 62 | expect(hashTable.remove('Dog')).to.equal('Dog removed'); 63 | expect(hashTable.remove('Cat2')).to.equal('Cat2 removed'); 64 | expect(hashTable.remove('Cat4')).to.equal('Cat4 removed'); 65 | }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/linked-list.spec.js: -------------------------------------------------------------------------------- 1 | const LinkedList = require('../linked-list/linkedList'); 2 | const chai = require('chai'); 3 | 4 | const expect = chai.expect; 5 | 6 | describe('Linked List', () => { 7 | let newLinkedList; 8 | 9 | it('should be a function', () => { 10 | expect(LinkedList).to.exist; 11 | expect(LinkedList).to.be.a('function'); 12 | }); 13 | 14 | describe('returns a LinkedList object used to interact with the private Linked List object', function () { 15 | beforeEach(() => { 16 | newLinkedList = new LinkedList(); 17 | }); 18 | it('should return a module object', () => { 19 | expect(newLinkedList).to.be.an('object'); 20 | }); 21 | }); 22 | 23 | describe('Linked List has methods available through Linked List Object', function () { 24 | beforeEach(() => { 25 | newLinkedList = new LinkedList(); 26 | }); 27 | it('should have a method named `getHead`', () => { 28 | expect(newLinkedList.getHead).to.exist; 29 | expect(newLinkedList.getHead).to.be.a('function'); 30 | }); 31 | it('should have a method named `getTail`', () => { 32 | expect(newLinkedList.getTail).to.exist; 33 | expect(newLinkedList.getTail).to.be.a('function'); 34 | }); 35 | it('should have a method named `add`', () => { 36 | expect(newLinkedList.add).to.exist; 37 | expect(newLinkedList.add).to.be.a('function'); 38 | }); 39 | it('should have a method named `remove`', () => { 40 | expect(newLinkedList.remove).to.exist; 41 | expect(newLinkedList.remove).to.be.a('function'); 42 | }); 43 | it('should have a method named `get`', () => { 44 | expect(newLinkedList.get).to.exist; 45 | expect(newLinkedList.get).to.be.a('function'); 46 | }); 47 | it('should have a method named `insert`', () => { 48 | expect(newLinkedList.insert).to.exist; 49 | expect(newLinkedList.insert).to.be.a('function'); 50 | }); 51 | }); 52 | 53 | describe('`getHead` method', () => { 54 | let urlList; 55 | 56 | beforeEach(() => { 57 | urlList = new LinkedList(); 58 | }); 59 | it('should retrieve the value of the first node in a list', () => { 60 | expect(urlList.getHead).to.be.a('function'); 61 | expect(urlList.getHead()).to.be.null; 62 | }); 63 | }); 64 | 65 | describe('`getTail` method', () => { 66 | let urlList; 67 | 68 | beforeEach(() => { 69 | urlList = new LinkedList(); 70 | }); 71 | it('should retrieve the value of the first node in a list', () => { 72 | expect(urlList.getTail).to.be.a('function'); 73 | expect(urlList.getTail()).to.be.null; 74 | }); 75 | }); 76 | 77 | describe('`add` method', () => { 78 | let newNodeA; 79 | let newLinkedListA; 80 | let newLinkedListB; 81 | let newLinkedListC; 82 | 83 | beforeEach(() => { 84 | newLinkedListA = new LinkedList(); // return new node 85 | newLinkedListB = new LinkedList(); // for `head` and `tail` 86 | newLinkedListC = new LinkedList(); // for `tail` 87 | newNodeA = newLinkedListA.add('http://slashdot.org'); 88 | }); 89 | 90 | describe('should return a new node object after appending the node to the list', () => { 91 | it('should return a new node object', () => { 92 | expect(newNodeA).to.not.be.undefined; 93 | expect(newNodeA.value).to.be.equal('http://slashdot.org'); 94 | }); 95 | it('should have a property named `value`', () => { 96 | expect(newNodeA.value).to.exist; 97 | }); 98 | it('should have a property named `next`', () => { 99 | expect(newNodeA.next).to.be.null; 100 | }); 101 | }); 102 | 103 | describe('should append new nodes', () => { 104 | it('`head` and `tail` should reference the same node object when adding to an empty list', () => { 105 | // add a new node! 106 | newLinkedListB.add('http://devleague.com'); 107 | 108 | // test! 109 | expect(newLinkedListB.getHead().value).to.equal('http://devleague.com'); 110 | expect(newLinkedListB.getTail().value).to.equal('http://devleague.com'); 111 | 112 | // really the same? 113 | expect(newLinkedListB.getHead()).to.equal(newLinkedListB.getTail()) 114 | }); 115 | }); 116 | 117 | describe('should append even more nodes', () => { 118 | it('`tail` should reference the most recently added node', () => { 119 | // add new nodes 120 | newLinkedListC.add('http://eff.org'); 121 | newLinkedListC.add('http://devleague.com'); 122 | 123 | // tests! 124 | // console.log(newLinkedListC.getHead()); 125 | expect(newLinkedListC.getHead().value).to.equal('http://eff.org'); 126 | expect(newLinkedListC.getTail().value).to.equal('http://devleague.com'); 127 | 128 | // add another node 129 | newLinkedListC.add('http://xkcd.org'); 130 | 131 | // test! 132 | expect(newLinkedListC.getHead().value).to.equal('http://eff.org'); 133 | expect(newLinkedListC.getTail().value).to.equal('http://xkcd.org'); 134 | }); 135 | }); 136 | }); 137 | describe('`get` method', () => { 138 | let urlList; 139 | let bookList; 140 | 141 | beforeEach(() => { 142 | urlList = new LinkedList(); 143 | bookList = new LinkedList(); 144 | 145 | const urlArr = [ 146 | 'news.ycombinator.com', 147 | 'mozilla.org', 148 | 'eff.org', 149 | 'icann.org', 150 | ]; 151 | 152 | const bookArr = [ 153 | 'Ready Player One', 154 | '1982', 155 | 'Neuromancer', 156 | 'Snow Crash', 157 | ]; 158 | 159 | urlArr.forEach((url) => { 160 | urlList.add(url); 161 | }); 162 | bookArr.forEach((book) => { 163 | bookList.add(book); 164 | }); 165 | }); 166 | 167 | describe('takes an argument', () => { 168 | it('should find a node by it\'s index in the Linked List', () => { 169 | // urlList Tests 170 | expect(urlList.get(0).value).to.equal('news.ycombinator.com'); 171 | expect(urlList.get(1).value).to.equal('mozilla.org'); 172 | expect(urlList.get(2).value).to.equal('eff.org'); 173 | expect(urlList.get(3).value).to.equal('icann.org'); 174 | 175 | expect(urlList.getHead().value).to.equal('news.ycombinator.com'); 176 | expect(urlList.getTail().value).to.equal('icann.org'); 177 | 178 | // bookList Tests 179 | expect(bookList.get(0).value).to.equal('Ready Player One'); 180 | expect(bookList.get(1).value).to.equal('1982'); 181 | expect(bookList.get(2).value).to.equal('Neuromancer'); 182 | expect(bookList.get(3).value).to.equal('Snow Crash'); 183 | 184 | expect(bookList.getHead().value).to.equal('Ready Player One'); 185 | expect(bookList.getTail().value).to.equal('Snow Crash'); 186 | }); 187 | it('should return `false` if no node is found', () => { 188 | expect(urlList.get(4)).to.be.false; 189 | expect(urlList.get(5)).to.be.false; 190 | expect(bookList.get(4)).to.be.false; 191 | expect(bookList.get(5)).to.be.false; 192 | }); 193 | }); 194 | }); 195 | 196 | describe('`remove` method', () => { 197 | let urlList; 198 | let bookList; 199 | 200 | beforeEach(() => { 201 | urlList = new LinkedList(); 202 | bookList = new LinkedList(); 203 | 204 | const urlArr = [ 205 | 'news.ycombinator.com', 206 | 'mozilla.org', 207 | 'eff.org', 208 | 'icann.org', 209 | ]; 210 | 211 | const bookArr = [ 212 | 'Ready Player One', 213 | '1982', 214 | 'Neuromancer', 215 | 'Snow Crash', 216 | ]; 217 | 218 | urlArr.forEach((url) => { 219 | urlList.add(url); 220 | }); 221 | bookArr.forEach((book) => { 222 | bookList.add(book); 223 | }); 224 | }); 225 | 226 | describe('takes an argument', () => { 227 | it('should remove a node by it\'s index in the Linked List', () => { 228 | // urlList Tests 229 | // remove middle node 230 | urlList.remove(2); 231 | 232 | // test new node at position 2 233 | expect(urlList.get(2).value).to.equal('icann.org'); 234 | 235 | // remove last node 236 | urlList.remove(2); 237 | 238 | // retrieve new node at position 2 239 | expect(urlList.get(2)).to.be.false; 240 | expect(urlList.getHead().value).to.equal('news.ycombinator.com'); 241 | expect(urlList.getTail().value).to.equal('mozilla.org'); 242 | 243 | // bookList Tests 244 | // remove first node 245 | bookList.remove(0); 246 | expect(bookList.get(0).value).to.equal('1982'); 247 | // console.log(bookList.get(0)) 248 | 249 | bookList.remove(1); 250 | expect(bookList.getHead().value).to.equal('1982'); 251 | expect(bookList.getTail().value).to.equal('Snow Crash'); 252 | }); 253 | it('should return `false` if a node cannot be found to be removed', () => { 254 | expect(urlList.remove(9)).to.be.false; 255 | expect(urlList.remove(4)).to.be.false; 256 | expect(bookList.remove(4)).to.be.false; 257 | expect(bookList.remove(6)).to.be.false; 258 | }); 259 | }); 260 | }); 261 | 262 | describe('`insert` method', () => { 263 | let urlList; 264 | let bookList; 265 | 266 | beforeEach(() => { 267 | urlList = new LinkedList(); 268 | bookList = new LinkedList(); 269 | 270 | const urlArr = [ 271 | 'news.ycombinator.com', 272 | 'icann.org', 273 | ]; 274 | 275 | const bookArr = [ 276 | 'Neuromancer', 277 | 'Snow Crash', 278 | ]; 279 | 280 | urlArr.forEach((url) => { 281 | urlList.add(url); 282 | }); 283 | bookArr.forEach((book) => { 284 | bookList.add(book); 285 | }); 286 | }); 287 | describe('takes two arguments, a `value` and an `index`', () => { 288 | it('should add a new node at a given index', () => { 289 | // insert into second position of list 290 | urlList.insert('mozilla.org', 1); 291 | expect(urlList.get(0).value).to.be.equal('news.ycombinator.com'); 292 | expect(urlList.get(1).value).to.be.equal('mozilla.org'); 293 | expect(urlList.get(2).value).to.be.equal('icann.org'); 294 | 295 | // insert into beginning of list 296 | bookList.insert('Ready Player One', 0); 297 | expect(bookList.get(0).value).to.be.equal('Ready Player One'); 298 | expect(bookList.get(1).value).to.be.equal('Neuromancer'); 299 | expect(bookList.get(2).value).to.be.equal('Snow Crash'); 300 | 301 | urlList.insert('devleague.com', 1); 302 | expect(urlList.get(0).value).to.be.equal('news.ycombinator.com'); 303 | expect(urlList.get(1).value).to.be.equal('devleague.com'); 304 | expect(urlList.get(2).value).to.be.equal('mozilla.org'); 305 | expect(urlList.get(3).value).to.be.equal('icann.org'); 306 | 307 | // insert into index `1` 308 | bookList.insert('The Stranger', 1); 309 | expect(bookList.getHead().value).to.be.equal('Ready Player One'); 310 | expect(bookList.get(0).value).to.be.equal('Ready Player One'); 311 | expect(bookList.get(1).value).to.be.equal('The Stranger'); 312 | expect(bookList.get(2).value).to.be.equal('Neuromancer'); 313 | }); 314 | it('should return `false` if the index given is a value larger than the List\'s length', () => { 315 | // urlList has two items, it's max index value is 1 316 | expect(urlList.insert('boingboing.net', 3)).to.be.false; 317 | expect(urlList.getHead().value).to.be.equal('news.ycombinator.com'); 318 | expect(urlList.get(0).value).to.be.equal('news.ycombinator.com'); 319 | expect(urlList.get(1).value).to.be.equal('icann.org'); 320 | expect(urlList.getTail().value).to.be.equal('icann.org'); 321 | 322 | // test -1 323 | expect(bookList.insert('The Stranger', -1)).to.be.false; 324 | expect(bookList.getHead().value).to.be.equal('Neuromancer'); 325 | expect(bookList.get(0).value).to.be.equal('Neuromancer'); 326 | expect(bookList.get(1).value).to.be.equal('Snow Crash'); 327 | expect(bookList.getTail().value).to.equal('Snow Crash'); 328 | 329 | }); 330 | }); 331 | }); 332 | }); 333 | -------------------------------------------------------------------------------- /test/list.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const List = require('../list/list'); 3 | 4 | const { expect } = chai; 5 | 6 | describe( 'List', () => { 7 | let list; 8 | let values; 9 | let list2; 10 | let numValues; 11 | 12 | beforeEach(() => { 13 | values = ['lorem', 'ipsum', 'dolor', 'et']; 14 | list = new List(); 15 | values.forEach(value => list.pushBack(value)); 16 | 17 | numValues = [1, 2, 3, 4, 5]; 18 | list2 = new List(); 19 | numValues.forEach(value => list2.pushBack(value)); 20 | }); 21 | 22 | describe( 'Constructor', () => { 23 | it( 'should return a new empty List', () => { 24 | const list2 = new List('Cat'); 25 | const list3 = new List(); 26 | expect(list2).to.deep.equal({ 27 | head: { 28 | value: 'Cat', 29 | next: null, 30 | }, 31 | }); 32 | expect(list3).to.deep.equal({}); 33 | }); 34 | }); 35 | describe( '`pushback` method', () => { 36 | it( 'should add a new node to the end of the list', () => { 37 | expect(list).to.deep.equal({ 38 | head: { 39 | value: 'lorem', 40 | next: { 41 | value: 'ipsum', 42 | next: { 43 | value: 'dolor', 44 | next: { 45 | value: 'et', 46 | next: null, 47 | }, 48 | }, 49 | }, 50 | }, 51 | }); 52 | expect(list.pushBack('Cat')).to.deep.equal({ 53 | head: { 54 | value: 'lorem', 55 | next: { 56 | value: 'ipsum', 57 | next: { 58 | value: 'dolor', 59 | next: { 60 | value: 'et', 61 | next: { 62 | value: 'Cat', 63 | next: null, 64 | }, 65 | }, 66 | }, 67 | }, 68 | }, 69 | }); 70 | }); 71 | }); 72 | describe( '`getLength` method', () => { 73 | it( 'should the entire List with a new node', () => { 74 | const list2 = new List('Cat'); 75 | const list3 = new List(); 76 | 77 | expect(list.getLength()).to.equal(4); 78 | expect(list2.getLength()).to.equal(1); 79 | expect(list3.getLength()).to.equal(0); 80 | }); 81 | }); 82 | describe( '`gethead` method', () => { 83 | it( 'should the return head of the List', () => { 84 | expect(list.getHead()).to.deep.equal({ 85 | value: 'lorem', 86 | next: { 87 | value: 'ipsum', 88 | next: { 89 | value: 'dolor', 90 | next: { 91 | value: 'et', 92 | next: null, 93 | }, 94 | }, 95 | }, 96 | }); 97 | }); 98 | }); 99 | describe( '`getTail` method', () => { 100 | it( 'should return the tail of the list', () => { 101 | expect(list.getTail()).to.deep.equal({ 102 | value: 'et', 103 | next: null, 104 | }); 105 | }); 106 | }); 107 | describe( '`get` method', () => { 108 | it( 'should retrieve the Node with a given index', () => { 109 | expect(list.get(0)).to.deep.equal({ 110 | value: 'lorem', 111 | next: { 112 | value: 'ipsum', 113 | next: { 114 | value: 'dolor', 115 | next: { 116 | value: 'et', 117 | next: null, 118 | }, 119 | }, 120 | }, 121 | }); 122 | expect(list.get(1)).to.deep.equal({ 123 | value: 'ipsum', 124 | next: { 125 | value: 'dolor', 126 | next: { 127 | value: 'et', 128 | next: null, 129 | }, 130 | }, 131 | }); 132 | expect(list.get(2)).to.deep.equal({ 133 | value: 'dolor', 134 | next: { 135 | value: 'et', 136 | next: null, 137 | }, 138 | }); 139 | expect(list.get(3)).to.deep.equal({ 140 | value: 'et', 141 | next: null, 142 | }); 143 | }); 144 | it( 'should throw an error if the index exceeds list\'s size', () => { 145 | expect(list.get.bind(list, 100)).to.throw('Index exceeds list\'s size'); 146 | expect(list.get.bind(list, -1)).to.throw('Index exceeds list\'s size'); 147 | expect(list.get.bind(list, 5)).to.throw('Index exceeds list\'s size'); 148 | }); 149 | }); 150 | describe( '`push` method', () => { 151 | it( '`push(0, *)` should at a new node at the head', () => { 152 | expect(list.push('cat', 0)).to.deep.equal({ 153 | value: 'cat', 154 | next: { 155 | value: 'lorem', 156 | next: { 157 | value: 'ipsum', 158 | next: { 159 | value: 'dolor', 160 | next: { 161 | value: 'et', 162 | next: null, 163 | }, 164 | }, 165 | }, 166 | }, 167 | }); 168 | }); 169 | it( 'should add a new node at index', () => { 170 | expect(list.push('Cat', 2)).to.deep.equal({ 171 | value: 'lorem', 172 | next: { 173 | value: 'ipsum', 174 | next: { 175 | value: 'Cat', 176 | next: { 177 | value: 'dolor', 178 | next: { 179 | value: 'et', 180 | next: null, 181 | }, 182 | }, 183 | }, 184 | }, 185 | }); 186 | }); 187 | it( 'should throw an error if the index exceeds list\'s size', () => { 188 | expect(list.push.bind(list, 'Cat', 100)).to.throw('Index exceeds list\'s size'); 189 | expect(list.push.bind(list, 'Cat', -1)).to.throw('Index exceeds list\'s size'); 190 | expect(list.push.bind(list, 'Cat', 5)).to.throw('Index exceeds list\'s size'); 191 | }); 192 | }); 193 | describe( '`find` method', () => { 194 | it( 'should return the first node that value matches', () => { 195 | expect(list.find('lorem')).to.deep.equal({ 196 | value: 'lorem', 197 | next: { 198 | value: 'ipsum', 199 | next: { 200 | value: 'dolor', 201 | next: { 202 | value: 'et', 203 | next: null, 204 | }, 205 | }, 206 | }, 207 | }); 208 | expect(list.find('ipsum')).to.deep.equal({ 209 | value: 'ipsum', 210 | next: { 211 | value: 'dolor', 212 | next: { 213 | value: 'et', 214 | next: null, 215 | }, 216 | }, 217 | }); 218 | expect(list.find('dolor')).to.deep.equal({ 219 | value: 'dolor', 220 | next: { 221 | value: 'et', 222 | next: null, 223 | }, 224 | }); 225 | expect(list.find('et')).to.deep.equal({ 226 | value: 'et', 227 | next: null, 228 | }); 229 | }); 230 | it( 'should return null if the value is not in the list', () => { 231 | expect(list.find('foobar')).to.equal(null); 232 | expect(list.find(undefined)).to.equal(null); 233 | expect(list.find(null)).to.equal(null); 234 | expect(list.find(1001)).to.equal(null); 235 | }); 236 | 237 | }); 238 | describe( '`getValues` method', () => { 239 | it( 'should return an array of the list', () => { 240 | expect(list.getValues()).to.deep.equal(values); 241 | }); 242 | }); 243 | describe( '`reduce` method', () => { 244 | it( 'should reduce the list to a single value', () => { 245 | const numValues = [1, 2, 3, 4, 5]; 246 | const list2 = new List(); 247 | numValues.forEach(value => list2.pushBack(value)); 248 | 249 | expect(list2.reduce((prev, curr) => { 250 | return prev + curr; 251 | }, 0)).to.equal(15); 252 | 253 | expect(list2.reduce((prev, curr) => { 254 | return prev + curr; 255 | }, 2)).to.equal(15); 256 | 257 | expect(list.reduce((prev, curr) => { 258 | return prev + curr; 259 | }, 0)).to.equal('loremipsumdoloret'); 260 | }); 261 | }); 262 | 263 | describe( '`forEach` method', () => { 264 | it( 'should traverse the list and executes the callback function for each element in the list.', () => { 265 | 266 | let numSum = 0; 267 | 268 | list2.forEach((el) => { 269 | numSum += el; 270 | }); 271 | expect(numSum).to.deep.equal(15); 272 | 273 | let stringSum = ''; 274 | list.forEach((el) => { 275 | stringSum += el; 276 | }); 277 | expect(stringSum).to.deep.equal('loremipsumdoloret'); 278 | }); 279 | }); 280 | 281 | describe( '`map` method', () => { 282 | it( 'should execute callbackFn on each item from the list and returns the results as another list.', () => { 283 | expect(list2.map((el) => { 284 | return ++el; 285 | })).to.deep.equal({ 286 | head: { 287 | value: 2, 288 | next: { 289 | value: 3, 290 | next: { 291 | value: 4, 292 | next: { 293 | value: 5, 294 | next: { 295 | value: 6, 296 | next: null, 297 | }, 298 | }, 299 | }, 300 | }, 301 | }, 302 | }); 303 | expect(list.map((el) => { 304 | return `${el} foo`; 305 | })).to.deep.equal({ 306 | head: { 307 | value: 'lorem foo', 308 | next: { 309 | value: 'ipsum foo', 310 | next: { 311 | value: 'dolor foo', 312 | next: { 313 | value: 'et foo', 314 | next: null, 315 | }, 316 | }, 317 | }, 318 | }, 319 | }); 320 | }); 321 | }); 322 | 323 | describe( '`remove` method', () => { 324 | it( 'should remove nodes with specific values from the list', () => { 325 | expect(list.remove('ipsum')).to.deep.equal({ 326 | head: { 327 | value: 'lorem', 328 | next: { 329 | value: 'dolor', 330 | next: { 331 | value: 'et', 332 | next: null, 333 | }, 334 | }, 335 | }, 336 | }); 337 | expect(list.remove('et')).to.deep.equal({ 338 | head: { 339 | value: 'lorem', 340 | next: { 341 | value: 'dolor', 342 | next: null, 343 | }, 344 | }, 345 | }); 346 | expect(list.remove('dolor')).to.deep.equal({ 347 | head: { 348 | value: 'lorem', 349 | next: null, 350 | }, 351 | }); 352 | list.remove('lorem'); 353 | expect(list).to.deep.equal({ 354 | head: {}, 355 | }); 356 | }); 357 | }); 358 | 359 | }); 360 | -------------------------------------------------------------------------------- /test/queue.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const Queue = require('../queue/queue'); 3 | const PriorityQueue = require('../queue/priority-queue'); 4 | 5 | const { expect } = chai; 6 | 7 | describe( 'Queues and Priority Queues', () => { 8 | describe( 'Queue', () => { 9 | describe( '`enqueue` and `dequeue` methods', () => { 10 | let queue; 11 | 12 | beforeEach(() => { 13 | queue = new Queue(); 14 | }); 15 | 16 | it( 'should return `null` if the queue is empty', () => { 17 | expect(queue.dequeue()).to.equal(null); 18 | expect(queue.dequeue()).to.equal(null); 19 | expect(queue.dequeue()).to.equal(null); 20 | }); 21 | it( 'should return the last value pushed onto the stack', () => { 22 | queue.enqueue('turtle'); 23 | queue.enqueue('dog'); 24 | queue.enqueue('cat'); 25 | expect(queue.dequeue()).to.equal('turtle'); 26 | }); 27 | it('should remove the last element from the stack when popped', () => { 28 | queue.enqueue('turtle'); 29 | queue.enqueue('dog'); 30 | queue.enqueue('cat'); 31 | queue.dequeue(); 32 | expect(queue.dequeue()).to.equal('dog'); 33 | }); 34 | it('should return null when all of the nodes have been dequeued off the queue', () => { 35 | queue.enqueue('turtle'); 36 | queue.enqueue('dog'); 37 | queue.enqueue('cat'); 38 | queue.dequeue(); 39 | queue.dequeue(); 40 | queue.dequeue(); 41 | expect(queue.dequeue()).to.equal(null); 42 | }); 43 | }); 44 | describe( '`isEmpty` method', () => { 45 | let queue; 46 | 47 | beforeEach(() => { 48 | queue = new Queue(); 49 | }); 50 | 51 | it( 'should initially be empty', () => { 52 | expect(queue.isEmpty()).to.equal(true); 53 | }); 54 | 55 | it( 'should return false when an node is added to the queue', () => { 56 | queue.enqueue('turtle'); 57 | expect(queue.isEmpty()).to.equal(false); 58 | }); 59 | }); 60 | }); 61 | 62 | describe( 'Priority Queue', () => { 63 | let priorityQueue; 64 | let priorityQueue2; 65 | 66 | beforeEach(() => { 67 | priorityQueue = new PriorityQueue(); 68 | priorityQueue2 = new PriorityQueue(); 69 | }); 70 | 71 | describe( 'constructor', () => { 72 | it( 'should initially be empty', () => { 73 | expect(priorityQueue).to.deep.equal({ 74 | _nodes: [], 75 | }); 76 | expect(priorityQueue.isEmpty()).to.equal(true); 77 | }); 78 | }); 79 | 80 | describe( '`enqueue` method', () => { 81 | it( 'should be queued in order of priority', () => { 82 | priorityQueue.enqueue(1, 'BMO'); 83 | priorityQueue.enqueue(2, 'Cake'); 84 | priorityQueue.enqueue(3, 'Finn'); 85 | priorityQueue.enqueue(4, 'Jake'); 86 | expect(priorityQueue).to.deep.equal({ 87 | _nodes: [ 88 | { key: 'BMO', priority: 1 }, 89 | { key: 'Cake', priority: 2 }, 90 | { key: 'Finn', priority: 3 }, 91 | { key: 'Jake', priority: 4 }, 92 | ], 93 | }); 94 | priorityQueue2.enqueue(4, 'BMO'); 95 | priorityQueue2.enqueue(3, 'Cake'); 96 | priorityQueue2.enqueue(2, 'Finn'); 97 | priorityQueue2.enqueue(1, 'Jake'); 98 | expect(priorityQueue2).to.deep.equal({ 99 | _nodes: [ 100 | { key: 'Jake', priority: 1 }, 101 | { key: 'Finn', priority: 2 }, 102 | { key: 'Cake', priority: 3 }, 103 | { key: 'BMO', priority: 4 }, 104 | ], 105 | }); 106 | }); 107 | it( 'should be queued in the order they arrive if the prioroty is the same', () => { 108 | priorityQueue.enqueue(1, 'BMO'); 109 | priorityQueue.enqueue(1, 'Cake'); 110 | priorityQueue.enqueue(1, 'Finn'); 111 | priorityQueue.enqueue(1, 'Jake'); 112 | expect(priorityQueue).to.deep.equal({ 113 | _nodes: [ 114 | { key: 'BMO', priority: 1 }, 115 | { key: 'Cake', priority: 1 }, 116 | { key: 'Finn', priority: 1 }, 117 | { key: 'Jake', priority: 1 }, 118 | ], 119 | }); 120 | }); 121 | }); 122 | describe( '`dequeue` method', () => { 123 | it( 'should dequeue the highest priority items first', () => { 124 | priorityQueue.enqueue(4, 'BMO'); 125 | priorityQueue.enqueue(3, 'Cake'); 126 | priorityQueue.enqueue(2, 'Finn'); 127 | priorityQueue.enqueue(1, 'Jake'); 128 | expect(priorityQueue.dequeue()).to.deep.equal('Jake'); 129 | expect(priorityQueue.dequeue()).to.deep.equal('Finn'); 130 | expect(priorityQueue.dequeue()).to.deep.equal('Cake'); 131 | expect(priorityQueue.dequeue()).to.deep.equal('BMO'); 132 | expect(priorityQueue.dequeue()).to.deep.equal(null); 133 | }); 134 | it( 'should return null when the list is empty', () => { 135 | priorityQueue.enqueue(4, 'BMO'); 136 | priorityQueue.enqueue(3, 'Cake'); 137 | priorityQueue.enqueue(2, 'Finn'); 138 | priorityQueue.enqueue(1, 'Jake'); 139 | expect(priorityQueue.dequeue()).to.deep.equal('Jake'); 140 | expect(priorityQueue.dequeue()).to.deep.equal('Finn'); 141 | expect(priorityQueue.dequeue()).to.deep.equal('Cake'); 142 | expect(priorityQueue.dequeue()).to.deep.equal('BMO'); 143 | expect(priorityQueue.dequeue()).to.deep.equal(null); 144 | expect(priorityQueue.dequeue()).to.deep.equal(null); 145 | expect(priorityQueue.dequeue()).to.deep.equal(null); 146 | expect(priorityQueue.dequeue()).to.deep.equal(null); 147 | }); 148 | }); 149 | 150 | }); 151 | }); 152 | -------------------------------------------------------------------------------- /test/rabin-karp.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const searchRabinKarp = require('../rabin-karp/rk'); 3 | 4 | const { expect } = chai; 5 | 6 | describe( 'Rabin-Karp', () => { 7 | describe( '`searchRabinKarp` method', () => { 8 | it( 'str.length < text.length and match', () => { 9 | expect(searchRabinKarp('abcdefgh', 'cde')).to.deep.equal([2]); 10 | }); 11 | it( 'str.length < text.length and no match', () => { 12 | expect(searchRabinKarp('abcdefgh', 'klm')).to.deep.equal([]); 13 | }); 14 | it( 'str.length < text.length and several matches', () => { 15 | expect(searchRabinKarp('abcdefabcdefabcdef', 'cd')).to.deep.equal([2, 8, 14]); 16 | }); 17 | it( 'str.length == text.length and match', () => { 18 | expect(searchRabinKarp('abc', 'abc')).to.deep.equal([0]); 19 | }); 20 | it( 'str.length == text.length and no match', () => { 21 | expect(searchRabinKarp('abc', 'def')).to.deep.equal([]); 22 | }); 23 | it( 'str.length > text.length', () => { 24 | expect(searchRabinKarp('abc', 'abcd')).to.deep.equal([]); 25 | }); 26 | it( 'long string', () => { 27 | expect(searchRabinKarp('abcdabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc', 'cd')).to.deep.equal([2]); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/search.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const LinearSearch = require('../search/linearSearch'); 3 | const BinarySearchTree = require('../search/binary-search-tree'); 4 | 5 | const { expect } = chai; 6 | 7 | describe( 'Search', () => { 8 | describe( 'Binary Search', () => { 9 | let bst1; 10 | let bst2; 11 | 12 | beforeEach(() => { 13 | bst1 = new BinarySearchTree(); 14 | bst2 = new BinarySearchTree(); 15 | }); 16 | describe( '`add` method', () => { 17 | it( 'should add a new node to the tree', () => { 18 | expect(bst1.add(40)).to.deep.equal({ 19 | value: 40, 20 | left: null, 21 | right: null, 22 | }); 23 | 24 | bst1.add(40); 25 | bst1.add(25); 26 | bst1.add(78); 27 | bst1.add(10); 28 | expect(bst1.add(32)).to.deep.equal({ 29 | value: 40, 30 | left: { 31 | value: 25, 32 | left: { 33 | value: 10, 34 | left: null, 35 | right: null, 36 | }, 37 | right: { 38 | value: 32, 39 | left: null, 40 | right: null, 41 | }, 42 | }, 43 | right: { 44 | value: 78, 45 | left: null, 46 | right: null, 47 | }, 48 | }); 49 | }); 50 | }); 51 | 52 | describe( '`get` method', () => { 53 | it( 'should find and return the node based on it\'s value', () => { 54 | bst1.add(40); 55 | bst1.add(25); 56 | bst1.add(78); 57 | bst1.add(10); 58 | bst1.add(32); 59 | const node = bst1.get(bst1.root, 10); 60 | expect(node).to.deep.equal({ 61 | current: { 62 | value: 10, 63 | left: null, 64 | right: null, 65 | }, 66 | parent: { 67 | value: 25, 68 | left: { 69 | value: 10, 70 | left: null, 71 | right: null, 72 | }, 73 | right: { 74 | value: 32, 75 | left: null, 76 | right: null, 77 | }, 78 | }, 79 | isLeft: true, 80 | }); 81 | }); 82 | it( 'should should return null if the value is not found', () => { 83 | bst1.add(40); 84 | bst1.add(25); 85 | bst1.add(78); 86 | bst1.add(10); 87 | bst1.add(32); 88 | const result = bst1.get(bst1.root, 1000000); 89 | expect(result).to.equal(null); 90 | }); 91 | it( 'should should return null if the BST is empty', () => { 92 | const result = bst1.get(bst1.root, 1000000); 93 | expect(result).to.equal(null); 94 | }); 95 | }); 96 | describe( '`remove` method', () => { 97 | it( 'should remove a leaf node from the tree', () => { 98 | bst1.add(40); 99 | bst1.add(25); 100 | bst1.add(78); 101 | bst1.add(10); 102 | bst1.add(32); 103 | bst1.remove(32); 104 | expect(bst1.root).to.deep.equal({ 105 | value: 40, 106 | left: { 107 | value: 25, 108 | left: { 109 | value: 10, 110 | left: null, 111 | right: null, 112 | }, 113 | right: null, 114 | }, 115 | right: { 116 | value: 78, 117 | left: null, 118 | right: null, 119 | }, 120 | }); 121 | }); 122 | 123 | it( 'should remove a root node with zero children and left of parent', () => { 124 | bst1.add(40); 125 | bst1.add(25); 126 | bst1.add(78); 127 | bst1.add(10); 128 | bst1.add(32); 129 | bst1.remove(40); 130 | expect(bst1.root).to.deep.equal({ 131 | value: 32, 132 | left: { 133 | value: 25, 134 | left: { 135 | value: 10, 136 | left: null, 137 | right: null, 138 | }, 139 | right: null, 140 | }, 141 | right: { 142 | value: 78, 143 | left: null, 144 | right: null, 145 | }, 146 | }); 147 | }); 148 | 149 | it( 'should remove a root node with zero children and right of parent', () => { 150 | bst1.add(40); 151 | bst1.add(25); 152 | bst1.add(78); 153 | bst1.add(10); 154 | bst1.add(32); 155 | bst1.remove(32); 156 | expect(bst1.root).to.deep.equal({ 157 | value: 40, 158 | left: { 159 | value: 25, 160 | left: { 161 | value: 10, 162 | left: null, 163 | right: null, 164 | }, 165 | right: null, 166 | }, 167 | right: { 168 | value: 78, 169 | left: null, 170 | right: null, 171 | }, 172 | }); 173 | }); 174 | 175 | it( 'should remove a node with two children when the replacement node is on the left', () => { 176 | bst1.add(40); 177 | bst1.add(25); 178 | bst1.add(78); 179 | bst1.add(10); 180 | bst1.add(32); 181 | bst1.add(5); 182 | bst1.add(35); 183 | bst1.add(30); 184 | bst1.add(34); 185 | bst1.add(38); 186 | bst1.remove(32); 187 | 188 | // This is what the correct answer SHOULD be - still not working 189 | expect(bst1.root).to.deep.equal({ 190 | value: 40, 191 | left: { 192 | value: 25, 193 | left: { 194 | value: 10, 195 | left: { 196 | value: 5, 197 | left: null, 198 | right: null, 199 | }, 200 | right: null, 201 | }, 202 | right: { 203 | value: 34, 204 | left: { 205 | value: 30, 206 | left: null, 207 | right: null, 208 | }, 209 | right: { 210 | value: 35, 211 | left: null, 212 | right: { 213 | value: 38, 214 | left: null, 215 | right: null, 216 | }, 217 | }, 218 | }, 219 | }, 220 | right: { 221 | value: 78, 222 | left: null, 223 | right: null, 224 | }, 225 | }); 226 | }); 227 | 228 | it( 'should remove a node with one left child from the tree', () => { 229 | bst1.add(40); 230 | bst1.add(25); 231 | bst1.add(78); 232 | bst1.add(10); 233 | bst1.add(32); 234 | bst1.add(5); 235 | bst1.remove(10); 236 | expect(bst1.root).to.deep.equal({ 237 | value: 40, 238 | left: { 239 | value: 25, 240 | left: { 241 | value: 5, 242 | left: null, 243 | right: null, 244 | }, 245 | right: { 246 | value: 32, 247 | left: null, 248 | right: null, 249 | }, 250 | }, 251 | right: { 252 | value: 78, 253 | left: null, 254 | right: null, 255 | }, 256 | }); 257 | }); 258 | 259 | it( 'should remove a node with one right child from the tree', () => { 260 | bst1.add(40); 261 | bst1.add(25); 262 | bst1.add(78); 263 | bst1.add(10); 264 | bst1.add(32); 265 | bst1.add(35); 266 | bst1.remove(32); 267 | expect(bst1.root).to.deep.equal({ 268 | value: 40, 269 | left: { 270 | value: 25, 271 | left: { 272 | value: 10, 273 | left: null, 274 | right: null, 275 | }, 276 | right: null, 277 | }, 278 | right: { 279 | value: 78, 280 | left: null, 281 | right: null, 282 | }, 283 | }); 284 | }); 285 | }); 286 | describe( '`contains` method', () => { 287 | it( 'should return true if the node is in the tree', () => { 288 | bst1.add(40); 289 | bst1.add(25); 290 | bst1.add(78); 291 | bst1.add(10); 292 | bst1.add(32); 293 | expect(bst1.contains(40)).to.equal(true); 294 | expect(bst1.contains(25)).to.equal(true); 295 | expect(bst1.contains(78)).to.equal(true); 296 | expect(bst1.contains(10)).to.equal(true); 297 | expect(bst1.contains(32)).to.equal(true); 298 | }); 299 | it( 'should return false if the node is not in the tree', () => { 300 | bst1.add(40); 301 | bst1.add(25); 302 | bst1.add(78); 303 | bst1.add(10); 304 | bst1.add(32); 305 | expect(bst1.contains(420)).to.equal(false); 306 | expect(bst1.contains(0)).to.equal(false); 307 | expect(bst1.contains(1)).to.equal(false); 308 | expect(bst1.contains(2)).to.equal(false); 309 | expect(bst1.contains(1000000)).to.equal(false); 310 | expect(bst1.contains(-1)).to.equal(false); 311 | expect(bst1.contains(6969)).to.equal(false); 312 | }); 313 | }); 314 | describe( '`findMin` method', () => { 315 | it( 'should return smallest node in the tree', () => { 316 | bst1.add(40); 317 | bst1.add(25); 318 | bst1.add(78); 319 | bst1.add(10); 320 | bst1.add(32); 321 | expect(bst1.findMin()).to.equal(10); 322 | }); 323 | }); 324 | describe( '`findMax` method', () => { 325 | it( 'should return largest node in the tree', () => { 326 | bst1.add(40); 327 | bst1.add(25); 328 | bst1.add(78); 329 | bst1.add(10); 330 | bst1.add(32); 331 | expect(bst1.findMax()).to.equal(78); 332 | }); 333 | }); 334 | describe( '`getDepth` method', () => { 335 | it( 'should return largest node in the tree', () => { 336 | bst1.add(40); 337 | bst1.add(25); 338 | bst1.add(78); 339 | bst1.add(10); 340 | bst1.add(32); 341 | expect(bst1.getDepth()).to.equal(2); 342 | 343 | bst2.add(40); 344 | bst2.add(25); 345 | bst2.add(78); 346 | bst2.add(10); 347 | bst2.add(32); 348 | bst2.add(5); 349 | bst2.add(35); 350 | bst2.add(15); 351 | bst2.add(60); 352 | bst2.add(79); 353 | expect(bst2.getDepth()).to.equal(3); 354 | }); 355 | }); 356 | describe( '`countLeaves` method', () => { 357 | it( 'should return number of leaves in the tree', () => { 358 | bst1.add(40); 359 | bst1.add(25); 360 | bst1.add(78); 361 | bst1.add(10); 362 | bst1.add(32); 363 | expect(bst1.countLeaves()).to.equal(3); 364 | 365 | bst2.add(40); 366 | bst2.add(25); 367 | bst2.add(78); 368 | bst2.add(10); 369 | bst2.add(32); 370 | bst2.add(5); 371 | bst2.add(35); 372 | bst2.add(15); 373 | bst2.add(60); 374 | bst2.add(79); 375 | expect(bst2.countLeaves()).to.equal(5); 376 | }); 377 | }); 378 | describe( '`nodeAverages` method', () => { 379 | it( 'should return array of the averages for of all nodes at the same depth', () => { 380 | bst1.add(40); 381 | bst1.add(25); 382 | bst1.add(78); 383 | bst1.add(10); 384 | bst1.add(32); 385 | expect(bst1.nodeAverages()).to.deep.equal([40, 51.5, 21]); 386 | }); 387 | }); 388 | describe( 'Breadth First Search', () => { 389 | describe( '`breadthFirstLTR` method', () => { 390 | it( 'should return traversal path', () => { 391 | bst1.add(40); 392 | bst1.add(25); 393 | bst1.add(78); 394 | bst1.add(10); 395 | bst1.add(32); 396 | expect(bst1.breadthFirstLTR()).to.deep.equal([40, 40, 25, 78, 25, 78, 10, 32, 10, 32]); 397 | }); 398 | }); 399 | describe( '`breadthFirstRTL` method', () => { 400 | it( 'should return traversal path', () => { 401 | bst1.add(40); 402 | bst1.add(25); 403 | bst1.add(78); 404 | bst1.add(10); 405 | bst1.add(32); 406 | expect(bst1.breadthFirstRTL()).to.deep.equal([40, 40, 78, 25, 78, 25, 32, 10, 32, 10]); 407 | }); 408 | }); 409 | }); 410 | 411 | describe( 'Depth First Search', () => { 412 | describe( '`preOrder` method', () => { 413 | it( 'should return traversal path', () => { 414 | bst1.add(40); 415 | bst1.add(25); 416 | bst1.add(78); 417 | bst1.add(10); 418 | bst1.add(32); 419 | expect(bst1.preOrder()).to.deep.equal([40, 25, 10, 32, 78]); 420 | }); 421 | }); 422 | describe( '`inOrder` method', () => { 423 | it( 'should return traversal path', () => { 424 | bst1.add(40); 425 | bst1.add(25); 426 | bst1.add(78); 427 | bst1.add(10); 428 | bst1.add(32); 429 | expect(bst1.preOrder()).to.deep.equal([40, 25, 10, 32, 78]); 430 | }); 431 | }); 432 | describe( '`postOrder` method', () => { 433 | it( 'should return traversal path', () => { 434 | bst1.add(40); 435 | bst1.add(25); 436 | bst1.add(78); 437 | bst1.add(10); 438 | bst1.add(32); 439 | expect(bst1.postOrder()).to.deep.equal([10, 32, 25, 78, 40]); 440 | }); 441 | }); 442 | }); 443 | describe( '`convertToLinkedList` method', () => { 444 | it( 'should return a linked list of the tree', () => { 445 | bst1.add(40); 446 | bst1.add(25); 447 | bst1.add(78); 448 | bst1.add(10); 449 | bst1.add(32); 450 | expect(bst1.convertToLinkedList()).to.deep.equal({ 451 | tail: { 452 | value: 78, 453 | next: null, 454 | }, 455 | head: { 456 | value: 10, 457 | next: { 458 | value: 25, 459 | next: { 460 | value: 32, 461 | next: { 462 | value: 40, 463 | next: { 464 | value: 78, 465 | next: null, 466 | }, 467 | }, 468 | }, 469 | }, 470 | }, 471 | }); 472 | }); 473 | }); 474 | }); 475 | 476 | describe( 'Linear Search', () => { 477 | let linearSearch; 478 | 479 | beforeEach(() => { 480 | const arr = ['cat', 'dog', 'mouse', 'turtle']; 481 | linearSearch = new LinearSearch(arr); 482 | }); 483 | 484 | describe( '`search` method', () => { 485 | it( 'should return `-1` if empty', () => { 486 | const newLinearSearch = new LinearSearch([]); 487 | expect(newLinearSearch.search()).to.be.equal(-1); 488 | }); 489 | 490 | it( 'should return `-1` if the element is not found', () => { 491 | expect(linearSearch.search('pig')).to.be.equal(-1); 492 | }); 493 | 494 | it( 'should return the index of the element if it\'s found', () => { 495 | expect(linearSearch.search('cat')).to.be.equal(0); 496 | expect(linearSearch.search('dog')).to.be.equal(1); 497 | expect(linearSearch.search('mouse')).to.be.equal(2); 498 | expect(linearSearch.search('turtle')).to.be.equal(3); 499 | }); 500 | }); 501 | }); 502 | }); 503 | -------------------------------------------------------------------------------- /test/shortest-path.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const Graph = require('../shortest-path/dijkstra'); 3 | const GridGraph = require('../graph/gridGraph'); 4 | const astar = require('../shortest-path/aStar'); 5 | 6 | const { expect } = chai; 7 | 8 | describe( 'Shortest Path', () => { 9 | describe( 'Dijkstra', () => { 10 | describe( '`shortestPath` method', () => { 11 | let g; 12 | 13 | beforeEach(() => { 14 | g = new Graph(); 15 | 16 | g.addVertex('A', { 17 | B: 7, 18 | C: 8, 19 | }); 20 | g.addVertex('B', { 21 | A: 7, 22 | F: 2, 23 | }); 24 | g.addVertex('C', { 25 | A: 8, 26 | F: 6, 27 | G: 4, 28 | }); 29 | g.addVertex('D', { 30 | F: 8, 31 | }); 32 | g.addVertex('E', { 33 | H: 1, 34 | }); 35 | g.addVertex('F', { 36 | B: 2, 37 | C: 6, 38 | D: 8, 39 | G: 9, 40 | H: 3, 41 | }); 42 | g.addVertex('G', { 43 | C: 4, 44 | F: 9, 45 | }); 46 | g.addVertex('H', { 47 | E: 1, 48 | F: 3, 49 | }); 50 | }); 51 | 52 | it( 'should return array of the shortest path', () => { 53 | const shortestPath = g.shortestPath('A', 'H') 54 | .concat(['A']) 55 | .reverse(); 56 | 57 | expect(shortestPath).to.deep.equal(['A', 'B', 'F', 'H']); 58 | }); 59 | }); 60 | }); 61 | describe( 'A Star', () => { 62 | const pathToString = (result) => { 63 | return result.map((node) => { 64 | return '(' + node.x + ',' + node.y + ')'; 65 | }).join(''); 66 | }; 67 | 68 | const runSearch = (gridGraph, start, end, options) => { 69 | if (!(gridGraph instanceof GridGraph)) { 70 | gridGraph = new Graph(gridGraph); 71 | } 72 | start = gridGraph.grid[start[0]][start[1]]; 73 | end = gridGraph.grid[end[0]][end[1]]; 74 | const sTime = new Date(); 75 | const result = astar.search(gridGraph, start, end, options); 76 | const eTime = new Date(); 77 | 78 | return { 79 | result, 80 | text: pathToString(result), 81 | time: (eTime - sTime), 82 | }; 83 | }; 84 | 85 | const gridGraph = new GridGraph([ 86 | [1, 1, 0, 1], 87 | [0, 1, 1, 0], 88 | [0, 0, 1, 1], 89 | ]); 90 | // '(0,1)(1,1)(1,2)(2,2)(2,3)' 91 | const result = runSearch(gridGraph, [0, 0], [2, 3]); 92 | 93 | it( 'should return array of the shortest path', () => { 94 | expect(result.text).to.equal('(0,1)(1,1)(1,2)(2,2)(2,3)'); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/sorting.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | 3 | const expect = chai.expect; 4 | chai.should(); 5 | 6 | const bubbleModule = require('../sorting/bubblesort.js'); 7 | const bubbleRecursive = require('../sorting/bubble-recursive.js'); 8 | const quickModule = require('../sorting/quicksort.js'); 9 | const quickRecursive = require('../sorting/quick-recursive.js'); 10 | const mergeModule = require('../sorting/mergesort.js'); 11 | const insertionModule = require('../sorting/insertionsort.js'); 12 | const selectionModule = require('../sorting/selectionsort.js'); 13 | 14 | describe('Sorting', () => { 15 | describe('Bubble Sort', () => { 16 | const bubble = bubbleModule(); 17 | const result = bubble.bubbleSort([3, 2, 1]); 18 | 19 | it('should be a function that exists', () => { 20 | expect(bubbleModule).to.be.a('function'); 21 | }); 22 | it('should be a module that exists', () => { 23 | expect(bubble).to.be.a('object'); 24 | }); 25 | it('should be return a sorted array', () => { 26 | expect( result ).to.deep.equal([1, 2, 3]); 27 | }); 28 | }); 29 | 30 | describe('Bubble Sort - Recursive', () => { 31 | const arr = [5, 1, 4, 2, 8, 7, 9, 9, 2, 4, 5, 6]; 32 | 33 | it('should be a function that exists', () => { 34 | expect(bubbleRecursive).to.be.a('function'); 35 | }); 36 | it('should be return a sorted array', () => { 37 | expect( bubbleRecursive( arr ) ).to.deep.equal([1, 2, 2, 4, 4, 5, 5, 6, 7, 8, 9, 9 ]); 38 | }); 39 | }); 40 | 41 | describe('Quick Sort - Recursive', () => { 42 | const arr = [5, 1, 4, 2, 8, 7, 9, 9, 2, 4, 5, 6]; 43 | 44 | it('should be a module that exists', () => { 45 | expect(quickRecursive).to.be.a('function'); 46 | }); 47 | it('should be return a sorted array', () => { 48 | expect( quickRecursive(arr) ).to.deep.equal([1, 2, 2, 4, 4, 5, 5, 6, 7, 8, 9, 9 ]); 49 | }); 50 | }); 51 | 52 | describe('Quick Sort', () => { 53 | const arr = [5, 1, 4, 2, 8, 7, 9, 9, 2, 4, 5, 6]; 54 | const quick = quickModule(); 55 | 56 | it('should be a module that exists', () => { 57 | expect(quickModule).to.be.a('function'); 58 | }); 59 | it('should be return a sorted array', () => { 60 | expect( quick.quickSort(arr) ).to.deep.equal([1, 2, 2, 4, 4, 5, 5, 6, 7, 8, 9, 9 ]); 61 | }); 62 | }); 63 | 64 | describe('Merge Sort', () => { 65 | const arr = [5, 1, 4, 2, 8, 7, 9, 9, 2, 4, 5, 6]; 66 | const merge = mergeModule(); 67 | 68 | it('should be a module that exists', () => { 69 | expect(mergeModule).to.be.a('function'); 70 | }); 71 | it('should be return a sorted array', () => { 72 | expect( merge.mergeSort(arr) ).to.deep.equal([1, 2, 2, 4, 4, 5, 5, 6, 7, 8, 9, 9 ]); 73 | }); 74 | }); 75 | 76 | describe('Insertion Sort', () => { 77 | const arr = [5, 1, 4, 2, 8, 7, 9, 9, 2, 4, 5, 6]; 78 | const insert = insertionModule(); 79 | 80 | it('should be a module that exists', () => { 81 | expect(insertionModule).to.be.a('function'); 82 | }); 83 | it('should be return a sorted array', () => { 84 | expect( insert.insertionSort( arr ) ).to.deep.equal([1, 2, 2, 4, 4, 5, 5, 6, 7, 8, 9, 9 ]); 85 | }); 86 | }); 87 | 88 | describe('Selection Sort', () => { 89 | const arr = [5, 1, 4, 2, 8, 7, 9, 9, 2, 4, 5, 6]; 90 | const select = selectionModule(); 91 | 92 | it('should be a module that exists', () => { 93 | expect(selectionModule).to.be.a('function'); 94 | }); 95 | it('should be return a sorted array', () => { 96 | expect( select.selectionSort( arr ) ).to.deep.equal([1, 2, 2, 4, 4, 5, 5, 6, 7, 8, 9, 9 ]); 97 | }); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /test/stack.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const { expect } = chai; 3 | chai.should(); 4 | 5 | const Stack = require('../stack/stack'); 6 | 7 | describe( 'Stack', () => { 8 | describe( '"push" and "pop" methods', () => { 9 | let stack; 10 | 11 | beforeEach(() => { 12 | stack = new Stack(); 13 | }); 14 | 15 | it( 'should return `null` if the stack is empty', () => { 16 | expect(stack.pop()).to.equal(null); 17 | expect(stack.pop()).to.equal(null); 18 | expect(stack.pop()).to.equal(null); 19 | }); 20 | it( 'should return the last value pushed onto the stack', () => { 21 | stack.push('turtle'); 22 | stack.push('dog'); 23 | stack.push('cat'); 24 | expect(stack.pop()).to.equal('cat'); 25 | }); 26 | it('should remove the last element from the stack when popped', () => { 27 | stack.push('turtle'); 28 | stack.push('dog'); 29 | stack.push('cat'); 30 | stack.pop(); 31 | expect(stack.pop()).to.equal('dog'); 32 | }); 33 | it('should rereturn null when all of the nodes have been popped off the stack', () => { 34 | stack.push('turtle'); 35 | stack.push('dog'); 36 | stack.push('cat'); 37 | stack.pop(); 38 | stack.pop(); 39 | stack.pop(); 40 | expect(stack.pop()).to.equal(null); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/trie.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const { expect } = chai; 3 | chai.should(); 4 | 5 | const Trie = require('../trie/trie'); 6 | 7 | describe( 'Trie', () => { 8 | 9 | describe( '"add" and "exists" behavior', () => { 10 | 11 | let trie; 12 | 13 | beforeEach(() => { 14 | trie = new Trie(); 15 | }); 16 | 17 | it( 'add should only accept strings', () => { 18 | const boolErrorTest = () => trie.add(true); 19 | const numberErrorTest = () => trie.add(10); 20 | const arrayErrorTest = () => trie.add(['bad', 'words']); 21 | const anonObjectErrorTest = () => trie.add({ bad: 'words' }); 22 | const nullErrorTest = () => trie.add(null); 23 | const undefinedErrorTest = () => trie.add(); 24 | expect(boolErrorTest).to.throw(TypeError); 25 | expect(numberErrorTest).to.throw(TypeError); 26 | expect(arrayErrorTest).to.throw(TypeError); 27 | expect(anonObjectErrorTest).to.throw(TypeError); 28 | expect(nullErrorTest).to.throw(TypeError); 29 | expect(undefinedErrorTest).to.throw(TypeError); 30 | }); 31 | 32 | it( 'exists should return "true" if the word has been added', () => { 33 | trie.add('cat'); 34 | trie.exists('cat').should.be.true; 35 | }); 36 | 37 | it( 'exists should return "false" if the word has not been added', () => { 38 | trie.add('cat'); 39 | trie.exists('dog').should.be.false; 40 | }); 41 | 42 | it( 'exists should return "false" if the word is in the trie and not an actual word', () => { 43 | trie.add('cat'); 44 | trie.add('catacombs'); 45 | trie.add('catatonic'); 46 | trie.add('catalyst'); 47 | trie.exists('cat').should.be.true; 48 | trie.exists('catacombs').should.be.true; 49 | trie.exists('catatonic').should.be.true; 50 | trie.exists('catalyst').should.be.true; 51 | trie.exists('c').should.be.false; 52 | trie.exists('ca').should.be.false; 53 | trie.exists('cata').should.be.false; 54 | trie.exists('catat').should.be.false; 55 | trie.exists('catac').should.be.false; 56 | trie.exists('catal').should.be.false; 57 | trie.exists('cataly').should.be.false; 58 | trie.exists('catalys').should.be.false; 59 | trie.exists('catalyts').should.be.false; 60 | }); 61 | 62 | it( 'should not add duplicate data to the trie', () => { 63 | trie.add('cat'); 64 | const snapshot1 = JSON.stringify(trie); 65 | 66 | for (let i = 0, len = 100; i < len; i++) { 67 | trie.add('cat'); 68 | } 69 | const snapshot2 = JSON.stringify(trie); 70 | 71 | snapshot1.should.be.equal(snapshot2); 72 | }); 73 | 74 | it( 'should handle spaces gracefully', () => { 75 | trie.add('cat'); 76 | trie.add('cat'); 77 | trie.add('d o g'); 78 | trie.add(' mouse'); 79 | trie.add(' '); 80 | trie.add(' '); 81 | 82 | trie.exists('cat').should.be.true; 83 | 84 | trie.exists('d o g').should.be.true; 85 | trie.exists('dog').should.be.false; 86 | 87 | trie.exists(' mouse').should.be.true; 88 | trie.exists('mouse').should.be.false; 89 | 90 | trie.exists(' ').should.be.true; 91 | trie.exists(' ').should.be.false; 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /trie/README.md: -------------------------------------------------------------------------------- 1 | # Trie 2 | 3 | > pronouced "trie" as in `reTRIEve` 4 | 5 | Trie, a.k.a. a digital tree, is a search tree in which we can store data in a tree-like structure. In the case of this exercise, nodes (letters) will chain upon each other to build out a word. The last node of a word is called a leaf, signifying the end of a valid word. 6 | 7 | For example, the word, `cat`, `catch`, and `car`. 8 | 9 | ``` 10 | root 11 | | 12 | └── c 13 | | 14 | └── a 15 | | 16 | ├─- t (leaf) 17 | | | 18 | | └── c 19 | | | 20 | | └── h (leaf) 21 | | 22 | └── r (leaf) 23 | ``` 24 | 25 | ## Complexity 26 | 27 | Access Search Insertion Deletion 28 | O(k) O(k) O(k) O(k) 29 | 30 | where k is the word length. 31 | 32 | ### Usage 33 | 34 | Running node on the `index.js` file located in the root of the repository will be your interactive test (in addition to the tests spec'd out in the `tests` directory). 35 | 36 | ```javascript 37 | let trie = new Trie(); 38 | trie.add('cat'); 39 | trie.add('dog'); 40 | 41 | trie.exists('cat'); // returns true 42 | trie.exists('dog'); // returns true 43 | trie.exists('mouse'); //returns false 44 | ``` 45 | 46 | #### References 47 | 48 | [Wikipedia: Trie](https://en.wikipedia.org/wiki/Trie) 49 | -------------------------------------------------------------------------------- /trie/trie.js: -------------------------------------------------------------------------------- 1 | class TrieNode { 2 | constructor(value) { 3 | this.isWord = false; 4 | this.value = value; 5 | this.children = {}; 6 | } 7 | } 8 | 9 | const isString = ( word ) => { 10 | if ( typeof word !== 'string' ) { 11 | throw new TypeError('Input must be of type string'); 12 | } 13 | }; 14 | 15 | class Trie { 16 | constructor() { 17 | this.words = 0; 18 | this.root = new TrieNode(''); 19 | } 20 | 21 | add(word) { 22 | isString(word); 23 | let currNode = this.root; 24 | 25 | for (let i = 0; i < word.length; i++) { 26 | const letter = word[i]; 27 | let nextNode = currNode.children[letter]; 28 | 29 | if (nextNode === undefined) { 30 | nextNode = new TrieNode(letter); 31 | } 32 | currNode.children[letter] = nextNode; 33 | currNode = nextNode; 34 | 35 | } 36 | if (currNode.isWord === false) { 37 | this.words++; 38 | currNode.isWord = true; 39 | } 40 | } 41 | 42 | exists(word) { 43 | isString(word); 44 | let currNode = this.root; 45 | 46 | for (let i = 0; i < word.length; i++) { 47 | const letter = word[i]; 48 | if (!currNode.children[letter]) { 49 | return false; 50 | } 51 | currNode = currNode.children[letter]; 52 | } 53 | return !!currNode.isWord; 54 | } 55 | } 56 | 57 | module.exports = Trie; 58 | --------------------------------------------------------------------------------