├── .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 | [](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 |
115 |
117 |
118 | Joe Karlsson
119 | |
120 |
121 |
122 |
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------