diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..df4ad4d9 --- /dev/null +++ b/.babelrc @@ -0,0 +1,7 @@ +{ + "presets": ["env"], + "plugins": [ + "add-module-exports", + "transform-es2015-modules-umd" + ] +} diff --git a/.bithoundrc b/.bithoundrc new file mode 100644 index 00000000..46722b1d --- /dev/null +++ b/.bithoundrc @@ -0,0 +1,19 @@ +{ + "ignore": [ + "**/.vscode/**", + "**/node_modules/**", + "**/dist/**", + "**/examples/**" + ], + "test": [ + "**/test/**" + ], + "critics": { + "lint": { + "engine": "eslint" + }, + "wc": { + "limit": 1000 + } + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..8b1709b6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = LF +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..af313c2f --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,34 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "globals": { + "expect": true, + "it": true, + "describe": true, + "beforeEach": true, + "afterEach": true, + "document": false, + "navigator": false, + "window": false + }, + "parser": "babel-eslint", + "extends": "airbnb-base", + "rules": { + "class-methods-use-this": 0, + "no-plusplus": 0, + "arrow-parens": 0, + "no-console": 0, + "import/prefer-default-export": 0, + "comma-dangle": 0, + "no-underscore-dangle": 0, + "no-param-reassign": 0, + "no-return-assign": 0, + "no-restricted-globals": 0, + "no-multi-assign": 0, + "prefer-destructuring": ["error", {"object": true, "array": false}] + } +} diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 00000000..d33152ab --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "javascript-ds-algorithms-book" + } +} diff --git a/.gitignore b/.gitignore index 2458e8b0..25cf67d5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,9 @@ *.log node_modules coverage +.nyc_output +coverage.lcov +mochawesome-report/* +dist/js/* +dist/ts/* +snippet.js diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..65552b8d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: node_js +node_js: + - node +before_script: + - npm install -g --silent firebase-tools +install: + - npm install +script: + - npm run go +after_success: + - npm run coverage + - test $TRAVIS_BRANCH = "third-edition" && firebase deploy --token $FIREBASE_TOKEN --non-interactive +notifications: + email: + on_failure: change + on_success: change diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..8b53d61d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Chrome", + "url": "http://127.0.0.1:8887/examples", + "webRoot": "${workspaceRoot}" + }, + { + "type": "node", + "request": "launch", + "name": "Mocha JS", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "args": [ + "-u", + "tdd", + "--timeout", + "999999", + "--compilers", + "js:babel-core/register", + "--colors", + "${workspaceRoot}/test/js/**/*.spec.js" + ], + "internalConsoleOptions": "openOnSessionStart" + }, + { + "type": "node", + "request": "launch", + "name": "Mocha TS", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "args": [ + "-u", + "tdd", + "--timeout", + "999999", + "-r", + "ts-node/register", + "--colors", + "${workspaceRoot}/test/ts/**/**/*.spec.ts" + ], + "protocol": "auto", + "internalConsoleOptions": "openOnSessionStart" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..55712c19 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..9b4e4d2c --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,23 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "go", + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "dev", + "group": { + "kind": "test", + "isDefault": true + } + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ade741a9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Change Log +All notable changes to the "javascript-datastructures-algorithms" third edition source code bundle will be documented in this file. + +Based on [Keep a Changelog](http://keepachangelog.com/). + +## [Unreleased] +- Initial release diff --git a/README.md b/README.md index 64771d99..00ae7465 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,87 @@ Learning JavaScript Data Structures and Algorithms ==================================== -Source code of **Learning JavaScript Data Structures and Algorithms** book. +[![Build Status](https://travis-ci.org/loiane/javascript-datastructures-algorithms.svg?branch=third-edition)](https://travis-ci.org/loiane/javascript-datastructures-algorithms) +[![codecov](https://codecov.io/gh/loiane/javascript-datastructures-algorithms/branch/third-edition/graph/badge.svg)](https://codecov.io/gh/loiane/javascript-datastructures-algorithms) -| 1st edition | 2nd edition | -| ------------- |:-------------:| -| ![1st edition](https://d1ldz4te4covpm.cloudfront.net/sites/default/files/imagecache/ppv4_main_book_cover/4874OS_Learning%20JavaScript%20Data%20Structures%20and%20Algorithms.jpg) | ![2nd edition](https://d255esdrn735hr.cloudfront.net/sites/default/files/imagecache/ppv4_main_book_cover/5493OS_5348_Learning%20JavaScript%20Data%20Structures%20and%20Algorithms,%20Second%20Edition.jpg) | -| [Book link](http://amzn.to/1Y1OWPx)| [Book link](http://amzn.to/1TSkcA1)| +Source code of **Learning JavaScript Data Structures and Algorithms** book, third edition. + +Work in Progress. + +## List of available chapters: + +* 01: [JavaScript, ECMAScript and TypeScript: a quick overview](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter01) +* 02: [Arrays](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter02) +* 03: [Stacks](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter03) +* 04: [Queues and Deques](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter04) +* 05: [LinkedLists](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter05) +* 06: [Sets](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter06) +* 07: [Dictionaries and Hashes](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter07) +* 08: [Recursion](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter08) +* 09: [Trees](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter09) +* 10: [Heap](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter10) +* 11: [Graphs](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter11) +* 12: [Sorting and Searching Algorithms](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter12) +* 13: [String and Math Algorithms](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter13) +* 14: [Algorithm Design and Techniques](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter14) +* 15: [Algorithm Complexity](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter15) + +### Third Edition Updates + +* Algorithms using ES2015+ (ES6+) +* New data structures and algorithms +* All chapters rewritten and reviewed +* Three (3) new chapters +* Creation of a Data Structures and Algorithms library that can be used in the browser or with Node.js +* Algorithms tested with Mocha + Chai (test code available in `test` directory) +* **TypeScript** version of the source code included (library and tests) + +## Project Structure + +`src/js/index.js` file contains all the data structures and algorithms listed by chapter. + +``` +|_examples (how to use each data structure and algorithm, organized by chapter) +|_src +|___js (source code: JavaScript version) +|_____data-structures +|_______models (classes used by DS: Node, ValuePair, ...) +|_____others (other algorithms such as palindome checker, hanoi tower) +|___ts (source code: TypeScript version) +|_____data-structures +|_______models +|_____others +|_test (unit tests with Mocha and Chai for src) +|___js (tests for JavaScript code) +|___ts (tests for TypeScript code) +``` + +## Installing and running the book examples With Node + +* Install [Node](https://nodejs.org) +* Open terminal/cmd and change directoty to this project folder: `cd /Users/.../javascript-datastructures-algorithms` (Linux/Max) or `cd C:/.../javascript-datastructures-algorithms` +* run `npm install` to install all depencies +* To see the examples, run `http-server html` or `npm run serve`. Open your browser `http:\\localhost:8080` to see the book examples +* Or `cd html/chapter01` and run each javascript file with node: `node 02-Variables` + +## Running the examples in the browser + +* Right click on the html file you would like to see the examples, right click and 'Open with Chrome (or any other browser)' + +* Or open the `examples/index.html` file to easily nagivate through all examples: + +* Demo: [https://javascript-ds-algorithms-book.firebaseapp.com](https://javascript-ds-algorithms-book.firebaseapp.com) + + + +Happy Coding! + +## Other editions + +| 1st edition | 2nd edition | 3rd edition | +| ------------- |:-------------:|:-------------:| +| ![1st edition](https://d1ldz4te4covpm.cloudfront.net/sites/default/files/imagecache/ppv4_main_book_cover/4874OS_Learning%20JavaScript%20Data%20Structures%20and%20Algorithms.jpg) | ![2nd edition](https://d255esdrn735hr.cloudfront.net/sites/default/files/imagecache/ppv4_main_book_cover/5493OS_5348_Learning%20JavaScript%20Data%20Structures%20and%20Algorithms,%20Second%20Edition.jpg) | ![2nd edition](https://d1ldz4te4covpm.cloudfront.net/sites/default/files/imagecache/ppv4_main_book_cover/B09377_MockupCover_0.png) | +| [Book link](http://amzn.to/1Y1OWPx)| [Book link](http://amzn.to/1TSkcA1)| [Book link](http://a.co/cbMlYmJ)| Book link - first edition: - [Packt](https://www.packtpub.com/application-development/learning-javascript-data-structures-and-algorithms) @@ -19,24 +94,9 @@ Book link - second edition: - [Amazon](http://amzn.to/1TSkcA1) - [Brazilian Portuguese version](https://novatec.com.br/livros/estruturas-de-dados-algoritmos-em-javascript/) -### List of Chapters: - -* 01: [JavaScript: a quick overview](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter01) -* 02: [Arrays](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter02) -* 03: [Stacks](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter03) -* 04: [Queues](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter04) -* 05: [Linked Lists](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter05) -* 06: [Sets](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter06) -* 07: [Dictionaries and Hashes](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter07) -* 08: [Trees](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter08) -* 09: [Graphs](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter09) -* 10: [Sorting and searching algorithms](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter10) -* 11: [Pattern of algorithms](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter11) -* 12: [Algorithm Complexity](https://github.com/loiane/javascript-datastructures-algorithms/tree/second-edition/chapter12) - -### First Edition source code: - -Please refer to [this link](https://github.com/loiane/javascript-datastructures-algorithms/tree/master) + Book link - third edition: + - [Packt](https://www.packtpub.com/web-development/learning-javascript-data-structures-and-algorithms-third-edition) + - [Amazon](http://a.co/cbMlYmJ) ### Found an issue or have a question? diff --git a/chapter01/02-Variables.js b/chapter01/02-Variables.js deleted file mode 100644 index 2920e5be..00000000 --- a/chapter01/02-Variables.js +++ /dev/null @@ -1,37 +0,0 @@ -var num = 1; //{1} -num = 3; //{2} - -var price = 1.5; //{3} -var name = 'Packt'; //{4} -var trueValue = true; //{5} -var nullVar = null; //{6} -var und; //7 - -console.log("num: "+ num); -console.log("name: "+ name); -console.log("trueValue: "+ trueValue); -console.log("price: "+ price); -console.log("nullVar: "+ nullVar); -console.log("und: "+ und); - -//******* Variable Scope - -var myVariable = 'global'; -myOtherVariable = 'global'; - -function myFunction(){ - var myVariable = 'local'; - return myVariable; -} - -function myOtherFunction(){ - myOtherVariable = 'local'; - return myOtherVariable; -} - -console.log(myVariable); //{1} -console.log(myFunction()); //{2} - -console.log(myOtherVariable); //{3} -console.log(myOtherFunction()); //{4} -console.log(myOtherVariable); //{5} \ No newline at end of file diff --git a/chapter01/10-ObjectOrientedJS.js b/chapter01/10-ObjectOrientedJS.js deleted file mode 100644 index c67a6c78..00000000 --- a/chapter01/10-ObjectOrientedJS.js +++ /dev/null @@ -1,42 +0,0 @@ -/* Object example 1 */ - -var obj = new Object(); - -/* Object example 2 */ - -var obj = {}; - -obj = { - name: { - first: 'Gandalf', - last: 'the Grey' - }, - address: 'Middle Earth' -}; - -/* Object example 3 */ - -function Book(title, pages, isbn){ - this.title = title; - this.pages = pages; - this.isbn = isbn; - this.printIsbn = function(){ - console.log(this.isbn); - } -} - -var book = new Book('title', 'pag', 'isbn'); - -console.log(book.title); //outputs the book title - -book.title = 'new title'; //update the value of the book title - -console.log(book.title); //outputs the updated value - -Book.prototype.printTitle = function(){ - console.log(this.title); -}; - -book.printTitle(); - -book.printIsbn(); \ No newline at end of file diff --git a/chapter01/11-ES6letconst.js b/chapter01/11-ES6letconst.js deleted file mode 100644 index 6be49b70..00000000 --- a/chapter01/11-ES6letconst.js +++ /dev/null @@ -1,47 +0,0 @@ -//******* EcmaScript 6: let and const keywords -// EcmaScript 6 Constants -const PI = 3.141593; -//PI = 3.0; //throws error -console.log(PI); - -//******* EcmaScript 6: let is the new var -var framework = 'Angular'; -var framework = 'React'; -console.log(framework); - -let language = 'JavaScript!'; -//let language = 'Ruby!'; //throws error -console.log(language); - -//******* EcmaScript 6: variables scope -let movie = 'Lord of the Rings'; -//var movie = 'Batman v Superman'; //throws error, variable movie already declared - -function starWarsFan(){ - let movie = 'Star Wars'; - return movie; -} - -function marvelFan(){ - movie = 'The Avengers'; - return movie; -} - -function blizzardFan(){ - let isFan = true; - let phrase = 'Warcraft'; - console.log('Before if: ' + phrase); - if (isFan){ - let phrase = 'initial text'; - phrase = 'For the Horde!'; - console.log('Inside if: ' + phrase); - } - phrase = 'For the Alliance!'; - console.log('After if: ' + phrase); -} - -console.log(movie); -console.log(starWarsFan()); -console.log(marvelFan()); -console.log(movie); -blizzardFan(); \ No newline at end of file diff --git a/chapter01/12-Es6StringTemplates.js b/chapter01/12-Es6StringTemplates.js deleted file mode 100644 index 30dc62c1..00000000 --- a/chapter01/12-Es6StringTemplates.js +++ /dev/null @@ -1,7 +0,0 @@ -// Template literals -var book = { - name: 'Learning JavaScript DataStructures and Algorithms' -}; - -console.log(`You are reading ${book.name}., - and this is a new line`); \ No newline at end of file diff --git a/chapter01/13-ES6ArrowFunctions.js b/chapter01/13-ES6ArrowFunctions.js deleted file mode 100644 index 317d576f..00000000 --- a/chapter01/13-ES6ArrowFunctions.js +++ /dev/null @@ -1,10 +0,0 @@ -//ES6: arrow functions -let circleArea = (r) => { - const PI = 3.14; - let area = PI * r * r; - return area; -} -console.log(circleArea(2)); - -let circleArea2 = (r) => 3.14 * r * r; -console.log(circleArea2(2)); \ No newline at end of file diff --git a/chapter01/15-ES6EnhancedObjectProperties.js b/chapter01/15-ES6EnhancedObjectProperties.js deleted file mode 100644 index 5f1dd9f1..00000000 --- a/chapter01/15-ES6EnhancedObjectProperties.js +++ /dev/null @@ -1,34 +0,0 @@ -// Destructuring Assignment + Property Shorthand -var [x, y] = ['a', 'b']; -var obj = { x, y }; -console.log(obj); // { x: "a", y: "b" } - -[x, y] = [y, x]; -var temp = x; -x = y; -y = temp; - -//code above is the same as -var x = 'a'; -var y = 'b'; -var obj2 = { x: x, y: y }; -console.log(obj2); // { x: "a", y: "b" } - - -// Method Properties -var hello = { - name : 'abcdef', - printHello(){ - console.log('Hello'); - } -} -console.log(hello.printHello()); - -//code above is the same as: -var hello2 = { - name: 'abcdef', - printHello: function printHello() { - console.log('Hello'); - } -}; -console.log(hello2.printHello()); \ No newline at end of file diff --git a/chapter01/16-ES6Classes.js b/chapter01/16-ES6Classes.js deleted file mode 100644 index 2ec073d9..00000000 --- a/chapter01/16-ES6Classes.js +++ /dev/null @@ -1,88 +0,0 @@ -// ES6 classes -class Book { - constructor (title, pages, isbn) { - this.title = title; - this.pages = pages; - this.isbn = isbn; - } - printIsbn(){ - console.log(this.isbn); - } -} - -let book = new Book('title', 'pag', 'isbn'); - -console.log(book.title); //outputs the book title - -book.title = 'new title'; //update the value of the book title - -console.log(book.title); //outputs the book title - - -//inheritance -class ITBook extends Book { - - constructor (title, pages, isbn, technology) { - super(title, pages, isbn); - this.technology = technology; - } - - printTechnology(){ - console.log(this.technology); - } -} - -let jsBook = new ITBook('Learning JS Algorithms', '200', '1234567890', 'JavaScript'); - -console.log(jsBook.title); -console.log(jsBook.printTechnology()); - -//getter and setters -class Person { - - constructor (name) { - this._name = name; - } - - get name() { - return this._name; - } - - set name(value) { - this._name = value; - } -} - -let lotrChar = new Person('Frodo'); -console.log(lotrChar.name); -lotrChar.name = 'Gandalf'; -console.log(lotrChar.name); - -lotrChar._name = 'Sam'; -console.log(lotrChar.name); - - -//using symbols for private atributes - -var _name = Symbol(); -class Person2 { - - constructor (name) { - this[_name] = name; - } - - get name() { - return this[_name]; - } - - set name(value) { - this[_name] = value; - } -} - -let lotrChar2 = new Person2('Frodo'); -console.log(lotrChar2.name); -lotrChar2.name = 'Gandalf'; -console.log(lotrChar2.name); - -console.log(Object.getOwnPropertySymbols(lotrChar2)); \ No newline at end of file diff --git a/chapter02/06-ES6Methods.js b/chapter02/06-ES6Methods.js deleted file mode 100644 index bf4eb36a..00000000 --- a/chapter02/06-ES6Methods.js +++ /dev/null @@ -1,112 +0,0 @@ -let numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; - -//********** forEch using arrow functions -console.log('forEach using arrow functions'); - -numbers.forEach(x => { - console.log((x % 2 == 0)); -}); - -//********** using for..of loop -console.log('using for..of loop'); - -for (let n of numbers) { - console.log((n % 2 == 0) ? 'even' : 'odd'); -} - -//********** Using the new ES6 iterator (@@iterator) -console.log('Using the new ES6 iterator (@@iterator)'); - -let iterator = numbers[Symbol.iterator](); -console.log(iterator.next().value); //1 -console.log(iterator.next().value); //2 -console.log(iterator.next().value); //3 -console.log(iterator.next().value); //4 -console.log(iterator.next().value); //5 - -//********** Array entries, keys and values -console.log('Array entries, keys and values'); - -console.log('Array.entries'); -let aEntries = numbers.entries(); //retrieve iterator of key/value -console.log(aEntries.next().value); // [0, 1] - position 0, value 1 -console.log(aEntries.next().value); // [1, 2] - position 1, value 2 -console.log(aEntries.next().value); // [2, 3] - position 2, value 3 - -console.log('Array.keys'); -let aKeys = numbers.keys(); //retrieve iterator of keys -console.log(aKeys.next()); // {value: 0, done: false } done false means iterator has more values -console.log(aKeys.next()); // {value: 1, done: false } -console.log(aKeys.next()); // {value: 2, done: false } - -console.log('Array.values'); -let aValues = numbers.values(); -console.log(aValues.next()); // {value: 1, done: false } done false means iterator has more values -console.log(aValues.next()); // {value: 2, done: false } -console.log(aValues.next()); // {value: 3, done: false } - -//********** Using the from method -console.log('Using the from method'); - -let evens = Array.from(numbers, x => (x % 2 == 0)); -console.log(evens); - -let numbers2 = Array.from(numbers); -console.log(numbers2); - -//********** Using Array.of -console.log('Using Array.of'); - -let numbers3 = Array.of(1); -let numbers4 = Array.of(1,2,3,4,5,6); -let numbersCopy = Array.of(...numbers4); -console.log(numbers3); -console.log(numbers4); -console.log(numbersCopy); - -//********** Using the fill method -console.log('Using the fill method'); - -numbersCopy.fill(0); -console.log(numbersCopy); - -numbersCopy.fill(2, 1); -console.log(numbersCopy); - -numbersCopy.fill(1, 3, 5); -console.log(numbersCopy); - -let ones = Array(6).fill(1); -console.log(ones); - -//********** Using the copyWithin method -console.log('Using the copyWithin method'); - -let copyArray = [1, 2, 3, 4, 5, 6]; -console.log(copyArray); - -copyArray = copyArray.copyWithin(0, 3); //pos 3 value is copied to pos 0 -console.log(copyArray); - -copyArray = [1, 2, 3, 4, 5, 6]; -copyArray = copyArray.copyWithin(1, 3, 5); //pos 3-4 values are copied to pos 1-2 -console.log(copyArray); - -//********** methods find and findIndex -console.log('methods find and findIndex'); - -function multipleOf13(element, index, array) { - return (element % 13 == 0) ? true : false; -} - -console.log(numbers.find(multipleOf13)); -console.log(numbers.findIndex(multipleOf13)); - -//********** EcmaScript 7: using the method includes -console.log('EcmaScript 7: using the method includes'); - -console.log(numbers.includes(15)); -console.log(numbers.includes(20)); - -let numbers5 = [7,6,5,4,3,2,1]; -console.log(numbers5.includes(4,5)); diff --git a/chapter02/07-TypedArrays.js b/chapter02/07-TypedArrays.js deleted file mode 100644 index 66a5b29a..00000000 --- a/chapter02/07-TypedArrays.js +++ /dev/null @@ -1,26 +0,0 @@ -let length = 5; -let int16 = new Int16Array(length); - -let array16 = []; -array16.length = length; - -for (let i=0; i - - - - - - - - - - - - \ No newline at end of file diff --git a/chapter03/02-UsingStacks.js b/chapter03/02-UsingStacks.js deleted file mode 100644 index 9876e892..00000000 --- a/chapter03/02-UsingStacks.js +++ /dev/null @@ -1,24 +0,0 @@ -let stack = new Stack3(); - console.log(stack.isEmpty()); //outputs true - stack.push(5); - stack.push(8); - console.log(stack.peek()); // outputs 8 - stack.push(11); - console.log(stack.size()); // outputs 3 - console.log(stack.isEmpty()); //outputs false - stack.push(15); - stack.pop(); - stack.pop(); - console.log(stack.size()); // outputs 2 - stack.print(); // outputs [5, 8] - - -//how to ensure true privacy -//in case using Stack 2 uncomment code below -/*let objectSymbols = Object.getOwnPropertySymbols(stack); - - console.log(objectSymbols.length); // 1 - console.log(objectSymbols); // [Symbol()] - console.log(objectSymbols[0]); // Symbol() - stack[objectSymbols[0]].push(1); - stack.print(); //5, 8, 1*/ \ No newline at end of file diff --git a/chapter03/03-BalancedSymbols.js b/chapter03/03-BalancedSymbols.js deleted file mode 100644 index 0d4df4c6..00000000 --- a/chapter03/03-BalancedSymbols.js +++ /dev/null @@ -1,41 +0,0 @@ -function parenthesesChecker(symbols){ - - let stack = new Stack(), - balanced = true, - index = 0, - symbol, top, - opens = "([{", - closers = ")]}"; - - while (index < symbols.length && balanced){ - symbol = symbols.charAt(index); - if (opens.indexOf(symbol) >= 0){ - stack.push(symbol); - console.log(`open symbol - stacking ${symbol}`); - } else { - console.log(`close symbol ${symbol}`); - if (stack.isEmpty()){ - balanced = false; - console.log('Stack is empty, no more symbols to pop and compare'); - } else { - top = stack.pop(); - //if (!matches(top, symbol)){ - if (!(opens.indexOf(top) === closers.indexOf(symbol))) { - balanced = false; - console.log(`poping symbol ${top} - is not a match compared to ${symbol}`); - } else { - console.log(`poping symbol ${top} - is is a match compared to ${symbol}`); - } - } - } - index++; - } - if (balanced && stack.isEmpty()){ - return true; - } - return false; -} - -console.log(parenthesesChecker('{([])}')); //true -console.log(parenthesesChecker('{{([][])}()}')); //true -console.log(parenthesesChecker('[{()]')); //false \ No newline at end of file diff --git a/chapter03/05-TowerOfHanoi.js b/chapter03/05-TowerOfHanoi.js deleted file mode 100644 index ce0fd6dd..00000000 --- a/chapter03/05-TowerOfHanoi.js +++ /dev/null @@ -1,26 +0,0 @@ -function towerOfHanoi(n, from, to, helper){ - - if (n > 0){ - towerOfHanoi(n-1, from, helper, to); - to.push(from.pop()); - console.log('-----'); - console.log('Source: ' + from.toString()); - console.log('Dest: ' + to.toString()); - console.log('Helper: ' + helper.toString()); - towerOfHanoi(n-1, helper, to, from); - } -} - -var source = new Stack(); -source.push(3); -source.push(2); -source.push(1); - -var dest = new Stack(); -var helper = new Stack(); - -towerOfHanoi(source.size(), source, dest, helper); - -source.print(); -helper.print(); -dest.print(); \ No newline at end of file diff --git a/chapter04/01-Queue.js b/chapter04/01-Queue.js deleted file mode 100644 index bb22df69..00000000 --- a/chapter04/01-Queue.js +++ /dev/null @@ -1,32 +0,0 @@ -function Queue() { - - let items = []; - - this.enqueue = function(element){ - items.push(element); - }; - - this.dequeue = function(){ - return items.shift(); - }; - - this.front = function(){ - return items[0]; - }; - - this.isEmpty = function(){ - return items.length == 0; - }; - - this.clear = function(){ - items = []; - }; - - this.size = function(){ - return items.length; - }; - - this.print = function(){ - console.log(items.toString()); - }; -} diff --git a/chapter04/01-Queue2.js b/chapter04/01-Queue2.js deleted file mode 100644 index d4bc9891..00000000 --- a/chapter04/01-Queue2.js +++ /dev/null @@ -1,49 +0,0 @@ -let Queue2 = (function () { - - const items = new WeakMap(); - - class Queue2 { - - constructor () { - items.set(this, []); - } - - enqueue(element) { - let q = items.get(this); - q.push(element); - } - - dequeue() { - let q = items.get(this); - let r = q.shift(); - return r; - } - - front() { - let q = items.get(this); - return q[0]; - } - - isEmpty(){ - return items.get(this).length == 0; - } - - size(){ - let q = items.get(this); - return q.length; - } - - clear(){ - items.set(this, []); - } - - print(){ - console.log(this.toString()); - } - - toString(){ - return items.get(this).toString(); - } - } - return Queue2; -})(); diff --git a/chapter04/02-UsingQueues.html b/chapter04/02-UsingQueues.html deleted file mode 100644 index 611d3571..00000000 --- a/chapter04/02-UsingQueues.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/chapter04/02-UsingQueues.js b/chapter04/02-UsingQueues.js deleted file mode 100644 index 5411bad6..00000000 --- a/chapter04/02-UsingQueues.js +++ /dev/null @@ -1,12 +0,0 @@ -let queue = new Queue2(); -console.log(queue.isEmpty()); //outputs true -queue.enqueue("John"); -queue.enqueue("Jack"); -queue.print(); -queue.enqueue("Camila"); -queue.print(); -console.log(queue.size()); //outputs 3 -console.log(queue.isEmpty()); //outputs false -queue.dequeue(); -queue.dequeue(); -queue.print(); \ No newline at end of file diff --git a/chapter04/03-PriorityQueue.js b/chapter04/03-PriorityQueue.js deleted file mode 100644 index 3b029e4d..00000000 --- a/chapter04/03-PriorityQueue.js +++ /dev/null @@ -1,54 +0,0 @@ -function PriorityQueue() { - - let items = []; - - function QueueElement (element, priority){ // {1} - this.element = element; - this.priority = priority; - } - - this.enqueue = function(element, priority){ - let queueElement = new QueueElement(element, priority); - - let added = false; - for (let i=0; i - - - - - - - - - - \ No newline at end of file diff --git a/chapter04/03-PriorityQueue2.js b/chapter04/03-PriorityQueue2.js deleted file mode 100644 index d629334b..00000000 --- a/chapter04/03-PriorityQueue2.js +++ /dev/null @@ -1,80 +0,0 @@ -let PriorityQueue2 = (function () { - - class QueueElement { - constructor(element, priority){ - this.element = element; - this.priority = priority; - } - } - - const items = new WeakMap(); - - class PriorityQueue2 { //extends Queue2 { //with this approach the private properties are not reachable through inheritance - - constructor () { - items.set(this, []); - } - - enqueue(element, priority){ - let queueElement = new QueueElement(element, priority); - - let q = items.get(this); - - let added = false; - for (let i=0; i 1){ - for (let i=0; i= 0 && position <= length){ - - let node = new Node(element), - current = head, - previous, - index = 0; - - if (position === 0){ //add on first position - - node.next = current; - head = node; - - } else { - while (index++ < position){ - previous = current; - current = current.next; - } - node.next = current; - previous.next = node; - } - - length++; //update size of list - - return true; - - } else { - return false; - } - }; - - this.removeAt = function(position){ - - //check for out-of-bounds values - if (position > -1 && position < length){ - - let current = head, - previous, - index = 0; - - //removing first item - if (position === 0){ - head = current.next; - } else { - - while (index++ < position){ - - previous = current; - current = current.next; - } - - //link previous with current's next - skip it to remove - previous.next = current.next; - } - - length--; - - return current.element; - - } else { - return null; - } - }; - - this.remove = function(element){ - - let index = this.indexOf(element); - return this.removeAt(index); - }; - - this.indexOf = function(element){ - - let current = head, - index = 0; - - while (current) { - if (element === current.element) { - return index; - } - index++; - current = current.next; - } - - return -1; - }; - - this.isEmpty = function() { - return length === 0; - }; - - this.size = function() { - return length; - }; - - this.getHead = function(){ - return head; - }; - - this.toString = function(){ - - let current = head, - string = ''; - - while (current) { - string += current.element + (current.next ? ', ' : ''); - current = current.next; - } - return string; - - }; - - this.print = function(){ - console.log(this.toString()); - }; -} diff --git a/chapter05/01-LinkedList2.js b/chapter05/01-LinkedList2.js deleted file mode 100644 index 98452857..00000000 --- a/chapter05/01-LinkedList2.js +++ /dev/null @@ -1,170 +0,0 @@ -let LinkedList2 = (function () { - - class Node { - constructor(element){ - this.element = element; - this.next = null; - } - } - - const length = new WeakMap(); - const head = new WeakMap(); - - class LinkedList2 { - - constructor () { - length.set(this, 0); - head.set(this, null); - } - - append(element) { - - let node = new Node(element), - current; - - if (this.getHead() === null) { //first node on list - head.set(this, node); - } else { - - current = this.getHead(); - - //loop the list until find last item - while (current.next) { - current = current.next; - } - - //get last item and assign next to added item to make the link - current.next = node; - } - - //update size of list - let l = this.size(); - l++; - length.set(this, l); - } - - insert(position, element) { - - //check for out-of-bounds values - if (position >= 0 && position <= this.size()) { - - let node = new Node(element), - current = this.getHead(), - previous, - index = 0; - - if (position === 0) { //add on first position - - node.next = current; - head.set(this, node); - - } else { - while (index++ < position) { - previous = current; - current = current.next; - } - node.next = current; - previous.next = node; - } - - //update size of list - let l = this.size(); - l++; - length.set(this, l); - - return true; - - } else { - return false; - } - } - - removeAt(position) { - - //check for out-of-bounds values - if (position > -1 && position < this.size()) { - - let current = this.getHead(), - previous, - index = 0; - - //removing first item - if (position === 0) { - head.set(this, current.next); - } else { - - while (index++ < position) { - - previous = current; - current = current.next; - } - - //link previous with current's next - skip it to remove - previous.next = current.next; - } - - let l = this.size(); - l--; - length.set(this, l); - - return current.element; - - } else { - return null; - } - } - - remove(element) { - - let index = this.indexOf(element); - return this.removeAt(index); - } - - indexOf(element) { - - let current = this.getHead(), - index = 0; - - while (current) { - if (element === current.element) { - return index; - } - index++; - current = current.next; - } - - return -1; - } - - isEmpty() { - return this.size() === 0; - } - - size() { - return length.get(this); - } - - getHead() { - return head.get(this); - } - - toString() { - - let current = this.getHead(), - string = ''; - - while (current) { - string += current.element + (current.next ? ', ' : ''); - current = current.next; - } - return string; - - } - - print() { - console.log(this.toString()); - } - } - - return LinkedList2; -})(); diff --git a/chapter05/02-UsingLinkedLists.html b/chapter05/02-UsingLinkedLists.html deleted file mode 100644 index 97210e70..00000000 --- a/chapter05/02-UsingLinkedLists.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/chapter05/02-UsingLinkedLists.js b/chapter05/02-UsingLinkedLists.js deleted file mode 100644 index bef9d86e..00000000 --- a/chapter05/02-UsingLinkedLists.js +++ /dev/null @@ -1,32 +0,0 @@ -let list = new LinkedList2(); -list.append(15); -list.print(); -console.log(list.indexOf(15)); -list.append(10); -list.print(); -console.log(list.indexOf(10)); -list.append(13); -list.print(); -console.log(list.indexOf(13)); -console.log(list.indexOf(10)); -list.append(11); -list.append(12); -list.print(); -console.log(list.removeAt(1)); -list.print() -console.log(list.removeAt(3)); -list.print(); -list.append(14); -list.print(); -list.insert(0,16); -list.print(); -list.insert(1,17); -list.print(); -list.insert(list.size(),18); -list.print(); -list.remove(16); -list.print(); -list.remove(11); -list.print(); -list.remove(18); -list.print(); \ No newline at end of file diff --git a/chapter05/03-DoublyLinkedList.js b/chapter05/03-DoublyLinkedList.js deleted file mode 100644 index 6d4de9c5..00000000 --- a/chapter05/03-DoublyLinkedList.js +++ /dev/null @@ -1,217 +0,0 @@ -function DoublyLinkedList() { - - let Node = function(element){ - - this.element = element; - this.next = null; - this.prev = null; //NEW - }; - - let length = 0; - let head = null; - let tail = null; //NEW - - this.append = function(element){ - - let node = new Node(element), - current; - - if (head === null){ //first node on list - head = node; - tail = node; //NEW - } else { - - //attach to the tail node //NEW - tail.next = node; - node.prev = tail; - tail = node; - } - - length++; //update size of list - }; - - this.insert = function(position, element){ - - //check for out-of-bounds values - if (position >= 0 && position <= length){ - - let node = new Node(element), - current = head, - previous, - index = 0; - - if (position === 0){ //add on first position - - if (!head){ //NEW - head = node; - tail = node; - } else { - node.next = current; - current.prev = node; //NEW {1} - head = node; - } - - } else if (position === length) { //last item //NEW - - current = tail; // {2} - current.next = node; - node.prev = current; - tail = node; - - } else { - while (index++ < position){ //{3} - previous = current; - current = current.next; - } - node.next = current; - previous.next = node; - - current.prev = node; //NEW - node.prev = previous; //NEW - } - - length++; //update size of list - - return true; - - } else { - return false; - } - }; - - this.removeAt = function(position){ - - //check for out-of-bounds values - if (position > -1 && position < length){ - - let current = head, - previous, - index = 0; - - //removing first item - if (position === 0){ - - head = current.next; // {1} - - //if there is only one item, then we update tail as well //NEW - if (length === 1){ // {2} - tail = null; - } else { - head.prev = null; // {3} - } - - } else if (position === length-1){ //last item //NEW - - current = tail; // {4} - tail = current.prev; - tail.next = null; - - } else { - - while (index++ < position){ // {5} - - previous = current; - current = current.next; - } - - //link previous with current's next - skip it to remove - previous.next = current.next; // {6} - current.next.prev = previous; //NEW - } - - length--; - - return current.element; - - } else { - return null; - } - }; - - this.remove = function(element){ - - let index = this.indexOf(element); - return this.removeAt(index); - }; - - this.indexOf = function(element){ - - let current = head, - index = -1; - - //check first item - if (element == current.element){ - return 0; - } - - index++; - - //check in the middle of the list - while(current.next){ - - if (element == current.element){ - return index; - } - - current = current.next; - index++; - } - - //check last item - if (element == current.element){ - return index; - } - - return -1; - }; - - this.isEmpty = function() { - return length === 0; - }; - - this. size = function() { - return length; - }; - - this.toString = function(){ - - let current = head, - s = current ? current.element : ''; - - while(current && current.next){ - current = current.next; - s += ', ' + current.element; - } - - return s; - }; - - this.inverseToString = function() { - - let current = tail, - s = current ? current.element : ''; - - while(current && current.prev){ - current = current.prev; - s += ', ' + current.element; - } - - return s; - }; - - this.print = function(){ - console.log(this.toString()); - }; - - this.printInverse = function(){ - console.log(this.inverseToString()); - }; - - this.getHead = function(){ - return head; - }; - - this.getTail = function(){ - return tail; - } -} \ No newline at end of file diff --git a/chapter05/03-DoublyLinkedList2.js b/chapter05/03-DoublyLinkedList2.js deleted file mode 100644 index 9fe7d87b..00000000 --- a/chapter05/03-DoublyLinkedList2.js +++ /dev/null @@ -1,242 +0,0 @@ -let DoublyLinkedList2 = (function () { - - class Node { - constructor(element) { - this.element = element; - this.next = null; - this.prev = null; //NEW - } - } - - const length = new WeakMap(); - const head = new WeakMap(); - const tail = new WeakMap(); //NEW - - class DoublyLinkedList2 { - - constructor () { - length.set(this, 0); - head.set(this, null); - tail.set(this, null); - } - - append(element) { - - let node = new Node(element), - current, _tail; - - if (this.getHead() === null) { //first node on list - head.set(this, node); - tail.set(this, node); //NEW - } else { - //attach to the tail node //NEW - _tail = this.getTail(); - _tail.next = node; - node.prev = _tail; - tail.set(this, node); - } - - //update size of list - let l = this.size(); - l++; - length.set(this, l); - } - - insert(position, element) { - - //check for out-of-bounds values - if (position >= 0 && position <= this.size()) { - - let node = new Node(element), - current = this.getHead(), - previous, - index = 0; - - if (position === 0) { //add on first position - - if (!this.getHead()) { //NEW - head.set(this, node); - tail.set(this, node); - } else { - node.next = current; - current.prev = node; //NEW {1} - head.set(this, node); - } - - } else if (position === this.size()) { //last item //NEW - - current = tail; // {2} - current.next = node; - node.prev = current; - tail.set(this, node); - - } else { - while (index++ < position) { //{3} - previous = current; - current = current.next; - } - node.next = current; - previous.next = node; - - current.prev = node; //NEW - node.prev = previous; //NEW - } - - //update size of list - let l = this.size(); - l++; - length.set(this, l); - - return true; - - } else { - return false; - } - } - - removeAt(position) { - - //check for out-of-bounds values - if (position > -1 && position < this.size()) { - - let _head = this.getHead(), - _tail = this.getTail(), - current = _head, - previous, - index = 0; - - //removing first item - if (position === 0) { - - _head = current.next; // {1} - - //if there is only one item, then we update tail as well //NEW - if (this.size() === 1) { // {2} - _tail = null; - } else { - _head.prev = null; // {3} - } - - } else if (position === this.size() - 1) { //last item //NEW - - current = _tail; // {4} - _tail = current.prev; - _tail.next = null; - - } else { - - while (index++ < position) { // {5} - - previous = current; - current = current.next; - } - - //link previous with current's next - skip it to remove - previous.next = current.next; // {6} - current.next.prev = previous; //NEW - } - - head.set(this,_head); - tail.set(this,_tail); - - //update size of list - let l = this.size(); - l--; - length.set(this, l); - - return current.element; - - } else { - return null; - } - } - - remove(element) { - - let index = this.indexOf(element); - return this.removeAt(index); - } - - indexOf(element) { - - let current = this.getHead(), - index = -1; - - //check first item - if (element == current.element) { - return 0; - } - - index++; - - //check in the middle of the list - while (current.next) { - - if (element == current.element) { - return index; - } - - current = current.next; - index++; - } - - //check last item - if (element == current.element) { - return index; - } - - return -1; - } - - isEmpty() { - return this.size() === 0; - } - - size() { - return length.get(this); - } - - toString() { - - let current = this.getHead(), - s = current ? current.element : ''; - - while (current && current.next) { - current = current.next; - s += ', ' + current.element; - } - - return s; - } - - inverseToString() { - - let current = this.getTail(), - s = current ? current.element : ''; - - while (current && current.prev) { - current = current.prev; - s += ', ' + current.element; - } - - return s; - } - - print() { - console.log(this.toString()); - } - - printInverse() { - console.log(this.inverseToString()); - } - - getHead() { - return head.get(this); - } - - getTail() { - return tail.get(this); - } - } - return DoublyLinkedList2; -})(); \ No newline at end of file diff --git a/chapter05/04-UsingDoublyLinkedLists.html b/chapter05/04-UsingDoublyLinkedLists.html deleted file mode 100644 index 58f959d5..00000000 --- a/chapter05/04-UsingDoublyLinkedLists.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/chapter05/04-UsingDoublyLinkedLists.js b/chapter05/04-UsingDoublyLinkedLists.js deleted file mode 100644 index 9ad4a17c..00000000 --- a/chapter05/04-UsingDoublyLinkedLists.js +++ /dev/null @@ -1,49 +0,0 @@ -let list = new DoublyLinkedList2(); - -list.append(15); -list.print(); -list.printInverse(); - -list.append(16); -list.print(); -list.printInverse(); - -list.append(17); -list.print(); -list.printInverse(); - -list.insert(0,13); -list.print(); -list.printInverse(); - -list.insert(4,18); -list.print(); -list.printInverse(); - -list.insert(1,14); -list.print(); -list.printInverse(); - -list.removeAt(0); -list.print(); -list.printInverse(); - -list.removeAt(list.size()-1); -list.print(); -list.printInverse(); - -list.removeAt(1); -list.print(); -list.printInverse(); - -list.remove(16); -list.print(); -list.printInverse(); - -list.remove(14); -list.print(); -list.printInverse(); - -list.remove(17); -list.print(); -list.printInverse(); \ No newline at end of file diff --git a/chapter05/05-CircularLinkedList.js b/chapter05/05-CircularLinkedList.js deleted file mode 100644 index c3151148..00000000 --- a/chapter05/05-CircularLinkedList.js +++ /dev/null @@ -1,190 +0,0 @@ -function CircularLinkedList() { - - let Node = function(element){ - - this.element = element; - this.next = null; - }; - - let length = 0; - let head = null; - - this.append = function(element){ - - let node = new Node(element), - current; - - if (head === null){ //first node on list - head = node; - } else { - - current = head; - - //loop the list until find last item - while(current.next !== head){ //last element will be head instead of NULL - current = current.next; - } - - //get last item and assign next to added item to make the link - current.next = node; - } - - //set node.next to head - to have circular list - node.next = head; - - length++; //update size of list - }; - - this.insert = function(position, element){ - - //check for out-of-bounds values - if (position >= 0 && position <= length){ - - let node = new Node(element), - current = head, - previous, - index = 0; - - if (position === 0){ //add on first position - - if(!head){ // if no node in list - head = node; - node.next = head; - }else{ - node.next = current; - - //update last element - while(current.next !== head){ //last element will be head instead of NULL - current = current.next; - } - - head = node; - current.next = head; - } - - - } else { - while (index++ < position){ - previous = current; - current = current.next; - } - node.next = current; - previous.next = node; - } - - length++; //update size of list - - return true; - - } else { - return false; - } - }; - - this.removeAt = function(position){ - - //check for out-of-bounds values - if (position > -1 && position < length){ - - let current = head, - previous, - index = 0; - - //removing first item - if (position === 0){ - - while(current.next !== head){ //needs to update last element first - current = current.next; - } - - head = head.next; - current.next = head; - - } else { //no need to update last element for circular list - - while (index++ < position){ - - previous = current; - current = current.next; - } - - //link previous with current's next - skip it to remove - previous.next = current.next; - } - - length--; - - return current.element; - - } else { - return null; - } - }; - - this.remove = function(element){ - - let index = this.indexOf(element); - return this.removeAt(index); - }; - - this.indexOf = function(element){ - - let current = head, - index = -1; - - //check first item - if (element == current.element){ - return 0; - } - - index++; - - //check in the middle of the list - while(current.next !== head){ - - if (element == current.element){ - return index; - } - - current = current.next; - index++; - } - - //check last item - if (element == current.element){ - return index; - } - - return -1; - }; - - this.isEmpty = function() { - return length === 0; - }; - - this.size = function() { - return length; - }; - - this.getHead = function(){ - return head; - }; - - this.toString = function(){ - - let current = head, - s = current.element; - - while(current.next !== head){ - current = current.next; - s += ', ' + current.element; - } - - return s.toString(); - }; - - this.print = function(){ - console.log(this.toString()); - }; -} - diff --git a/chapter05/05-CircularLinkedList2.js b/chapter05/05-CircularLinkedList2.js deleted file mode 100644 index 6f7574ef..00000000 --- a/chapter05/05-CircularLinkedList2.js +++ /dev/null @@ -1,204 +0,0 @@ -let CircularLinkedList2 = (function () { - - class Node { - constructor(element) { - this.element = element; - this.next = null; - } - } - - const length = new WeakMap(); - const head = new WeakMap(); - - class CircularLinkedList2 { - - constructor () { - length.set(this, 0); - head.set(this, null); - } - - append(element) { - - let node = new Node(element), - current; - - if (this.getHead() === null) { //first node on list - head.set(this, node); - } else { - - current = this.getHead(); - - //loop the list until find last item - while (current.next !== this.getHead()) { //last element will be head instead of NULL - current = current.next; - } - - //get last item and assign next to added item to make the link - current.next = node; - } - - //set node.next to head - to have circular list - node.next = this.getHead(); - - //update size of list - let l = this.size(); - l++; - length.set(this, l); - } - - insert(position, element) { - - //check for out-of-bounds values - if (position >= 0 && position <= this.size()) { - - let node = new Node(element), - current = this.getHead(), - previous, - index = 0; - - if (position === 0) { //add on first position - - if(!this.getHead()) { // if no node in list - head.set(this, node); - node.next = this.getHead(); - } else { - node.next = current; - //update last element - while(current.next !== this.getHead()) { //last element will be head instead of NULL - current = current.next; - } - head.set(this, node); - current.next = this.getHead(); - } - - } else { - while (index++ < position) { - previous = current; - current = current.next; - } - node.next = current; - previous.next = node; - } - - //update size of list - let l = this.size(); - l++; - length.set(this, l); - - return true; - - } else { - return false; - } - } - - removeAt(position) { - - //check for out-of-bounds values - if (position > -1 && position < this.size()) { - - let current = this.getHead(), - previous, - index = 0; - - //removing first item - if (position === 0) { - - while (current.next !== this.getHead()) { //needs to update last element first - current = current.next; - } - - head.set(this, this.getHead().next); - current.next = this.getHead(); - - } else { //no need to update last element for circular list - - while (index++ < position) { - - previous = current; - current = current.next; - } - - //link previous with current's next - skip it to remove - previous.next = current.next; - } - - let l = this.size(); - l--; - length.set(this, l); - - return current.element; - - } else { - return null; - } - } - - remove(element) { - - let index = this.indexOf(element); - return this.removeAt(index); - } - - indexOf(element) { - - let current = this.getHead(), - index = -1; - - //check first item - if (element == current.element) { - return 0; - } - - index++; - - //check in the middle of the list - while (current.next !== this.getHead()) { - - if (element == current.element) { - return index; - } - - current = current.next; - index++; - } - - //check last item - if (element == current.element) { - return index; - } - - return -1; - } - - isEmpty() { - return this.size() === 0; - } - - size() { - return length.get(this); - } - - getHead() { - return head.get(this); - } - - toString() { - - let current = this.getHead(), - s = current.element; - - while (current.next !== this.getHead()) { - current = current.next; - s += ', ' + current.element; - } - - return s.toString(); - } - - print() { - console.log(this.toString()); - } - } - return CircularLinkedList2; -})(); diff --git a/chapter05/06-UsingCircularLinkedList.html b/chapter05/06-UsingCircularLinkedList.html deleted file mode 100644 index ab39ba27..00000000 --- a/chapter05/06-UsingCircularLinkedList.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/chapter05/06-UsingCircularLinkedList.js b/chapter05/06-UsingCircularLinkedList.js deleted file mode 100644 index a1feebbb..00000000 --- a/chapter05/06-UsingCircularLinkedList.js +++ /dev/null @@ -1,28 +0,0 @@ -let circularLinkedList = new CircularLinkedList2(); - -circularLinkedList.append(15); -circularLinkedList.print(); - -circularLinkedList.append(16); -circularLinkedList.print(); - -circularLinkedList.insert(0,14); -circularLinkedList.print(); - -circularLinkedList.insert(1,14.5); -circularLinkedList.print(); - -circularLinkedList.insert(4,17); -circularLinkedList.print(); - -circularLinkedList.removeAt(0); -circularLinkedList.print(); - -circularLinkedList.removeAt(1); -circularLinkedList.print(); - -circularLinkedList.removeAt(2); -circularLinkedList.print(); - -console.log(circularLinkedList.indexOf(14.5)); -console.log(circularLinkedList.indexOf(16)); \ No newline at end of file diff --git a/chapter06/01-Set.js b/chapter06/01-Set.js deleted file mode 100644 index 3fa90281..00000000 --- a/chapter06/01-Set.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * ECMSCRIPT 6 already have a Set class implementation: - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set - * We will try to copy the same functionalities - * @constructor - */ -function Set() { - - let items = {}; - - this.add = function(value){ - if (!this.has(value)){ - items[value] = value; - return true; - } - return false; - }; - - this.delete = function(value){ - if (this.has(value)){ - delete items[value]; - return true; - } - return false; - }; - - this.has = function(value){ - return items.hasOwnProperty(value); - //return value in items; - }; - - this.clear = function(){ - items = {}; - }; - - /** - * Modern browsers function - * IE9+, FF4+, Chrome5+, Opera12+, Safari5+ - * @returns {Number} - */ - this.size = function(){ - return Object.keys(items).length; - }; - - /** - * cross browser compatibility - legacy browsers - * for modern browsers use size function - * @returns {number} - */ - this.sizeLegacy = function(){ - let count = 0; - for(let key in items) { - if(items.hasOwnProperty(key)) - ++count; - } - return count; - }; - - /** - * Modern browsers function - * IE9+, FF4+, Chrome5+, Opera12+, Safari5+ - * @returns {Array} - */ - this.values = function(){ - let values = []; - for (let i=0, keys=Object.keys(items); i otherSet.size()){ //{1} - return false; - } else { - let values = this.values(); - for (let i=0; i otherSet.size()){ - return false; - } else { - let values = this.values(); - for (let i=0; i - - - - - - - - - - - \ No newline at end of file diff --git a/chapter06/02-UsingSets.js b/chapter06/02-UsingSets.js deleted file mode 100644 index e74269ac..00000000 --- a/chapter06/02-UsingSets.js +++ /dev/null @@ -1,18 +0,0 @@ -let set = new Set(); - -set.add(1); -console.log(set.values()); //outputs [1] -console.log(set.has(1)); //outputs true -console.log(set.size()); //outputs 1 - -set.add(2); -console.log(set.values()); //outputs [1, 2] -console.log(set.has(2)); //true -console.log(set.size()); //2 -console.log(set.sizeLegacy()); //3 - -set.delete(1); -console.log(set.values()); //outputs [2] - -set.delete(2); -console.log(set.values()); //outputs [] \ No newline at end of file diff --git a/chapter06/03-Operations.js b/chapter06/03-Operations.js deleted file mode 100644 index dc7ece35..00000000 --- a/chapter06/03-Operations.js +++ /dev/null @@ -1,65 +0,0 @@ -//--------- Union ---------- - -let setA = new Set(); -setA.add(1); -setA.add(2); -setA.add(3); - -let setB = new Set(); -setB.add(3); -setB.add(4); -setB.add(5); -setB.add(6); - -let unionAB = setA.union(setB); -console.log(unionAB.values()); - - -//--------- Intersection ---------- - -let setA = new Set(); -setA.add(1); -setA.add(2); -setA.add(3); - -let setB = new Set(); -setB.add(2); -setB.add(3); -setB.add(4); - -let intersectionAB = setA.intersection(setB); -console.log(intersectionAB.values()); - -//--------- Difference ---------- - -let setA = new Set(); -setA.add(1); -setA.add(2); -setA.add(3); - -let setB = new Set(); -setB.add(2); -setB.add(3); -setB.add(4); - -let differenceAB = setA.difference(setB); -console.log(differenceAB.values()); - -//--------- Subset ---------- - -let setA = new Set(); -setA.add(1); -setA.add(2); - -let setB = new Set(); -setB.add(1); -setB.add(2); -setB.add(3); - -let setC = new Set(); -setC.add(2); -setC.add(3); -setC.add(4); - -console.log(setA.subset(setB)); -console.log(setA.subset(setC)); diff --git a/chapter06/04-UsingES6Set.js b/chapter06/04-UsingES6Set.js deleted file mode 100644 index a39e4100..00000000 --- a/chapter06/04-UsingES6Set.js +++ /dev/null @@ -1,71 +0,0 @@ -let set = new Set(); - -set.add(1); -console.log(set.values()); //outputs @Iterator -console.log(set.has(1)); //outputs true -console.log(set.size); //outputs 1 - -set.add(2); -console.log(set.values()); //outputs [1, 2] -console.log(set.has(2)); //true -console.log(set.size); //2 - -set.delete(1); -console.log(set.values()); //outputs [2] - -set.delete(2); -console.log(set.values()); //outputs [] - -let setA = new Set(); -setA.add(1); -setA.add(2); -setA.add(3); - -let setB = new Set(); -setB.add(2); -setB.add(3); -setB.add(4); - -//--------- Union ---------- -let unionAb = new Set(); -for (let x of setA) unionAb.add(x); -for (let x of setB) unionAb.add(x); -console.log(unionAb); - -//--------- Intersection ---------- -let intersection = function(setA, setB){ - let intersectionSet = new Set(); - - for (let x of setA){ - if (setB.has(x)){ - intersectionSet.add(x); - } - } - - return intersectionSet; -}; -let intersectionAB = intersection(setA, setB); -console.log(intersectionAB); - -//alternative - works on FF only -//intersectionAb = new Set([x for (x of setA) if (setB.has(x))]); -//console.log(intersectionAB); - -//--------- Difference ---------- -let difference = function(setA, setB){ - let differenceSet = new Set(); - - for (let x of setA){ - if (!setB.has(x)){ - differenceSet.add(x); - } - } - - return differenceSet; -}; -let differenceAB = difference(setA, setB); -console.log(differenceAB); - -//alternative - works on FF only -//differenceAB = new Set([x for (x of setA) if (!setB.has(x))]); -//console.log(differenceAB); diff --git a/chapter07/01-Dictionaries.js b/chapter07/01-Dictionaries.js deleted file mode 100644 index a22ec419..00000000 --- a/chapter07/01-Dictionaries.js +++ /dev/null @@ -1,59 +0,0 @@ -function Dictionary(){ - - var items = {}; - - this.set = function(key, value){ - items[key] = value; //{1} - }; - - this.delete = function(key){ - if (this.has(key)){ - delete items[key]; - return true; - } - return false; - }; - - this.has = function(key){ - return items.hasOwnProperty(key); - //return value in items; - }; - - this.get = function(key) { - return this.has(key) ? items[key] : undefined; - }; - - this.clear = function(){ - items = {}; - }; - - this.size = function(){ - return Object.keys(items).length; - }; - - this.keys = function(){ - return Object.keys(items); - }; - - this.values = function(){ - var values = []; - for (var k in items) { - if (this.has(k)) { - values.push(items[k]); - } - } - return values; - }; - - this.each = function(fn) { - for (var k in items) { - if (this.has(k)) { - fn(k, items[k]); - } - } - }; - - this.getItems = function(){ - return items; - } -} \ No newline at end of file diff --git a/chapter07/02-UsingDictionaries.js b/chapter07/02-UsingDictionaries.js deleted file mode 100644 index dc0bf258..00000000 --- a/chapter07/02-UsingDictionaries.js +++ /dev/null @@ -1,19 +0,0 @@ -var dictionary = new Dictionary(); - -dictionary.set('Gandalf', 'gandalf@email.com'); -dictionary.set('John', 'johnsnow@email.com'); -dictionary.set('Tyrion', 'tyrion@email.com'); - -console.log(dictionary.has('Gandalf')); //outputs true -console.log(dictionary.size()); //outputs 3 - -console.log(dictionary.keys()); //outputs ["Gandalf", "John", "Tyrion"] -console.log(dictionary.values()); //outputs ["gandalf@email.com", "johnsnow@email.com", "tyrion@email.com"] -console.log(dictionary.get('Tyrion')); //outputs tyrion@email.com - -dictionary.delete('John'); - -console.log(dictionary.keys()); //outputs ["Gandalf", "Tyrion"] -console.log(dictionary.values()); //outputs ["gandalf@email.com", "tyrion@email.com"] - -console.log(dictionary.getItems()); //Object {Gandalf: "gandalf@email.com", Tyrion: "tyrion@email.com"} diff --git a/chapter07/05-HashCollisionSeparateChaining.js b/chapter07/05-HashCollisionSeparateChaining.js deleted file mode 100644 index 5cac0e7d..00000000 --- a/chapter07/05-HashCollisionSeparateChaining.js +++ /dev/null @@ -1,85 +0,0 @@ -function HashTableSeparateChaining(){ - - var table = []; - - var ValuePair = function(key, value){ - this.key = key; - this.value = value; - - this.toString = function() { - return '[' + this.key + ' - ' + this.value + ']'; - } - }; - - var loseloseHashCode = function (key) { - var hash = 0; - for (var i = 0; i < key.length; i++) { - hash += key.charCodeAt(i); - } - return hash % 37; - }; - - var hashCode = function(key){ - return loseloseHashCode(key); - }; - - this.put = function(key, value){ - var position = hashCode(key); - console.log(position + ' - ' + key); - - if (table[position] == undefined) { - table[position] = new LinkedList(); - } - table[position].append(new ValuePair(key, value)); - }; - - this.get = function(key) { - var position = hashCode(key); - - if (table[position] !== undefined && !table[position].isEmpty()){ - - //iterate linked list to find key/value - var current = table[position].getHead(); - - do { - if (current.element.key === key){ - return current.element.value; - } - current = current.next; - } while(current); - } - return undefined; - }; - - this.remove = function(key){ - - var position = hashCode(key); - - if (table[position] !== undefined){ - - //iterate linked list to find key/value - var current = table[position].getHead(); - - do { - if (current.element.key === key){ - table[position].remove(current.element); - if (table[position].isEmpty()){ - table[position] = undefined; - } - return true; - } - current = current.next; - } while(current); - } - - return false; - }; - - this.print = function() { - for (var i = 0; i < table.length; ++i) { - if (table[i] !== undefined) { - console.log(table[i].toString()); - } - } - }; -} \ No newline at end of file diff --git a/chapter07/06-UsingHashCollisionSeparateChaining.html b/chapter07/06-UsingHashCollisionSeparateChaining.html deleted file mode 100644 index d671e133..00000000 --- a/chapter07/06-UsingHashCollisionSeparateChaining.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/chapter07/07-HashCollisionLinearProbing.js b/chapter07/07-HashCollisionLinearProbing.js deleted file mode 100644 index 4b1172be..00000000 --- a/chapter07/07-HashCollisionLinearProbing.js +++ /dev/null @@ -1,94 +0,0 @@ -function HashLinearProbing(){ - - var table = []; - - var ValuePair = function(key, value){ - this.key = key; - this.value = value; - - this.toString = function() { - return '[' + this.key + ' - ' + this.value + ']'; - } - }; - - var loseloseHashCode = function (key) { - var hash = 0; - for (var i = 0; i < key.length; i++) { - hash += key.charCodeAt(i); - } - return hash % 37; - }; - - var hashCode = function(key){ - return loseloseHashCode(key); - }; - - this.put = function(key, value){ - var position = hashCode(key); - console.log(position + ' - ' + key); - - if (table[position] == undefined) { - table[position] = new ValuePair(key, value); - } else { - var index = ++position; - while (table[index] != undefined){ - index++; - } - table[index] = new ValuePair(key, value); - } - }; - - this.get = function(key) { - var position = hashCode(key); - - if (table[position] !== undefined){ - if (table[position].key === key) { - return table[position].value; - } else { - var index = ++position; - while (table[index] !== undefined && (table[index] && table[index].key !== key)){ - index++; - } - if (table[index] && table[index].key === key) { - return table[index].value; - } - } - } else { //search for possible deleted value - var index = ++position; - while (table[index] == undefined || index == table.length || - (table[index] !== undefined && table[index] && table[index].key !== key)){ - index++; - } - if (table[index] && table[index].key === key) { - return table[index].value; - } - } - return undefined; - }; - - this.remove = function(key){ - var position = hashCode(key); - - if (table[position] !== undefined){ - if (table[position].key === key) { - table[position] = undefined; - } else { - var index = ++position; - while (table[index] === undefined || table[index].key !== key){ - index++; - } - if (table[index].key === key) { - table[index] = undefined; - } - } - } - }; - - this.print = function() { - for (var i = 0; i < table.length; ++i) { - if (table[i] !== undefined) { - console.log(i + ' -> ' + table[i].toString()); - } - } - }; -} diff --git a/chapter07/08-UsingHashCollisionLinearProbing.js b/chapter07/08-UsingHashCollisionLinearProbing.js deleted file mode 100644 index 36a33e61..00000000 --- a/chapter07/08-UsingHashCollisionLinearProbing.js +++ /dev/null @@ -1,37 +0,0 @@ -var hashLinearProbing = new HashLinearProbing(); - -hashLinearProbing.put('Gandalf', 'gandalf@email.com'); -hashLinearProbing.put('John', 'johnsnow@email.com'); -hashLinearProbing.put('Tyrion', 'tyrion@email.com'); -hashLinearProbing.put('Aaron', 'aaron@email.com'); -hashLinearProbing.put('Donnie', 'donnie@email.com'); -hashLinearProbing.put('Ana', 'ana@email.com'); -hashLinearProbing.put('Jonathan', 'jonathan@email.com'); -hashLinearProbing.put('Jamie', 'jamie@email.com'); -hashLinearProbing.put('Sue', 'sue@email.com'); -hashLinearProbing.put('Mindy', 'mindy@email.com'); -hashLinearProbing.put('Paul', 'paul@email.com'); -hashLinearProbing.put('Nathan', 'nathan@email.com'); - -console.log('**** Printing Hash **** '); - -hashLinearProbing.print(); - -console.log('**** Get **** '); - -console.log(hashLinearProbing.get('Nathan')); -console.log(hashLinearProbing.get('Loiane')); - -console.log('**** Remove **** '); - -hashLinearProbing.remove('Gandalf'); -console.log(hashLinearProbing.get('Gandalf')); -hashLinearProbing.print(); - -console.log('**** Remove Test 2 **** '); -console.log('Removing Jonathan', hashLinearProbing.remove('Jonathan')); -console.log('**** Print **** '); -hashLinearProbing.print(); -console.log('Get Jamie', hashLinearProbing.get('Jamie')); -console.log('**** Print **** '); -hashLinearProbing.print(); \ No newline at end of file diff --git a/chapter07/09-ES6Map.js b/chapter07/09-ES6Map.js deleted file mode 100644 index 5e0c9dae..00000000 --- a/chapter07/09-ES6Map.js +++ /dev/null @@ -1,17 +0,0 @@ -var map = new Map(); - -map.set('Gandalf', 'gandalf@email.com'); -map.set('John', 'johnsnow@email.com'); -map.set('Tyrion', 'tyrion@email.com'); - -console.log(map.has('Gandalf')); //outputs true -console.log(map.size); //outputs 3 - -console.log(map.keys()); //outputs ["Gandalf", "John", "Tyrion"] -console.log(map.values()); //outputs ["gandalf@email.com", "johnsnow@email.com", "tyrion@email.com"] -console.log(map.get('Tyrion')); //outputs tyrion@email.com - -map.delete('John'); - -console.log(map.keys()); //outputs ["Gandalf", "Tyrion"] -console.log(map.values()); //outputs ["gandalf@email.com", "tyrion@email.com"] \ No newline at end of file diff --git a/chapter07/10-ES6WeakMap.js b/chapter07/10-ES6WeakMap.js deleted file mode 100644 index 8efa16b8..00000000 --- a/chapter07/10-ES6WeakMap.js +++ /dev/null @@ -1,18 +0,0 @@ -var map = new WeakMap(); - -var ob1 = {name:'Gandalf'}, - ob2 = {name:'John'}, - ob3 = {name:'Tyrion'}; - -map.set(ob1, 'gandalf@email.com'); -map.set(ob2, 'johnsnow@email.com'); -map.set(ob3, 'tyrion@email.com'); - -console.log(map.has(ob1)); //outputs true -console.log(map.has(ob2)); //outputs true -console.log(map.has(ob3)); //outputs true - -console.log(map.get(ob3)); //outputs tyrion@email.com - -map.delete(ob2); -console.log(map.has(ob2)); //outputs false diff --git a/chapter07/11-ES6WeakSet.js b/chapter07/11-ES6WeakSet.js deleted file mode 100644 index e54a40de..00000000 --- a/chapter07/11-ES6WeakSet.js +++ /dev/null @@ -1,16 +0,0 @@ -var set = new WeakSet(); - -var ob1 = {name:'Gandalf'}, - ob2 = {name:'John'}, - ob3 = {name:'Tyrion'}; - -set.add(ob1); -set.add(ob2); -set.add(ob3); - -console.log(set.has(ob1)); //outputs true -console.log(set.has(ob2)); //outputs true -console.log(set.has(ob3)); //outputs true - -set.delete(ob2); -console.log(set.has(ob2)); //outputs false diff --git a/chapter08/02-UsingBinarySearchTree.html b/chapter08/02-UsingBinarySearchTree.html deleted file mode 100644 index dd40cf48..00000000 --- a/chapter08/02-UsingBinarySearchTree.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/chapter08/02-UsingBinarySearchTree.js b/chapter08/02-UsingBinarySearchTree.js deleted file mode 100644 index eb139837..00000000 --- a/chapter08/02-UsingBinarySearchTree.js +++ /dev/null @@ -1,52 +0,0 @@ -var tree = new BinarySearchTree(); - -tree.insert(11); -tree.insert(7); -tree.insert(15); -tree.insert(5); -tree.insert(3); -tree.insert(9); -tree.insert(8); -tree.insert(10); -tree.insert(13); -tree.insert(12); -tree.insert(14); -tree.insert(20); -tree.insert(18); -tree.insert(25); -tree.insert(6); - -console.log('********* in-order transverse ***********'); -function printNode(value){ - console.log(value); -} -tree.inOrderTraverse(printNode); - -console.log('********* pre-order transverse ***********'); -tree.preOrderTraverse(printNode); - -console.log('********* post-order transverse ***********'); -tree.postOrderTraverse(printNode); - - -console.log('********* max and min ***********'); -console.log(tree.max()); -console.log(tree.min()); -console.log(tree.search(1) ? 'Key 1 found.' : 'Key 1 not found.'); -console.log(tree.search(8) ? 'Key 8 found.' : 'Key 8 not found.'); - - -console.log('********* remove 6 ***********'); -tree.remove(6); -tree.inOrderTraverse(printNode); - -console.log('********* remove 5 ***********'); -tree.remove(5); -tree.inOrderTraverse(printNode); - -console.log('********* remove 15 ***********'); -tree.remove(15); -tree.inOrderTraverse(printNode); - -console.log('********* raw data structure ***********'); -console.log(tree.getRoot()); diff --git a/chapter08/03-AVLTree.js b/chapter08/03-AVLTree.js deleted file mode 100644 index 2eed4f3e..00000000 --- a/chapter08/03-AVLTree.js +++ /dev/null @@ -1,149 +0,0 @@ -function AVLTree() { - - var Node = function(key){ - this.key = key; - this.left = null; - this.right = null; - }; - - var root = null; - - this.getRoot = function(){ - return root; - }; - - var heightNode = function(node) { - if (node === null) { - return -1; - } else { - return Math.max(heightNode(node.left), heightNode(node.right)) + 1; - } - }; - - var rotationLL = function(node) { - var tmp = node.left; - node.left = tmp.right; - tmp.right = node; - - return tmp; - }; - - var rotationRR = function(node) { - var tmp = node.right; - node.right = tmp.left; - tmp.left = node; - - return tmp; - }; - - var rotationLR = function(node) { - node.left = rotationRR(node.left); - return rotationLL(node); - }; - - var rotationRL = function(node) { - node.right = rotationLL(node.right); - return rotationRR(node); - }; - - var insertNode = function(node, element) { - - if (node === null) { - node = new Node(element); - - } else if (element < node.key) { - - node.left = insertNode(node.left, element); - - if (node.left !== null) { - - if ((heightNode(node.left) - heightNode(node.right)) > 1){ - if (element < node.left.key){ - node = rotationLL(node); - } else { - node = rotationLR(node); - } - } - } - } else if (element > node.key) { - - node.right = insertNode(node.right, element); - - if (node.right !== null) { - - if ((heightNode(node.right) - heightNode(node.left)) > 1){ - - if (element > node.right.key){ - node = rotationRR(node); - } else { - node = rotationRL(node); - } - } - } - } - - return node; - }; - - this.insert = function(element) { - root = insertNode(root, element); - }; - - var parentNode; - var nodeToBeDeleted; - - var removeNode = function(node, element) { - if (node === null) { - return null; - } - parentNode = node; - - if (element < node.key) { - node.left = removeNode(node.left, element); - } else { - nodeToBeDeleted = node; - node.right = removeNode(node.right, element); - } - - if (node === parentNode) { //remove node - if (nodeToBeDeleted !== null && element === nodeToBeDeleted.key) { - if (nodeToBeDeleted === parentNode) { - node = node.left; - } else { - var tmp = nodeToBeDeleted.key; - nodeToBeDeleted.key = parentNode.key; - parentNode.key = tmp; - node = node.right; - } - } - } else { //do balancing - - if (node.left === undefined) node.left = null; - if (node.right === undefined) node.right = null; - - if ((heightNode(node.left) - heightNode(node.right)) === 2) { - if (element < node.left.key) { - node = rotationLR(node); - } else { - node = rotationLL(node); - } - } - - if ((heightNode(node.right) - heightNode(node.left)) === 2) { - if (element > node.right.key) { - node = rotationRL(node); - } else { - node = rotationRR(node); - } - } - } - - return node; - }; - - this.remove = function(element) { - parentNode = null; - nodeToBeDeleted = null; - root = removeNode(root, element); - }; -} \ No newline at end of file diff --git a/chapter08/04-UsingAVLTree.html b/chapter08/04-UsingAVLTree.html deleted file mode 100644 index fda15f71..00000000 --- a/chapter08/04-UsingAVLTree.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/chapter08/04-UsingAVLTree.js b/chapter08/04-UsingAVLTree.js deleted file mode 100644 index fd77d395..00000000 --- a/chapter08/04-UsingAVLTree.js +++ /dev/null @@ -1,62 +0,0 @@ -var avlTree = new AVLTree(); - -avlTree.insert(1); -avlTree.insert(2); -avlTree.insert(3); -avlTree.insert(4); -avlTree.insert(5); -avlTree.insert(6); -avlTree.insert(7); -avlTree.insert(14); -avlTree.insert(15); -avlTree.insert(13); -avlTree.insert(12); -avlTree.insert(11); - -//RR rotation -/*avlTree.insert(50); -avlTree.insert(30); -avlTree.insert(70); -avlTree.insert(60); -avlTree.insert(80); -avlTree.insert(90);*/ - -//LL rotation -/*avlTree.insert(50); -avlTree.insert(30); -avlTree.insert(70); -avlTree.insert(10); -avlTree.insert(40); -avlTree.insert(5);*/ - -//LR rotation -/*avlTree.insert(50); -avlTree.insert(30); -avlTree.insert(70); -avlTree.insert(40); -avlTree.insert(10); -avlTree.insert(35);*/ - -//RL rotation -/*avlTree.insert(70); -avlTree.insert(50); -avlTree.insert(80); -avlTree.insert(72); -avlTree.insert(90); -avlTree.insert(75);*/ - -console.log('********* raw data structure ***********'); -console.log(avlTree.getRoot()); - -/*avlTree.remove(12); -avlTree.remove(15); -avlTree.remove(11); -avlTree.remove(14); -avlTree.remove(13); -avlTree.remove(7); -avlTree.remove(6); -avlTree.remove(2); -avlTree.remove(4); - -console.log(avlTree.getRoot());*/ - diff --git a/chapter08/05-RedBlackTree.js b/chapter08/05-RedBlackTree.js deleted file mode 100644 index 7a985d2e..00000000 --- a/chapter08/05-RedBlackTree.js +++ /dev/null @@ -1,101 +0,0 @@ -function RedBlackTree() { - - var Colors = { - RED: 0, - BLACK: 1 - }; - - var Node = function (key, color) { - this.key = key; - this.left = null; - this.right = null; - this.color = color; - - this.flipColor = function(){ - if (this.color === Colors.RED) { - this.color = Colors.BLACK; - } else { - this.color = Colors.RED; - } - }; - }; - - var root = null; - - this.getRoot = function () { - return root; - }; - - var isRed = function(node){ - if (!node){ - return false; - } - return node.color === Colors.RED; - }; - - var flipColors = function(node){ - node.left.flipColor(); - node.right.flipColor(); - }; - - var rotateLeft = function(node){ - var temp = node.right; - if (temp !== null) { - node.right = temp.left; - temp.left = node; - temp.color = node.color; - node.color = Colors.RED; - } - return temp; - }; - - var rotateRight = function (node) { - var temp = node.left; - if (temp !== null) { - node.left = temp.right; - temp.right = node; - temp.color = node.color; - node.color = Colors.RED; - } - return temp; - }; - - var insertNode = function(node, element) { - - if (node === null) { - return new Node(element, Colors.RED); - } - - var newRoot = node; - - if (element < node.key) { - - node.left = insertNode(node.left, element); - - } else if (element > node.key) { - - node.right = insertNode(node.right, element); - - } else { - node.key = element; - } - - if (isRed(node.right) && !isRed(node.left)) { - newRoot = rotateLeft(node); - } - - if (isRed(node.left) && isRed(node.left.left)) { - newRoot = rotateRight(node); - } - if (isRed(node.left) && isRed(node.right)) { - flipColors(node); - } - - return newRoot; - }; - - this.insert = function(element) { - root = insertNode(root, element); - root.color = Colors.BLACK; - }; -} diff --git a/chapter08/06-UsingRedBlackTree.html b/chapter08/06-UsingRedBlackTree.html deleted file mode 100644 index f0fdfcc9..00000000 --- a/chapter08/06-UsingRedBlackTree.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/chapter08/06-UsingRedBlackTree.js b/chapter08/06-UsingRedBlackTree.js deleted file mode 100644 index 31136f99..00000000 --- a/chapter08/06-UsingRedBlackTree.js +++ /dev/null @@ -1,17 +0,0 @@ -var rbTree = new RedBlackTree(); - -rbTree.insert(1); -rbTree.insert(2); -rbTree.insert(3); -rbTree.insert(4); -rbTree.insert(5); -rbTree.insert(6); -rbTree.insert(7); -rbTree.insert(14); -rbTree.insert(15); -rbTree.insert(13); -rbTree.insert(12); -rbTree.insert(11); - -console.log('********* raw data structure ***********'); -console.log(rbTree.getRoot()); \ No newline at end of file diff --git a/chapter09/01-Graph.js b/chapter09/01-Graph.js deleted file mode 100644 index b7a1b847..00000000 --- a/chapter09/01-Graph.js +++ /dev/null @@ -1,172 +0,0 @@ -function Graph() { - - var vertices = []; //list - - var adjList = new Dictionary(); - - this.addVertex = function(v){ - vertices.push(v); - adjList.set(v, []); //initialize adjacency list with array as well; - }; - - this.addEdge = function(v, w){ - adjList.get(v).push(w); - //adjList.get(w).push(v); //commented to run the improved DFS with topological sorting - }; - - this.toString = function(){ - var s = ''; - for (var i=0; i '; - var neighbors = adjList.get(vertices[i]); - for (var j=0; j - - - - - - - - - - \ No newline at end of file diff --git a/chapter09/04-UsingShortestPathAlgorithms.js b/chapter09/04-UsingShortestPathAlgorithms.js deleted file mode 100644 index 5bc5ac79..00000000 --- a/chapter09/04-UsingShortestPathAlgorithms.js +++ /dev/null @@ -1,45 +0,0 @@ -//adjacent matrix -var i; - -var graph = [[0, 2, 4, 0, 0, 0], - [0, 0, 2, 4, 2, 0], - [0, 0, 0, 0, 3, 0], - [0, 0, 0, 0, 0, 2], - [0, 0, 0, 3, 0, 2], - [0, 0, 0, 0, 0, 0]]; - -var shortestPath = new ShortestPath(graph); - -console.log("********* Dijkstra's Algorithm - Shortest Path ***********"); - -var dist = shortestPath.dijkstra(0); - -for (i = 0; i < dist.length; i++){ - console.log(i + '\t\t' + dist[i]); -} - -console.log("********* Floyd-Warshall Algorithm - All-Pairs Shortest Path ***********"); - -var INF = Number.MAX_SAFE_INTEGER; -graph = [[0, 2, 4, INF, INF, INF], - [INF, 0, 2, 4, 2, INF], - [INF, INF, 0, INF, 3, INF], - [INF, INF, INF, 0, INF, 2], - [INF, INF, INF, 3, 0, 2], - [INF, INF, INF, INF, INF, 0]]; - -shortestPath = new ShortestPath(graph); - -dist = shortestPath.floydWarshall(); - -var s = ''; -for (i=0; i - - - - - - - - - - \ No newline at end of file diff --git a/chapter09/06-UsingMinimumSpanningTree.js b/chapter09/06-UsingMinimumSpanningTree.js deleted file mode 100644 index 286f1dff..00000000 --- a/chapter09/06-UsingMinimumSpanningTree.js +++ /dev/null @@ -1,29 +0,0 @@ -var i; - -var graph = [[0, 2, 4, 0, 0, 0], - [2, 0, 2, 4, 2, 0], - [4, 2, 0, 0, 3, 0], - [0, 4, 0, 0, 3, 2], - [0, 2, 3, 3, 0, 2], - [0, 0, 0, 2, 2, 0]]; - -var mst = new MinimumSpanningTree(graph); - - -console.log("********* Prim's Algorithm - Minimum Spanning Tree ***********"); - -var parent = mst.prim(); - -console.log('Edge Weight'); -for (i = 1; i < graph.length; i++){ - console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]); -} - -console.log("********* Kruskal Algorithm - Minimum Spanning Tree ***********"); - -parent = mst.kruskal(); - -console.log('Edge Weight'); -for (i = 1; i < graph.length; i++){ - console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]); -} \ No newline at end of file diff --git a/chapter10/01-SortingSearchingAlgorithms.js b/chapter10/01-SortingSearchingAlgorithms.js deleted file mode 100755 index 916e7403..00000000 --- a/chapter10/01-SortingSearchingAlgorithms.js +++ /dev/null @@ -1,417 +0,0 @@ -function ArrayList(){ - - var array = []; - - this.insert = function(item){ - array.push(item); - }; - - var swap = function(array, index1, index2){ - var aux = array[index1]; - array[index1] = array[index2]; - array[index2] = aux; - //ES2015 swap - Firefox only, for other browser, uncomment code above and coment line below - //[array[index1], array[index2]] = [array[index2], array[index1]]; - }; - - this.toString= function(){ - return array.join(); - }; - - this.array= function(){ - return array; - }; - - this.bubbleSort = function(){ - var length = array.length; - - for (var i=0; i array[j+1]){ - console.log('swap ' + array[j] + ' with ' + array[j+1]); - swap(array, j, j+1); - } - } - } - }; - - this.modifiedBubbleSort = function(){ - var length = array.length; - - for (var i=0; i array[j+1]){ - console.log('swap ' + array[j] + ' with ' + array[j+1]); - swap(j, j+1); - } - } - } - - }; - - this.selectionSort = function(){ - var length = array.length, - indexMin; - - for (var i=0; iarray[j]){ - console.log('new index min ' + array[j]); - indexMin = j; - } - } - if (i !== indexMin){ - console.log('swap ' + array[i] + ' with ' + array[indexMin]); - swap(i, indexMin); - } - } - }; - - this.insertionSort = function(){ - var length = array.length, - j, temp; - for (var i=1; i0 && array[j-1] > temp){ - console.log('shift ' + array[j-1]); - array[j] = array[j-1]; - j--; - } - console.log('insert ' + temp); - array[j] = temp; - } - }; - - var insertionSort_ = function(array){ - var length = array.length, - j, temp; - for (var i=1; i0 && array[j-1] > temp){ - array[j] = array[j-1]; - j--; - } - array[j] = temp; - } - }; - - this.mergeSort = function(){ - array = mergeSortRec(array); - }; - - var mergeSortRec = function(array){ - - var length = array.length; - - if(length === 1) { - console.log(array); - return array; - } - - var mid = Math.floor(length / 2), - left = array.slice(0, mid), - right = array.slice(mid, length); - - return merge(mergeSortRec(left), mergeSortRec(right)); - }; - - var merge = function(left, right){ - var result = [], - il = 0, - ir = 0; - - while(il < left.length && ir < right.length) { - - if(left[il] < right[ir]) { - result.push(left[il++]); - } else{ - result.push(right[ir++]); - } - } - - while (il < left.length){ - result.push(left[il++]); - } - - while (ir < right.length){ - result.push(right[ir++]); - } - - console.log(result); - - return result; - }; - - this.quickSort = function(){ - quick(array, 0, array.length - 1); - }; - - var partition = function(array, left, right) { - - var pivot = array[Math.floor((right + left) / 2)], - i = left, - j = right; - - console.log('pivot is ' + pivot + '; left is ' + left + '; right is ' + right); - - while (i <= j) { - while (array[i] < pivot) { - i++; - console.log('i = ' + i); - } - - while (array[j] > pivot) { - j--; - console.log('j = ' + j); - } - - if (i <= j) { - console.log('swap ' + array[i] + ' with ' + array[j]); - swap(array, i, j); - i++; - j--; - } - } - - return i; - }; - - var quick = function(array, left, right){ - - var index; - - if (array.length > 1) { - - index = partition(array, left, right); - - if (left < index - 1) { - quick(array, left, index - 1); - } - - if (index < right) { - quick(array, index, right); - } - } - return array; - }; - - this.heapSort = function(){ - var heapSize = array.length; - - buildHeap(array); - - while (heapSize > 1) { - heapSize--; - console.log('swap (' + + array[0] + ',' + array[heapSize] + ')'); - swap(array, 0, heapSize); - console.log('heapify ' + array.join()); - heapify(array, heapSize, 0); - } - }; - - var buildHeap = function(array){ - console.log('building heap'); - var heapSize = array.length; - for (var i = Math.floor(array.length / 2); i >= 0; i--) { - heapify(array, heapSize, i); - } - console.log('heap created: ' + array.join()); - }; - - var heapify = function(array, heapSize, i){ - var left = i * 2 + 1, - right = i * 2 + 2, - largest = i; - - if (left < heapSize && array[left] > array[largest]) { - largest = left; - } - - if (right < heapSize && array[right] > array[largest]) { - largest = right; - } - - console.log('Heapify Index = '+ i + ' and Heap Size = ' + heapSize); - - if (largest !== i) { - console.log('swap index ' + i + ' with ' + largest + ' (' + + array[i] + ',' + array[largest] + ')'); - swap(array, i, largest); - console.log('heapify ' + array.join()); - heapify(array, heapSize, largest); - } - }; - - this.countingSort = function(){ - - var i, - maxValue = this.findMaxValue(), - sortedIndex = 0, - counts = new Array(maxValue + 1); - - for (i = 0; i < array.length; i++) { - if (!counts[array[i]]) { - counts[array[i]] = 0; - } - counts[array[i]]++; - } - - console.log('Frequencies: ' + counts.join()); - - for (i = 0; i < counts.length; i++) { - while (counts[i] > 0) { - array[sortedIndex++] = i; - counts[i]--; - } - } - }; - - this.bucketSort = function(bucketSize){ - - var i, - minValue = this.findMinValue(), - maxValue = this.findMaxValue(), - BUCKET_SIZE = 5; - - console.log('minValue ' + minValue); - console.log('maxValue ' + maxValue); - - bucketSize = bucketSize || BUCKET_SIZE; - var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; - var buckets = new Array(bucketCount); - console.log('bucketSize = ' + bucketCount); - for (i = 0; i < buckets.length; i++) { - buckets[i] = []; - } - - for (i = 0; i < array.length; i++) { - buckets[Math.floor((array[i] - minValue) / bucketSize)].push(array[i]); - console.log('pushing item ' + array[i] + ' to bucket index ' + Math.floor((array[i] - minValue) / bucketSize)); - } - - array = []; - for (i = 0; i < buckets.length; i++) { - insertionSort_(buckets[i]); - - console.log('bucket sorted ' + i + ': ' + buckets[i].join()); - - for (var j = 0; j < buckets[i].length; j++) { - array.push(buckets[i][j]); - } - } - }; - - this.radixSort = function(radixBase){ - - var i, - minValue = this.findMinValue(), - maxValue = this.findMaxValue(), - radixBase = radixBase || 10; - - // Perform counting sort for each significant digit), starting at 1 - var significantDigit = 1; - while (((maxValue - minValue) / significantDigit) >= 1) { - console.log('radix sort for digit ' + significantDigit); - array = countingSortForRadix(array, radixBase, significantDigit, minValue); - console.log(array.join()); - significantDigit *= radixBase; - } - }; - - var countingSortForRadix = function(array, radixBase, significantDigit, minValue){ - var i, countsIndex, - counts = new Array(radixBase), - aux = new Array(radixBase); - - for (i = 0; i < radixBase; i++) { - counts[i] = 0; - } - - for (i = 0; i < array.length; i++) { - countsIndex = Math.floor(((array[i] - minValue) / significantDigit) % radixBase); - counts[countsIndex]++; - } - - for (i = 1; i < radixBase; i++) { - counts[i] += counts[i - 1]; - } - - for (i = array.length - 1; i >= 0; i--) { - countsIndex = Math.floor(((array[i] - minValue) / significantDigit) % radixBase); - aux[--counts[countsIndex]] = array[i]; - } - - for (i = 0; i < array.length; i++) { - array[i] = aux[i]; - } - - return array; - }; - - this.sequentialSearch = function(item){ - - for (var i=0; i array[i]){ - min = array[i]; - } - } - - return min; - }; - - this.binarySearch = function(item){ - this.quickSort(); - - var low = 0, - high = array.length - 1, - mid, element; - - while (low <= high){ - mid = Math.floor((low + high) / 2); - element = array[mid]; - console.log('mid element is ' + element); - if (element < item) { - low = mid + 1; - console.log('low is ' + low); - } else if (element > item) { - high = mid - 1; - console.log('high is ' + high); - } else { - console.log('found it'); - return mid; - } - } - return -1; - }; - -} \ No newline at end of file diff --git a/chapter10/02-UsingSortingAlgorithms.html b/chapter10/02-UsingSortingAlgorithms.html deleted file mode 100755 index 7a666fdb..00000000 --- a/chapter10/02-UsingSortingAlgorithms.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/chapter10/02-UsingSortingAlgorithms.js b/chapter10/02-UsingSortingAlgorithms.js deleted file mode 100755 index 92b63e8f..00000000 --- a/chapter10/02-UsingSortingAlgorithms.js +++ /dev/null @@ -1,126 +0,0 @@ -function createNonSortedArray(size){ - var array = new ArrayList(); - - for (var i = size; i> 0; i--){ - array.insert(i); - } - - return array; -} - -function createRandomNonSortedArray(){ - var array = new ArrayList(); - - array.insert(3); - array.insert(5); - array.insert(1); - array.insert(6); - array.insert(4); - array.insert(7); - array.insert(2); - - return array; -} - -function printArray(array){ - console.log(array.toString()); -} - -function createNonSortedArrayAndPrint(size){ - var array = createNonSortedArray(size); - printArray(array); - - return array; -} - -console.log('********** Bubble Sort **********'); - -var array = createNonSortedArrayAndPrint(5); - -array.bubbleSort(); - -printArray(array); - -console.log('********** Modified Bubble Sort **********'); - -array = createNonSortedArrayAndPrint(5); - -array.modifiedBubbleSort(); - -printArray(array); - -console.log('********** Selection Sort **********'); - -array = createNonSortedArrayAndPrint(5); - -array.selectionSort(); - -printArray(array); - -console.log('********** Insertion Sort **********'); - -array = createNonSortedArrayAndPrint(5); - -array.insertionSort(); - -printArray(array); - -console.log('********** Merge Sort **********'); - -array = createNonSortedArrayAndPrint(8); - -array.mergeSort(); - -printArray(array); - -console.log('********** Quick Sort **********'); -array = createRandomNonSortedArray(); - -printArray(array); - -array.quickSort(); - -printArray(array); - -console.log('********** Heap Sort **********'); -array = createRandomNonSortedArray(); - -printArray(array); - -array.heapSort(); - -printArray(array); - - -console.log('********** Counting Sort **********'); - -array = createNonSortedArrayAndPrint(8); - -array.countingSort(); - -printArray(array); - -console.log('********** Bucket Sort **********'); - -array = createNonSortedArrayAndPrint(8); - -array.bucketSort(3); - -printArray(array); - -console.log('********** Radix Sort **********'); - -var array = new ArrayList(); - -array.insert(30); -array.insert(52); -array.insert(13); -array.insert(25); -array.insert(31); -array.insert(23); -array.insert(2); - -array.radixSort(); - -console.log('Result: '); -printArray(array); diff --git a/chapter10/03-UsingSearchingAlgorithms.html b/chapter10/03-UsingSearchingAlgorithms.html deleted file mode 100755 index a4e4b6a3..00000000 --- a/chapter10/03-UsingSearchingAlgorithms.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/chapter10/03-UsingSearchingAlgorithms.js b/chapter10/03-UsingSearchingAlgorithms.js deleted file mode 100755 index 62201e80..00000000 --- a/chapter10/03-UsingSearchingAlgorithms.js +++ /dev/null @@ -1,33 +0,0 @@ -function createNonSortedArray(items){ - var array = new ArrayList(); - - for (var i = items; i> 0; i--){ - array.insert(i); - } - - return array; -} - -var array = createNonSortedArray(5); - -console.log('********** Sequential Sort #3 **********'); - -console.log(array.sequentialSearch(3)); - -console.log('********** Min **********'); - -console.log(array.findMinValue()); - -console.log('********** Max **********'); - -console.log(array.findMaxValue()); - -console.log('********** Binary Search #3 **********'); - -console.log(array.binarySearch(3)); - -console.log('********** Binary Search #2 **********'); - -var array = createNonSortedArray(8); - -console.log(array.binarySearch(2)); \ No newline at end of file diff --git a/chapter11/03-MinCoinChangeDP.js b/chapter11/03-MinCoinChangeDP.js deleted file mode 100644 index 90a4a541..00000000 --- a/chapter11/03-MinCoinChangeDP.js +++ /dev/null @@ -1,38 +0,0 @@ -function MinCoinChange(coins){ - - var cache = {}; - - this.makeChange = function(amount) { - var me = this; - if (!amount) { - return []; - } - if (cache[amount]) { - return cache[amount]; - } - var min = [], newMin, newAmount; - for (var i=0; i= 0){ - newMin = me.makeChange(newAmount); - } - if ( - newAmount >= 0 && - (newMin.length < min.length-1 || !min.length) && - (newMin.length || !newAmount) - ){ - min = [coin].concat(newMin); - console.log('new Min ' + min + ' for ' + amount); - } - } - return (cache[amount] = min); - }; -} - - -var minCoinChange = new MinCoinChange([1, 5, 10, 25]); -console.log(minCoinChange.makeChange(36)); - -var minCoinChange2 = new MinCoinChange([1, 3, 4]); -console.log(minCoinChange2.makeChange(6)); \ No newline at end of file diff --git a/chapter11/04-MinCoinChangeGreedy.js b/chapter11/04-MinCoinChangeGreedy.js deleted file mode 100644 index 3050f7ed..00000000 --- a/chapter11/04-MinCoinChangeGreedy.js +++ /dev/null @@ -1,25 +0,0 @@ -function MinCoinChange(coins){ - - var coins = coins; - - var cache = {}; - - this.makeChange = function(amount) { - var change = [], - total = 0; - for (var i=coins.length; i>=0; i--){ - var coin = coins[i]; - while (total + coin <= amount) { - change.push(coin); - total += coin; - } - } - return change; - }; -} - -var minCoinChange = new MinCoinChange([1, 5, 10, 25]); -console.log(minCoinChange.makeChange(36)); - -var minCoinChange2 = new MinCoinChange([1, 3, 4]); -console.log(minCoinChange2.makeChange(6)); \ No newline at end of file diff --git a/chapter11/05-KnapsackProblemDP.html b/chapter11/05-KnapsackProblemDP.html deleted file mode 100644 index 316f9722..00000000 --- a/chapter11/05-KnapsackProblemDP.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/chapter11/05-KnapsackProblemDP.js b/chapter11/05-KnapsackProblemDP.js deleted file mode 100644 index fb6adbe7..00000000 --- a/chapter11/05-KnapsackProblemDP.js +++ /dev/null @@ -1,53 +0,0 @@ -function knapSack(capacity, weights, values, n) { - - var i, w, a, b, kS = []; - - for (i = 0; i <= n; i++) { - kS[i] = []; - } - - for (i = 0; i <= n; i++){ - for (w = 0; w <= capacity; w++){ - if (i == 0 || w == 0){ - kS[i][w] = 0; - - } else if (weights[i-1] <= w){ - a = values[i-1] + kS[i-1][w-weights[i-1]]; - b = kS[i-1][w]; - kS[i][w] = (a > b) ? a : b; //max(a,b) - console.log(a + ' can be part of the solution'); - } else{ - kS[i][w] = kS[i-1][w]; - } - } - console.log(kS[i].join()); - } - - //extra algorithm to find the items that are part of the solution - findValues(n, capacity, kS, values, weights); - - return kS[n][capacity]; -} - -function findValues(n, capacity, kS, weights, values){ - var i=n, k=capacity; - - console.log('Items that are part of the solution:'); - - while (i>0 && k>0){ - if (kS[i][k] !== kS[i-1][k]){ - console.log('item '+i+' can be part of solution w,v: ' + weights[i-1] + ',' + values[i-1]); - i--; - k = k - kS[i][k]; - } else { - i--; - } - } -} - -var values = [3,4,5], - weights = [2,3,4], - capacity = 5, - n = values.length; - -console.log('Total value that can be carried: ' + knapSack(capacity, weights, values, n)); \ No newline at end of file diff --git a/chapter11/06-KnapSackProblemRecursive.html b/chapter11/06-KnapSackProblemRecursive.html deleted file mode 100644 index cc000756..00000000 --- a/chapter11/06-KnapSackProblemRecursive.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/chapter11/06-KnapSackProblemRecursive.js b/chapter11/06-KnapSackProblemRecursive.js deleted file mode 100644 index 583092e1..00000000 --- a/chapter11/06-KnapSackProblemRecursive.js +++ /dev/null @@ -1,22 +0,0 @@ -function knapSack(capacity, weights, values, n) { - - if (n == 0 || capacity == 0){ - return 0; - } - - if (weights[n-1] > capacity){ - return knapSack(capacity, weights, values, n-1); - - } else { - var a = values[n-1] + knapSack(capacity-weights[n-1], weights, values, n-1), - b = knapSack(capacity, weights, values, n-1); - return (a > b) ? a : b; - } -} - -var values = [3,4,5], - weights = [2,3,4], - capacity = 5, - n = values.length; - -console.log(knapSack(capacity, weights, values, n)); \ No newline at end of file diff --git a/chapter11/07-KnapSackProblemGreedy.html b/chapter11/07-KnapSackProblemGreedy.html deleted file mode 100644 index 227d7b4b..00000000 --- a/chapter11/07-KnapSackProblemGreedy.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/chapter11/07-KnapSackProblemGreedy.js b/chapter11/07-KnapSackProblemGreedy.js deleted file mode 100644 index 4c12e1a6..00000000 --- a/chapter11/07-KnapSackProblemGreedy.js +++ /dev/null @@ -1,28 +0,0 @@ -function knapSack(capacity, values, weights) { - var n = values.length, - load = 0, - i = 0, - val = 0; - - for (i=0; i - - - - - - - - - \ No newline at end of file diff --git a/chapter11/08-LongestCommonSubsequenceDP.js b/chapter11/08-LongestCommonSubsequenceDP.js deleted file mode 100644 index b5eaf3f1..00000000 --- a/chapter11/08-LongestCommonSubsequenceDP.js +++ /dev/null @@ -1,105 +0,0 @@ -function lcs(wordX, wordY) { - - var m = wordX.length, - n = wordY.length, - l = [], - i, j, a, b; - - for (i = 0; i <= m; ++i) { - l[i] = []; - for (j = 0; j <= n; ++j) { - l[i][j] = 0; - } - } - - for (i=0; i<=m; i++) { - for (j=0; j<=n; j++) { - if (i == 0 || j == 0){ - l[i][j] = 0; - - } else if (wordX[i-1] == wordY[j-1]) { - l[i][j] = l[i-1][j-1] + 1; - - } else { - a = l[i-1][j]; - b = l[i][j-1]; - l[i][j] = (a > b) ? a : b; //max(a,b) - } - } - console.log(l[i].join()); - } - - return l[m][n]; -} - -//complete algorithm that prints the LCS as well - -function lcs2(wordX, wordY) { - - var m = wordX.length, - n = wordY.length, - l = [], - solution = [], - i, j, a, b; - - for (i = 0; i <= m; ++i) { - l[i] = []; - solution[i] = []; - for (j = 0; j <= n; ++j) { - l[i][j] = 0; - solution[i][j] = '0'; - } - } - - for (i=0; i<=m; i++) { - for (j=0; j<=n; j++) { - if (i == 0 || j == 0){ - l[i][j] = 0; - - } else if (wordX[i-1] == wordY[j-1]) { - l[i][j] = l[i-1][j-1] + 1; - solution[i][j] = 'diagonal'; - - } else { - a = l[i-1][j]; - b = l[i][j-1]; - l[i][j] = (a > b) ? a : b; //max(a,b) - - solution[i][j] = (l[i][j] == l[i - 1][j]) ? 'top' : 'left'; - } - } - console.log(l[i].join()); - console.log(solution[i].join()); - } - - printSolution(solution, l, wordX, wordY, m, n); - - return l[m][n]; -} - -function printSolution(solution, l, wordX, wordY, m, n){ - - var a = m, b = n, i, j, - x = solution[a][b], - answer = ''; - - while (x !== '0') { - if (solution[a][b] === 'diagonal') { - answer = wordX[a - 1] + answer; - a--; - b--; - } else if (solution[a][b] === 'left') { - b--; - } else if (solution[a][b] === 'top') { - a--; - } - x = solution[a][b]; - } - - console.log('lcs: '+ answer); -} - -var wordX = 'acbaed', - wordY = 'abcadf'; - -console.log(lcs2(wordX, wordY)); \ No newline at end of file diff --git a/chapter11/09-LongestCommonSubsequenceRecursive.html b/chapter11/09-LongestCommonSubsequenceRecursive.html deleted file mode 100644 index 59573751..00000000 --- a/chapter11/09-LongestCommonSubsequenceRecursive.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/chapter11/09-LongestCommonSubsequenceRecursive.js b/chapter11/09-LongestCommonSubsequenceRecursive.js deleted file mode 100644 index 1c96fab1..00000000 --- a/chapter11/09-LongestCommonSubsequenceRecursive.js +++ /dev/null @@ -1,19 +0,0 @@ -function lcs(wordwordX, wordwordY, m, n) { - - if (m == 0 || n == 0){ - return 0; - } - - if (wordwordX[m-1] == wordY[n-1]){ - return 1 + lcs(wordX, wordY, m-1, n-1); - } else { - var a = lcs(wordX, wordY, m, n-1), - b = lcs(wordX, wordY, m-1, n); - return (a > b) ? a : b; - } -} - -var wordX = 'acbaed', - wordY = 'abcadf'; - -console.log(lcs(wordX, wordY, wordX.length, wordY.length)); \ No newline at end of file diff --git a/chapter11/10-MatrixChainMultiplicationDP.html b/chapter11/10-MatrixChainMultiplicationDP.html deleted file mode 100644 index 2108a100..00000000 --- a/chapter11/10-MatrixChainMultiplicationDP.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/chapter11/10-MatrixChainMultiplicationDP.js b/chapter11/10-MatrixChainMultiplicationDP.js deleted file mode 100644 index e335b3cc..00000000 --- a/chapter11/10-MatrixChainMultiplicationDP.js +++ /dev/null @@ -1,56 +0,0 @@ -function matrixChainOrder(p, n) { - - var i, j, k, l, q, - m = [], s=[]; - - for (i = 1; i <= n; i++){ - m[i] = []; - m[i][i] = 0; - - } - - for (i = 0; i <= n; i++){ //to help printing the optimal solution - s[i] = []; //auxiliary - for (j=0; j<=n; j++){ - s[i][j] = 0; - } - } - - for (l=2; l - - - - - - - - - \ No newline at end of file diff --git a/chapter11/11-MatrixChainMultiplicationRecursive.js b/chapter11/11-MatrixChainMultiplicationRecursive.js deleted file mode 100644 index 0b327524..00000000 --- a/chapter11/11-MatrixChainMultiplicationRecursive.js +++ /dev/null @@ -1,26 +0,0 @@ -function matrixChainOrder(p, i, j){ - - if(i == j) { - return 0; - } - - var k, count, - min = Number.MAX_SAFE_INTEGER; - - for (k = i; k - - - - - - - - - \ No newline at end of file diff --git a/chapter11/12-IntroFunctionalProgramming.js b/chapter11/12-IntroFunctionalProgramming.js deleted file mode 100644 index 47787255..00000000 --- a/chapter11/12-IntroFunctionalProgramming.js +++ /dev/null @@ -1,147 +0,0 @@ -console.log('Using imperative JS'); - -var printArray = function(array){ - for (var i=0; i array[i]){ - minValue = array[i]; - } - } - - return minValue; -}; - -console.log(findMinArray([8,6,4,5,9])); - -console.log('Finding the min value in an array - functional ES2015'); -const min_ = function(array){ - return Math.min(...array) -}; - -//simplifying using arrow functions -const min = arr => Math.min(...arr); - -console.log(min_([8,6,4,5,9])); -console.log(min([8,6,4,5,9])); - -//concat + reduce -console.log('merge arrays - imperative'); - -var mergeArrays_ = function(arrays){ - var count = arrays.length, - newArray = [], - k =0; - for (var i=0; i [].concat(...arrays); -console.log(mergeArrays([1, 2, 3], [4, 5], [6])); - -console.log('sum values of arrays - imperative'); -var sumValues = function(array){ - var total = array[0]; - for (var i=1; i arr.reduce((a, b) => a + b); - -console.log(sum([1, 2, 3, 4, 5])); - -//map -var daysOfWeek = [ - {name: 'Monday', value: 1}, - {name: 'Tuesday', value: 2}, - {name: 'Wednesday', value: 7} -]; - -var daysOfWeekValues_ = []; -for (var i = 0; i < daysOfWeek.length; i++) { - daysOfWeekValues_.push(daysOfWeek[i].value); -} - -//to -var daysOfWeekValues = daysOfWeek.map(function(day) { - return day.value; -}); -console.log(daysOfWeekValues); - - -//filter -var positiveNumbers_ = function(array){ - var positive = []; - for (var i = 0; i < array.length; i++) { - if (array[i] >= 0){ - positive.push(array[i]); - } - } - return positive; -} -console.log(positiveNumbers_([-1,1,2,-2])); - -var positiveNumbers = function(array){ - return array.filter(function(num){ - return num >= 0; - }) -}; -console.log(positiveNumbers([-1,1,2,-2])); \ No newline at end of file diff --git a/chapter12/05-BigONotation.html b/chapter12/05-BigONotation.html deleted file mode 100644 index 317a5bfb..00000000 --- a/chapter12/05-BigONotation.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/chapter12/05-BigONotation.js b/chapter12/05-BigONotation.js deleted file mode 100644 index a48bd6ab..00000000 --- a/chapter12/05-BigONotation.js +++ /dev/null @@ -1,65 +0,0 @@ -//*************** o(1) -function increment(num){ - console.log('cost for increment with input ' + num + ' is 1'); - return ++num; -} - -increment(1); -increment(2); - -//*************** o(n) - -function createNonSortedArray(size){ - var array = []; - - for (var i = size; i> 0; i--){ - array[i] = i; - } - - return array; -} - -function sequentialSearch(array, item){ - var cost = 0; - for (var i=0; i array[j+1]){ - swap(array, j, j+1); - } - } - } - console.log('cost for bubbleSort with input size ' + length + ' is ' + cost); -} - -var array1 = createNonSortedArray(99); -var array2 = createNonSortedArray(999); -var array3 = createNonSortedArray(9999); -bubbleSort(array1); -bubbleSort(array2); -bubbleSort(array3); \ No newline at end of file diff --git a/chapter12/bigOChart/chart.js b/chapter12/bigOChart/chart.js deleted file mode 100644 index 765794df..00000000 --- a/chapter12/bigOChart/chart.js +++ /dev/null @@ -1,37 +0,0 @@ -google.load('visualization', '1.0', {'packages':['corechart']}); -google.setOnLoadCallback(drawChart); - -function drawChart() { - - var data = new google.visualization.DataTable(); - data.addColumn('string', 'n'); - data.addColumn('number', 'O(1)'); - data.addColumn('number', 'O(log n)'); - data.addColumn('number', 'O(n)'); - data.addColumn('number', 'O(n log n)'); - data.addColumn('number', 'O(n^2)'); - data.addColumn('number', 'O(2^n)'); - - for (var i = 0; i <= 30; i++) { - data.addRow([i+'', 1, Math.log(i), i, Math.log(i)*i, Math.pow(i,2), Math.pow(2,i)]); - } - - var options = {'title':'Big O Notation Complexity Chart', - 'width':700, - 'height':600, - 'backgroundColor':{stroke:'#CCC',strokeWidth:1}, - 'curveType':'function', - 'hAxis':{ - title:'Elements (n)', - showTextEvery:5 - }, - 'vAxis':{ - title:'Operations', - viewWindowMode:'explicit', - viewWindow:{min:0,max:450} - } - }; - - var chart = new google.visualization.LineChart(document.getElementById('chart_div')); - chart.draw(data, options); -} \ No newline at end of file diff --git a/chapter12/bigOChart/index.html b/chapter12/bigOChart/index.html deleted file mode 100644 index 31118263..00000000 --- a/chapter12/bigOChart/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - -
- - \ No newline at end of file diff --git a/examples/PacktDataStructuresAlgorithms.min.js b/examples/PacktDataStructuresAlgorithms.min.js new file mode 100644 index 00000000..b06c53f5 --- /dev/null +++ b/examples/PacktDataStructuresAlgorithms.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("PacktDataStructuresAlgorithms",[],t):"object"==typeof exports?exports.PacktDataStructuresAlgorithms=t():e.PacktDataStructuresAlgorithms=t()}("undefined"!=typeof self?self:this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=14)}([function(e,t,n){var r,i,o;!function(n,u){i=[t],r=u,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e){"use strict";function t(e,t,n){var r=n(e,t);return r===l.LESS_THAN||r===l.EQUALS}function n(e,t,n){var r=n(e,t);return r===l.BIGGER_THAN||r===l.EQUALS}function r(e,t){return e===t?0:e0&&void 0!==arguments[0]?arguments[0]:n.defaultEquals;i(this,e),this.equalsFn=t,this.count=0,this.head=void 0}return o(e,[{key:"push",value:function(e){var t=new r.Node(e),n=void 0;if(null==this.head)this.head=t;else{for(n=this.head;null!=n.next;)n=n.next;n.next=t}this.count++}},{key:"getElementAt",value:function(e){if(e>=0&&e<=this.count){for(var t=this.head,n=0;n=0&&t<=this.count){var n=new r.Node(e);if(0===t){var i=this.head;n.next=i,this.head=n}else{var o=this.getElementAt(t-1);n.next=o.next,o.next=n}return this.count++,!0}return!1}},{key:"removeAt",value:function(e){if(e>=0&&e1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare;if(e&&e.length>0){for(var r=e[0],i=1;i1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare;if(e&&e.length>0){for(var r=e[0],i=1;i0)this.lowestCount--,this.items[this.lowestCount]=e;else{for(var t=this.count;t>0;t--)this.items[t]=this.items[t-1];this.count++,this.items[0]=e}}},{key:"addBack",value:function(e){this.items[this.count]=e,this.count++}},{key:"removeFront",value:function(){if(!this.isEmpty()){var e=this.items[this.lowestCount];return delete this.items[this.lowestCount],this.lowestCount++,e}}},{key:"removeBack",value:function(){if(!this.isEmpty()){this.count--;var e=this.items[this.count];return delete this.items[this.count],e}}},{key:"peekFront",value:function(){if(!this.isEmpty())return this.items[this.lowestCount]}},{key:"peekBack",value:function(){if(!this.isEmpty())return this.items[this.count-1]}},{key:"isEmpty",value:function(){return 0===this.size()}},{key:"clear",value:function(){this.items={},this.count=0,this.lowestCount=0}},{key:"size",value:function(){return this.count-this.lowestCount}},{key:"toString",value:function(){if(this.isEmpty())return"";for(var e=""+this.items[this.lowestCount],t=this.lowestCount+1;t0&&void 0!==arguments[0]?arguments[0]:n.defaultEquals;o(this,t);var r=u(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return r.tail=void 0,r}return a(t,e),l(t,[{key:"push",value:function(e){var t=new i.DoublyNode(e);null==this.head?(this.head=t,this.tail=t):(this.tail.next=t,t.prev=this.tail,this.tail=t),this.count++}},{key:"insert",value:function(e,t){if(t>=0&&t<=this.count){var n=new i.DoublyNode(e),r=this.head;if(0===t)null==this.head?(this.head=n,this.tail=n):(n.next=this.head,this.head.prev=n,this.head=n);else if(t===this.count)r=this.tail,r.next=n,n.prev=r,this.tail=n;else{var o=this.getElementAt(t-1);r=o.next,n.next=r,o.next=n,r.prev=n,n.prev=o}return this.count++,!0}return!1}},{key:"removeAt",value:function(e){if(e>=0&&e0&&void 0!==arguments[0]?arguments[0]:n.defaultToString;i(this,e),this.toStrFn=t,this.table={}}return o(e,[{key:"set",value:function(e,t){if(null!=e&&null!=t){var n=this.toStrFn(e);return this.table[n]=new r.ValuePair(e,t),!0}return!1}},{key:"get",value:function(e){var t=this.table[this.toStrFn(e)];return null==t?void 0:t.value}},{key:"hasKey",value:function(e){return null!=this.table[this.toStrFn(e)]}},{key:"remove",value:function(e){return!!this.hasKey(e)&&(delete this.table[this.toStrFn(e)],!0)}},{key:"values",value:function(){return this.keyValues().map(function(e){return e.value})}},{key:"keys",value:function(){return this.keyValues().map(function(e){return e.key})}},{key:"keyValues",value:function(){return Object.values(this.table)}},{key:"forEach",value:function(e){for(var t=this.keyValues(),n=0;n0&&void 0!==arguments[0]?arguments[0]:n.defaultCompare;i(this,e),this.compareFn=t,this.root=null}return o(e,[{key:"insert",value:function(e){null==this.root?this.root=new r.Node(e):this.insertNode(this.root,e)}},{key:"insertNode",value:function(e,t){this.compareFn(t,e.key)===n.Compare.LESS_THAN?null==e.left?e.left=new r.Node(t):this.insertNode(e.left,t):null==e.right?e.right=new r.Node(t):this.insertNode(e.right,t)}},{key:"getRoot",value:function(){return this.root}},{key:"search",value:function(e){return this.searchNode(this.root,e)}},{key:"searchNode",value:function(e,t){return null!=e&&(this.compareFn(t,e.key)===n.Compare.LESS_THAN?this.searchNode(e.left,t):this.compareFn(t,e.key)!==n.Compare.BIGGER_THAN||this.searchNode(e.right,t))}},{key:"inOrderTraverse",value:function(e){this.inOrderTraverseNode(this.root,e)}},{key:"inOrderTraverseNode",value:function(e,t){null!=e&&(this.inOrderTraverseNode(e.left,t),t(e.key),this.inOrderTraverseNode(e.right,t))}},{key:"preOrderTraverse",value:function(e){this.preOrderTraverseNode(this.root,e)}},{key:"preOrderTraverseNode",value:function(e,t){null!=e&&(t(e.key),this.preOrderTraverseNode(e.left,t),this.preOrderTraverseNode(e.right,t))}},{key:"postOrderTraverse",value:function(e){this.postOrderTraverseNode(this.root,e)}},{key:"postOrderTraverseNode",value:function(e,t){null!=e&&(this.postOrderTraverseNode(e.left,t),this.postOrderTraverseNode(e.right,t),t(e.key))}},{key:"min",value:function(){return this.minNode(this.root)}},{key:"minNode",value:function(e){for(var t=e;null!=t&&null!=t.left;)t=t.left;return t}},{key:"max",value:function(){return this.maxNode(this.root)}},{key:"maxNode",value:function(e){for(var t=e;null!=t&&null!=t.right;)t=t.right;return t}},{key:"remove",value:function(e){this.root=this.removeNode(this.root,e)}},{key:"removeNode",value:function(e,t){if(null==e)return null;if(this.compareFn(t,e.key)===n.Compare.LESS_THAN)return e.left=this.removeNode(e.left,t),e;if(this.compareFn(t,e.key)===n.Compare.BIGGER_THAN)return e.right=this.removeNode(e.right,t),e;if(null==e.left&&null==e.right)return e=null;if(null==e.left)return e=e.right;if(null==e.right)return e=e.left;var r=this.minNode(e.right);return e.key=r.key,e.right=this.removeNode(e.right,r.key),e}}]),e}();t.default=u,e.exports=t.default})},function(e,t,n){var r,i,o;!function(n,u){i=[t],r=u,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e){"use strict";function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare,r=e.length,i=void 0,o=1;o0&&n(e[u-1],i)===t.Compare.BIGGER_THAN;)e[u]=e[u-1],u--;e[u]=i}return e}})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(0)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e,n,r,i){for(var o=e[Math.floor((r+n)/2)],u=n,a=r;u<=a;){for(;i(e[u],o)===t.Compare.LESS_THAN;)u++;for(;i(e[a],o)===t.Compare.BIGGER_THAN;)a--;u<=a&&((0,t.swap)(e,u,a),u++,a--)}return u}function r(e,t,i,o){var u=void 0;return e.length>1&&(u=n(e,t,i,o),t1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare;return r(e,0,e.length-1,n)}Object.defineProperty(e,"__esModule",{value:!0}),e.quickSort=i})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(15),n(3),n(16),n(17),n(18),n(4),n(7),n(19),n(20),n(1),n(8),n(21),n(22),n(23),n(24),n(9),n(25),n(26),n(27),n(28),n(30),n(31),n(10),n(32),n(53),n(33),n(34),n(35),n(36),n(37),n(38),n(39),n(40),n(41),n(42),n(43),n(44),n(45),n(12),n(46),n(13),n(47),n(48),n(49),n(50),n(51),n(52),n(6),n(0)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n,r,i,o,u,a,f,l,s,c,h,p,v,d,y,b,g,m,k,_,O,E,S,j,P,w,x,C,T,N,L,A,M,H,F,B,I,D,R,G,z,q,V,U,Y,W,K,X){"use strict";function Q(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(e,"__esModule",{value:!0}),e.findMinValue=e.findMaxValue=e.sequentialSearch=e.interpolationSearch=e.binarySearch=e.shellSort=e.selectionSort=e.radixSort=e.quickSort=e.mergeSort=e.insertionSort=e.countingSort=e.bucketSort=e.modifiedBubbleSort=e.bubbleSort=e.shuffle=e.kruskal=e.prim=e.floydWarshall=e.dijkstra=e.DFS=e.depthFirstSearch=e.BFS=e.breadthFirstSearch=e.Graph=e.heapSort=e.MaxHeap=e.MinHeap=e.AVLTree=e.BinarySearchTree=e.fibonacciMemoization=e.fibonacciIterative=e.fibonacci=e.factorial=e.factorialIterative=e.HashTableLinearProbingLazy=e.HashTableLinearProbing=e.HashTableSeparateChaining=e.HashTable=e.Dictionary=e.Set=e.StackLinkedList=e.SortedLinkedList=e.CircularLinkedList=e.DoublyLinkedList=e.LinkedList=e.palindromeChecker=e.hotPotato=e.Deque=e.Queue=e.parenthesesChecker=e.decimalToBinary=e.baseConverter=e.hanoiStack=e.hanoi=e.Stack=e.StackArray=e.util=void 0,Object.defineProperty(e,"StackArray",{enumerable:!0,get:function(){return Q(t).default}}),Object.defineProperty(e,"Stack",{enumerable:!0,get:function(){return Q(n).default}}),Object.defineProperty(e,"hanoi",{enumerable:!0,get:function(){return Q(r).default}}),Object.defineProperty(e,"hanoiStack",{enumerable:!0,get:function(){return Q(r).default}}),Object.defineProperty(e,"baseConverter",{enumerable:!0,get:function(){return Q(i).default}}),Object.defineProperty(e,"decimalToBinary",{enumerable:!0,get:function(){return Q(i).default}}),Object.defineProperty(e,"parenthesesChecker",{enumerable:!0,get:function(){return Q(o).default}}),Object.defineProperty(e,"Queue",{enumerable:!0,get:function(){return Q(u).default}}),Object.defineProperty(e,"Deque",{enumerable:!0,get:function(){return Q(a).default}}),Object.defineProperty(e,"hotPotato",{enumerable:!0,get:function(){return Q(f).default}}),Object.defineProperty(e,"palindromeChecker",{enumerable:!0,get:function(){return Q(l).default}}),Object.defineProperty(e,"LinkedList",{enumerable:!0,get:function(){return Q(s).default}}),Object.defineProperty(e,"DoublyLinkedList",{enumerable:!0,get:function(){return Q(c).default}}),Object.defineProperty(e,"CircularLinkedList",{enumerable:!0,get:function(){return Q(h).default}}),Object.defineProperty(e,"SortedLinkedList",{enumerable:!0,get:function(){return Q(p).default}}),Object.defineProperty(e,"StackLinkedList",{enumerable:!0,get:function(){return Q(v).default}}),Object.defineProperty(e,"Set",{enumerable:!0,get:function(){return Q(d).default}}),Object.defineProperty(e,"Dictionary",{enumerable:!0,get:function(){return Q(y).default}}),Object.defineProperty(e,"HashTable",{enumerable:!0,get:function(){return Q(b).default}}),Object.defineProperty(e,"HashTableSeparateChaining",{enumerable:!0,get:function(){return Q(g).default}}),Object.defineProperty(e,"HashTableLinearProbing",{enumerable:!0,get:function(){return Q(m).default}}),Object.defineProperty(e,"HashTableLinearProbingLazy",{enumerable:!0,get:function(){return Q(k).default}}),Object.defineProperty(e,"factorialIterative",{enumerable:!0,get:function(){return Q(_).default}}),Object.defineProperty(e,"factorial",{enumerable:!0,get:function(){return Q(_).default}}),Object.defineProperty(e,"fibonacci",{enumerable:!0,get:function(){return Q(O).default}}),Object.defineProperty(e,"fibonacciIterative",{enumerable:!0,get:function(){return Q(O).default}}),Object.defineProperty(e,"fibonacciMemoization",{enumerable:!0,get:function(){return Q(O).default}}),Object.defineProperty(e,"BinarySearchTree",{enumerable:!0,get:function(){return Q(E).default}}),Object.defineProperty(e,"AVLTree",{enumerable:!0,get:function(){return Q(S).default}}),Object.defineProperty(e,"MinHeap",{enumerable:!0,get:function(){return j.MinHeap}}),Object.defineProperty(e,"MaxHeap",{enumerable:!0,get:function(){return j.MaxHeap}}),Object.defineProperty(e,"heapSort",{enumerable:!0,get:function(){return Q(P).default}}),Object.defineProperty(e,"Graph",{enumerable:!0,get:function(){return Q(w).default}}),Object.defineProperty(e,"breadthFirstSearch",{enumerable:!0,get:function(){return x.breadthFirstSearch}}),Object.defineProperty(e,"BFS",{enumerable:!0,get:function(){return x.BFS}}),Object.defineProperty(e,"depthFirstSearch",{enumerable:!0,get:function(){return C.depthFirstSearch}}),Object.defineProperty(e,"DFS",{enumerable:!0,get:function(){return C.DFS}}),Object.defineProperty(e,"dijkstra",{enumerable:!0,get:function(){return T.dijkstra}}),Object.defineProperty(e,"floydWarshall",{enumerable:!0,get:function(){return N.floydWarshall}}),Object.defineProperty(e,"prim",{enumerable:!0,get:function(){return L.prim}}),Object.defineProperty(e,"kruskal",{enumerable:!0,get:function(){return A.kruskal}}),Object.defineProperty(e,"shuffle",{enumerable:!0,get:function(){return M.shuffle}}),Object.defineProperty(e,"bubbleSort",{enumerable:!0,get:function(){return H.bubbleSort}}),Object.defineProperty(e,"modifiedBubbleSort",{enumerable:!0,get:function(){return F.modifiedBubbleSort}}),Object.defineProperty(e,"bucketSort",{enumerable:!0,get:function(){return B.bucketSort}}),Object.defineProperty(e,"countingSort",{enumerable:!0,get:function(){return I.countingSort}}),Object.defineProperty(e,"insertionSort",{enumerable:!0,get:function(){return D.insertionSort}}),Object.defineProperty(e,"mergeSort",{enumerable:!0,get:function(){return R.mergeSort}}),Object.defineProperty(e,"quickSort",{enumerable:!0,get:function(){return G.quickSort}}),Object.defineProperty(e,"radixSort",{enumerable:!0,get:function(){return z.radixSort}}),Object.defineProperty(e,"selectionSort",{enumerable:!0,get:function(){return q.selectionSort}}),Object.defineProperty(e,"shellSort",{enumerable:!0,get:function(){return V.shellSort}}),Object.defineProperty(e,"binarySearch",{enumerable:!0,get:function(){return U.binarySearch}}),Object.defineProperty(e,"interpolationSearch",{enumerable:!0,get:function(){return Y.interpolationSearch}}),Object.defineProperty(e,"sequentialSearch",{enumerable:!0,get:function(){return W.sequentialSearch}}),Object.defineProperty(e,"findMaxValue",{enumerable:!0,get:function(){return K.findMaxValue}}),Object.defineProperty(e,"findMinValue",{enumerable:!0,get:function(){return K.findMinValue}});var J=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}(X);e.util=J})},function(e,t,n){var r,i,o;!function(n,u){i=[e,t],r=u,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n7&&void 0!==arguments[7]?arguments[7]:[];if(e<=0)return f;if(1===e){i.push(t.pop());var l={};l[o]=t.toString(),l[u]=r.toString(),l[a]=i.toString(),f.push(l)}else{n(e-1,t,i,r,o,a,u,f),i.push(t.pop());var s={};s[o]=t.toString(),s[u]=r.toString(),s[a]=i.toString(),f.push(s),n(e-1,r,t,i,u,o,a,f)}return f}function r(e){for(var t=new o.default,r=new o.default,i=new o.default,u=e;u>0;u--)t.push(u);return n(e,t,i,r,"source","helper","dest")}function i(e,t,n,r){var o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[];return e<=0?o:(1===e?o.push([t,r]):(i(e-1,t,r,n,o),o.push([t,r]),i(e-1,n,t,r,o)),o)}Object.defineProperty(e,"__esModule",{value:!0}),e.hanoiStack=r,e.hanoi=i;var o=function(e){return e&&e.__esModule?e:{default:e}}(t)})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(3)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e){for(var t=new i.default,n=e,r=void 0,o="";n>0;)r=Math.floor(n%2),t.push(r),n=Math.floor(n/2);for(;!t.isEmpty();)o+=t.pop().toString();return o}function r(e,t){var n=new i.default,r=e,o=void 0,u="";if(!(t>=2&&t<=36))return"";for(;r>0;)o=Math.floor(r%t),n.push(o),r=Math.floor(r/t);for(;!n.isEmpty();)u+="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[n.pop()];return u}Object.defineProperty(e,"__esModule",{value:!0}),e.decimalToBinary=n,e.baseConverter=r;var i=function(e){return e&&e.__esModule?e:{default:e}}(t)})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(3)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e){for(var t=new r.default,n="([{",i=")]}",o=!0,u=0,a=void 0,f=void 0;u=0?t.push(a):t.isEmpty()?o=!1:(f=t.pop(),n.indexOf(f)!==i.indexOf(a)&&(o=!1)),u++;return!(!o||!t.isEmpty())}Object.defineProperty(e,"__esModule",{value:!0}),e.parenthesesChecker=n;var r=function(e){return e&&e.__esModule?e:{default:e}}(t)})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(4)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e,t){for(var n=new r.default,i=[],o=0;o1;){for(var u=0;u1&&i;)o=t.removeFront(),u=t.removeBack(),o!==u&&(i=!1);return i}Object.defineProperty(e,"__esModule",{value:!0}),e.palindromeChecker=n;var r=function(e){return e&&e.__esModule?e:{default:e}}(t)})},function(e,t,n){var r,i,o;!function(u,a){i=[e,t,n(0),n(1),n(5)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n,r,i){"use strict";function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var f=function(e){return e&&e.__esModule?e:{default:e}}(r),l=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:n.defaultEquals;return o(this,t),u(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e))}return a(t,e),l(t,[{key:"push",value:function(e){var t=new i.Node(e),n=void 0;null==this.head?this.head=t:(n=this.getElementAt(this.size()-1),n.next=t),t.next=this.head,this.count++}},{key:"insert",value:function(e,t){if(t>=0&&t<=this.count){var n=new i.Node(e),r=this.head;if(0===t)null==this.head?(this.head=n,n.next=this.head):(n.next=r,r=this.getElementAt(this.size()),this.head=n,r.next=this.head);else{var o=this.getElementAt(t-1);n.next=o.next,o.next=n}return this.count++,!0}return!1}},{key:"removeAt",value:function(e){if(e>=0&&e0&&void 0!==arguments[0]?arguments[0]:n.defaultEquals,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:n.defaultCompare;i(this,t);var u=o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return u.equalsFn=e,u.compareFn=r,u}return u(t,e),f(t,[{key:"push",value:function(e){if(this.isEmpty())l(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"push",this).call(this,e);else{var n=this.getIndexNextSortedElement(e);l(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"insert",this).call(this,e,n)}}},{key:"insert",value:function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;if(this.isEmpty())return l(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"insert",this).call(this,e,0===n?n:0);var r=this.getIndexNextSortedElement(e);return l(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"insert",this).call(this,e,r)}},{key:"getIndexNextSortedElement",value:function(e){for(var t=this.head,r=0;r0&&(o=i,u=r),u.forEach(function(e){o.includes(e)&&n.add(e)}),n}},{key:"difference",value:function(t){var n=new e;return this.values().forEach(function(e){t.has(e)||n.add(e)}),n}},{key:"isSubsetOf",value:function(e){if(this.size()>e.size())return!1;var t=!0;return this.values().every(function(n){return!!e.has(n)||(t=!1,!1)}),t}},{key:"isEmpty",value:function(){return 0===this.size()}},{key:"size",value:function(){return Object.keys(this.items).length}},{key:"clear",value:function(){this.items={}}},{key:"toString",value:function(){if(this.isEmpty())return"";for(var e=this.values(),t=""+e[0],n=1;n0&&void 0!==arguments[0]?arguments[0]:n.defaultToString;i(this,e),this.toStrFn=t,this.table={}}return o(e,[{key:"loseloseHashCode",value:function(e){if("number"==typeof e)return e;for(var t=this.toStrFn(e),n=0,r=0;r "+this.table[e[0]].toString()+"}",n=1;n "+this.table[e[n]].toString()+"}";return t}}]),e}();t.default=u,e.exports=t.default})},function(e,t,n){var r,i,o;!function(u,a){i=[e,t,n(0),n(1),n(2)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n,r,i){"use strict";function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var u=function(e){return e&&e.__esModule?e:{default:e}}(r),a=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:n.defaultToString;o(this,e),this.toStrFn=t,this.table={}}return a(e,[{key:"loseloseHashCode",value:function(e){if("number"==typeof e)return e;for(var t=this.toStrFn(e),n=0,r=0;r "+this.table[e[0]].toString()+"}",n=1;n "+this.table[e[n]].toString()+"}";return t}}]),e}();t.default=f,e.exports=t.default})},function(e,t,n){var r,i,o;!function(u,a){i=[e,t,n(0),n(2)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n,r){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:n.defaultToString;i(this,e),this.toStrFn=t,this.table={}}return o(e,[{key:"loseloseHashCode",value:function(e){if("number"==typeof e)return e;for(var t=this.toStrFn(e),n=0,r=0;r "+this.table[e[0]].toString()+"}",n=1;n "+this.table[e[n]].toString()+"}";return t}}]),e}();t.default=u,e.exports=t.default})},function(e,t,n){var r,i,o;!function(u,a){i=[e,t,n(0),n(29)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n,r){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:n.defaultToString;i(this,e),this.toStrFn=t,this.table={}}return o(e,[{key:"loseloseHashCode",value:function(e){if("number"==typeof e)return e;for(var t=this.toStrFn(e),n=0,r=0;r "+this.table[e[0]].toString()+"}",n=1;n "+this.table[e[n]].toString()+"}";return t}}]),e}();t.default=u,e.exports=t.default})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(2)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(e,"__esModule",{value:!0}),e.ValuePairLazy=void 0;e.ValuePairLazy=function(e){function t(e,i){var o=arguments.length>2&&void 0!==arguments[2]&&arguments[2];n(this,t);var u=r(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,i));return u.key=e,u.value=i,u.isDeleted=o,u}return i(t,e),t}(t.ValuePair)})},function(e,t,n){var r,i,o;!function(n,u){i=[t],r=u,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e){"use strict";function t(e){if(!(e<0)){for(var t=1,n=e;n>1;n--)t*=n;return t}}function n(e){if(!(e<0))return 1===e||0===e?1:e*n(e-1)}Object.defineProperty(e,"__esModule",{value:!0}),e.factorialIterative=t,e.factorial=n})},function(e,t,n){var r,i,o;!function(n,u){i=[t],r=u,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e){"use strict";function t(e){return e<1?0:e<=2?1:t(e-1)+t(e-2)}function n(e){if(e<1)return 0;for(var t=0,n=1,r=e,i=2;i<=e;i++)r=n+t,t=n,n=r;return r}function r(e){if(e<1)return 0;var t=[0,1];return function e(n){return null!=t[n]?t[n]:(t[n]=e(n-1)+e(n-2),t[n]=e(n-1)+e(n-2))}(e)}Object.defineProperty(e,"__esModule",{value:!0}),e.fibonacci=t,e.fibonacciIterative=n,e.fibonacciMemoization=r})},function(e,t,n){var r,i,o;!function(u,a){i=[e,t,n(0),n(10),n(11)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n,r,i){"use strict";function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var f=function(e){return e&&e.__esModule?e:{default:e}}(r),l=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:n.defaultCompare;o(this,t);var r=u(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return r.compareFn=e,r.root=null,r}return a(t,e),l(t,[{key:"getNodeHeight",value:function(e){return null==e?-1:Math.max(this.getNodeHeight(e.left),this.getNodeHeight(e.right))+1}},{key:"rotationLL",value:function(e){var t=e.left;return e.left=t.right,t.right=e,t}},{key:"rotationRR",value:function(e){var t=e.right;return e.right=t.left,t.left=e,t}},{key:"rotationLR",value:function(e){return e.left=this.rotationRR(e.left),this.rotationLL(e)}},{key:"rotationRL",value:function(e){return e.right=this.rotationLL(e.right),this.rotationRR(e)}},{key:"getBalanceFactor",value:function(e){switch(this.getNodeHeight(e.left)-this.getNodeHeight(e.right)){case-2:return c.UNBALANCED_RIGHT;case-1:return c.SLIGHTLY_UNBALANCED_RIGHT;case 1:return c.SLIGHTLY_UNBALANCED_LEFT;case 2:return c.UNBALANCED_LEFT;default:return c.BALANCED}}},{key:"insert",value:function(e){this.root=this.insertNode(this.root,e)}},{key:"insertNode",value:function(e,t){if(null==e)return new i.Node(t);if(this.compareFn(t,e.key)===n.Compare.LESS_THAN)e.left=this.insertNode(e.left,t);else{if(this.compareFn(t,e.key)!==n.Compare.BIGGER_THAN)return e;e.right=this.insertNode(e.right,t)}var r=this.getBalanceFactor(e);if(r===c.UNBALANCED_LEFT){if(this.compareFn(t,e.left.key)!==n.Compare.LESS_THAN)return this.rotationLR(e);e=this.rotationLL(e)}if(r===c.UNBALANCED_RIGHT){if(this.compareFn(t,e.right.key)!==n.Compare.BIGGER_THAN)return this.rotationRL(e);e=this.rotationRR(e)}return e}},{key:"removeNode",value:function(e,n){if(null==(e=s(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"removeNode",this).call(this,e,n)))return e;var r=this.getBalanceFactor(e);if(r===c.UNBALANCED_LEFT){if(this.getBalanceFactor(e.left)===c.BALANCED||this.getBalanceFactor(e.left)===c.SLIGHTLY_UNBALANCED_LEFT)return this.rotationLL(e);if(this.getBalanceFactor(e.left)===c.SLIGHTLY_UNBALANCED_RIGHT)return this.rotationLR(e.left)}if(r===c.UNBALANCED_RIGHT){if(this.getBalanceFactor(e.right)===c.BALANCED||this.getBalanceFactor(e.right)===c.SLIGHTLY_UNBALANCED_RIGHT)return this.rotationRR(e);if(this.getBalanceFactor(e.right)===c.SLIGHTLY_UNBALANCED_LEFT)return this.rotationRL(e.right)}return e}}]),t}(f.default);t.default=h,e.exports=t.default})},function(e,t,n){var r,i,o;!function(u,a){i=[e,t,n(0)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n){"use strict";function r(e,t,i,o){var u=t,a=2*t+1,f=2*t+2;a0&&(u=a),f0&&(u=f),u!==t&&((0,n.swap)(e,t,u),r(e,u,i,o))}function i(e,t){for(var n=Math.floor(e.length/2);n>=0;n-=1)r(e,n,e.length,t);return e}function o(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:n.defaultCompare,o=e.length;for(i(e,t);o>1;)(0,n.swap)(e,0,--o),r(e,0,o,t);return e}Object.defineProperty(t,"__esModule",{value:!0}),t.default=o,e.exports=t.default})},function(e,t,n){var r,i,o;!function(u,a){i=[e,t,n(9)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var i=function(e){return e&&e.__esModule?e:{default:e}}(n),o=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]&&arguments[0];r(this,e),this.isDirected=t,this.vertices=[],this.adjList=new i.default}return o(e,[{key:"addVertex",value:function(e){this.vertices.includes(e)||(this.vertices.push(e),this.adjList.set(e,[]))}},{key:"addEdge",value:function(e,t){this.adjList.get(e)||this.addVertex(e),this.adjList.get(t)||this.addVertex(t),this.adjList.get(e).push(t),this.isDirected||this.adjList.get(t).push(e)}},{key:"getVertices",value:function(){return this.vertices}},{key:"getAdjList",value:function(){return this.adjList}},{key:"toString",value:function(){for(var e="",t=0;t ";for(var n=this.adjList.get(this.vertices[t]),r=0;r1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare,r=e.length,i=0;i1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare,r=e.length,i=0;ir&&(r=e[i]);for(var o=Math.floor((r-n)/t)+1,u=[],a=0;a1&&void 0!==arguments[1]?arguments[1]:5;return e.length<2?e:i(r(e,t))}Object.defineProperty(e,"__esModule",{value:!0}),e.bucketSort=o})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(6)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e){if(e.length<2)return e;var n=(0,t.findMaxValue)(e),r=0,i=new Array(n+1);return e.forEach(function(e){i[e]||(i[e]=0),i[e]++}),i.forEach(function(t,n){for(;t>0;)e[r++]=n,t--}),e}Object.defineProperty(e,"__esModule",{value:!0}),e.countingSort=n})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(0)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e,n,r){for(var i=0,o=0,u=[];i1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare;if(e.length>1){var o=e,u=o.length,a=Math.floor(u/2);e=n(r(e.slice(0,a),i),r(e.slice(a,u),i),i)}return e}Object.defineProperty(e,"__esModule",{value:!0}),e.mergeSort=r})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(6)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:10;if(e.length<2)return e;for(var r=(0,t.findMinValue)(e),o=(0,t.findMaxValue)(e),u=1;(o-r)/u>=1;)e=i(e,n,u,r),u*=n;return e}Object.defineProperty(e,"__esModule",{value:!0}),e.radixSort=n;var r=function(e,t,n,r){return Math.floor((e-t)/n%r)},i=function(e,t,n,i){for(var o=void 0,u=[],a=[],f=0;f=0;c--)o=r(e[c],i,n,t),a[--u[o]]=e[c];for(var h=0;h1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare,r=e.length,i=void 0,o=0;o1&&void 0!==arguments[1]?arguments[1]:t.defaultCompare,r=e.length/2;r>0;){for(var i=r;i=r&&n(e[o-r],u)===t.Compare.BIGGER_THAN;)e[o]=e[o-r],o-=r;e[o]=u}r=2===r?1:Math.floor(5*r/11)}return e}Object.defineProperty(e,"__esModule",{value:!0}),e.shellSort=n})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(0),n(13)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t,n){"use strict";function r(e,r){for(var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:t.defaultCompare,o=(0,n.quickSort)(e),u=0,a=o.length-1;u<=a;){var f=Math.floor((u+a)/2),l=o[f];if(i(l,r)===t.Compare.LESS_THAN)u=f+1;else{if(i(l,r)!==t.Compare.BIGGER_THAN)return f;a=f-1}}return t.DOES_NOT_EXIST}Object.defineProperty(e,"__esModule",{value:!0}),e.binarySearch=r})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(0)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e,n){for(var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:t.defaultCompare,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:t.defaultEquals,o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:t.defaultDiff,u=e.length,a=0,f=u-1,l=-1,s=-1;a<=f&&(0,t.biggerEquals)(n,e[a],r)&&(0,t.lesserEquals)(n,e[f],r);){if(s=o(n,e[a])/o(e[f],e[a]),l=a+Math.floor((f-a)*s),i(e[l],n))return l;r(e[l],n)===t.Compare.LESS_THAN?a=l+1:f=l-1}return t.DOES_NOT_EXIST}Object.defineProperty(e,"__esModule",{value:!0}),e.interpolationSearch=n})},function(e,t,n){var r,i,o;!function(u,a){i=[t,n(0)],r=a,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e,n){for(var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:t.defaultEquals,i=0;i0&&void 0!==arguments[0]?arguments[0]:t.defaultCompare;i(this,e),this.compareFn=n,this.heap=[]}return o(e,[{key:"getLeftIndex",value:function(e){return 2*e+1}},{key:"getRightIndex",value:function(e){return 2*e+2}},{key:"getParentIndex",value:function(e){if(0!==e)return Math.floor((e-1)/2)}},{key:"size",value:function(){return this.heap.length}},{key:"isEmpty",value:function(){return this.size()<=0}},{key:"clear",value:function(){this.heap=[]}},{key:"find",value:function(){return this.isEmpty()?void 0:this.heap[0]}},{key:"insert",value:function(e){if(null!=e){var t=this.heap.length;return this.heap.push(e),this.siftUp(t),!0}return!1}},{key:"siftDown",value:function(e){var n=e,r=this.getLeftIndex(e),i=this.getRightIndex(e),o=this.size();rt.Compare.BIGGER_THAN&&(n=r),it.Compare.BIGGER_THAN&&(n=i),e!==n&&((0,t.swap)(this.heap,e,n),this.siftDown(n))}},{key:"siftUp",value:function(e){for(var n=this.getParentIndex(e);e>0&&this.compareFn(this.heap[n],this.heap[e])>t.Compare.BIGGER_THAN;)(0,t.swap)(this.heap,n,e),e=n,n=this.getParentIndex(e)}},{key:"extract",value:function(){if(!this.isEmpty()){if(1===this.size())return this.heap.shift();var e=this.heap.shift();return this.siftDown(0),e}}},{key:"heapify",value:function(e){e&&(this.heap=e,this.heap.unshift(null));for(var t=this.size()-1;t>0;t--)this.siftDown(t)}}]),e}();e.MaxHeap=function(e){function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:t.defaultCompare;i(this,o);var r=n(this,(o.__proto__||Object.getPrototypeOf(o)).call(this,e));return r.compareFn=e,r.compareFn=(0,t.reverseCompare)(e),r}return r(o,e),o}(u)})}])}); \ No newline at end of file diff --git a/chapter01/01-HelloWorld.html b/examples/chapter01/01-HelloWorld.html similarity index 100% rename from chapter01/01-HelloWorld.html rename to examples/chapter01/01-HelloWorld.html diff --git a/examples/chapter01/01-HelloWorld.js b/examples/chapter01/01-HelloWorld.js new file mode 100644 index 00000000..b67b0e2c --- /dev/null +++ b/examples/chapter01/01-HelloWorld.js @@ -0,0 +1,10 @@ +// @ts-check +/* eslint-disable */ + +function output(t) { + document.write('

' + t + '

'); +} + +alert('Hello, World!'); +console.log('Hello, World!'); +output('Hello, World!'); diff --git a/chapter01/02-Variables.html b/examples/chapter01/02-Variables.html similarity index 100% rename from chapter01/02-Variables.html rename to examples/chapter01/02-Variables.html diff --git a/examples/chapter01/02-Variables.js b/examples/chapter01/02-Variables.js new file mode 100644 index 00000000..4d1cf7d5 --- /dev/null +++ b/examples/chapter01/02-Variables.js @@ -0,0 +1,40 @@ +// @ts-check +/* eslint-disable */ + +var num = 1; // {1} +num = 3; // {2} + +var price = 1.5; // {3} +var myName = 'Packt'; // {4} +var trueValue = true; // {5} +var nullVar = null; // {6} +var und; // {7} + +console.log('num: ' + num); +console.log('myName: ' + myName); +console.log('trueValue: ' + trueValue); +console.log('price: ' + price); +console.log('nullVar: ' + nullVar); +console.log('und: ' + und); + +// ******* Variable Scope + +var myVariable = 'global'; +myOtherVariable = 'global'; + +function myFunction() { + var myVariable = 'local'; + return myVariable; +} + +function myOtherFunction() { + myOtherVariable = 'local'; + return myOtherVariable; +} + +console.log(myVariable); //{1} +console.log(myFunction()); //{2} + +console.log(myOtherVariable); //{3} +console.log(myOtherFunction()); //{4} +console.log(myOtherVariable); //{5} diff --git a/chapter01/03-Operators.html b/examples/chapter01/03-Operators.html similarity index 100% rename from chapter01/03-Operators.html rename to examples/chapter01/03-Operators.html diff --git a/chapter01/03-Operators.js b/examples/chapter01/03-Operators.js similarity index 58% rename from chapter01/03-Operators.js rename to examples/chapter01/03-Operators.js index d1044b92..7895b17b 100755 --- a/chapter01/03-Operators.js +++ b/examples/chapter01/03-Operators.js @@ -1,28 +1,25 @@ -/* Arithmetic operators */ -var num = 0; +// @ts-check +/* eslint-disable */ +/* Arithmetic operators */ +var num = 0; // {1} console.log('num value is ' + num); num = num + 2; - console.log('New num value is ' + num); num = num * 3; - console.log('New num value is ' + num); num = num / 2; - console.log('New num value is ' + num); num++; - num--; console.log('New num value is ' + num); -console.log('num mod 2 value is ' + (num % 2)); - +console.log('num mod 2 value is ' + num % 2); /* Assignment operators */ num += 1; @@ -45,24 +42,24 @@ console.log('num <= 1 : ' + (num <= 1)); /* Logical operators */ console.log('true && false : ' + (true && false)); console.log('true || false : ' + (true || false)); -console.log('!true : ' + (!true)); +console.log('!true : ' + !true); /* Bitwise operators */ -console.log('5 & 1:', (5 & 1)); //same as 0101 & 0001 (result 0001 / 1) -console.log('5 | 1:', (5 | 1)); //same as 0101 | 0001 (result 0101 / 5) -console.log('~ 5:', (~5)); //same as ~0101 (result 1010 / 10) -console.log('5 ^ 1:', (5 ^ 1)); //same as 0101 ^ 0001 (result 0100 / 4) -console.log('5 << 1:', (5 << 1)); //same as 0101 << 1 (result 1010 / 10) -console.log('5 >> 1:', (5 >> 1)); //same as 0101 >> 1 (result 0010 / 2) +console.log('5 & 1:', 5 & 1); // same as 0101 & 0001 (result 0001 / 1) +console.log('5 | 1:', 5 | 1); // same as 0101 | 0001 (result 0101 / 5) +console.log('~ 5:', ~5); // same as ~0101 (result 1010 / 10) +console.log('5 ^ 1:', 5 ^ 1); // same as 0101 ^ 0001 (result 0100 / 4) +console.log('5 << 1:', 5 << 1); // same as 0101 << 1 (result 1010 / 10) +console.log('5 >> 1:', 5 >> 1); // same as 0101 >> 1 (result 0010 / 2) /* typeOf */ console.log('typeof num:', typeof num); console.log('typeof Packt:', typeof 'Packt'); console.log('typeof true:', typeof true); -console.log('typeof [1,2,3]:', typeof [1,2,3]); -console.log('typeof {name:John}:', typeof {name:'John'}); +console.log('typeof [1,2,3]:', typeof [1, 2, 3]); +console.log('typeof {name:John}:', typeof { name: 'John' }); /* delete */ -var myObj = {name: 'John', age: 21}; +var myObj = { name: 'John', age: 21 }; delete myObj.age; -console.log(myObj); //Object {name: "John"} \ No newline at end of file +console.log(myObj); // Object {name: "John"} diff --git a/chapter01/04-TruthyFalsy.html b/examples/chapter01/04-TruthyFalsy.html similarity index 100% rename from chapter01/04-TruthyFalsy.html rename to examples/chapter01/04-TruthyFalsy.html diff --git a/examples/chapter01/04-TruthyFalsy.js b/examples/chapter01/04-TruthyFalsy.js new file mode 100644 index 00000000..c64fa3e1 --- /dev/null +++ b/examples/chapter01/04-TruthyFalsy.js @@ -0,0 +1,27 @@ +// @ts-check +/* eslint-disable */ + +function testTruthy(val) { + return val ? console.log('truthy') : console.log('falsy'); +} + +testTruthy(true); // true +testTruthy(false); // false +testTruthy(new Boolean(false)); // true (object is always true) + +testTruthy(''); // false +testTruthy('a'); // true +testTruthy('Packt'); // true +testTruthy(new String('')); // true (object is always true) + +testTruthy(1); // true +testTruthy(-1); // true +testTruthy(NaN); // false +testTruthy(new Number(NaN)); // true (object is always true) + +testTruthy({}); // true (object is always true) + +var obj = { name: 'John' }; +testTruthy(obj); // true +testTruthy(obj.name); // true +testTruthy(obj.age); // age (property does not exist) diff --git a/chapter01/05-EqualsOperators.html b/examples/chapter01/05-EqualsOperators.html similarity index 100% rename from chapter01/05-EqualsOperators.html rename to examples/chapter01/05-EqualsOperators.html diff --git a/examples/chapter01/05-EqualsOperators.js b/examples/chapter01/05-EqualsOperators.js new file mode 100644 index 00000000..d5b43ecc --- /dev/null +++ b/examples/chapter01/05-EqualsOperators.js @@ -0,0 +1,39 @@ +// @ts-check +/* eslint-disable */ + +// Packt == true + +console.log('packt' ? true : false); +// outputs true + +console.log('packt' == true); +// 1 - converts Boolean using toNumber +// 'packt' == 1 +// 2 - converts String using toNumber +// NaN == 1 +// outputs false + +console.log('packt' == false); +// 1 - converts Boolean using toNumber +// 'packt' == 0 +// 2 - converts String using toNumber +// NaN == 0 +// outputs false + +console.log([0] == true); +// 1 - converts Boolean using toNumber +// [0] == 1 +// 2 - converts Object using toPrimitive +// 2.1 - [0].valueOf() is not primitive +// 2.2 - [0].toString is 0 +// 0 == 1 +// outputs false + +//* ****************************** === +console.log('packt' === true); // false + +console.log('packt' === 'packt'); // true + +var person1 = { name: 'John' }; +var person2 = { name: 'John' }; +console.log(person1 === person2); // false, different objects diff --git a/chapter01/06-ConditionalStatements.html b/examples/chapter01/06-ConditionalStatements.html similarity index 100% rename from chapter01/06-ConditionalStatements.html rename to examples/chapter01/06-ConditionalStatements.html diff --git a/examples/chapter01/06-ConditionalStatements.js b/examples/chapter01/06-ConditionalStatements.js new file mode 100644 index 00000000..c939967c --- /dev/null +++ b/examples/chapter01/06-ConditionalStatements.js @@ -0,0 +1,54 @@ +// @ts-check +/* eslint-disable */ + +/* Example 01 - if */ +var num = 1; +if (num === 1) { + console.log('num is equal to 1'); +} + +/* Example 02 - if-else */ +var num = 0; +if (num === 1) { + console.log('num is equal to 1'); +} else { + console.log('num is not equal to 1, the value of num is ' + num); +} + +/* Example 03 - if-else-if-else... */ +var month = 5; +if (month === 1) { + console.log('January'); +} else if (month === 2) { + console.log('February'); +} else if (month === 3) { + console.log('March'); +} else { + console.log('Month is not January, February or March'); +} + +/* Example 04 - switch */ +var month = 5; +switch (month) { + case 1: + console.log('January'); + break; + case 2: + console.log('February'); + break; + case 3: + console.log('March'); + break; + default: + console.log('Month is not January, February or March'); +} + +/* Example 05 - ternary operator - if..else */ +if (num === 1) { + num--; +} else { + num++; +} + +// is the same as +num === 1 ? num-- : num++; diff --git a/chapter01/07-Loops.html b/examples/chapter01/07-Loops.html similarity index 100% rename from chapter01/07-Loops.html rename to examples/chapter01/07-Loops.html diff --git a/chapter01/07-Loops.js b/examples/chapter01/07-Loops.js similarity index 56% rename from chapter01/07-Loops.js rename to examples/chapter01/07-Loops.js index 61cf20cb..9d12b19e 100755 --- a/chapter01/07-Loops.js +++ b/examples/chapter01/07-Loops.js @@ -1,23 +1,24 @@ -console.log('**** for example ****'); +// @ts-check +/* eslint-disable */ +console.log('**** for example ****'); /* for - example */ -for (var i=0; i<10; i++) { - console.log(i); +for (var i = 0; i < 10; i++) { + console.log(i); } console.log('**** while example ****'); /* while - example */ var i = 0; -while(i<10) -{ - console.log(i); - i++; +while (i < 10) { + console.log(i); + i++; } console.log('**** do-while example ****'); /* do-while - example */ var i = 0; do { - console.log(i); - i++; -} while (i<10) \ No newline at end of file + console.log(i); + i++; +} while (i < 10); diff --git a/chapter01/08-Functions.html b/examples/chapter01/08-Functions.html similarity index 100% rename from chapter01/08-Functions.html rename to examples/chapter01/08-Functions.html diff --git a/chapter01/08-Functions.js b/examples/chapter01/08-Functions.js similarity index 66% rename from chapter01/08-Functions.js rename to examples/chapter01/08-Functions.js index 9915e566..52d5f9c8 100644 --- a/chapter01/08-Functions.js +++ b/examples/chapter01/08-Functions.js @@ -1,12 +1,15 @@ +// @ts-check +/* eslint-disable */ + function sayHello() { - console.log('Hello!'); + console.log('Hello!'); } sayHello(); /* function with parameter */ function output(text) { - console.log(text); + console.log(text); } output('Hello!'); @@ -17,13 +20,8 @@ output(); /* function using the return statement */ function sum(num1, num2) { - return num1 + num2; + return num1 + num2; } -var result = sum(1,2); +var result = sum(1, 2); output(result); - - - - - diff --git a/chapter01/10-ObjectOrientedJS.html b/examples/chapter01/09-ObjectOrientedJS.html similarity index 59% rename from chapter01/10-ObjectOrientedJS.html rename to examples/chapter01/09-ObjectOrientedJS.html index 1b3848bd..cfbe1a04 100644 --- a/chapter01/10-ObjectOrientedJS.html +++ b/examples/chapter01/09-ObjectOrientedJS.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter01/09-ObjectOrientedJS.js b/examples/chapter01/09-ObjectOrientedJS.js new file mode 100644 index 00000000..28c15a1c --- /dev/null +++ b/examples/chapter01/09-ObjectOrientedJS.js @@ -0,0 +1,42 @@ +// @ts-check +/* eslint-disable */ + +/* Object example 1 */ +var obj = new Object(); + +/* Object example 2 */ +var obj = {}; + +obj = { + name: { + first: 'Gandalf', + last: 'the Grey' + }, + address: 'Middle Earth' +}; + +/* Object example 3 */ +function Book(title, pages, isbn) { + this.title = title; + this.pages = pages; + this.isbn = isbn; + this.printIsbn = function() { + console.log(this.isbn); + }; +} + +var book = new Book('title', 'pag', 'isbn'); + +console.log(book.title); // outputs the book title + +book.title = 'new title'; // update the value of the book title + +console.log(book.title); // outputs the updated value + +Book.prototype.printTitle = function() { + console.log(this.title); +}; + +book.printTitle(); + +book.printIsbn(); diff --git a/chapter02/07-TypedArrays.html b/examples/chapter01/10-ES2015-ES6-letconst.html similarity index 57% rename from chapter02/07-TypedArrays.html rename to examples/chapter01/10-ES2015-ES6-letconst.html index 5e021e79..ba8c81d0 100644 --- a/chapter02/07-TypedArrays.html +++ b/examples/chapter01/10-ES2015-ES6-letconst.html @@ -5,6 +5,7 @@ - + + - \ No newline at end of file + diff --git a/examples/chapter01/10-ES2015-ES6-letconst.js b/examples/chapter01/10-ES2015-ES6-letconst.js new file mode 100644 index 00000000..0295e187 --- /dev/null +++ b/examples/chapter01/10-ES2015-ES6-letconst.js @@ -0,0 +1,31 @@ +// @ts-check +/* eslint-disable */ + +//* ****** EcmaScript 2015 (ES6): let and const keywords + +//* ****** EcmaScript 2015 (ES6): let is the new var (https://goo.gl/he0udZ) +var framework = 'Angular'; +var framework = 'React'; +console.log(framework); + +let language = 'JavaScript!'; // {1} +// let language = 'Ruby!'; // {2} - throws error +console.log(language); + + +//* ****** EcmaScript 2015 (ES6): const (https://goo.gl/YUQj3r) +const PI = 3.141593; +// PI = 3.0; //throws error +console.log(PI); + +const jsFramework = { + name: 'Angular' +}; +jsFramework.name = 'React'; + +// error, cannot reassign object reference +/* +jsFramework = { + name: 'Vue' +}; +*/ diff --git a/examples/chapter01/11-ES2015-ES6-variableScope.html b/examples/chapter01/11-ES2015-ES6-variableScope.html new file mode 100644 index 00000000..b4d3476f --- /dev/null +++ b/examples/chapter01/11-ES2015-ES6-variableScope.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter01/11-ES2015-ES6-variableScope.html.js b/examples/chapter01/11-ES2015-ES6-variableScope.html.js new file mode 100644 index 00000000..3f5f022a --- /dev/null +++ b/examples/chapter01/11-ES2015-ES6-variableScope.html.js @@ -0,0 +1,46 @@ +// @ts-check +/* eslint-disable */ + +//* ****** EcmaScript 2015 (ES6): variables scope (https://goo.gl/NbsVvg) +let movie = 'Lord of the Rings'; // {1} +// var movie = 'Batman v Superman'; //throws error, variable movie already declared + +function starWarsFan() { + const movie = 'Star Wars'; // {2} + return movie; +} + +function marvelFan() { + movie = 'The Avengers'; // {3} + return movie; +} + +function blizzardFan() { + const isFan = true; + let phrase = 'Warcraft'; // {4} + console.log('Before if: ' + phrase); + if (isFan) { + let phrase = 'initial text'; // {5} + phrase = 'For the Horde!'; // {6} + console.log('Inside if: ' + phrase); + } + phrase = 'For the Alliance!'; // {7} + console.log('After if: ' + phrase); +} + +console.log(movie); // {8} +console.log(starWarsFan()); // {9} +console.log(marvelFan()); // {10} +console.log(movie); // {11} +blizzardFan(); // {12} + +// output +// Lord of the Rings +// Star Wars +// The Avengers +// The Avengers +// Before if: Warcraft +// Inside if: For the Horde! +// After if: For the Alliance! + + diff --git a/chapter01/15-ES6EnhancedObjectProperties.html b/examples/chapter01/12-ES2015-ES6-StringTemplates.html similarity index 55% rename from chapter01/15-ES6EnhancedObjectProperties.html rename to examples/chapter01/12-ES2015-ES6-StringTemplates.html index 3e040b16..c7eb7ef3 100644 --- a/chapter01/15-ES6EnhancedObjectProperties.html +++ b/examples/chapter01/12-ES2015-ES6-StringTemplates.html @@ -5,7 +5,7 @@ - + - \ No newline at end of file + diff --git a/examples/chapter01/12-ES2015-ES6-StringTemplates.js b/examples/chapter01/12-ES2015-ES6-StringTemplates.js new file mode 100644 index 00000000..b1a65b40 --- /dev/null +++ b/examples/chapter01/12-ES2015-ES6-StringTemplates.js @@ -0,0 +1,14 @@ +// @ts-check +/* eslint-disable */ + +//* ****** EcmaScript 2015 (ES6): Template literals (https://goo.gl/4N36CS) +const book = { + name: 'Learning JavaScript DataStructures and Algorithms' +}; + +console.log('You are reading ' + book.name + '.,\n and this is a new line\n and so is this.'); + +console.log(`You are reading ${book.name}., + and this is a new line + and so is this.`); + diff --git a/chapter01/13-ES6ArrowFunctions.html b/examples/chapter01/13-ES2015-ES6-ArrowFunctions.html similarity index 56% rename from chapter01/13-ES6ArrowFunctions.html rename to examples/chapter01/13-ES2015-ES6-ArrowFunctions.html index 1d085ec4..0a922d1c 100644 --- a/chapter01/13-ES6ArrowFunctions.html +++ b/examples/chapter01/13-ES2015-ES6-ArrowFunctions.html @@ -5,7 +5,7 @@ - + - \ No newline at end of file + diff --git a/examples/chapter01/13-ES2015-ES6-ArrowFunctions.js b/examples/chapter01/13-ES2015-ES6-ArrowFunctions.js new file mode 100644 index 00000000..8f4775f3 --- /dev/null +++ b/examples/chapter01/13-ES2015-ES6-ArrowFunctions.js @@ -0,0 +1,23 @@ +// @ts-check +/* eslint-disable */ + +//* ****** EcmaScript 2015 (ES6): arrow functions (https://goo.gl/nM414v) +var circleAreaES5 = function circleArea(r) { + var PI = 3.14; + var area = PI * r * r; + return area; +}; +console.log(circleAreaES5(2)); + +const circleArea = r => { // {1} + const PI = 3.14; + const area = PI * r * r; + return area; +}; +console.log(circleArea(2)); + +const circleArea2 = r => 3.14 * r * r; +console.log(circleArea2(2)); + +const hello = () => console.log('hello!'); +hello(); diff --git a/chapter01/14-ES6ParameterHandling.html b/examples/chapter01/14-ES2015-ES6-ParameterHandling.html similarity index 55% rename from chapter01/14-ES6ParameterHandling.html rename to examples/chapter01/14-ES2015-ES6-ParameterHandling.html index 1d220b1b..703cf2c9 100644 --- a/chapter01/14-ES6ParameterHandling.html +++ b/examples/chapter01/14-ES2015-ES6-ParameterHandling.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter01/14-ES2015-ES6-ParameterHandling.js b/examples/chapter01/14-ES2015-ES6-ParameterHandling.js new file mode 100644 index 00000000..66a6e089 --- /dev/null +++ b/examples/chapter01/14-ES2015-ES6-ParameterHandling.js @@ -0,0 +1,48 @@ +// @ts-check +/* eslint-disable */ + +//* ****** EcmaScript 2015 (ES6): Default Parameter Values (https://goo.gl/AP5EYb) +function sum(x = 1, y = 2, z = 3) { + return x + y + z; +} +console.log(sum(4, 2)); // outputs 9 + +// function above is the same as +function sum2(x, y, z) { + if (x === undefined) x = 1; + if (y === undefined) y = 2; + if (z === undefined) z = 3; + return x + y + z; +} +console.log(sum2(4, 2)); // outputs 9 + +// or +function sum3() { + var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; + var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2; + var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 3; + + return x + y + z; +} +console.log(sum3(4, 2)); // outputs 9 + +//* ****** EcmaScript 6: spread operator ('...') (https://goo.gl/8equk5) +let params = [3, 4, 5]; +console.log(sum(...params)); // ES2015 +console.log(sum.apply(undefined, params)); // ES5 + +let numbers = [1, 2, ...params]; // pushing values into array +console.log(numbers); + +//* ****** EcmaScript 6: rest parameter ('...') (https://goo.gl/LaJZqU) +function restParamaterFunction(x, y, ...a) { + return (x + y) * a.length; +} +console.log(restParamaterFunction(1, 2, 'hello', true, 7)); // outputs 9; + +// code above is the same as ES5: +function restParamaterFunction2(x, y) { + var a = Array.prototype.slice.call(arguments, 2); + return (x + y) * a.length; +} +console.log(restParamaterFunction2(1, 2, 'hello', true, 7)); diff --git a/examples/chapter01/15-ES2015-ES6-EnhancedObjectProperties.html b/examples/chapter01/15-ES2015-ES6-EnhancedObjectProperties.html new file mode 100644 index 00000000..c915b7d8 --- /dev/null +++ b/examples/chapter01/15-ES2015-ES6-EnhancedObjectProperties.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter01/15-ES2015-ES6-EnhancedObjectProperties.js b/examples/chapter01/15-ES2015-ES6-EnhancedObjectProperties.js new file mode 100644 index 00000000..00ec03ad --- /dev/null +++ b/examples/chapter01/15-ES2015-ES6-EnhancedObjectProperties.js @@ -0,0 +1,37 @@ +// @ts-check +/* eslint-disable */ + +//* ****** EcmaScript 2015 (ES6): Destructuring Assignment + Property Shorthand (https://goo.gl/VsLecp ) +let [x, y] = ['a', 'b']; +let obj = { x, y }; +console.log(obj); // { x: "a", y: "b" } + +// swap (https://goo.gl/EyFAII) +[x, y] = [y, x]; +var temp = x; +x = y; +y = temp; + +// code above is the same as +var x2 = 'a'; +var y2 = 'b'; +var obj2 = { x2: x2, y2: y2 }; +console.log(obj2); // { x: "a", y: "b" } + +// Method Properties (https://goo.gl/DKU2PN) +const hello = { + name: 'abcdef', + printHello() { + console.log('Hello'); + } +}; +console.log(hello.printHello()); + +// code above is the same as: +var hello2 = { + name: 'abcdef', + printHello: function printHello() { + console.log('Hello'); + } +}; +console.log(hello2.printHello()); diff --git a/chapter01/12-Es6StringTemplates.html b/examples/chapter01/16-ES2015-ES6-Classes.html similarity index 62% rename from chapter01/12-Es6StringTemplates.html rename to examples/chapter01/16-ES2015-ES6-Classes.html index 3d730209..95f0d6d5 100644 --- a/chapter01/12-Es6StringTemplates.html +++ b/examples/chapter01/16-ES2015-ES6-Classes.html @@ -5,7 +5,6 @@ - - + - \ No newline at end of file + diff --git a/examples/chapter01/16-ES2015-ES6-Classes.js b/examples/chapter01/16-ES2015-ES6-Classes.js new file mode 100644 index 00000000..c088b6bb --- /dev/null +++ b/examples/chapter01/16-ES2015-ES6-Classes.js @@ -0,0 +1,89 @@ +// @ts-check +/* eslint-disable */ + +//* ****** EcmaScript 2015 (ES6): classes (https://goo.gl/UhK1n4) +class Book { + constructor(title, pages, isbn) { + this.title = title; + this.pages = pages; + this.isbn = isbn; + } + printIsbn() { + console.log(this.isbn); + } +} + +let book = new Book('title', 'pag', 'isbn'); + +console.log(book.title); // outputs the book title + +book.title = 'new title'; // update the value of the book title + +console.log(book.title); // outputs the book title + +// inheritance (https://goo.gl/hgQvo9) +class ITBook extends Book { // {1} + constructor(title, pages, isbn, technology) { + super(title, pages, isbn); // {2} + this.technology = technology; + } + + printTechnology() { + console.log(this.technology); + } +} + +let jsBook = new ITBook( + 'Learning JS Algorithms', + '200', + '1234567890', + 'JavaScript' +); + +console.log(jsBook.title); +console.log(jsBook.printTechnology()); + +// getter and setters (https://goo.gl/SMRYsv) +class Person { + constructor(name) { + this._name = name; // {1} + } + + get name() { // {2} + return this._name; + } + + set name(value) { // {3} + this._name = value; + } +} + +let lotrChar = new Person('Frodo'); +console.log(lotrChar.name); // {4} +lotrChar.name = 'Gandalf'; // {5} +console.log(lotrChar.name); +lotrChar._name = 'Sam'; // {6} +console.log(lotrChar.name); + +// using symbols for private atributes +var _name = Symbol(); +class Person2 { + constructor(name) { + this[_name] = name; + } + + get name() { + return this[_name]; + } + + set name(value) { + this[_name] = value; + } +} + +let lotrChar2 = new Person2('Frodo'); +console.log(lotrChar2.name); +lotrChar2.name = 'Gandalf'; +console.log(lotrChar2.name); + +console.log(Object.getOwnPropertySymbols(lotrChar2)); diff --git a/examples/chapter01/17-Book.js b/examples/chapter01/17-Book.js new file mode 100644 index 00000000..8b8b4f48 --- /dev/null +++ b/examples/chapter01/17-Book.js @@ -0,0 +1,11 @@ +// @ts-check +/* eslint-disable */ + +export default class Book { + constructor(title) { + this.title = title; + } + printTitle() { + console.log(this.title); + } +} diff --git a/examples/chapter01/17-CalcArea.js b/examples/chapter01/17-CalcArea.js new file mode 100644 index 00000000..085e9ef0 --- /dev/null +++ b/examples/chapter01/17-CalcArea.js @@ -0,0 +1,9 @@ +// @ts-check +/* eslint-disable */ + +export const circleArea = r => 3.14 * (r ** 2); + +export const squareArea = s => s * s; + +// export { circleArea, squareArea }; // {1} +export { circleArea as circle, squareArea as square }; diff --git a/examples/chapter01/17-ES2015-ES6-Modules-node.js b/examples/chapter01/17-ES2015-ES6-Modules-node.js new file mode 100644 index 00000000..7512f4ca --- /dev/null +++ b/examples/chapter01/17-ES2015-ES6-Modules-node.js @@ -0,0 +1,11 @@ +// @ts-check +/* eslint-disable */ + +const area = require('./lib/17-CalcArea'); +const Book = require('./lib/17-Book'); + +console.log(area.circle(2)); +console.log(area.square(2)); + +const myBook = new Book('some title'); +myBook.printTitle(); diff --git a/examples/chapter01/17-ES2015-ES6-Modules.html b/examples/chapter01/17-ES2015-ES6-Modules.html new file mode 100644 index 00000000..fb2709ae --- /dev/null +++ b/examples/chapter01/17-ES2015-ES6-Modules.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter01/17-ES2015-ES6-Modules.js b/examples/chapter01/17-ES2015-ES6-Modules.js new file mode 100644 index 00000000..da26b541 --- /dev/null +++ b/examples/chapter01/17-ES2015-ES6-Modules.js @@ -0,0 +1,20 @@ +// @ts-check +/* eslint-disable */ +// import { circleArea, squareArea } from './17-CalcArea'; // {2} +// import { circleArea as circle } from './17-CalcArea'; + +// console.log(circleArea(2)); +// console.log(squareArea(2)); + +/* Different way of importing the module */ +// import * as area from './17-CalcArea'; +// import Book from './17-Book'; + +import * as area from './17-CalcArea.js'; // we need the .js to run this code in the browser +import Book from './17-Book.js'; + +console.log(area.circle(2)); +console.log(area.square(2)); + +const myBook = new Book('some title'); +myBook.printTitle(); diff --git a/examples/chapter01/17-ES2015-Modules-node/17-Book.mjs b/examples/chapter01/17-ES2015-Modules-node/17-Book.mjs new file mode 100644 index 00000000..076f2bb0 --- /dev/null +++ b/examples/chapter01/17-ES2015-Modules-node/17-Book.mjs @@ -0,0 +1,8 @@ +export default class Book { + constructor(title) { + this.title = title; + } + printTitle() { + console.log(this.title); + } +} diff --git a/examples/chapter01/17-ES2015-Modules-node/17-CalcArea.mjs b/examples/chapter01/17-ES2015-Modules-node/17-CalcArea.mjs new file mode 100644 index 00000000..ca4454a5 --- /dev/null +++ b/examples/chapter01/17-ES2015-Modules-node/17-CalcArea.mjs @@ -0,0 +1,6 @@ +export const circleArea = r => 3.14 * (r ** 2); + +export const squareArea = s => s * s; + +// export { circleArea, squareArea }; // {1} +export { circleArea as circle, squareArea as square }; diff --git a/examples/chapter01/17-ES2015-Modules-node/17-ES2015-ES6-Modules.mjs b/examples/chapter01/17-ES2015-Modules-node/17-ES2015-ES6-Modules.mjs new file mode 100644 index 00000000..53817060 --- /dev/null +++ b/examples/chapter01/17-ES2015-Modules-node/17-ES2015-ES6-Modules.mjs @@ -0,0 +1,8 @@ +import * as area from './17-CalcArea.mjs'; +import Book from './17-Book.mjs'; + +console.log(area.circle(2)); +console.log(area.square(2)); + +const myBook = new Book('some title'); +myBook.printTitle(); diff --git a/examples/chapter01/18-ES2016-ES7-ExponentiationOperator.html b/examples/chapter01/18-ES2016-ES7-ExponentiationOperator.html new file mode 100644 index 00000000..129d0193 --- /dev/null +++ b/examples/chapter01/18-ES2016-ES7-ExponentiationOperator.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/chapter01/18-ES2016-ES7-ExponentiationOperator.js b/examples/chapter01/18-ES2016-ES7-ExponentiationOperator.js new file mode 100644 index 00000000..705933de --- /dev/null +++ b/examples/chapter01/18-ES2016-ES7-ExponentiationOperator.js @@ -0,0 +1,12 @@ +// @ts-check +/* eslint-disable */ + +//* ****** EcmaScript 2016 (ES7): Exponentiation operator (https://goo.gl/Z6dCFB) +let r = 2; +const area = 3.14 * r * r; +const area2 = 3.14 * Math.pow(r, 2); +const area3 = 3.14 * r ** 2; + +console.log(area); +console.log(area2); +console.log(area3); diff --git a/examples/chapter01/lib/17-Book.js b/examples/chapter01/lib/17-Book.js new file mode 100644 index 00000000..802d4191 --- /dev/null +++ b/examples/chapter01/lib/17-Book.js @@ -0,0 +1,63 @@ +(function (global, factory) { + if (typeof define === "function" && define.amd) { + define(["module", "exports"], factory); + } else if (typeof exports !== "undefined") { + factory(module, exports); + } else { + var mod = { + exports: {} + }; + factory(mod, mod.exports); + global.Book = mod.exports; + } +})(this, function (module, exports) { + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + var Book = function () { + function Book(title) { + _classCallCheck(this, Book); + + this.title = title; + } + + _createClass(Book, [{ + key: "printTitle", + value: function printTitle() { + console.log(this.title); + } + }]); + + return Book; + }(); + + exports.default = Book; + module.exports = exports["default"]; +}); \ No newline at end of file diff --git a/examples/chapter01/lib/17-CalcArea.js b/examples/chapter01/lib/17-CalcArea.js new file mode 100644 index 00000000..63335f1d --- /dev/null +++ b/examples/chapter01/lib/17-CalcArea.js @@ -0,0 +1,30 @@ +(function (global, factory) { + if (typeof define === "function" && define.amd) { + define(["exports"], factory); + } else if (typeof exports !== "undefined") { + factory(exports); + } else { + var mod = { + exports: {} + }; + factory(mod.exports); + global.CalcArea = mod.exports; + } +})(this, function (exports) { + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + var circleArea = exports.circleArea = function circleArea(r) { + return 3.14 * Math.pow(r, 2); + }; + + var squareArea = exports.squareArea = function squareArea(s) { + return s * s; + }; + + // export { circleArea, squareArea }; // {1} + exports.circle = circleArea; + exports.square = squareArea; +}); \ No newline at end of file diff --git a/examples/chapter01/lib/17-ES2015-ES6-Modules-bundle.js b/examples/chapter01/lib/17-ES2015-ES6-Modules-bundle.js new file mode 100644 index 00000000..bc101140 --- /dev/null +++ b/examples/chapter01/lib/17-ES2015-ES6-Modules-bundle.js @@ -0,0 +1,158 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o b.age ? 1 : -1; + }; + return MyObject; +}()); +function compareTwoObjects(a, b) { + console.log(a.compareTo(b)); + console.log(b.compareTo(a)); +} diff --git a/examples/chapter01/typescript/hello-world.ts b/examples/chapter01/typescript/hello-world.ts new file mode 100644 index 00000000..7cb7ace8 --- /dev/null +++ b/examples/chapter01/typescript/hello-world.ts @@ -0,0 +1,46 @@ +let myName = 'Packt'; +// myName = 10; + +let age = 20; // number +let existsFlag = true; // boolean +let language = 'JavaScript'; // string + +let favoriteLanguage: string; +let langs = ['JavaScript', 'Ruby', 'Python']; +favoriteLanguage = langs[0]; + +interface Person { + name: string; + age: number; +} + +function printName(person: Person) { + console.log(person.name); +} + +const john = { name: 'John', age: 21 }; +const mary = { name: 'Mary', age: 21, phone: '123-45678' }; +printName(john); +printName(mary); + +interface Comparable { + compareTo(b: T): number; +} + +class MyObject implements Comparable { + age: number; + + compareTo(b: MyObject): number { + if (this.age === b.age) { + return 0; + } + return this.age > b.age ? 1 : -1; + } +} + +function compareTwoObjects(a: MyObject, b: MyObject) { + console.log(a.compareTo(b)); + console.log(b.compareTo(a)); +} + + diff --git a/chapter02/01-Introduction.html b/examples/chapter02/01-Introduction.html similarity index 100% rename from chapter02/01-Introduction.html rename to examples/chapter02/01-Introduction.html diff --git a/examples/chapter02/01-Introduction.js b/examples/chapter02/01-Introduction.js new file mode 100755 index 00000000..59311202 --- /dev/null +++ b/examples/chapter02/01-Introduction.js @@ -0,0 +1,26 @@ +// @ts-check + +const averageTempJan = 31.9; +const averageTempFeb = 35.3; +const averageTempMar = 42.4; +const averageTempApr = 52; +const averageTempMay = 60.8; + +const averageTemp = []; +averageTemp[0] = 31.9; +averageTemp[1] = 35.3; +averageTemp[2] = 42.4; +averageTemp[3] = 52; +averageTemp[4] = 60.8; + +console.log('averageTempJan', averageTempJan); +console.log('averageTempFeb', averageTempFeb); +console.log('averageTempMar', averageTempMar); +console.log('averageTempApr', averageTempApr); +console.log('averageTempMay', averageTempMay); + +console.log('averageTemp[0]', averageTemp[0]); +console.log('averageTemp[1]', averageTemp[1]); +console.log('averageTemp[2]', averageTemp[2]); +console.log('averageTemp[3]', averageTemp[3]); +console.log('averageTemp[4]', averageTemp[4]); diff --git a/chapter02/02-CreatingAndInitialingArrays.html b/examples/chapter02/02-CreatingAndInitialingArrays.html similarity index 100% rename from chapter02/02-CreatingAndInitialingArrays.html rename to examples/chapter02/02-CreatingAndInitialingArrays.html diff --git a/examples/chapter02/02-CreatingAndInitialingArrays.js b/examples/chapter02/02-CreatingAndInitialingArrays.js new file mode 100755 index 00000000..b07aaed3 --- /dev/null +++ b/examples/chapter02/02-CreatingAndInitialingArrays.js @@ -0,0 +1,36 @@ +// @ts-check +/* eslint-disable */ + +let daysOfWeek = new Array(); // {1} + +daysOfWeek = new Array(7); // {2} + +daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); // {3} + +// preferred +daysOfWeek = []; + +daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; // {3} + +console.log('daysOfWeek.length', daysOfWeek.length); + +for (let i = 0; i < daysOfWeek.length; i++) { + console.log(`daysOfWeek[${i}]`, daysOfWeek[i]); +} + +// fibonacci numbers +// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... +const fibonacci = []; // {1} +fibonacci[1] = 1; // {2} +fibonacci[2] = 1; // {3} + +for (let i = 3; i < 20; i++) { + fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2]; // //{4} +} + +for (let i = 1; i < fibonacci.length; i++) { // {5} + console.log(`fibonacci[${i}]`, fibonacci[i]); // {6} +} + +// instead of {5} and {6} we can simply use +console.log('fibonacci', fibonacci); diff --git a/chapter02/03-AddingRemovingElements.html b/examples/chapter02/03-AddingRemovingElements.html similarity index 100% rename from chapter02/03-AddingRemovingElements.html rename to examples/chapter02/03-AddingRemovingElements.html diff --git a/examples/chapter02/03-AddingRemovingElements.js b/examples/chapter02/03-AddingRemovingElements.js new file mode 100755 index 00000000..9a12e833 --- /dev/null +++ b/examples/chapter02/03-AddingRemovingElements.js @@ -0,0 +1,105 @@ +// @ts-check +/* eslint-disable */ + +function logArray(message = '', array) { + const output = `${message} => (${array.length}) [${array.join(', ')}]`; + document.writeln(`

${output}

`); + console.log(message, array); +} + +let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + +console.log('Original array', numbers); + +function printArray(myArray) { + for (let i = 0; i < myArray.length; i++) { + console.log(myArray[i]); + } +} + +/****************************** Inserting an element in the end of the Array */ + +// add a new element to the numbers array +numbers[numbers.length] = 10; +console.log('Add 10 to the end', numbers); + +numbers.push(11); +console.log('Add 11 with push', numbers); + +numbers.push(12, 13); +console.log('Add 12 and 13 with push', numbers); + + +// insert first position manually +Array.prototype.insertFirstPosition = function(value) { + for (let i = this.length; i >= 0; i--) { + this[i] = this[i - 1]; + } + this[0] = value; +}; + +numbers.insertFirstPosition(-1); +console.log('Add -1 with insertFirstPosition', numbers); +// printArray(numbers); + +// using method unshift +numbers.unshift(-2); +console.log('Add -2 with unshift', numbers); +//printArray(numbers); + +numbers.unshift(-4, -3); +console.log('Add -4 and -3 with unshift', numbers); +// printArray(numbers); + +// ************** Removing elements + +numbers.pop(); +console.log('Removed last value with pop', numbers); + +for (let i = 0; i < numbers.length; i++) { + numbers[i] = numbers[i + 1]; +} + +console.log('Removed first value manually', numbers); +console.log('Lenght after value removed manually', numbers.length); + +//reset array +numbers = [-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + +Array.prototype.reIndex = function(myArray) { + const newArray = []; + for(let i = 0; i < myArray.length; i++ ) { + if (myArray[i] !== undefined) { + // console.log(myArray[i]); + newArray.push(myArray[i]); + } + } + return newArray; +} + +// remove first position manually and reIndex +Array.prototype.removeFirstPosition = function() { + for (let i = 0; i < this.length; i++) { + this[i] = this[i + 1]; + } + return this.reIndex(this); +}; + +numbers = numbers.removeFirstPosition(); +console.log('Removed first with removeFirstPosition + reIndex', numbers); + +// using method shift +numbers.shift(); +console.log('Removed first with shift', numbers); +console.log('Lenght after removed first with shift', numbers.length); + +//* *** Removing and Adding elements from the middle of the array or specific position +// splice method - parameter (index, howManyPositionsToBeRemoved, item1...itemX) +numbers.splice(5, 3); +console.log('Removing 3 elements (3, 4, 5) starting index 5', numbers); + +numbers.splice(5, 0, 2, 3, 4); +console.log('Adding 3 elements (2, 3, 4) starting index 5', numbers); + +numbers.splice(5, 3, 2, 3, 4); +console.log('Removing 3 elements starting index 5 and adding (2, 3, 4)', numbers); diff --git a/chapter02/04-TwoDimensionalMultiDimensional.html b/examples/chapter02/04-TwoDimensionalMultiDimensional.html similarity index 100% rename from chapter02/04-TwoDimensionalMultiDimensional.html rename to examples/chapter02/04-TwoDimensionalMultiDimensional.html diff --git a/examples/chapter02/04-TwoDimensionalMultiDimensional.js b/examples/chapter02/04-TwoDimensionalMultiDimensional.js new file mode 100755 index 00000000..97dfb941 --- /dev/null +++ b/examples/chapter02/04-TwoDimensionalMultiDimensional.js @@ -0,0 +1,78 @@ +// @ts-check +/* eslint-disable */ + +let averageTempDay1 = [72, 75, 79, 79, 81, 81]; +let averageTempDay2 = [81, 79, 75, 75, 73, 72]; + +let averageTemp = []; + +// same as +averageTemp[0] = [72, 75, 79, 79, 81, 81]; +averageTemp[1] = [81, 79, 75, 75, 73, 73]; + +function printMatrix(myMatrix) { + for (let i = 0; i < myMatrix.length; i++) { + for (let j = 0; j < myMatrix[i].length; j++) { + console.log(myMatrix[i][j]); + } + } +} + +// printMatrix(averageTemp); +console.log('averageTemp two-dimensional array:'); + +// same as + +// day 1 +averageTemp[0] = []; +averageTemp[0][0] = 72; +averageTemp[0][1] = 75; +averageTemp[0][2] = 79; +averageTemp[0][3] = 79; +averageTemp[0][4] = 81; +averageTemp[0][5] = 81; +// day 2 +averageTemp[1] = []; +averageTemp[1][0] = 81; +averageTemp[1][1] = 79; +averageTemp[1][2] = 75; +averageTemp[1][3] = 75; +averageTemp[1][4] = 73; +averageTemp[1][5] = 73; + +// printMatrix(averageTemp); +console.table(averageTemp); + +//* * Multidimensional Matrix + +// Matrix 3x3x3 - Cube + +const matrix3x3x3 = []; +for (let i = 0; i < 3; i++) { + matrix3x3x3[i] = []; + for (let j = 0; j < 3; j++) { + matrix3x3x3[i][j] = []; + for (let z = 0; z < 3; z++) { + matrix3x3x3[i][j][z] = i + j + z; + } + } +} + +for (let i = 0; i < matrix3x3x3.length; i++) { + for (let j = 0; j < matrix3x3x3[i].length; j++) { + for (let z = 0; z < matrix3x3x3[i][j].length; z++) { + console.log(matrix3x3x3[i][j][z]); + } + } +} + +// user-friendly-output +const matrix3x3x3Output = []; +for (let i = 0; i < 3; i++) { + matrix3x3x3Output[i] = []; + for (let j = 0; j < 3; j++) { + matrix3x3x3Output[i][j] = `[${matrix3x3x3[i][j].join(', ')}]`; + } +} +console.log('matrix3x3x3 three-dimensional array:'); +console.table(matrix3x3x3Output); diff --git a/chapter02/05-ArrayMethods.html b/examples/chapter02/05-ArrayMethods.html similarity index 100% rename from chapter02/05-ArrayMethods.html rename to examples/chapter02/05-ArrayMethods.html diff --git a/examples/chapter02/05-ArrayMethods.js b/examples/chapter02/05-ArrayMethods.js new file mode 100755 index 00000000..67d97602 --- /dev/null +++ b/examples/chapter02/05-ArrayMethods.js @@ -0,0 +1,47 @@ +// @ts-check + +//* ************** Joining multiple arrays +const zero = 0; +const positiveNumbers = [1, 2, 3]; +const negativeNumbers = [-3, -2, -1]; +let numbers = negativeNumbers.concat(zero, positiveNumbers); + +console.log('zero', zero); +console.log('positiveNumbers', positiveNumbers); +console.log('negativeNumbers', negativeNumbers); +console.log('negativeNumbers.concat(zero, positiveNumbers)', numbers); + +/* function isEven(x) { + // returns true if x is a multiple of 2. + console.log(x); + return x % 2 === 0 ? true : false; +} */ // ES5 syntax +const isEven = x => x % 2 === 0; + +numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; +console.log('numbers', numbers); + +// it is going to execute the function only once +console.log('numbers.every(isEven)', numbers.every(isEven)); + +// is going to execute the function twice +console.log('numbers.some(isEven)', numbers.some(isEven)); + +/* numbers.forEach(function(x) { + console.log(x % 2 == 0); +}); */ // ES5 sintax for function below + +numbers.forEach(x => console.log(`numbers.forEach: ${x} % 2 === 0`, x % 2 === 0)); + + +console.log('numbers.map(isEven)', numbers.map(isEven)); + +console.log('numbers.filter(isEven)', numbers.filter(isEven)); + +/* console.log('numbers.reduce', + numbers.reduce(function(previous, current, index) { + return previous + current; + }) +); */ // ES5 sintax for function below + +console.log('numbers.reduce', numbers.reduce((previous, current) => previous + current)); diff --git a/chapter02/06-ES6Methods.html b/examples/chapter02/06-ES2015Methods.html old mode 100644 new mode 100755 similarity index 59% rename from chapter02/06-ES6Methods.html rename to examples/chapter02/06-ES2015Methods.html index fdbeda65..cd7abd9c --- a/chapter02/06-ES6Methods.html +++ b/examples/chapter02/06-ES2015Methods.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter02/06-ES2015Methods.js b/examples/chapter02/06-ES2015Methods.js new file mode 100755 index 00000000..6513e2d3 --- /dev/null +++ b/examples/chapter02/06-ES2015Methods.js @@ -0,0 +1,117 @@ +// @ts-check + +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + +//* ********* using for..of loop +for (const n of numbers) { + console.log(`for..of loop: ${n} % 2 === 0`, n % 2 === 0 ? 'even' : 'odd'); +} + +//* ********* Using the new ES6 iterator (@@iterator) +console.log('Using the new ES6 iterator (@@iterator)'); + +let iterator = numbers[Symbol.iterator](); +console.log('iterator.next().value', iterator.next().value); // 1 +console.log('iterator.next().value', iterator.next().value); // 2 +console.log('iterator.next().value', iterator.next().value); // 3 +console.log('iterator.next().value', iterator.next().value); // 4 +console.log('iterator.next().value', iterator.next().value); // 5 + +// or use code below +iterator = numbers[Symbol.iterator](); +for (const n of iterator) { + console.log(`${n} of iterator`, n); +} + +//* ********* Array entries, keys and values +console.log('Array entries, keys and values'); + +console.log('Array.entries'); +let aEntries = numbers.entries(); // retrieve iterator of key/value +console.log('aEntries.next().value', aEntries.next().value); // [0, 1] - position 0, value 1 +console.log('aEntries.next().value', aEntries.next().value); // [1, 2] - position 1, value 2 +console.log('aEntries.next().value', aEntries.next().value); // [2, 3] - position 2, value 3 + +// or use code below +aEntries = numbers.entries(); +for (const n of aEntries) { + console.log(`entry of ${n}`, n); +} + +console.log('Array.keys'); +const aKeys = numbers.keys(); // retrieve iterator of keys +console.log('aKeys.next()', aKeys.next()); // {value: 0, done: false } done false means iterator has more values +console.log('aKeys.next()', aKeys.next()); // {value: 1, done: false } +console.log('aKeys.next()', aKeys.next()); // {value: 2, done: false } + +console.log('Array.values - Only Edge and Safari - Oct 2017'); +// const aValues = numbers.values(); +// console.log(aValues.next()); // {value: 1, done: false } done false means iterator has more values +// console.log(aValues.next()); // {value: 2, done: false } +// console.log(aValues.next()); // {value: 3, done: false } + +//* ********* Using the from method +console.log('Using the from method'); + +const evens = Array.from(numbers, x => x % 2 === 0); +console.log('Array.from(numbers, x => x % 2 === 0)', evens); + +const numbers2 = Array.from(numbers); +console.log('Array.from(numbers)', numbers2); + +//* ********* Using Array.of +console.log('Using Array.of'); + +const numbers3 = Array.of(1); +const numbers4 = Array.of(1, 2, 3, 4, 5, 6); +const numbersCopy = Array.of(...numbers4); +console.log('Array.of(1)', numbers3); +console.log('Array.of(1, 2, 3, 4, 5, 6)', numbers4); +console.log('Array.of(...numbers4)', numbersCopy); + +//* ********* Using the fill method +console.log('Using the fill method'); + +numbersCopy.fill(0); +console.log('numbersCopy.fill(0)', numbersCopy); + +numbersCopy.fill(2, 1); +console.log('numbersCopy.fill(2, 1)', numbersCopy); + +numbersCopy.fill(1, 3, 5); +console.log('numbersCopy.fill(1, 3, 5)', numbersCopy); + +const ones = Array(6).fill(1); +console.log('Array(6).fill(1)', ones); + +//* ********* Using the copyWithin method +console.log('Using the copyWithin method'); + +let copyArray = [1, 2, 3, 4, 5, 6]; +console.log('copyArray', copyArray); + +copyArray = copyArray.copyWithin(0, 3); // pos 3 value is copied to pos 0 +console.log('copyArray.copyWithin(0, 3)', copyArray); + +copyArray = [1, 2, 3, 4, 5, 6]; +copyArray = copyArray.copyWithin(1, 3, 5); // pos 3-4 values are copied to pos 1-2 +console.log('copyArray.copyWithin(1, 3, 5)', copyArray); + +//* ********* methods find and findIndex +console.log('methods find and findIndex'); + +function multipleOf13(element) { + return element % 13 === 0; +} + +console.log('numbers.find(multipleOf13)', numbers.find(multipleOf13)); +console.log('numbers.findIndex(multipleOf13)', numbers.findIndex(multipleOf13)); + +//* ********* EcmaScript 2016 (ES7): using the method includes +console.log('EcmaScript 2016 (ES7): using the method includes'); + +console.log('numbers.includes(15)', numbers.includes(15)); +console.log('numbers.includes(20)', numbers.includes(20)); + +const numbers5 = [7, 6, 5, 4, 3, 2, 1]; +console.log('numbers5.includes(4, 5)', numbers5.includes(4, 5)); diff --git a/chapter06/04-UsingES6Set.html b/examples/chapter02/07-Sorting.html similarity index 61% rename from chapter06/04-UsingES6Set.html rename to examples/chapter02/07-Sorting.html index a08e635c..0e32a5b2 100644 --- a/chapter06/04-UsingES6Set.html +++ b/examples/chapter02/07-Sorting.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter02/07-Sorting.js b/examples/chapter02/07-Sorting.js new file mode 100644 index 00000000..f9efe1df --- /dev/null +++ b/examples/chapter02/07-Sorting.js @@ -0,0 +1,71 @@ +// @ts-check + +let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; +console.log('numbers', numbers); + + +numbers = numbers.reverse(); +console.log('numbers.reverse()', numbers); + +console.log('numbers.sort()', numbers.sort()); + +/* console.log( + numbers.sort(function(a, b) { + return a - b; + }) +); */ // ES5 syntax + +console.log('numbers.sort((a, b) => a - b)', numbers.sort((a, b) => a - b)); + +function compare(a, b) { + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + // a must be equal to b + return 0; +} + +console.log('numbers.sort(compare)', numbers.sort(compare)); + +// Sorting objects +const friends = [ + { name: 'John', age: 30 }, + { name: 'Ana', age: 20 }, + { name: 'Chris', age: 25 }, // trailing comma ES2017 +]; + +function comparePerson(a, b) { + if (a.age < b.age) { + return -1; + } + if (a.age > b.age) { + return 1; + } + return 0; +} + +console.log('friends.sort(comparePerson)', friends.sort(comparePerson)); + +let names = ['Ana', 'ana', 'john', 'John']; +console.log('names', names); +console.log('names.sort()', names.sort()); + +names = ['Ana', 'ana', 'john', 'John']; // reset to array original state +console.log('names.sort(compareFunction)', names.sort((a, b) => { + if (a.toLowerCase() < b.toLowerCase()) { + return -1; + } + if (a.toLowerCase() > b.toLowerCase()) { + return 1; + } + return 0; +})); +names = ['Ana', 'ana', 'john', 'John']; // reset to array original state +console.log('names.sort((a, b) => a.localeCompare(b))', names.sort((a, b) => a.localeCompare(b))); + +const names2 = ['Maève', 'Maeve']; +console.log('names2', names); +console.log('names2.sort((a, b) => a.localeCompare(b))', names2.sort((a, b) => a.localeCompare(b))); diff --git a/chapter01/16-ES6Classes.html b/examples/chapter02/08-Searching.html similarity index 60% rename from chapter01/16-ES6Classes.html rename to examples/chapter02/08-Searching.html index 15a8f004..6dd9aac1 100644 --- a/chapter01/16-ES6Classes.html +++ b/examples/chapter02/08-Searching.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter02/08-Searching.js b/examples/chapter02/08-Searching.js new file mode 100644 index 00000000..1388a873 --- /dev/null +++ b/examples/chapter02/08-Searching.js @@ -0,0 +1,17 @@ +// @ts-check + +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; +console.log('numbers', numbers); + +//* ** toString +console.log('numbers.toString()', numbers.toString()); + +console.log('numbers.indexOf(10)', numbers.indexOf(10)); +console.log('numbers.indexOf(100)', numbers.indexOf(100)); + +numbers.push(10); +console.log('push 10: numbers.lastIndexOf(10)', numbers.lastIndexOf(10)); +console.log('push 10: numbers.lastIndexOf(100)', numbers.lastIndexOf(100)); + +const numbersString = numbers.join('-'); +console.log('numbers.join("-")', numbersString); diff --git a/chapter01/11-ES6letconst.html b/examples/chapter02/09-TypedArrays.html old mode 100644 new mode 100755 similarity index 60% rename from chapter01/11-ES6letconst.html rename to examples/chapter02/09-TypedArrays.html index e57da24c..9257fe4a --- a/chapter01/11-ES6letconst.html +++ b/examples/chapter02/09-TypedArrays.html @@ -5,7 +5,6 @@ - - + - \ No newline at end of file + diff --git a/examples/chapter02/09-TypedArrays.js b/examples/chapter02/09-TypedArrays.js new file mode 100755 index 00000000..be92f508 --- /dev/null +++ b/examples/chapter02/09-TypedArrays.js @@ -0,0 +1,24 @@ +const length = 5; +const int16 = new Int16Array(length); + +const array16 = []; +array16.length = length; + +for (let i = 0; i < length; i++) { + int16[i] = i + 1; +} + +console.log(int16); + +// Int8Array(); +// Uint8Array(); +// Uint8ClampedArray(); +// Int16Array(); +// Uint16Array(); +// Int32Array(); +// Uint32Array(); +// Float32Array(); +// Float64Array(); + +// http://www.html5rocks.com/en/tutorials/webgl/typed_arrays/ +// http://www.i-programmer.info/programming/javascript/6135-javascript-data-structures-typed-arrays.html diff --git a/examples/chapter02/10-ArraysAndTypeScript.ts b/examples/chapter02/10-ArraysAndTypeScript.ts new file mode 100644 index 00000000..63e97f2e --- /dev/null +++ b/examples/chapter02/10-ArraysAndTypeScript.ts @@ -0,0 +1,32 @@ +// const numbers: number[]; +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +console.log('numbers', numbers); + +// let names: string[]; +let names = ['Ana', 'ana', 'john', 'John']; +console.log('names', names); +console.log('names.sort()', names.sort()); + +interface Person { + name: string; + age: number; +} + +// const friends: {name: string, age: number}[]; +const friends = [ + { name: 'John', age: 30 }, + { name: 'Ana', age: 20 }, + { name: 'Chris', age: 25 }, // trailing comma ES2017 +]; + +function comparePerson(a: Person, b: Person) { + if (a.age < b.age) { + return -1; + } + if (a.age > b.age) { + return 1; + } + return 0; +} + +console.log('friends.sort(comparePerson)', friends.sort(comparePerson)); diff --git a/examples/chapter03/01-Stack.html b/examples/chapter03/01-Stack.html new file mode 100755 index 00000000..3025a012 --- /dev/null +++ b/examples/chapter03/01-Stack.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/examples/chapter03/01-Stack.js b/examples/chapter03/01-Stack.js new file mode 100755 index 00000000..d3994b08 --- /dev/null +++ b/examples/chapter03/01-Stack.js @@ -0,0 +1,33 @@ +// import Stack from './../../src/js/data-structures/stack.js'; // ES2015 modules +// import StackArray from './../../src/js/data-structures/stack-array.js'; // ES2015 modules +// const Stack = require('../../dist/js/data-structures/stack'); // for node +// const Stack = stack; // older browsers - remove from html script import: type="module" +const { Stack } = PacktDataStructuresAlgorithms; + +const stack = new Stack(); // new StackArray(); + +// using WeakMap to store Stack items we ensure true privacy +// console.log(Object.getOwnPropertyNames(stack)); +// console.log(Object.keys(stack)); +// console.log(stack.items); + +console.log('stack.isEmpty() => ', stack.isEmpty()); // outputs true + +stack.push(5); +stack.push(8); + +console.log('stack after push 5 and 8 => ', stack.toString()); + +console.log('stack.peek() => ', stack.peek()); // outputs 8 + +stack.push(11); + +console.log('stack.size() after push 11 => ', stack.size()); // outputs 3 +console.log('stack.isEmpty() => ', stack.isEmpty()); // outputs false + +stack.push(15); + +stack.pop(); +stack.pop(); + +console.log('stack.size() after push 15 and pop twice => ', stack.size()); // outputs 2 diff --git a/examples/chapter03/01-StackSymbol.js b/examples/chapter03/01-StackSymbol.js new file mode 100755 index 00000000..c525f359 --- /dev/null +++ b/examples/chapter03/01-StackSymbol.js @@ -0,0 +1,47 @@ +const _items = Symbol('stackItems'); + +class Stack { + constructor() { + this[_items] = []; + } + + push(element) { + this[_items].push(element); + } + + pop() { + return this[_items].pop(); + } + + peek() { + return this[_items][this[_items].length - 1]; + } + + isEmpty() { + return this[_items].length === 0; + } + + size() { + return this[_items].length; + } + + clear() { + this[_items] = []; + } + + print() { + console.log(this.toString()); + } + + toString() { + return this[_items].toString(); + } +} + +const stack = new Stack(); +const objectSymbols = Object.getOwnPropertySymbols(stack); +console.log(objectSymbols.length); // 1 +console.log(objectSymbols); // [Symbol()] +console.log(objectSymbols[0]); // Symbol() +stack[objectSymbols[0]].push(1); +stack.print(); // 5, 8, 1 diff --git a/examples/chapter03/01-StackWeakMap.js b/examples/chapter03/01-StackWeakMap.js new file mode 100644 index 00000000..7471b8d1 --- /dev/null +++ b/examples/chapter03/01-StackWeakMap.js @@ -0,0 +1,70 @@ +// @ts-check + + +const _items = new WeakMap(); +const _count = new WeakMap(); + +class Stack { + constructor() { + _count.set(this, 0); + _items.set(this, {}); + } + + push(element) { + const items = _items.get(this); + const count = _count.get(this); + items[count] = element; + _count.set(this, count + 1); + } + + pop() { + if (this.isEmpty()) { + return undefined; + } + const items = _items.get(this); + let count = _count.get(this); + count--; + _count.set(this, count); + const result = items[count]; + delete items[count]; + return result; + } + + peek() { + if (this.isEmpty()) { + return undefined; + } + const items = _items.get(this); + const count = _count.get(this); + return items[count - 1]; + } + + isEmpty() { + return _count.get(this) === 0; + } + + size() { + return _count.get(this); + } + + clear() { + /* while (!this.isEmpty()) { + this.pop(); + } */ + _count.set(this, 0); + _items.set(this, {}); + } + + toString() { + if (this.isEmpty()) { + return ''; + } + const items = _items.get(this); + const count = _count.get(this); + let objString = `${items[0]}`; + for (let i = 1; i < count; i++) { + objString = `${objString},${items[i]}`; + } + return objString; + } +} diff --git a/examples/chapter03/02-BalancedSymbols.html b/examples/chapter03/02-BalancedSymbols.html new file mode 100755 index 00000000..f2aea40d --- /dev/null +++ b/examples/chapter03/02-BalancedSymbols.html @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/examples/chapter03/02-BalancedSymbols.js b/examples/chapter03/02-BalancedSymbols.js new file mode 100755 index 00000000..614f44e7 --- /dev/null +++ b/examples/chapter03/02-BalancedSymbols.js @@ -0,0 +1,5 @@ +const { parenthesesChecker } = PacktDataStructuresAlgorithms; + +console.log('{([])}', parenthesesChecker('{([])}')); // true +console.log('{{([][])}()}', parenthesesChecker('{{([][])}()}')); // true +console.log('[{()]', parenthesesChecker('[{()]')); // false diff --git a/examples/chapter03/03-DecimalToBinary.html b/examples/chapter03/03-DecimalToBinary.html new file mode 100755 index 00000000..88b6c362 --- /dev/null +++ b/examples/chapter03/03-DecimalToBinary.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter03/03-DecimalToBinary.js b/examples/chapter03/03-DecimalToBinary.js new file mode 100755 index 00000000..f39fcb1d --- /dev/null +++ b/examples/chapter03/03-DecimalToBinary.js @@ -0,0 +1,13 @@ +const { decimalToBinary } = PacktDataStructuresAlgorithms; +const { baseConverter } = PacktDataStructuresAlgorithms; + +// 233 == 11101001 +// 2x(10x10) + 3x(10) + 3x(1) +console.log(decimalToBinary(233)); // 11101001 +console.log(decimalToBinary(10)); // 1010 +console.log(decimalToBinary(1000)); // 1111101000 + +console.log(baseConverter(100345, 2)); // 11000011111111001 +console.log(baseConverter(100345, 8)); // 303771 +console.log(baseConverter(100345, 16)); // 187F9 +console.log(baseConverter(100345, 35)); // 2BW0 diff --git a/examples/chapter03/04-TowerOfHanoi.html b/examples/chapter03/04-TowerOfHanoi.html new file mode 100755 index 00000000..f6d0cb52 --- /dev/null +++ b/examples/chapter03/04-TowerOfHanoi.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter03/04-TowerOfHanoi.js b/examples/chapter03/04-TowerOfHanoi.js new file mode 100755 index 00000000..f5d85cf7 --- /dev/null +++ b/examples/chapter03/04-TowerOfHanoi.js @@ -0,0 +1,6 @@ +const { hanoiStack } = PacktDataStructuresAlgorithms; +const { hanoi } = PacktDataStructuresAlgorithms; + +console.log(hanoiStack(3)); + +console.log(hanoi(3, 'source', 'helper', 'dest')); diff --git a/examples/chapter04/01-Queue.html b/examples/chapter04/01-Queue.html new file mode 100755 index 00000000..2e67f7f4 --- /dev/null +++ b/examples/chapter04/01-Queue.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter04/01-Queue.js b/examples/chapter04/01-Queue.js new file mode 100755 index 00000000..852a1b4d --- /dev/null +++ b/examples/chapter04/01-Queue.js @@ -0,0 +1,14 @@ +const { Queue } = PacktDataStructuresAlgorithms; + +const queue = new Queue(); +console.log(queue.isEmpty()); // outputs true +queue.enqueue('John'); +queue.enqueue('Jack'); +console.log(queue.toString()); // John,Jack +queue.enqueue('Camila'); +console.log(queue.toString()); // John,Jack,Camila +console.log(queue.size()); // outputs 3 +console.log(queue.isEmpty()); // outputs false +queue.dequeue(); // remove John +queue.dequeue(); // remove Jack +console.log(queue.toString()); // Camila diff --git a/examples/chapter04/02-Deque.html b/examples/chapter04/02-Deque.html new file mode 100755 index 00000000..99b838af --- /dev/null +++ b/examples/chapter04/02-Deque.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter04/02-Deque.js b/examples/chapter04/02-Deque.js new file mode 100755 index 00000000..4c84b6de --- /dev/null +++ b/examples/chapter04/02-Deque.js @@ -0,0 +1,17 @@ +const { Deque } = PacktDataStructuresAlgorithms; + +const deque = new Deque(); +console.log(deque.isEmpty()); // outputs true +deque.addBack('John'); +deque.addBack('Jack'); +console.log(deque.toString()); // John,Jack +deque.addBack('Camila'); +console.log(deque.toString()); // John,Jack,Camila +console.log(deque.size()); // outputs 3 +console.log(deque.isEmpty()); // outputs false +deque.removeFront(); // remove John +console.log(deque.toString()); // Jack,Camila +deque.removeBack(); // Camila decides to leave +console.log(deque.toString()); // Jack +deque.addFront('John'); // John comes back for information +console.log(deque.toString()); // John,Jack diff --git a/examples/chapter04/03-HotPotato.html b/examples/chapter04/03-HotPotato.html new file mode 100755 index 00000000..85d45104 --- /dev/null +++ b/examples/chapter04/03-HotPotato.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter04/03-HotPotato.js b/examples/chapter04/03-HotPotato.js new file mode 100755 index 00000000..686f6791 --- /dev/null +++ b/examples/chapter04/03-HotPotato.js @@ -0,0 +1,16 @@ +const { hotPotato } = PacktDataStructuresAlgorithms; + +const names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl']; +const result = hotPotato(names, 7); + +result.eliminated.forEach(name => { + console.log(`${name} was eliminated from the Hot Potato game.`); +}); + +console.log(`The winner is: ${result.winner}`); + +// Camila was eliminated from the Hot Potato game. +// Jack was eliminated from the Hot Potato game. +// Carl was eliminated from the Hot Potato game. +// Ingrid was eliminated from the Hot Potato game. +// The winner is: John diff --git a/examples/chapter04/04-PalindromeChecker.html b/examples/chapter04/04-PalindromeChecker.html new file mode 100755 index 00000000..785e0482 --- /dev/null +++ b/examples/chapter04/04-PalindromeChecker.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter04/04-PalindromeChecker.js b/examples/chapter04/04-PalindromeChecker.js new file mode 100755 index 00000000..99c29cf9 --- /dev/null +++ b/examples/chapter04/04-PalindromeChecker.js @@ -0,0 +1,8 @@ +const { palindromeChecker } = PacktDataStructuresAlgorithms; + +console.log('a', palindromeChecker('a')); +console.log('aa', palindromeChecker('aa')); +console.log('kayak', palindromeChecker('kayak')); +console.log('level', palindromeChecker('level')); +console.log('Was it a car or a cat I saw', palindromeChecker('Was it a car or a cat I saw')); +console.log('Step on no pets', palindromeChecker('Step on no pets')); diff --git a/examples/chapter05/01-LinkedList.html b/examples/chapter05/01-LinkedList.html new file mode 100644 index 00000000..df7c5591 --- /dev/null +++ b/examples/chapter05/01-LinkedList.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter05/01-LinkedList.js b/examples/chapter05/01-LinkedList.js new file mode 100644 index 00000000..8d7c6e49 --- /dev/null +++ b/examples/chapter05/01-LinkedList.js @@ -0,0 +1,48 @@ +const { LinkedList } = PacktDataStructuresAlgorithms; + +const list = new LinkedList(); + +console.log('push element 15'); +list.push(15); + +console.log('list.indexOf(15) => ', list.indexOf(15)); + +console.log('push element 10'); +list.push(10); + +console.log('list.toString() => ', list.toString()); +console.log('list.indexOf(10) => ', list.indexOf(10)); + +console.log('push element 13'); +list.push(13); + +console.log('list.toString() => ', list.toString()); +console.log('list.indexOf(13) => ', list.indexOf(13)); +console.log('list.indexOf(10) => ', list.indexOf(10)); + +console.log('push elements 11 and 12'); +list.push(11); +list.push(12); + +console.log('list.toString() => ', list.toString()); +console.log('list.removeAt(1) => ', list.removeAt(1)); +console.log('list.toString() => ', list.toString()); +console.log('list.removeAt(3) => ', list.removeAt(3)); +console.log('list.toString() => ', list.toString()); + +console.log('push element 14'); +list.push(14); + +console.log('list.toString() => ', list.toString()); +console.log('insert element 16 pos 0 => ', list.insert(16, 0)); +console.log('list.toString() => ', list.toString()); +console.log('insert element 17 pos 1 => ', list.insert(17, 1)); +console.log('list.toString() => ', list.toString()); +console.log('insert element 18 pos list.size() => ', list.insert(18, list.size())); +console.log('list.toString() => ', list.toString()); +console.log('remove element 16 => ', list.remove(16)); +console.log('list.toString() => ', list.toString()); +console.log('remove element 11 => ', list.remove(11)); +console.log('list.toString() => ', list.toString()); +console.log('remove element 18 => ', list.remove(18)); +console.log('list.toString() => ', list.toString()); diff --git a/examples/chapter05/02-DoublyLinkedList.html b/examples/chapter05/02-DoublyLinkedList.html new file mode 100644 index 00000000..1aea7c24 --- /dev/null +++ b/examples/chapter05/02-DoublyLinkedList.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter05/02-DoublyLinkedList.js b/examples/chapter05/02-DoublyLinkedList.js new file mode 100644 index 00000000..333f701c --- /dev/null +++ b/examples/chapter05/02-DoublyLinkedList.js @@ -0,0 +1,54 @@ +const { DoublyLinkedList } = PacktDataStructuresAlgorithms; + +const list = new DoublyLinkedList(); + +console.log('push element 15'); +list.push(15); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('push element 16'); +list.push(16); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('push element 17'); +list.push(17); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('insert element 13 pos 0 => ', list.insert(13, 0)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('insert element 18 pos 4 => ', list.insert(18, 4)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('insert element 14 pos 1 => ', list.insert(14, 1)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('list.removeAt(0) => ', list.removeAt(0)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('list.removeAt(list.size() - 1) => ', list.removeAt(list.size() - 1)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('list.removeAt(1) => ', list.removeAt(1)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('remove element 16 => ', list.remove(16)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('remove element 14 => ', list.remove(14)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); + +console.log('remove element 17 => ', list.remove(17)); +console.log('list.toString() => ', list.toString()); +console.log('list.inverseToString() => ', list.inverseToString()); diff --git a/examples/chapter05/03-CircularLinkedList.html b/examples/chapter05/03-CircularLinkedList.html new file mode 100644 index 00000000..10fbd6b6 --- /dev/null +++ b/examples/chapter05/03-CircularLinkedList.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter05/03-CircularLinkedList.js b/examples/chapter05/03-CircularLinkedList.js new file mode 100644 index 00000000..05178956 --- /dev/null +++ b/examples/chapter05/03-CircularLinkedList.js @@ -0,0 +1,32 @@ +const { CircularLinkedList } = PacktDataStructuresAlgorithms; + +const list = new CircularLinkedList(); + +console.log('push element 15'); +list.push(15); +console.log('list.toString() => ', list.toString()); + +console.log('push element 16'); +list.push(16); +console.log('list.toString() => ', list.toString()); + +console.log('insert element 14 pos 0 => ', list.insert(14, 0)); +console.log('list.toString() => ', list.toString()); + +console.log('insert element 14.5 pos 1 => ', list.insert(14.5, 1)); +console.log('list.toString() => ', list.toString()); + +console.log('insert element 17 pos 4 => ', list.insert(17, 4)); +console.log('list.toString() => ', list.toString()); + +console.log('list.removeAt(0) => ', list.removeAt(0)); +console.log('list.toString() => ', list.toString()); + +console.log('list.removeAt(1) => ', list.removeAt(1)); +console.log('list.toString() => ', list.toString()); + +console.log('list.removeAt(2) => ', list.removeAt(2)); +console.log('list.toString() => ', list.toString()); + +console.log('list.indexOf(14.5) => ', list.indexOf(14.5)); +console.log('list.indexOf(16) => ', list.indexOf(16)); diff --git a/examples/chapter05/04-SortedLinkedList.html b/examples/chapter05/04-SortedLinkedList.html new file mode 100644 index 00000000..9feb1a70 --- /dev/null +++ b/examples/chapter05/04-SortedLinkedList.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter05/04-SortedLinkedList.js b/examples/chapter05/04-SortedLinkedList.js new file mode 100644 index 00000000..c6351f8f --- /dev/null +++ b/examples/chapter05/04-SortedLinkedList.js @@ -0,0 +1,53 @@ +const { SortedLinkedList } = PacktDataStructuresAlgorithms; +const { util } = PacktDataStructuresAlgorithms; + +const list = new SortedLinkedList(); + +console.log(''); +for (let i = 5; i > 0; i--) { + list.push(i); +} + +console.log('list after pushing 5, 4, 3, 2, and 1 => ', list.toString()); + +console.log('list.removeAt(1) => ', list.removeAt(1)); + +console.log('remove element 16 => ', list.remove(5)); + +console.log('list.toString() => ', list.toString()); + +// ------- Example 02 + +class MyObj { + constructor(el1, el2) { + this.el1 = el1; + this.el2 = el2; + } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } +} + +function myObjCompare(a, b) { + return a.toString().localeCompare(b.toString()); +} + +const ds = new SortedLinkedList(util.defaultEquals, myObjCompare); + +console.log('*** SortedLinkedList with custom sorting function'); + +ds.push(new MyObj(3, 4)); +console.log('push MyObj(3, 4)'); +console.log('list.toString() => ', ds.toString()); + +ds.push(new MyObj(1, 2)); +console.log('push MyObj(1, 2)'); +console.log('list.toString() => ', ds.toString()); + +ds.push(new MyObj(5, 6)); +console.log('push MyObj(5, 6)'); +console.log('list.toString() => ', ds.toString()); + +ds.insert(new MyObj(0, 0), 4); +console.log('insert MyObj(0, 0) pos 4 (pos ignored)'); +console.log('list.toString() => ', ds.toString()); diff --git a/examples/chapter05/05-StackLinkedList.html b/examples/chapter05/05-StackLinkedList.html new file mode 100644 index 00000000..c511e977 --- /dev/null +++ b/examples/chapter05/05-StackLinkedList.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter05/05-StackLinkedList.js b/examples/chapter05/05-StackLinkedList.js new file mode 100644 index 00000000..866e7f91 --- /dev/null +++ b/examples/chapter05/05-StackLinkedList.js @@ -0,0 +1,24 @@ +const { StackLinkedList } = PacktDataStructuresAlgorithms; + +const stack = new StackLinkedList(); + +console.log('stack.isEmpty() => ', stack.isEmpty()); // outputs true + +stack.push(5); +stack.push(8); + +console.log('stack after push 5 and 8 => ', stack.toString()); + +console.log('stack.peek() => ', stack.peek()); // outputs 8 + +stack.push(11); + +console.log('stack.size() after push 11 => ', stack.size()); // outputs 3 +console.log('stack.isEmpty() => ', stack.isEmpty()); // outputs false + +stack.push(15); + +stack.pop(); +stack.pop(); + +console.log('stack.size() after push 15 and pop twice => ', stack.size()); // outputs 2 diff --git a/examples/chapter06/01-Set.html b/examples/chapter06/01-Set.html new file mode 100755 index 00000000..cf2f575b --- /dev/null +++ b/examples/chapter06/01-Set.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter06/01-Set.js b/examples/chapter06/01-Set.js new file mode 100755 index 00000000..e893c2c8 --- /dev/null +++ b/examples/chapter06/01-Set.js @@ -0,0 +1,19 @@ +const { Set } = PacktDataStructuresAlgorithms; + +const set = new Set(); + +set.add(1); +console.log(set.values()); // outputs [1] +console.log(set.has(1)); // outputs true +console.log(set.size()); // outputs 1 + +set.add(2); +console.log(set.values()); // outputs [1, 2] +console.log(set.has(2)); // true +console.log(set.size()); // 2 + +set.delete(1); +console.log(set.values()); // outputs [2] + +set.delete(2); +console.log(set.values()); // outputs [] diff --git a/examples/chapter06/02-SetOperations.html b/examples/chapter06/02-SetOperations.html new file mode 100755 index 00000000..b55d7e0d --- /dev/null +++ b/examples/chapter06/02-SetOperations.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter06/02-SetOperations.js b/examples/chapter06/02-SetOperations.js new file mode 100755 index 00000000..2608c7ef --- /dev/null +++ b/examples/chapter06/02-SetOperations.js @@ -0,0 +1,66 @@ +const { Set } = PacktDataStructuresAlgorithms; + +// --------- Union ---------- + +let setA = new Set(); +setA.add(1); +setA.add(2); +setA.add(3); + +let setB = new Set(); +setB.add(3); +setB.add(4); +setB.add(5); +setB.add(6); + +const unionAB = setA.union(setB); +console.log(unionAB.values()); // [1, 2, 3, 4, 5, 6] + +// --------- Intersection ---------- + +setA = new Set(); +setA.add(1); +setA.add(2); +setA.add(3); + +setB = new Set(); +setB.add(2); +setB.add(3); +setB.add(4); + +const intersectionAB = setA.intersection(setB); +console.log(intersectionAB.values()); // [2, 3] + +// --------- Difference ---------- + +setA = new Set(); +setA.add(1); +setA.add(2); +setA.add(3); + +setB = new Set(); +setB.add(2); +setB.add(3); +setB.add(4); + +const differenceAB = setA.difference(setB); +console.log(differenceAB.values()); // [1] + +// --------- Subset ---------- + +setA = new Set(); +setA.add(1); +setA.add(2); + +setB = new Set(); +setB.add(1); +setB.add(2); +setB.add(3); + +const setC = new Set(); +setC.add(2); +setC.add(3); +setC.add(4); + +console.log(setA.isSubsetOf(setB)); // true +console.log(setA.isSubsetOf(setC)); // false diff --git a/examples/chapter06/03-ES2015Set.html b/examples/chapter06/03-ES2015Set.html new file mode 100755 index 00000000..7fe40e1b --- /dev/null +++ b/examples/chapter06/03-ES2015Set.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter06/03-ES2015Set.js b/examples/chapter06/03-ES2015Set.js new file mode 100755 index 00000000..721ea4d1 --- /dev/null +++ b/examples/chapter06/03-ES2015Set.js @@ -0,0 +1,72 @@ +const set = new Set(); + +set.add(1); +console.log(set.values()); // outputs @Iterator +console.log(set.has(1)); // outputs true +console.log(set.size); // outputs 1 + +set.add(2); +console.log(set.values()); // outputs [1, 2] +console.log(set.has(2)); // true +console.log(set.size); // 2 + +set.delete(1); +console.log(set.values()); // outputs [2] + +set.delete(2); +console.log(set.values()); // outputs [] + +const setA = new Set(); +setA.add(1); +setA.add(2); +setA.add(3); + +const setB = new Set(); +setB.add(2); +setB.add(3); +setB.add(4); + +// --------- Union ---------- +const union = (set1, set2) => { + const unionAb = new Set(); + set1.forEach(value => unionAb.add(value)); + set2.forEach(value => unionAb.add(value)); + return unionAb; +}; +console.log(union(setA, setB)); + +console.log(new Set([...setA, ...setB])); + +// --------- Intersection ---------- +const intersection = (set1, set2) => { + const intersectionSet = new Set(); + set1.forEach(value => { + if (set2.has(value)) { + intersectionSet.add(value); + } + }); + return intersectionSet; +}; +console.log(intersection(setA, setB)); + +console.log(new Set([...setA].filter(x => setB.has(x)))); + +// alternative - works on FF only +// console.log(new Set([x for (x of setA) if (setB.has(x))])); + +// --------- Difference ---------- +const difference = (set1, set2) => { + const differenceSet = new Set(); + set1.forEach(value => { + if (!set2.has(value)) { + differenceSet.add(value); + } + }); + return differenceSet; +}; +console.log(difference(setA, setB)); + +console.log(new Set([...setA].filter(x => !setB.has(x)))); + +// alternative - works on FF only +// console.log(new Set([x for (x of setA) if (!setB.has(x))])); diff --git a/examples/chapter07/01-Dictionaries.html b/examples/chapter07/01-Dictionaries.html new file mode 100755 index 00000000..d972458c --- /dev/null +++ b/examples/chapter07/01-Dictionaries.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter07/01-Dictionaries.js b/examples/chapter07/01-Dictionaries.js new file mode 100755 index 00000000..53433cc0 --- /dev/null +++ b/examples/chapter07/01-Dictionaries.js @@ -0,0 +1,28 @@ +const { Dictionary } = PacktDataStructuresAlgorithms; + +const dictionary = new Dictionary(); + +dictionary.set('Gandalf', 'gandalf@email.com'); +dictionary.set('John', 'johnsnow@email.com'); +dictionary.set('Tyrion', 'tyrion@email.com'); + +console.log(dictionary.hasKey('Gandalf')); // true +console.log(dictionary.size()); // 3 + +console.log(dictionary.keys()); // ["Gandalf", "John", "Tyrion"] +console.log(dictionary.values()); // ["gandalf@email.com", "johnsnow@email.com", "tyrion@email.com"] +console.log(dictionary.get('Tyrion')); // tyrion@email.com + +dictionary.remove('John'); + +console.log(dictionary.keys()); // ["Gandalf", "Tyrion"] +console.log(dictionary.values()); // ["gandalf@email.com", "tyrion@email.com"] + +console.log(dictionary.keyValues()); // [{key: "Gandalf", value: "gandalf@email.com"}, {key: "Tyrion", value: "tyrion@email.com"}] +console.log(dictionary.toString()); // [#Gandalf: gandalf@email.com],[#Tyrion: tyrion@email.com] + +dictionary.forEach((k, v) => { + console.log('forEach: ', `key: ${k}, value: ${v}`); +}); +// forEach: key: Gandalf, value: gandalf@email.com +// forEach: key: Tyrion, value: tyrion@email.com diff --git a/examples/chapter07/02-HashTable.html b/examples/chapter07/02-HashTable.html new file mode 100755 index 00000000..67ad128c --- /dev/null +++ b/examples/chapter07/02-HashTable.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter07/02-HashTable.js b/examples/chapter07/02-HashTable.js new file mode 100755 index 00000000..0b99ef3b --- /dev/null +++ b/examples/chapter07/02-HashTable.js @@ -0,0 +1,45 @@ +const { HashTable } = PacktDataStructuresAlgorithms; + +const hash = new HashTable(); + +console.log(hash.hashCode('Ygritte') + ' - Ygritte'); +console.log(hash.hashCode('Jonathan') + ' - Jonathan'); +console.log(hash.hashCode('Jamie') + ' - Jamie'); +console.log(hash.hashCode('Jack') + ' - Jack'); +console.log(hash.hashCode('Jasmine') + ' - Jasmine'); +console.log(hash.hashCode('Jake') + ' - Jake'); +console.log(hash.hashCode('Nathan') + ' - Nathan'); +console.log(hash.hashCode('Athelstan') + ' - Athelstan'); +console.log(hash.hashCode('Sue') + ' - Sue'); +console.log(hash.hashCode('Aethelwulf') + ' - Aethelwulf'); +console.log(hash.hashCode('Sargeras') + ' - Sargeras'); + +hash.put('Ygritte', 'ygritte@email.com'); +hash.put('Jonathan', 'jonathan@email.com'); +hash.put('Jamie', 'jamie@email.com'); +hash.put('Jack', 'jack@email.com'); +hash.put('Jasmine', 'jasmine@email.com'); +hash.put('Jake', 'jake@email.com'); +hash.put('Nathan', 'nathan@email.com'); +hash.put('Athelstan', 'athelstan@email.com'); +hash.put('Sue', 'sue@email.com'); +hash.put('Aethelwulf', 'aethelwulf@email.com'); +hash.put('Sargeras', 'sargeras@email.com'); + +console.log('**** Printing Hash **** '); + +console.log(hash.toString()); +// {4 => [#Ygritte: ygritte@email.com]},{5 => [#Aethelwulf: aethelwulf@email.com]},{7 => [#Athelstan: athelstan@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Sargeras: sargeras@email.com]} + +console.log('**** Get **** '); + +console.log(hash.get('Ygritte')); // ygritte@email.com +console.log(hash.get('Loiane')); // jasmine@email.com + +console.log('**** Remove **** '); + +hash.remove('Ygritte'); +console.log(hash.get('Ygritte')); // undefined + +console.log(hash.toString()); +// {5 => [#Aethelwulf: aethelwulf@email.com]},{7 => [#Athelstan: athelstan@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Sargeras: sargeras@email.com]} diff --git a/examples/chapter07/03-HashCollisionSeparateChaining.html b/examples/chapter07/03-HashCollisionSeparateChaining.html new file mode 100755 index 00000000..cddcc3db --- /dev/null +++ b/examples/chapter07/03-HashCollisionSeparateChaining.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter07/03-HashCollisionSeparateChaining.js b/examples/chapter07/03-HashCollisionSeparateChaining.js new file mode 100755 index 00000000..f791ba89 --- /dev/null +++ b/examples/chapter07/03-HashCollisionSeparateChaining.js @@ -0,0 +1,46 @@ +const { HashTableSeparateChaining } = PacktDataStructuresAlgorithms; + +const hashTable = new HashTableSeparateChaining(); + +hashTable.put('Ygritte', 'ygritte@email.com'); +hashTable.put('Jonathan', 'jonathan@email.com'); +hashTable.put('Jamie', 'jamie@email.com'); +hashTable.put('Jack', 'jack@email.com'); +hashTable.put('Jasmine', 'jasmine@email.com'); +hashTable.put('Jake', 'jake@email.com'); +hashTable.put('Nathan', 'nathan@email.com'); +hashTable.put('Athelstan', 'athelstan@email.com'); +hashTable.put('Sue', 'sue@email.com'); +hashTable.put('Aethelwulf', 'aethelwulf@email.com'); +hashTable.put('Sargeras', 'sargeras@email.com'); + +console.log('**** Printing Hash **** '); + +console.log(hashTable.toString()); +// {4 => [#Ygritte: ygritte@email.com]},{5 => [#Jonathan: jonathan@email.com],[#Jamie: jamie@email.com],[#Sue: sue@email.com],[#Aethelwulf: aethelwulf@email.com]},{7 => [#Jack: jack@email.com],[#Athelstan: athelstan@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com],[#Sargeras: sargeras@email.com]} + +console.log('**** Get **** '); + +console.log(hashTable.get('Jamie')); // jamie@email.com +console.log(hashTable.get('Sue')); // sue@email.com +console.log(hashTable.get('Jonathan')); // jonathan@email.com +console.log(hashTable.get('Loiane')); // undefined + +console.log('**** Remove **** '); + +console.log(hashTable.remove('Ygritte')); // true +console.log(hashTable.get('Ygritte')); // undefined +console.log(hashTable.toString()); +// {5 => [#Jonathan: jonathan@email.com],[#Jamie: jamie@email.com],[#Sue: sue@email.com],[#Aethelwulf: aethelwulf@email.com]},{7 => [#Jack: jack@email.com],[#Athelstan: athelstan@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com],[#Sargeras: sargeras@email.com]} + +console.log(hashTable.remove('Sue')); // true +console.log(hashTable.toString()); +// {5 => [#Jonathan: jonathan@email.com],[#Jamie: jamie@email.com],[#Aethelwulf: aethelwulf@email.com]},{7 => [#Jack: jack@email.com],[#Athelstan: athelstan@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com],[#Sargeras: sargeras@email.com]} + +console.log(hashTable.remove('Jamie')); // true +console.log(hashTable.toString()); +// {5 => [#Jonathan: jonathan@email.com],[#Aethelwulf: aethelwulf@email.com]},{7 => [#Jack: jack@email.com],[#Athelstan: athelstan@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com],[#Sargeras: sargeras@email.com]} + +console.log(hashTable.remove('Aethelwulf')); // true +console.log(hashTable.toString()); +// {5 => [#Jonathan: jonathan@email.com]},{7 => [#Jack: jack@email.com],[#Athelstan: athelstan@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com],[#Sargeras: sargeras@email.com]} diff --git a/examples/chapter07/04-HashCollisionLinearProbing.html b/examples/chapter07/04-HashCollisionLinearProbing.html new file mode 100755 index 00000000..6f8bbe52 --- /dev/null +++ b/examples/chapter07/04-HashCollisionLinearProbing.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter07/04-HashCollisionLinearProbing.js b/examples/chapter07/04-HashCollisionLinearProbing.js new file mode 100755 index 00000000..b75cd98d --- /dev/null +++ b/examples/chapter07/04-HashCollisionLinearProbing.js @@ -0,0 +1,42 @@ +const { HashTableLinearProbing } = PacktDataStructuresAlgorithms; + +const hashTable = new HashTableLinearProbing(); + +hashTable.put('Ygritte', 'ygritte@email.com'); +hashTable.put('Jonathan', 'jonathan@email.com'); +hashTable.put('Jamie', 'jamie@email.com'); +hashTable.put('Jack', 'jack@email.com'); +hashTable.put('Jasmine', 'jasmine@email.com'); +hashTable.put('Jake', 'jake@email.com'); +hashTable.put('Nathan', 'nathan@email.com'); +hashTable.put('Athelstan', 'athelstan@email.com'); +hashTable.put('Sue', 'sue@email.com'); +hashTable.put('Aethelwulf', 'aethelwulf@email.com'); +hashTable.put('Sargeras', 'sargeras@email.com'); + +console.log('**** Printing Hash **** '); + +console.log(hashTable.toString()); +// {4 => [#Ygritte: ygritte@email.com]},{5 => [#Jonathan: jonathan@email.com]},{6 => [#Jamie: jamie@email.com]},{7 => [#Jack: jack@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com]},{11 => [#Athelstan: athelstan@email.com]},{12 => [#Sue: sue@email.com]},{13 => [#Aethelwulf: aethelwulf@email.com]},{14 => [#Sargeras: sargeras@email.com]} +console.log('**** Get **** '); + +console.log(hashTable.get('Nathan')); // nathan@email.com +console.log(hashTable.get('Loiane')); // undefined + +console.log('**** Remove **** '); + +hashTable.remove('Ygritte'); +console.log(hashTable.get('Ygritte')); // undefined +console.log(hashTable.toString()); +// {5 => [#Jonathan: jonathan@email.com]},{6 => [#Jamie: jamie@email.com]},{7 => [#Jack: jack@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com]},{11 => [#Athelstan: athelstan@email.com]},{12 => [#Sue: sue@email.com]},{13 => [#Aethelwulf: aethelwulf@email.com]},{14 => [#Sargeras: sargeras@email.com]} + +console.log('**** Remove Test 2 **** '); +console.log('Removing Jonathan', hashTable.remove('Jonathan')); // true +console.log('**** Print **** '); +console.log(hashTable.toString()); +// {5 => [#Jamie: jamie@email.com]},{6 => [#Sue: sue@email.com]},{7 => [#Jack: jack@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com]},{11 => [#Athelstan: athelstan@email.com]},{12 => [#Aethelwulf: aethelwulf@email.com]},{13 => [#Sargeras: sargeras@email.com]} + +console.log(hashTable.get('Jamie')); // jamie@email.com +console.log('**** Print **** '); +console.log(hashTable.toString()); +// {5 => [#Jamie: jamie@email.com]},{6 => [#Sue: sue@email.com]},{7 => [#Jack: jack@email.com]},{8 => [#Jasmine: jasmine@email.com]},{9 => [#Jake: jake@email.com]},{10 => [#Nathan: nathan@email.com]},{11 => [#Athelstan: athelstan@email.com]},{12 => [#Aethelwulf: aethelwulf@email.com]},{13 => [#Sargeras: sargeras@email.com]} diff --git a/chapter07/09-ES6Map.html b/examples/chapter07/05-ES2015Map.html old mode 100644 new mode 100755 similarity index 68% rename from chapter07/09-ES6Map.html rename to examples/chapter07/05-ES2015Map.html index e755aac7..8a2ade05 --- a/chapter07/09-ES6Map.html +++ b/examples/chapter07/05-ES2015Map.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter07/05-ES2015Map.js b/examples/chapter07/05-ES2015Map.js new file mode 100755 index 00000000..e944f988 --- /dev/null +++ b/examples/chapter07/05-ES2015Map.js @@ -0,0 +1,17 @@ +const map = new Map(); + +map.set('Gandalf', 'gandalf@email.com'); +map.set('John', 'johnsnow@email.com'); +map.set('Tyrion', 'tyrion@email.com'); + +console.log(map.has('Gandalf')); // true +console.log(map.size); // 3 + +console.log(map.keys()); // MapIterator {"Gandalf", "John", "Tyrion"} +console.log(map.values()); // MapIterator {"gandalf@email.com", "johnsnow@email.com", "tyrion@email.com"} +console.log(map.get('Tyrion')); // tyrion@email.com + +map.delete('John'); + +console.log(map.keys()); // MapIterator {"Gandalf", "Tyrion"} +console.log(map.values()); // MapIterator {"gandalf@email.com", "tyrion@email.com"} diff --git a/chapter07/10-ES6WeakMap.html b/examples/chapter07/06-ES2015WeakMap.html old mode 100644 new mode 100755 similarity index 67% rename from chapter07/10-ES6WeakMap.html rename to examples/chapter07/06-ES2015WeakMap.html index 25b28e2d..15daf46d --- a/chapter07/10-ES6WeakMap.html +++ b/examples/chapter07/06-ES2015WeakMap.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter07/06-ES2015WeakMap.js b/examples/chapter07/06-ES2015WeakMap.js new file mode 100755 index 00000000..1f2d01bc --- /dev/null +++ b/examples/chapter07/06-ES2015WeakMap.js @@ -0,0 +1,18 @@ +const map = new WeakMap(); + +const ob1 = { name: 'Gandalf' }; +const ob2 = { name: 'John' }; +const ob3 = { name: 'Tyrion' }; + +map.set(ob1, 'gandalf@email.com'); +map.set(ob2, 'johnsnow@email.com'); +map.set(ob3, 'tyrion@email.com'); + +console.log(map.has(ob1)); // true +console.log(map.has(ob2)); // true +console.log(map.has(ob3)); // true + +console.log(map.get(ob3)); // tyrion@email.com + +map.delete(ob2); +console.log(map.has(ob2)); // false diff --git a/chapter07/11-ES6WeakSet.html b/examples/chapter07/07-ES2015WeakSet.html old mode 100644 new mode 100755 similarity index 67% rename from chapter07/11-ES6WeakSet.html rename to examples/chapter07/07-ES2015WeakSet.html index 68710e76..7a4f82f4 --- a/chapter07/11-ES6WeakSet.html +++ b/examples/chapter07/07-ES2015WeakSet.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter07/07-ES2015WeakSet.js b/examples/chapter07/07-ES2015WeakSet.js new file mode 100755 index 00000000..0e849f1a --- /dev/null +++ b/examples/chapter07/07-ES2015WeakSet.js @@ -0,0 +1,16 @@ +var set = new WeakSet(); + +const ob1 = { name: 'Gandalf' }; +const ob2 = { name: 'John' }; +const ob3 = { name: 'Tyrion' }; + +set.add(ob1); +set.add(ob2); +set.add(ob3); + +console.log(set.has(ob1)); // true +console.log(set.has(ob2)); // true +console.log(set.has(ob3)); // true + +set.delete(ob2); +console.log(set.has(ob2)); // false diff --git a/chapter03/01-Stack.html b/examples/chapter08/01-IntroRecursion.html old mode 100755 new mode 100644 similarity index 60% rename from chapter03/01-Stack.html rename to examples/chapter08/01-IntroRecursion.html index e936b4e1..ae42e54f --- a/chapter03/01-Stack.html +++ b/examples/chapter08/01-IntroRecursion.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter08/01-IntroRecursion.js b/examples/chapter08/01-IntroRecursion.js new file mode 100644 index 00000000..d2843215 --- /dev/null +++ b/examples/chapter08/01-IntroRecursion.js @@ -0,0 +1,26 @@ + +// functions commented below - inifite loop + +/* +function recursiveFunction(someParam) { + recursiveFunction(someParam); +} + +function recursiveFunction1(someParam) { + recursiveFunction2(someParam); +} + +function recursiveFunction2(someParam) { + recursiveFunction1(someParam); +} +*/ + +function understandRecursion(doIunderstandRecursion) { + const recursionAnswer = confirm('Do you understand recursion?'); // function logic + if (recursionAnswer === true) { // base case or stop point + return true; + } + understandRecursion(recursionAnswer); // recursive call +} + +understandRecursion(false); diff --git a/chapter04/01-Queue.html b/examples/chapter08/02-Factorial.html similarity index 60% rename from chapter04/01-Queue.html rename to examples/chapter08/02-Factorial.html index 4220c419..94c6c103 100755 --- a/chapter04/01-Queue.html +++ b/examples/chapter08/02-Factorial.html @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/examples/chapter08/02-Factorial.js b/examples/chapter08/02-Factorial.js new file mode 100644 index 00000000..774006f9 --- /dev/null +++ b/examples/chapter08/02-Factorial.js @@ -0,0 +1,26 @@ +// @ts-check + +function factorialIterative(number) { + if (number < 0) { + return undefined; + } + let total = 1; + for (let n = number; n > 1; n--) { + total = total * n; + } + return total; +} + +console.log('factorialIterative(5): ', factorialIterative(5)); +console.log('factorialIterative(3): ', factorialIterative(3)); + +function factorial(n) { + // console.trace(); + if (n === 1 || n === 0) { + return 1; + } + return n * factorial(n - 1); +} + +console.log('factorial(5): ', factorial(5)); +console.log('factorial(3): ', factorial(3)); diff --git a/examples/chapter08/03-JSCallStack.html b/examples/chapter08/03-JSCallStack.html new file mode 100644 index 00000000..0f88040d --- /dev/null +++ b/examples/chapter08/03-JSCallStack.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/chapter08/03-JSCallStack.js b/examples/chapter08/03-JSCallStack.js new file mode 100644 index 00000000..fcfd6ef8 --- /dev/null +++ b/examples/chapter08/03-JSCallStack.js @@ -0,0 +1,11 @@ +let i = 0; +function recursiveFn() { + i++; + recursiveFn(); +} + +try { + recursiveFn(); +} catch (ex) { + console.log('i = ' + i + ' error: ' + ex); +} diff --git a/examples/chapter08/04-Fibonacci.html b/examples/chapter08/04-Fibonacci.html new file mode 100755 index 00000000..8413c457 --- /dev/null +++ b/examples/chapter08/04-Fibonacci.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/chapter08/04-Fibonacci.js b/examples/chapter08/04-Fibonacci.js new file mode 100644 index 00000000..3b395774 --- /dev/null +++ b/examples/chapter08/04-Fibonacci.js @@ -0,0 +1,43 @@ +function fibonacci(n){ + if (n < 1) return 0; // {1} + if (n <= 2) return 1; // {2} + return fibonacci(n - 1) + fibonacci(n - 2); // {3} +} + +console.log('fibonacci(2)', fibonacci(2)); +console.log('fibonacci(3)', fibonacci(3)); +console.log('fibonacci(4)', fibonacci(4)); +console.log('fibonacci(5)', fibonacci(5)); + +function fibonacciIterative(n){ + let fibNMinus2 = 0; + let fibNMinus1 = 1; + let fibN = n; + for (let i = 2; i <= n; i++) { // n >= 2 + fibN = fibNMinus1 + fibNMinus2; // f(n-1) + f(n-2) + fibNMinus2 = fibNMinus1; + fibNMinus1 = fibN; + } + return fibN; +} + +console.log('fibonacciIterative(2)', fibonacciIterative(2)); +console.log('fibonacciIterative(3)', fibonacciIterative(3)); +console.log('fibonacciIterative(4)', fibonacciIterative(4)); +console.log('fibonacciIterative(5)', fibonacciIterative(5)); + +function fibonacciMemoization(n) { + const memo = [0, 1]; + const fibonacci = (n) => { + if (memo[n] != null) return memo[n]; + return memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo); + }; + return fibonacci(n); +} + +console.log('fibonacciMemoization(2)', fibonacciMemoization(2)); +console.log('fibonacciMemoization(3)', fibonacciMemoization(3)); +console.log('fibonacciMemoization(4)', fibonacciMemoization(4)); +console.log('fibonacciMemoization(5)', fibonacciMemoization(5)); + +// https://jsperf.com/fibonacci-comparison-jsbook diff --git a/examples/chapter11/01-UsingGraphs.html b/examples/chapter11/01-UsingGraphs.html new file mode 100644 index 00000000..49eb9bff --- /dev/null +++ b/examples/chapter11/01-UsingGraphs.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter11/01-UsingGraphs.js b/examples/chapter11/01-UsingGraphs.js new file mode 100644 index 00000000..dde70c0d --- /dev/null +++ b/examples/chapter11/01-UsingGraphs.js @@ -0,0 +1,23 @@ +const { Graph } = PacktDataStructuresAlgorithms; + +const graph = new Graph(); + +const myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']; + +for (let i = 0; i < myVertices.length; i++) { + graph.addVertex(myVertices[i]); +} +graph.addEdge('A', 'B'); +graph.addEdge('A', 'C'); +graph.addEdge('A', 'D'); +graph.addEdge('C', 'D'); +graph.addEdge('C', 'G'); +graph.addEdge('D', 'G'); +graph.addEdge('D', 'H'); +graph.addEdge('B', 'E'); +graph.addEdge('B', 'F'); +graph.addEdge('E', 'I'); + +console.log('********* printing graph ***********'); + +console.log(graph.toString()); diff --git a/examples/chapter11/02-BFS.html b/examples/chapter11/02-BFS.html new file mode 100644 index 00000000..044da5d6 --- /dev/null +++ b/examples/chapter11/02-BFS.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter11/02-BFS.js b/examples/chapter11/02-BFS.js new file mode 100644 index 00000000..d65c8b66 --- /dev/null +++ b/examples/chapter11/02-BFS.js @@ -0,0 +1,54 @@ +const { Graph } = PacktDataStructuresAlgorithms; +const { Stack } = PacktDataStructuresAlgorithms; +const { BFS } = PacktDataStructuresAlgorithms; +const { breadthFirstSearch } = PacktDataStructuresAlgorithms; + +const graph = new Graph(); + +const myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']; + +for (let i = 0; i < myVertices.length; i++) { + graph.addVertex(myVertices[i]); +} +graph.addEdge('A', 'B'); +graph.addEdge('A', 'C'); +graph.addEdge('A', 'D'); +graph.addEdge('C', 'D'); +graph.addEdge('C', 'G'); +graph.addEdge('D', 'G'); +graph.addEdge('D', 'H'); +graph.addEdge('B', 'E'); +graph.addEdge('B', 'F'); +graph.addEdge('E', 'I'); + +console.log('********* printing graph ***********'); + +console.log(graph.toString()); + +console.log('********* bfs with callback ***********'); + +const printVertex = (value) => console.log('Visited vertex: ' + value); + +breadthFirstSearch(graph, myVertices[0], printVertex); + +console.log('********* sorthest path - BFS ***********'); +const shortestPathA = BFS(graph, myVertices[0]); +console.log(shortestPathA.distances); +console.log(shortestPathA.predecessors); + +//from A to all other vertices +const fromVertex = myVertices[0]; + +for (let i = 1; i < myVertices.length; i++) { + const toVertex = myVertices[i]; + const path = new Stack(); + for (let v = toVertex; v !== fromVertex; v = shortestPathA.predecessors[v]) { + path.push(v); + } + path.push(fromVertex); + let s = path.pop(); + while (!path.isEmpty()) { + s += ' - ' + path.pop(); + } + console.log(s); +} diff --git a/examples/chapter11/03-DFS.html b/examples/chapter11/03-DFS.html new file mode 100644 index 00000000..1a5bd4f3 --- /dev/null +++ b/examples/chapter11/03-DFS.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter11/03-DFS.js b/examples/chapter11/03-DFS.js new file mode 100644 index 00000000..c29686eb --- /dev/null +++ b/examples/chapter11/03-DFS.js @@ -0,0 +1,67 @@ +const { Graph } = PacktDataStructuresAlgorithms; +const { depthFirstSearch } = PacktDataStructuresAlgorithms; +const { DFS } = PacktDataStructuresAlgorithms; + +let graph = new Graph(true); + +let myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']; + +for (let i = 0; i < myVertices.length; i++) { + graph.addVertex(myVertices[i]); +} +graph.addEdge('A', 'B'); +graph.addEdge('A', 'C'); +graph.addEdge('A', 'D'); +graph.addEdge('C', 'D'); +graph.addEdge('C', 'G'); +graph.addEdge('D', 'G'); +graph.addEdge('D', 'H'); +graph.addEdge('B', 'E'); +graph.addEdge('B', 'F'); +graph.addEdge('E', 'I'); + +console.log('********* printing graph ***********'); + +console.log(graph.toString()); + +console.log('********* dfs with callback ***********'); + +const printVertex = value => console.log('Visited vertex: ' + value); + +depthFirstSearch(graph, printVertex); + +console.log('********* topological sort - DFS ***********'); + +graph = new Graph(true); // directed graph + +myVertices = ['A', 'B', 'C', 'D', 'E', 'F']; +for (i = 0; i < myVertices.length; i++) { + graph.addVertex(myVertices[i]); +} +graph.addEdge('A', 'C'); +graph.addEdge('A', 'D'); +graph.addEdge('B', 'D'); +graph.addEdge('B', 'E'); +graph.addEdge('C', 'F'); +graph.addEdge('F', 'E'); + +const result = DFS(graph); +console.log(result.discovery); +console.log(result.finished); +console.log(result.predecessors); + +const fTimes = result.finished; +s = ''; +for (let count = 0; count < myVertices.length; count++) { + let max = 0; + let maxName = null; + for (i = 0; i < myVertices.length; i++) { + if (fTimes[myVertices[i]] > max) { + max = fTimes[myVertices[i]]; + maxName = myVertices[i]; + } + } + s += ' - ' + maxName; + delete fTimes[maxName]; +} +console.log(s); diff --git a/examples/chapter11/04-Dijkstra.html b/examples/chapter11/04-Dijkstra.html new file mode 100644 index 00000000..8b9e0f74 --- /dev/null +++ b/examples/chapter11/04-Dijkstra.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter11/04-Dijkstra.js b/examples/chapter11/04-Dijkstra.js new file mode 100644 index 00000000..fbba7ec1 --- /dev/null +++ b/examples/chapter11/04-Dijkstra.js @@ -0,0 +1,18 @@ +const { dijkstra } = PacktDataStructuresAlgorithms; + +const graph = [ + [0, 2, 4, 0, 0, 0], + [0, 0, 2, 4, 2, 0], + [0, 0, 0, 0, 3, 0], + [0, 0, 0, 0, 0, 2], + [0, 0, 0, 3, 0, 2], + [0, 0, 0, 0, 0, 0] +]; + +console.log("********* Dijkstra's Algorithm - Shortest Path ***********"); + +var dist = dijkstra(graph, 0); + +for (i = 0; i < dist.length; i++){ + console.log(i + '\t\t' + dist[i]); +} diff --git a/examples/chapter11/05-Floyd-Warshall.html b/examples/chapter11/05-Floyd-Warshall.html new file mode 100644 index 00000000..be411625 --- /dev/null +++ b/examples/chapter11/05-Floyd-Warshall.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter11/05-Floyd-Warshall.js b/examples/chapter11/05-Floyd-Warshall.js new file mode 100644 index 00000000..18c2dee6 --- /dev/null +++ b/examples/chapter11/05-Floyd-Warshall.js @@ -0,0 +1,25 @@ +const { floydWarshall } = PacktDataStructuresAlgorithms; + +const INF = Infinity; +const graph = [ + [INF, 2, 4, INF, INF, INF], + [INF, INF, 2, 4, 2, INF], + [INF, INF, INF, INF, 3, INF], + [INF, INF, INF, INF, INF, 2], + [INF, INF, INF, 3, INF, 2], + [INF, INF, INF, INF, INF, INF] +]; + +console.log('********* Floyd-Warshall Algorithm - All-Pairs Shortest Path ***********'); + +dist = floydWarshall(graph); + +let s = ''; +for (let i = 0; i < dist.length; ++i) { + s = ''; + for (var j = 0; j < dist.length; ++j) { + if (dist[i][j] === INF) s += 'INF '; + else s += dist[i][j] + ' '; + } + console.log(s); +} diff --git a/examples/chapter11/06-Prim.html b/examples/chapter11/06-Prim.html new file mode 100644 index 00000000..7cbb2537 --- /dev/null +++ b/examples/chapter11/06-Prim.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter11/06-Prim.js b/examples/chapter11/06-Prim.js new file mode 100644 index 00000000..cb971a4e --- /dev/null +++ b/examples/chapter11/06-Prim.js @@ -0,0 +1,19 @@ +const { prim } = PacktDataStructuresAlgorithms; + +const graph = [ + [0, 2, 4, 0, 0, 0], + [2, 0, 2, 4, 2, 0], + [4, 2, 0, 0, 3, 0], + [0, 4, 0, 0, 3, 2], + [0, 2, 3, 3, 0, 2], + [0, 0, 0, 2, 2, 0] +]; + +console.log("********* Prim's Algorithm - Minimum Spanning Tree ***********"); + +const parent = prim(graph); + +console.log('Edge Weight'); +for (let i = 1; i < graph.length; i++) { + console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]); +} diff --git a/examples/chapter11/07-Kruskal.html b/examples/chapter11/07-Kruskal.html new file mode 100644 index 00000000..89e214fb --- /dev/null +++ b/examples/chapter11/07-Kruskal.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter11/07-Kruskal.js b/examples/chapter11/07-Kruskal.js new file mode 100644 index 00000000..460fbd83 --- /dev/null +++ b/examples/chapter11/07-Kruskal.js @@ -0,0 +1,19 @@ +const { kruskal } = PacktDataStructuresAlgorithms; + +const graph = [ + [0, 2, 4, 0, 0, 0], + [2, 0, 2, 4, 2, 0], + [4, 2, 0, 0, 3, 0], + [0, 4, 0, 0, 3, 2], + [0, 2, 3, 3, 0, 2], + [0, 0, 0, 2, 2, 0] +]; + +console.log('********* Kruskal Algorithm - Minimum Spanning Tree ***********'); + +const parent = kruskal(graph); + +console.log('Edge Weight'); +for (i = 1; i < graph.length; i++) { + console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]); +} diff --git a/examples/chapter12/01-BubbleSort.html b/examples/chapter12/01-BubbleSort.html new file mode 100644 index 00000000..88d9a1da --- /dev/null +++ b/examples/chapter12/01-BubbleSort.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter12/01-BubbleSort.js b/examples/chapter12/01-BubbleSort.js new file mode 100644 index 00000000..fff8970e --- /dev/null +++ b/examples/chapter12/01-BubbleSort.js @@ -0,0 +1,12 @@ +const { bubbleSort } = PacktDataStructuresAlgorithms; + +function createNonSortedArray(){ + var array = []; + for (let i = 5; i > 0; i--){ + array.push(i); + } + return array; +} + +const array = bubbleSort(createNonSortedArray()); +console.log(array); diff --git a/examples/chapter12/01-BucketSort.html b/examples/chapter12/01-BucketSort.html new file mode 100644 index 00000000..88d9a1da --- /dev/null +++ b/examples/chapter12/01-BucketSort.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter12/01-BucketSort.js b/examples/chapter12/01-BucketSort.js new file mode 100644 index 00000000..fff8970e --- /dev/null +++ b/examples/chapter12/01-BucketSort.js @@ -0,0 +1,12 @@ +const { bubbleSort } = PacktDataStructuresAlgorithms; + +function createNonSortedArray(){ + var array = []; + for (let i = 5; i > 0; i--){ + array.push(i); + } + return array; +} + +const array = bubbleSort(createNonSortedArray()); +console.log(array); diff --git a/examples/chapter12/01-CountingSort.html b/examples/chapter12/01-CountingSort.html new file mode 100644 index 00000000..88d9a1da --- /dev/null +++ b/examples/chapter12/01-CountingSort.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter12/01-CountingSort.js b/examples/chapter12/01-CountingSort.js new file mode 100644 index 00000000..fff8970e --- /dev/null +++ b/examples/chapter12/01-CountingSort.js @@ -0,0 +1,12 @@ +const { bubbleSort } = PacktDataStructuresAlgorithms; + +function createNonSortedArray(){ + var array = []; + for (let i = 5; i > 0; i--){ + array.push(i); + } + return array; +} + +const array = bubbleSort(createNonSortedArray()); +console.log(array); diff --git a/examples/chapter12/01-InsertionSort.html b/examples/chapter12/01-InsertionSort.html new file mode 100644 index 00000000..88d9a1da --- /dev/null +++ b/examples/chapter12/01-InsertionSort.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter12/01-InsertionSort.js b/examples/chapter12/01-InsertionSort.js new file mode 100644 index 00000000..fff8970e --- /dev/null +++ b/examples/chapter12/01-InsertionSort.js @@ -0,0 +1,12 @@ +const { bubbleSort } = PacktDataStructuresAlgorithms; + +function createNonSortedArray(){ + var array = []; + for (let i = 5; i > 0; i--){ + array.push(i); + } + return array; +} + +const array = bubbleSort(createNonSortedArray()); +console.log(array); diff --git a/examples/chapter12/01-MergeSort.html b/examples/chapter12/01-MergeSort.html new file mode 100644 index 00000000..88d9a1da --- /dev/null +++ b/examples/chapter12/01-MergeSort.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter12/01-MergeSort.js b/examples/chapter12/01-MergeSort.js new file mode 100644 index 00000000..fff8970e --- /dev/null +++ b/examples/chapter12/01-MergeSort.js @@ -0,0 +1,12 @@ +const { bubbleSort } = PacktDataStructuresAlgorithms; + +function createNonSortedArray(){ + var array = []; + for (let i = 5; i > 0; i--){ + array.push(i); + } + return array; +} + +const array = bubbleSort(createNonSortedArray()); +console.log(array); diff --git a/examples/chapter12/01-QuickSort.html b/examples/chapter12/01-QuickSort.html new file mode 100644 index 00000000..88d9a1da --- /dev/null +++ b/examples/chapter12/01-QuickSort.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter12/01-QuickSort.js b/examples/chapter12/01-QuickSort.js new file mode 100644 index 00000000..fff8970e --- /dev/null +++ b/examples/chapter12/01-QuickSort.js @@ -0,0 +1,12 @@ +const { bubbleSort } = PacktDataStructuresAlgorithms; + +function createNonSortedArray(){ + var array = []; + for (let i = 5; i > 0; i--){ + array.push(i); + } + return array; +} + +const array = bubbleSort(createNonSortedArray()); +console.log(array); diff --git a/examples/chapter12/01-RadixSort.html b/examples/chapter12/01-RadixSort.html new file mode 100644 index 00000000..88d9a1da --- /dev/null +++ b/examples/chapter12/01-RadixSort.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter12/01-RadixSort.js b/examples/chapter12/01-RadixSort.js new file mode 100644 index 00000000..fff8970e --- /dev/null +++ b/examples/chapter12/01-RadixSort.js @@ -0,0 +1,12 @@ +const { bubbleSort } = PacktDataStructuresAlgorithms; + +function createNonSortedArray(){ + var array = []; + for (let i = 5; i > 0; i--){ + array.push(i); + } + return array; +} + +const array = bubbleSort(createNonSortedArray()); +console.log(array); diff --git a/examples/chapter12/01-SelectionSort.html b/examples/chapter12/01-SelectionSort.html new file mode 100644 index 00000000..88d9a1da --- /dev/null +++ b/examples/chapter12/01-SelectionSort.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/chapter12/01-SelectionSort.js b/examples/chapter12/01-SelectionSort.js new file mode 100644 index 00000000..fff8970e --- /dev/null +++ b/examples/chapter12/01-SelectionSort.js @@ -0,0 +1,12 @@ +const { bubbleSort } = PacktDataStructuresAlgorithms; + +function createNonSortedArray(){ + var array = []; + for (let i = 5; i > 0; i--){ + array.push(i); + } + return array; +} + +const array = bubbleSort(createNonSortedArray()); +console.log(array); diff --git a/examples/examples-screenshot.png b/examples/examples-screenshot.png new file mode 100644 index 00000000..a264d142 Binary files /dev/null and b/examples/examples-screenshot.png differ diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 00000000..5a9bb80c --- /dev/null +++ b/examples/index.html @@ -0,0 +1,212 @@ + + + + + + Data Structures and Algorithms with JavaScript + + + + + + + + + + +
+
+
+ + Learning JavaScript Data Structures and Algorithms +
+ +
+ 01 + 02 + 03 + 04 + 05 + 06 + 07 + 08 + 09 + 10 + 11 +
+
+
+ Please open the Developer Tools Console to see the output + +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + + diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..f90fd865 --- /dev/null +++ b/firebase.json @@ -0,0 +1,11 @@ +{ + "hosting": { + "public": "examples", + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 00000000..49805359 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES5", + "module": "commonjs" , + "outDir": "./dist/js", + "removeComments": true, + "strict": true + }, + "exclude": ["node_modules", "dist"], + "include": ["src/js/**/*","html/**"] +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..28aa6962 --- /dev/null +++ b/package.json @@ -0,0 +1,80 @@ +{ + "name": "javascript-datastructures-algorithms", + "version": "0.0.1", + "description": "Learning JavaScript Data Structures and Algorithms", + "repository": { + "type": "git", + "url": "git+https://github.com/loiane/javascript-datastructures-algorithms.git" + }, + "keywords": [], + "author": "Loiane Groner", + "license": "MIT", + "bugs": { + "url": "https://github.com/loiane/javascript-datastructures-algorithms/issues" + }, + "homepage": "https://github.com/loiane/javascript-datastructures-algorithms", + "scripts": { + "clean": "rm -rf ./dist ./coverage ./.nyc_output ./coverage.lcov ./mochawesome-report", + "build:js": "babel src/js -d dist/js", + "build:ts": "tsc -p ./ --rootDir ./src/ts", + "build": "npm run build:js && npm run build:ts", + "lint:js": "eslint src/js && eslint test/js", + "lint:ts": "tslint -c tslint.json 'src/ts/**/*.ts' && tslint -c tslint.json 'test/ts/**/*.ts'", + "lint": "npm run lint:js && npm run lint:ts", + "test:js": "mocha --compilers js:babel-core/register ./test/js --recursive --reporter mochawesome", + "test:ts": "mocha -r ts-node/register ./test/ts/**/*.spec.ts ./test/ts/**/**/*.spec.ts --recursive", + "test": "npm run test:js && npm run test:ts", + "dev": "npm run clean && npm run lint && npm run webpack && npm run generate-report", + "dev2": "npm run clean && npm run lint && npm run webpack && npm run generate-report2", + "coverage": "npm run generate-report && nyc report --reporter=text-lcov > coverage.lcov && codecov", + "generate-report": "nyc --report-dir coverage npm run test && nyc report --reporter=text", + "generate-report2": "nyc --report-dir coverage npm run test", + "go": "npm run clean && npm run lint && npm run build && npm run test", + "webpack": "webpack --env build" + }, + "nyc": { + "include": [ + "src/ts/*.ts", + "src/ts/**/*.ts", + "src/js/*.js", + "src/js/**/*.js" + ], + "exclude": [ + "typings" + ], + "extension": [ + ".ts", + ".js" + ], + "reporter": [ + "json", + "html" + ], + "all": true + }, + "devDependencies": { + "@types/chai": "^4.0.10", + "@types/mocha": "^2.2.45", + "babel-cli": "^6.26.0", + "babel-core": "^6.26.0", + "babel-eslint": "^8.1.0", + "babel-loader": "^7.1.2", + "babel-plugin-add-module-exports": "^0.2.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-preset-env": "^1.6.1", + "chai": "^4.1.2", + "codecov": "^3.0.0", + "eslint": "^4.14.0", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-plugin-import": "^2.8.0", + "istanbul": "^v1.1.0-alpha.1", + "mocha": "^4.0.1", + "mochawesome": "^3.0.0", + "nyc": "^11.4.1", + "ts-node": "^4.1.0", + "tslint": "^5.8.0", + "typescript": "^2.6.2", + "webpack": "^3.10.0", + "yargs": "^10.0.3" + } +} diff --git a/src/js/algorithms/backtracking/rat-in-maze.js b/src/js/algorithms/backtracking/rat-in-maze.js new file mode 100644 index 00000000..36174e16 --- /dev/null +++ b/src/js/algorithms/backtracking/rat-in-maze.js @@ -0,0 +1,41 @@ +function isSafe(maze, x, y) { + const n = maze.length; + if (x >= 0 && y >= 0 && x < n && y < n && maze[x][y] !== 0) { + return true; + } + return false; +} + +function findPath(maze, x, y, solution) { + const n = maze.length; + if (x === n - 1 && y === n - 1) { + solution[x][y] = 1; + return true; + } + if (isSafe(maze, x, y) === true) { + solution[x][y] = 1; + if (findPath(maze, x + 1, y, solution)) { + return true; + } + if (findPath(maze, x, y + 1, solution)) { + return true; + } + solution[x][y] = 0; + return false; + } + return false; +} + +export function ratInAMaze(maze) { + const solution = []; + for (let i = 0; i < maze.length; i++) { + solution[i] = []; + for (let j = 0; j < maze[i].length; j++) { + solution[i][j] = 0; + } + } + if (findPath(maze, 0, 0, solution) === false) { + return solution; + } + return 'NO PATH FOUND'; +} diff --git a/src/js/algorithms/backtracking/sudoku-solver.js b/src/js/algorithms/backtracking/sudoku-solver.js new file mode 100644 index 00000000..48d98bd5 --- /dev/null +++ b/src/js/algorithms/backtracking/sudoku-solver.js @@ -0,0 +1,76 @@ +const UNASSIGNED = 0; + +function usedInRow(matrix, row, num) { + for (let col = 0; col < matrix.length; col++) { + if (matrix[row][col] === num) { + return true; + } + } + return false; +} + +function usedInCol(matrix, col, num) { + for (let row = 0; row < matrix.length; row++) { + if (matrix[row][col] === num) { + return true; + } + } + return false; +} + +function usedInBox(matrix, boxStartRow, boxStartCol, num) { + for (let row = 0; row < 3; row++) { + for (let col = 0; col < 3; col++) { + if (matrix[row + boxStartRow][col + boxStartCol] === num) { + return true; + } + } + } + return false; +} + +function isSafe(matrix, row, col, num) { + return ( + !usedInRow(matrix, row, num) && + !usedInCol(matrix, col, num) && + !usedInBox(matrix, row - (row % 3), col - (col % 3), num) + ); +} +function solveSudoku(matrix) { + let row = 0; + let col = 0; + let checkBlankSpaces = false; + + for (row = 0; row < matrix.length; row++) { + for (col = 0; col < matrix[row].length; col++) { + if (matrix[row][col] === UNASSIGNED) { + checkBlankSpaces = true; + break; + } + } + if (checkBlankSpaces === true) { + break; + } + } + if (checkBlankSpaces === false) { + return true; + } + + for (let num = 1; num <= 9; num++) { + if (isSafe(matrix, row, col, num)) { + matrix[row][col] = num; + if (solveSudoku(matrix)) { + return true; + } + matrix[row][col] = UNASSIGNED; + } + } + return false; +} + +export function sudokuSolver(matrix) { + if (solveSudoku(matrix) === true) { + return matrix; + } + return 'NO SOLUTION EXISTS!'; +} diff --git a/src/js/algorithms/dynamic-programing/knapsack-recursive.js b/src/js/algorithms/dynamic-programing/knapsack-recursive.js new file mode 100644 index 00000000..fbe646e1 --- /dev/null +++ b/src/js/algorithms/dynamic-programing/knapsack-recursive.js @@ -0,0 +1,11 @@ +export function knapSack(capacity, weights, values, n) { + if (n === 0 || capacity === 0) { + return 0; + } + if (weights[n - 1] > capacity) { + return knapSack(capacity, weights, values, n - 1); + } + const a = values[n - 1] + knapSack(capacity - weights[n - 1], weights, values, n - 1); + const b = knapSack(capacity, weights, values, n - 1); + return a > b ? a : b; +} diff --git a/src/js/algorithms/dynamic-programing/knapsack.js b/src/js/algorithms/dynamic-programing/knapsack.js new file mode 100644 index 00000000..14919f44 --- /dev/null +++ b/src/js/algorithms/dynamic-programing/knapsack.js @@ -0,0 +1,41 @@ +function findValues(n, capacity, kS) { + let i = n; + let k = capacity; + // console.log('Items that are part of the solution:'); + while (i > 0 && k > 0) { + if (kS[i][k] !== kS[i - 1][k]) { + /* console.log( + 'item ' + i + ' can be part of solution w,v: ' + weights[i - 1] + ',' + values[i - 1] + ); */ + i--; + k -= kS[i][k]; + } else { + i--; + } + } +} + +export function knapSack(capacity, weights, values, n) { + const kS = []; + for (let i = 0; i <= n; i++) { + kS[i] = []; + } + for (let i = 0; i <= n; i++) { + for (let w = 0; w <= capacity; w++) { + if (i === 0 || w === 0) { + kS[i][w] = 0; + } else if (weights[i - 1] <= w) { + const a = values[i - 1] + kS[i - 1][w - weights[i - 1]]; + const b = kS[i - 1][w]; + kS[i][w] = a > b ? a : b; // max(a,b) + // console.log(a + ' can be part of the solution'); + } else { + kS[i][w] = kS[i - 1][w]; + } + } + // console.log(kS[i].join()); + } + // extra algorithm to find the items that are part of the solution + findValues(n, capacity, kS); + return kS[n][capacity]; +} diff --git a/src/js/algorithms/dynamic-programing/longest-common-subsequence-print.js b/src/js/algorithms/dynamic-programing/longest-common-subsequence-print.js new file mode 100644 index 00000000..0ddc1b37 --- /dev/null +++ b/src/js/algorithms/dynamic-programing/longest-common-subsequence-print.js @@ -0,0 +1,52 @@ +function printSolution(solution, wordX, m, n) { + let a = m; + let b = n; + let x = solution[a][b]; + let answer = ''; + while (x !== '0') { + if (solution[a][b] === 'diagonal') { + answer = wordX[a - 1] + answer; + a--; + b--; + } else if (solution[a][b] === 'left') { + b--; + } else if (solution[a][b] === 'top') { + a--; + } + x = solution[a][b]; + } + // console.log('lcs: ' + answer); +} +export function lcs(wordX, wordY) { + const m = wordX.length; + const n = wordY.length; + const l = []; + const solution = []; + for (let i = 0; i <= m; i++) { + l[i] = []; + solution[i] = []; + for (let j = 0; j <= n; j++) { + l[i][j] = 0; + solution[i][j] = '0'; + } + } + for (let i = 0; i <= m; i++) { + for (let j = 0; j <= n; j++) { + if (i === 0 || j === 0) { + l[i][j] = 0; + } else if (wordX[i - 1] === wordY[j - 1]) { + l[i][j] = l[i - 1][j - 1] + 1; + solution[i][j] = 'diagonal'; + } else { + const a = l[i - 1][j]; + const b = l[i][j - 1]; + l[i][j] = a > b ? a : b; // max(a,b) + solution[i][j] = l[i][j] === l[i - 1][j] ? 'top' : 'left'; + } + } + // console.log(l[i].join()); + // console.log(solution[i].join()); + } + printSolution(solution, wordX, m, n); + return l[m][n]; +} diff --git a/src/js/algorithms/dynamic-programing/longest-common-subsequence.js b/src/js/algorithms/dynamic-programing/longest-common-subsequence.js new file mode 100644 index 00000000..762c70c6 --- /dev/null +++ b/src/js/algorithms/dynamic-programing/longest-common-subsequence.js @@ -0,0 +1,26 @@ +export function lcs(wordX, wordY) { + const m = wordX.length; + const n = wordY.length; + const l = []; + for (let i = 0; i <= m; i++) { + l[i] = []; + for (let j = 0; j <= n; j++) { + l[i][j] = 0; + } + } + for (let i = 0; i <= m; i++) { + for (let j = 0; j <= n; j++) { + if (i === 0 || j === 0) { + l[i][j] = 0; + } else if (wordX[i - 1] === wordY[j - 1]) { + l[i][j] = l[i - 1][j - 1] + 1; + } else { + const a = l[i - 1][j]; + const b = l[i][j - 1]; + l[i][j] = a > b ? a : b; // max(a,b) + } + } + // console.log(l[i].join()); + } + return l[m][n]; +} diff --git a/src/js/algorithms/dynamic-programing/matrix-chain-multiplication.js b/src/js/algorithms/dynamic-programing/matrix-chain-multiplication.js new file mode 100644 index 00000000..ae155cb3 --- /dev/null +++ b/src/js/algorithms/dynamic-programing/matrix-chain-multiplication.js @@ -0,0 +1,46 @@ +function printOptimalParenthesis(s, i, j) { + if (i === j) { + // console.log('A[' + i + ']'); + } else { + // console.log('('); + printOptimalParenthesis(s, i, s[i][j]); + printOptimalParenthesis(s, s[i][j] + 1, j); + // console.log(')'); + } +} + +export function matrixChainOrder(p) { + const n = p.length; + const m = []; + const s = []; + for (let i = 1; i <= n; i++) { + m[i] = []; + m[i][i] = 0; + } + for (let i = 0; i <= n; i++) { + // to help printing the optimal solution + s[i] = []; // auxiliary + for (let j = 0; j <= n; j++) { + s[i][j] = 0; + } + } + for (let l = 2; l < n; l++) { + for (let i = 1; i <= (n - l) + 1; i++) { + const j = (i + l) - 1; + m[i][j] = Number.MAX_SAFE_INTEGER; + for (let k = i; k <= j - 1; k++) { + // q = cost/scalar multiplications + const q = m[i][k] + m[k + 1][j] + ((p[i - 1] * p[k]) * p[j]); + if (q < m[i][j]) { + m[i][j] = q; + s[i][j] = k; // s[i,j] = Second auxiliary table that stores k + } + } + } + } + // console.log(m); + // console.log(s); + printOptimalParenthesis(s, 1, n - 1); + return m[1][n - 1]; +} + diff --git a/src/js/algorithms/dynamic-programing/min-coin-change.js b/src/js/algorithms/dynamic-programing/min-coin-change.js new file mode 100644 index 00000000..5456cd30 --- /dev/null +++ b/src/js/algorithms/dynamic-programing/min-coin-change.js @@ -0,0 +1,32 @@ +export function minCoinChange(coins, amount) { + const cache = []; + + const makeChange = (value) => { + if (!value) { + return []; + } + if (cache[value]) { + return cache[value]; + } + let min = []; + let newMin; + let newAmount; + for (let i = 0; i < coins.length; i++) { + const coin = coins[i]; + newAmount = value - coin; + if (newAmount >= 0) { + newMin = makeChange(newAmount); + } + if ( + newAmount >= 0 && + (newMin.length < min.length - 1 || !min.length) && + (newMin.length || !newAmount) + ) { + min = [coin].concat(newMin); + // console.log('new Min ' + min + ' for ' + amount); + } + } + return (cache[value] = min); + }; + return makeChange(amount); +} diff --git a/src/js/algorithms/graph/breadth-first-search.js b/src/js/algorithms/graph/breadth-first-search.js new file mode 100644 index 00000000..7cf3f395 --- /dev/null +++ b/src/js/algorithms/graph/breadth-first-search.js @@ -0,0 +1,74 @@ +import Queue from '../../data-structures/queue'; + +const Colors = { + WHITE: 0, + GREY: 1, + BLACK: 2 +}; + +const initializeColor = vertices => { + const color = {}; + for (let i = 0; i < vertices.length; i++) { + color[vertices[i]] = Colors.WHITE; + } + return color; +}; + +export const breadthFirstSearch = (graph, startVertex, callback) => { + const vertices = graph.getVertices(); + const adjList = graph.getAdjList(); + const color = initializeColor(vertices); + const queue = new Queue(); + + queue.enqueue(startVertex); + + while (!queue.isEmpty()) { + const u = queue.dequeue(); + const neighbors = adjList.get(u); + color[u] = Colors.GREY; + for (let i = 0; i < neighbors.length; i++) { + const w = neighbors[i]; + if (color[w] === Colors.WHITE) { + color[w] = Colors.GREY; + queue.enqueue(w); + } + } + color[u] = Colors.BLACK; + if (callback) { + callback(u); + } + } +}; + +export const BFS = (graph, startVertex) => { + const vertices = graph.getVertices(); + const adjList = graph.getAdjList(); + const color = initializeColor(vertices); + const queue = new Queue(); + const distances = {}; + const predecessors = {}; + queue.enqueue(startVertex); + for (let i = 0; i < vertices.length; i++) { + distances[vertices[i]] = 0; + predecessors[vertices[i]] = null; + } + while (!queue.isEmpty()) { + const u = queue.dequeue(); + const neighbors = adjList.get(u); + color[u] = Colors.GREY; + for (let i = 0; i < neighbors.length; i++) { + const w = neighbors[i]; + if (color[w] === Colors.WHITE) { + color[w] = Colors.GREY; + distances[w] = distances[u] + 1; + predecessors[w] = u; + queue.enqueue(w); + } + } + color[u] = Colors.BLACK; + } + return { + distances, + predecessors + }; +}; diff --git a/src/js/algorithms/graph/depth-first-search.js b/src/js/algorithms/graph/depth-first-search.js new file mode 100644 index 00000000..208925ce --- /dev/null +++ b/src/js/algorithms/graph/depth-first-search.js @@ -0,0 +1,86 @@ +// import Graph from '../../data-structures/graph'; + +const Colors = { + WHITE: 0, + GREY: 1, + BLACK: 2 +}; + +const initializeColor = vertices => { + const color = {}; + for (let i = 0; i < vertices.length; i++) { + color[vertices[i]] = Colors.WHITE; + } + return color; +}; + +const depthFirstSearchVisit = (u, color, adjList, callback) => { + color[u] = Colors.GREY; + if (callback) { + callback(u); + } + // console.log('Discovered ' + u); + const neighbors = adjList.get(u); + for (let i = 0; i < neighbors.length; i++) { + const w = neighbors[i]; + if (color[w] === Colors.WHITE) { + depthFirstSearchVisit(w, color, adjList, callback); + } + } + color[u] = Colors.BLACK; + // console.log('explored ' + u); +}; + +export const depthFirstSearch = (graph, callback) => { + const vertices = graph.getVertices(); + const adjList = graph.getAdjList(); + const color = initializeColor(vertices); + + for (let i = 0; i < vertices.length; i++) { + if (color[vertices[i]] === Colors.WHITE) { + depthFirstSearchVisit(vertices[i], color, adjList, callback); + } + } +}; + +const DFSVisit = (u, color, d, f, p, time, adjList) => { + // console.log('discovered ' + u); + color[u] = Colors.GREY; + d[u] = ++time; + const neighbors = adjList.get(u); + for (let i = 0; i < neighbors.length; i++) { + const w = neighbors[i]; + if (color[w] === Colors.WHITE) { + p[w] = u; + DFSVisit(w, color, d, f, p, time, adjList); + } + } + color[u] = Colors.BLACK; + f[u] = ++time; + // console.log('explored ' + u); +}; + +export const DFS = graph => { + const vertices = graph.getVertices(); + const adjList = graph.getAdjList(); + const color = initializeColor(vertices); + const d = {}; + const f = {}; + const p = {}; + const time = 0; + for (let i = 0; i < vertices.length; i++) { + f[vertices[i]] = 0; + d[vertices[i]] = 0; + p[vertices[i]] = null; + } + for (let i = 0; i < vertices.length; i++) { + if (color[vertices[i]] === Colors.WHITE) { + DFSVisit(vertices[i], color, d, f, p, time, adjList); + } + } + return { + discovery: d, + finished: f, + predecessors: p + }; +}; diff --git a/src/js/algorithms/graph/dijkstra.js b/src/js/algorithms/graph/dijkstra.js new file mode 100644 index 00000000..ec223687 --- /dev/null +++ b/src/js/algorithms/graph/dijkstra.js @@ -0,0 +1,32 @@ +const INF = Number.MAX_SAFE_INTEGER; +const minDistance = (dist, visited) => { + let min = INF; + let minIndex = -1; + for (let v = 0; v < dist.length; v++) { + if (visited[v] === false && dist[v] <= min) { + min = dist[v]; + minIndex = v; + } + } + return minIndex; +}; +export const dijkstra = (graph, src) => { + const dist = []; + const visited = []; + const { length } = graph; + for (let i = 0; i < length; i++) { + dist[i] = INF; + visited[i] = false; + } + dist[src] = 0; + for (let i = 0; i < length - 1; i++) { + const u = minDistance(dist, visited); + visited[u] = true; + for (let v = 0; v < length; v++) { + if (!visited[v] && graph[u][v] !== 0 && dist[u] !== INF && dist[u] + graph[u][v] < dist[v]) { + dist[v] = dist[u] + graph[u][v]; + } + } + } + return dist; +}; diff --git a/src/js/algorithms/graph/floyd-warshall.js b/src/js/algorithms/graph/floyd-warshall.js new file mode 100644 index 00000000..d8bb875e --- /dev/null +++ b/src/js/algorithms/graph/floyd-warshall.js @@ -0,0 +1,26 @@ +export const floydWarshall = graph => { + const dist = []; + const { length } = graph; + for (let i = 0; i < length; i++) { + dist[i] = []; + for (let j = 0; j < length; j++) { + if (i === j) { + dist[i][j] = 0; + } else if (!isFinite(graph[i][j])) { + dist[i][j] = Infinity; + } else { + dist[i][j] = graph[i][j]; + } + } + } + for (let k = 0; k < length; k++) { + for (let i = 0; i < length; i++) { + for (let j = 0; j < length; j++) { + if (dist[i][k] + dist[k][j] < dist[i][j]) { + dist[i][j] = dist[i][k] + dist[k][j]; + } + } + } + } + return dist; +}; diff --git a/src/js/algorithms/graph/kruskal.js b/src/js/algorithms/graph/kruskal.js new file mode 100644 index 00000000..d1435c28 --- /dev/null +++ b/src/js/algorithms/graph/kruskal.js @@ -0,0 +1,57 @@ +const INF = Number.MAX_SAFE_INTEGER; +const find = (i, parent) => { + while (parent[i]) { + i = parent[i]; // eslint-disable-line prefer-destructuring + } + return i; +}; +const union = (i, j, parent) => { + if (i !== j) { + parent[j] = i; + return true; + } + return false; +}; +const initializeCost = graph => { + const cost = []; + const { length } = graph; + for (let i = 0; i < length; i++) { + cost[i] = []; + for (let j = 0; j < length; j++) { + if (graph[i][j] === 0) { + cost[i][j] = INF; + } else { + cost[i][j] = graph[i][j]; + } + } + } + return cost; +}; +export const kruskal = graph => { + const { length } = graph; + const parent = []; + let ne = 0; + let a; + let b; + let u; + let v; + const cost = initializeCost(graph); + while (ne < length - 1) { + for (let i = 0, min = INF; i < length; i++) { + for (let j = 0; j < length; j++) { + if (cost[i][j] < min) { + min = cost[i][j]; + a = u = i; + b = v = j; + } + } + } + u = find(u, parent); + v = find(v, parent); + if (union(u, v, parent)) { + ne++; + } + cost[a][b] = cost[b][a] = INF; + } + return parent; +}; diff --git a/src/js/algorithms/graph/prim.js b/src/js/algorithms/graph/prim.js new file mode 100644 index 00000000..bbb54e95 --- /dev/null +++ b/src/js/algorithms/graph/prim.js @@ -0,0 +1,36 @@ +const INF = Number.MAX_SAFE_INTEGER; +const minKey = (graph, key, visited) => { + // Initialize min value + let min = INF; + let minIndex = 0; + for (let v = 0; v < graph.length; v++) { + if (visited[v] === false && key[v] < min) { + min = key[v]; + minIndex = v; + } + } + return minIndex; +}; +export const prim = graph => { + const parent = []; + const key = []; + const visited = []; + const { length } = graph; + for (let i = 0; i < length; i++) { + key[i] = INF; + visited[i] = false; + } + key[0] = 0; + parent[0] = -1; + for (let i = 0; i < length - 1; i++) { + const u = minKey(graph, key, visited); + visited[u] = true; + for (let v = 0; v < length; v++) { + if (graph[u][v] && !visited[v] && graph[u][v] < key[v]) { + parent[v] = u; + key[v] = graph[u][v]; + } + } + } + return parent; +}; diff --git a/src/js/algorithms/greedy/knapsack.js b/src/js/algorithms/greedy/knapsack.js new file mode 100644 index 00000000..294def52 --- /dev/null +++ b/src/js/algorithms/greedy/knapsack.js @@ -0,0 +1,18 @@ +export function knapSack(capacity, weights, values) { + const n = values.length; + let load = 0; + let val = 0; + for (let i = 0; i < n && load < capacity; i++) { + if (weights[i] <= capacity - load) { + val += values[i]; + load += weights[i]; + // console.log('using item ' + (i + 1) + ' for the solution'); + } else { + const r = (capacity - load) / weights[i]; + val += r * values[i]; + load += weights[i]; + // console.log('using ratio of ' + r + ' for item ' + (i + 1) + ' for the solution'); + } + } + return val; +} diff --git a/src/js/algorithms/greedy/longest-common-subsequence.js b/src/js/algorithms/greedy/longest-common-subsequence.js new file mode 100644 index 00000000..f7ec8272 --- /dev/null +++ b/src/js/algorithms/greedy/longest-common-subsequence.js @@ -0,0 +1,11 @@ +export function lcs(wordX, wordY, m = wordX.length, n = wordY.length) { + if (m === 0 || n === 0) { + return 0; + } + if (wordX[m - 1] === wordY[n - 1]) { + return 1 + lcs(wordX, wordY, m - 1, n - 1); + } + const a = lcs(wordX, wordY, m, n - 1); + const b = lcs(wordX, wordY, m - 1, n); + return a > b ? a : b; +} diff --git a/src/js/algorithms/greedy/matrix-chain-multiplication.js b/src/js/algorithms/greedy/matrix-chain-multiplication.js new file mode 100644 index 00000000..9909eab2 --- /dev/null +++ b/src/js/algorithms/greedy/matrix-chain-multiplication.js @@ -0,0 +1,14 @@ +export function matrixChainOrder(p, i = 1, j = p.length - 1) { + if (i === j) { + return 0; + } + let min = Number.MAX_SAFE_INTEGER; + for (let k = i; k < j; k++) { + const count = + matrixChainOrder(p, i, k) + matrixChainOrder(p, k + 1, j) + ((p[i - 1] * p[k]) * p[j]); + if (count < min) { + min = count; + } + } + return min; +} diff --git a/src/js/algorithms/greedy/min-coin-change.js b/src/js/algorithms/greedy/min-coin-change.js new file mode 100644 index 00000000..61abfe91 --- /dev/null +++ b/src/js/algorithms/greedy/min-coin-change.js @@ -0,0 +1,12 @@ +export function minCoinChange(coins, amount) { + const change = []; + let total = 0; + for (let i = coins.length; i >= 0; i--) { + const coin = coins[i]; + while (total + coin <= amount) { + change.push(coin); + total += coin; + } + } + return change; +} diff --git a/src/js/algorithms/search/binary-search.js b/src/js/algorithms/search/binary-search.js new file mode 100644 index 00000000..0c2f9fba --- /dev/null +++ b/src/js/algorithms/search/binary-search.js @@ -0,0 +1,24 @@ +import { Compare, defaultCompare, DOES_NOT_EXIST } from '../../util'; +import { quickSort } from '../sorting/quicksort'; + +export function binarySearch(array, value, compareFn = defaultCompare) { + const sortedArray = quickSort(array); + let low = 0; + let high = sortedArray.length - 1; + while (low <= high) { + const mid = Math.floor((low + high) / 2); + const element = sortedArray[mid]; + // console.log('mid element is ' + element); + if (compareFn(element, value) === Compare.LESS_THAN) { + low = mid + 1; + // console.log('low is ' + low); + } else if (compareFn(element, value) === Compare.BIGGER_THAN) { + high = mid - 1; + // console.log('high is ' + high); + } else { + // console.log('found it'); + return mid; + } + } + return DOES_NOT_EXIST; +} diff --git a/src/js/algorithms/search/interpolation-search.js b/src/js/algorithms/search/interpolation-search.js new file mode 100644 index 00000000..e2153349 --- /dev/null +++ b/src/js/algorithms/search/interpolation-search.js @@ -0,0 +1,40 @@ +import { + biggerEquals, + Compare, + defaultCompare, + defaultEquals, + defaultDiff, + DOES_NOT_EXIST, + lesserEquals +} from '../../util'; + +export function interpolationSearch( + array, + value, + compareFn = defaultCompare, + equalsFn = defaultEquals, + diffFn = defaultDiff +) { + const { length } = array; + let low = 0; + let high = length - 1; + let position = -1; + let delta = -1; + while ( + low <= high && + biggerEquals(value, array[low], compareFn) && + lesserEquals(value, array[high], compareFn) + ) { + delta = diffFn(value, array[low]) / diffFn(array[high], array[low]); + position = low + Math.floor((high - low) * delta); + if (equalsFn(array[position], value)) { + return position; + } + if (compareFn(array[position], value) === Compare.LESS_THAN) { + low = position + 1; + } else { + high = position - 1; + } + } + return DOES_NOT_EXIST; +} diff --git a/src/js/algorithms/search/min-max-search.js b/src/js/algorithms/search/min-max-search.js new file mode 100644 index 00000000..ddefb113 --- /dev/null +++ b/src/js/algorithms/search/min-max-search.js @@ -0,0 +1,26 @@ +import { defaultCompare, Compare } from '../../util'; + +export function findMaxValue(array, compareFn = defaultCompare) { + if (array && array.length > 0) { + let max = array[0]; + for (let i = 1; i < array.length; i++) { + if (compareFn(max, array[i]) === Compare.LESS_THAN) { + max = array[i]; + } + } + return max; + } + return undefined; +} +export function findMinValue(array, compareFn = defaultCompare) { + if (array && array.length > 0) { + let min = array[0]; + for (let i = 1; i < array.length; i++) { + if (compareFn(min, array[i]) === Compare.BIGGER_THAN) { + min = array[i]; + } + } + return min; + } + return undefined; +} diff --git a/src/js/algorithms/search/sequential-search.js b/src/js/algorithms/search/sequential-search.js new file mode 100644 index 00000000..346c23ac --- /dev/null +++ b/src/js/algorithms/search/sequential-search.js @@ -0,0 +1,10 @@ +import { defaultEquals, DOES_NOT_EXIST } from '../../util'; + +export function sequentialSearch(array, value, equalsFn = defaultEquals) { + for (let i = 0; i < array.length; i++) { + if (equalsFn(value, array[i])) { + return i; + } + } + return DOES_NOT_EXIST; +} diff --git "a/src/js/algorithms/shuffle/fisher\342\200\223yates.js" "b/src/js/algorithms/shuffle/fisher\342\200\223yates.js" new file mode 100644 index 00000000..f951a3ca --- /dev/null +++ "b/src/js/algorithms/shuffle/fisher\342\200\223yates.js" @@ -0,0 +1,11 @@ +import { swap } from '../../util'; + +export function shuffle(array) { + let currentIndex = array.length; + while (currentIndex !== 0) { + const randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + swap(array, currentIndex, randomIndex); + } + return array; +} diff --git a/src/js/algorithms/sorting/bubble-sort-improved.js b/src/js/algorithms/sorting/bubble-sort-improved.js new file mode 100644 index 00000000..a0aba370 --- /dev/null +++ b/src/js/algorithms/sorting/bubble-sort-improved.js @@ -0,0 +1,16 @@ +import { Compare, defaultCompare, swap } from '../../util'; + +export function modifiedBubbleSort(array, compareFn = defaultCompare) { + const { length } = array; + for (let i = 0; i < length; i++) { + // console.log('--- '); + for (let j = 0; j < length - 1 - i; j++) { + // console.log('compare ' + array[j] + ' with ' + array[j + 1]); + if (compareFn(array[j], array[j + 1]) === Compare.BIGGER_THAN) { + // console.log('swap ' + array[j] + ' with ' + array[j + 1]); + swap(array, j, j + 1); + } + } + } + return array; +} diff --git a/src/js/algorithms/sorting/bubble-sort.js b/src/js/algorithms/sorting/bubble-sort.js new file mode 100644 index 00000000..7d5b928e --- /dev/null +++ b/src/js/algorithms/sorting/bubble-sort.js @@ -0,0 +1,16 @@ +import { Compare, defaultCompare, swap } from '../../util'; + +export function bubbleSort(array, compareFn = defaultCompare) { + const { length } = array; + for (let i = 0; i < length; i++) { + // console.log('--- '); + for (let j = 0; j < length - 1; j++) { + // console.log('compare ' + array[j] + ' with ' + array[j + 1]); + if (compareFn(array[j], array[j + 1]) === Compare.BIGGER_THAN) { + // console.log('swap ' + array[j] + ' with ' + array[j + 1]); + swap(array, j, j + 1); + } + } + } + return array; +} diff --git a/src/js/algorithms/sorting/bucket-sort.js b/src/js/algorithms/sorting/bucket-sort.js new file mode 100644 index 00000000..1a11b699 --- /dev/null +++ b/src/js/algorithms/sorting/bucket-sort.js @@ -0,0 +1,39 @@ +import { insertionSort } from './insertion-sort'; + +function createBuckets(array, bucketSize) { + let minValue = array[0]; + let maxValue = array[0]; + for (let i = 1; i < array.length; i++) { + if (array[i] < minValue) { + minValue = array[i]; + } else if (array[i] > maxValue) { + maxValue = array[i]; + } + } + const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; + const buckets = []; + for (let i = 0; i < bucketCount; i++) { + buckets[i] = []; + } + for (let i = 0; i < array.length; i++) { + buckets[Math.floor((array[i] - minValue) / bucketSize)].push(array[i]); + } + return buckets; +} +function sortBuckets(buckets) { + const sortedArray = []; + for (let i = 0; i < buckets.length; i++) { + if (buckets[i] != null) { + insertionSort(buckets[i]); + sortedArray.push(...buckets[i]); + } + } + return sortedArray; +} +export function bucketSort(array, bucketSize = 5) { + if (array.length < 2) { + return array; + } + const buckets = createBuckets(array, bucketSize); + return sortBuckets(buckets); +} diff --git a/src/js/algorithms/sorting/counting-sort.js b/src/js/algorithms/sorting/counting-sort.js new file mode 100644 index 00000000..9a19201b --- /dev/null +++ b/src/js/algorithms/sorting/counting-sort.js @@ -0,0 +1,24 @@ +import { findMaxValue } from '../search/min-max-search'; + +export function countingSort(array) { + if (array.length < 2) { + return array; + } + const maxValue = findMaxValue(array); + let sortedIndex = 0; + const counts = new Array(maxValue + 1); + array.forEach(element => { + if (!counts[element]) { + counts[element] = 0; + } + counts[element]++; + }); + // console.log('Frequencies: ' + counts.join()); + counts.forEach((element, i) => { + while (element > 0) { + array[sortedIndex++] = i; + element--; + } + }); + return array; +} diff --git a/src/js/algorithms/sorting/heap-sort.js b/src/js/algorithms/sorting/heap-sort.js new file mode 100644 index 00000000..ff1c10f1 --- /dev/null +++ b/src/js/algorithms/sorting/heap-sort.js @@ -0,0 +1,34 @@ +import { defaultCompare, swap } from '../../util'; + +function heapify(array, index, heapSize, compareFn) { + let largest = index; + const left = (2 * index) + 1; + const right = (2 * index) + 2; + if (left < heapSize && compareFn(array[left], array[index]) > 0) { + largest = left; + } + if (right < heapSize && compareFn(array[right], array[largest]) > 0) { + largest = right; + } + if (largest !== index) { + swap(array, index, largest); + heapify(array, largest, heapSize, compareFn); + } +} + +function buildMaxHeap(array, compareFn) { + for (let i = Math.floor(array.length / 2); i >= 0; i -= 1) { + heapify(array, i, array.length, compareFn); + } + return array; +} + +export default function heapSort(array, compareFn = defaultCompare) { + let heapSize = array.length; + buildMaxHeap(array, compareFn); + while (heapSize > 1) { + swap(array, 0, --heapSize); + heapify(array, 0, heapSize, compareFn); + } + return array; +} diff --git a/src/js/algorithms/sorting/insertion-sort.js b/src/js/algorithms/sorting/insertion-sort.js new file mode 100644 index 00000000..e48689e6 --- /dev/null +++ b/src/js/algorithms/sorting/insertion-sort.js @@ -0,0 +1,19 @@ +import { Compare, defaultCompare } from '../../util'; + +export const insertionSort = (array, compareFn = defaultCompare) => { + const { length } = array; + let temp; + for (let i = 1; i < length; i++) { + let j = i; + temp = array[i]; + // console.log('to be inserted ' + temp); + while (j > 0 && compareFn(array[j - 1], temp) === Compare.BIGGER_THAN) { + // console.log('shift ' + array[j - 1]); + array[j] = array[j - 1]; + j--; + } + // console.log('insert ' + temp); + array[j] = temp; + } + return array; +}; diff --git a/src/js/algorithms/sorting/merge-sort.js b/src/js/algorithms/sorting/merge-sort.js new file mode 100644 index 00000000..5371e714 --- /dev/null +++ b/src/js/algorithms/sorting/merge-sort.js @@ -0,0 +1,21 @@ +import { Compare, defaultCompare } from '../../util'; + +function merge(left, right, compareFn) { + let i = 0; + let j = 0; + const result = []; + while (i < left.length && j < right.length) { + result.push(compareFn(left[i], right[j]) === Compare.LESS_THAN ? left[i++] : right[j++]); + } + return result.concat(i < left.length ? left.slice(i) : right.slice(j)); +} +export function mergeSort(array, compareFn = defaultCompare) { + if (array.length > 1) { + const { length } = array; + const middle = Math.floor(length / 2); + const left = mergeSort(array.slice(0, middle), compareFn); + const right = mergeSort(array.slice(middle, length), compareFn); + array = merge(left, right, compareFn); + } + return array; +} diff --git a/src/js/algorithms/sorting/quicksort.js b/src/js/algorithms/sorting/quicksort.js new file mode 100644 index 00000000..2ac40b4a --- /dev/null +++ b/src/js/algorithms/sorting/quicksort.js @@ -0,0 +1,41 @@ +import { Compare, defaultCompare, swap } from '../../util'; + +function partition(array, left, right, compareFn) { + const pivot = array[Math.floor((right + left) / 2)]; + let i = left; + let j = right; + // console.log('pivot is ' + pivot + '; left is ' + left + '; right is ' + right); + while (i <= j) { + while (compareFn(array[i], pivot) === Compare.LESS_THAN) { + i++; + // console.log('i = ' + i); + } + while (compareFn(array[j], pivot) === Compare.BIGGER_THAN) { + j--; + // console.log('j = ' + j); + } + if (i <= j) { + // console.log('swap ' + array[i] + ' with ' + array[j]); + swap(array, i, j); + i++; + j--; + } + } + return i; +} +function quick(array, left, right, compareFn) { + let index; + if (array.length > 1) { + index = partition(array, left, right, compareFn); + if (left < index - 1) { + quick(array, left, index - 1, compareFn); + } + if (index < right) { + quick(array, index, right, compareFn); + } + } + return array; +} +export function quickSort(array, compareFn = defaultCompare) { + return quick(array, 0, array.length - 1, compareFn); +} diff --git a/src/js/algorithms/sorting/radix-sort.js b/src/js/algorithms/sorting/radix-sort.js new file mode 100644 index 00000000..db21c37a --- /dev/null +++ b/src/js/algorithms/sorting/radix-sort.js @@ -0,0 +1,44 @@ +import { findMaxValue, findMinValue } from '../search/min-max-search'; + +const getBucketIndex = (value, minValue, significantDigit, radixBase) => + Math.floor(((value - minValue) / significantDigit) % radixBase); + +const countingSortForRadix = (array, radixBase, significantDigit, minValue) => { + let bucketsIndex; + const buckets = []; + const aux = []; + for (let i = 0; i < radixBase; i++) { + buckets[i] = 0; + } + for (let i = 0; i < array.length; i++) { + bucketsIndex = getBucketIndex(array[i], minValue, significantDigit, radixBase); + buckets[bucketsIndex]++; + } + for (let i = 1; i < radixBase; i++) { + buckets[i] += buckets[i - 1]; + } + for (let i = array.length - 1; i >= 0; i--) { + bucketsIndex = getBucketIndex(array[i], minValue, significantDigit, radixBase); + aux[--buckets[bucketsIndex]] = array[i]; + } + for (let i = 0; i < array.length; i++) { + array[i] = aux[i]; + } + return array; +}; +export function radixSort(array, radixBase = 10) { + if (array.length < 2) { + return array; + } + const minValue = findMinValue(array); + const maxValue = findMaxValue(array); + // Perform counting sort for each significant digit, starting at 1 + let significantDigit = 1; + while ((maxValue - minValue) / significantDigit >= 1) { + // console.log('radix sort for digit ' + significantDigit); + array = countingSortForRadix(array, radixBase, significantDigit, minValue); + // console.log(array.join()); + significantDigit *= radixBase; + } + return array; +} diff --git a/src/js/algorithms/sorting/selection-sort.js b/src/js/algorithms/sorting/selection-sort.js new file mode 100644 index 00000000..55a9c2c0 --- /dev/null +++ b/src/js/algorithms/sorting/selection-sort.js @@ -0,0 +1,21 @@ +import { Compare, defaultCompare, swap } from '../../util'; + +export const selectionSort = (array, compareFn = defaultCompare) => { + const { length } = array; + let indexMin; + for (let i = 0; i < length - 1; i++) { + indexMin = i; + // console.log('index ' + array[i]); + for (let j = i; j < length; j++) { + if (compareFn(array[indexMin], array[j]) === Compare.BIGGER_THAN) { + // console.log('new index min ' + array[j]); + indexMin = j; + } + } + if (i !== indexMin) { + // console.log('swap ' + array[i] + ' with ' + array[indexMin]); + swap(array, i, indexMin); + } + } + return array; +}; diff --git a/src/js/algorithms/sorting/shell-sort.js b/src/js/algorithms/sorting/shell-sort.js new file mode 100644 index 00000000..e13127f1 --- /dev/null +++ b/src/js/algorithms/sorting/shell-sort.js @@ -0,0 +1,22 @@ +import { Compare, defaultCompare } from '../../util'; + +export function shellSort(array, compareFn = defaultCompare) { + let increment = array.length / 2; + while (increment > 0) { + for (let i = increment; i < array.length; i++) { + let j = i; + const temp = array[i]; + while (j >= increment && compareFn(array[j - increment], temp) === Compare.BIGGER_THAN) { + array[j] = array[j - increment]; + j -= increment; + } + array[j] = temp; + } + if (increment === 2) { + increment = 1; + } else { + increment = Math.floor((increment * 5) / 11); + } + } + return array; +} diff --git a/src/js/data-structures/avl-tree.js b/src/js/data-structures/avl-tree.js new file mode 100644 index 00000000..108716e5 --- /dev/null +++ b/src/js/data-structures/avl-tree.js @@ -0,0 +1,160 @@ +import { Compare, defaultCompare } from '../util'; +import BinarySearchTree from './binary-search-tree'; +import { Node } from './models/node'; + +const BalanceFactor = { + UNBALANCED_RIGHT: 1, + SLIGHTLY_UNBALANCED_RIGHT: 2, + BALANCED: 3, + SLIGHTLY_UNBALANCED_LEFT: 4, + UNBALANCED_LEFT: 5 +}; + +export default class AVLTree extends BinarySearchTree { + constructor(compareFn = defaultCompare) { + super(compareFn); + this.compareFn = compareFn; + this.root = null; + } + getNodeHeight(node) { + if (node == null) { + return -1; + } + return Math.max(this.getNodeHeight(node.left), this.getNodeHeight(node.right)) + 1; + } + /** + * Left left case: rotate right + * + * b a + * / \ / \ + * a e -> rotationLL(b) -> c b + * / \ / \ + * c d d e + * + * @param node Node + */ + rotationLL(node) { + const tmp = node.left; + node.left = tmp.right; + tmp.right = node; + return tmp; + } + /** + * Right right case: rotate left + * + * a b + * / \ / \ + * c b -> rotationRR(a) -> a e + * / \ / \ + * d e c d + * + * @param node Node + */ + rotationRR(node) { + const tmp = node.right; + node.right = tmp.left; + tmp.left = node; + return tmp; + } + /** + * Left right case: rotate left then right + * @param node Node + */ + rotationLR(node) { + node.left = this.rotationRR(node.left); + return this.rotationLL(node); + } + /** + * Right left case: rotate right then left + * @param node Node + */ + rotationRL(node) { + node.right = this.rotationLL(node.right); + return this.rotationRR(node); + } + getBalanceFactor(node) { + const heightDifference = this.getNodeHeight(node.left) - this.getNodeHeight(node.right); + switch (heightDifference) { + case -2: + return BalanceFactor.UNBALANCED_RIGHT; + case -1: + return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT; + case 1: + return BalanceFactor.SLIGHTLY_UNBALANCED_LEFT; + case 2: + return BalanceFactor.UNBALANCED_LEFT; + default: + return BalanceFactor.BALANCED; + } + } + insert(key) { + this.root = this.insertNode(this.root, key); + } + insertNode(node, key) { + if (node == null) { + return new Node(key); + } else if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + node.left = this.insertNode(node.left, key); + } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { + node.right = this.insertNode(node.right, key); + } else { + return node; // duplicated key + } + // verify if tree is balanced + const balanceFactor = this.getBalanceFactor(node); + if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) { + if (this.compareFn(key, node.left.key) === Compare.LESS_THAN) { + // Left left case + node = this.rotationLL(node); + } else { + // Left right case + return this.rotationLR(node); + } + } + if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) { + if (this.compareFn(key, node.right.key) === Compare.BIGGER_THAN) { + // Right right case + node = this.rotationRR(node); + } else { + // Right left case + return this.rotationRL(node); + } + } + return node; + } + removeNode(node, key) { + node = super.removeNode(node, key); // {1} + if (node == null) { + return node; + } + // verify if tree is balanced + const balanceFactor = this.getBalanceFactor(node); + if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) { + // Left left case + if ( + this.getBalanceFactor(node.left) === BalanceFactor.BALANCED || + this.getBalanceFactor(node.left) === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT + ) { + return this.rotationLL(node); + } + // Left right case + if (this.getBalanceFactor(node.left) === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) { + return this.rotationLR(node.left); + } + } + if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) { + // Right right case + if ( + this.getBalanceFactor(node.right) === BalanceFactor.BALANCED || + this.getBalanceFactor(node.right) === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT + ) { + return this.rotationRR(node); + } + // Right left case + if (this.getBalanceFactor(node.right) === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) { + return this.rotationRL(node.right); + } + } + return node; + } +} diff --git a/src/js/data-structures/binary-search-tree.js b/src/js/data-structures/binary-search-tree.js new file mode 100644 index 00000000..b05de8b2 --- /dev/null +++ b/src/js/data-structures/binary-search-tree.js @@ -0,0 +1,135 @@ +import { Compare, defaultCompare } from '../util'; +import { Node } from './models/node'; + +export default class BinarySearchTree { + constructor(compareFn = defaultCompare) { + this.compareFn = compareFn; + this.root = null; + } + insert(key) { + // special case: first key + if (this.root == null) { + this.root = new Node(key); + } else { + this.insertNode(this.root, key); + } + } + insertNode(node, key) { + if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + if (node.left == null) { + node.left = new Node(key); + } else { + this.insertNode(node.left, key); + } + } else if (node.right == null) { + node.right = new Node(key); + } else { + this.insertNode(node.right, key); + } + } + getRoot() { + return this.root; + } + search(key) { + return this.searchNode(this.root, key); + } + searchNode(node, key) { + if (node == null) { + return false; + } + if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + return this.searchNode(node.left, key); + } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { + return this.searchNode(node.right, key); + } + return true; + } + inOrderTraverse(callback) { + this.inOrderTraverseNode(this.root, callback); + } + inOrderTraverseNode(node, callback) { + if (node != null) { + this.inOrderTraverseNode(node.left, callback); + callback(node.key); + this.inOrderTraverseNode(node.right, callback); + } + } + preOrderTraverse(callback) { + this.preOrderTraverseNode(this.root, callback); + } + preOrderTraverseNode(node, callback) { + if (node != null) { + callback(node.key); + this.preOrderTraverseNode(node.left, callback); + this.preOrderTraverseNode(node.right, callback); + } + } + postOrderTraverse(callback) { + this.postOrderTraverseNode(this.root, callback); + } + postOrderTraverseNode(node, callback) { + if (node != null) { + this.postOrderTraverseNode(node.left, callback); + this.postOrderTraverseNode(node.right, callback); + callback(node.key); + } + } + min() { + return this.minNode(this.root); + } + minNode(node) { + let current = node; + while (current != null && current.left != null) { + current = current.left; + } + return current; + } + max() { + return this.maxNode(this.root); + } + maxNode(node) { + let current = node; + while (current != null && current.right != null) { + current = current.right; + } + return current; + } + remove(key) { + this.root = this.removeNode(this.root, key); + } + removeNode(node, key) { + if (node == null) { + return null; + } + if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + node.left = this.removeNode(node.left, key); + return node; + } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { + node.right = this.removeNode(node.right, key); + return node; + } + // key is equal to node.item + // handle 3 special conditions + // 1 - a leaf node + // 2 - a node with only 1 child + // 3 - a node with 2 children + // case 1 + if (node.left == null && node.right == null) { + node = null; + return node; + } + // case 2 + if (node.left == null) { + node = node.right; + return node; + } else if (node.right == null) { + node = node.left; + return node; + } + // case 3 + const aux = this.minNode(node.right); + node.key = aux.key; + node.right = this.removeNode(node.right, aux.key); + return node; + } +} diff --git a/src/js/data-structures/circular-linked-list.js b/src/js/data-structures/circular-linked-list.js new file mode 100644 index 00000000..cc565558 --- /dev/null +++ b/src/js/data-structures/circular-linked-list.js @@ -0,0 +1,72 @@ +import { defaultEquals } from '../util'; +import LinkedList from './linked-list'; +import { Node } from './models/linked-list-models'; + +export default class CircularLinkedList extends LinkedList { + constructor(equalsFn = defaultEquals) { + super(equalsFn); + } + push(element) { + const node = new Node(element); + let current; + if (this.head == null) { + this.head = node; + } else { + current = this.getElementAt(this.size() - 1); + current.next = node; + } + // set node.next to head - to have circular list + node.next = this.head; + this.count++; + } + insert(element, index) { + if (index >= 0 && index <= this.count) { + const node = new Node(element); + let current = this.head; + if (index === 0) { + if (this.head == null) { + // if no node in list + this.head = node; + node.next = this.head; + } else { + node.next = current; + current = this.getElementAt(this.size()); + // update last element + this.head = node; + current.next = this.head; + } + } else { + const previous = this.getElementAt(index - 1); + node.next = previous.next; + previous.next = node; + } + this.count++; + return true; + } + return false; + } + removeAt(index) { + if (index >= 0 && index < this.count) { + let current = this.head; + if (index === 0) { + if (this.size() === 1) { + this.head = undefined; + } else { + const removed = this.head; + current = this.getElementAt(this.size() - 1); + this.head = this.head.next; + current.next = this.head; + current = removed; + } + } else { + // no need to update last element for circular list + const previous = this.getElementAt(index - 1); + current = previous.next; + previous.next = current.next; + } + this.count--; + return current.element; + } + return undefined; + } +} diff --git a/src/js/data-structures/deque.js b/src/js/data-structures/deque.js new file mode 100644 index 00000000..82b50617 --- /dev/null +++ b/src/js/data-structures/deque.js @@ -0,0 +1,88 @@ +// @ts-check + +export default class Deque { + constructor() { + this.count = 0; + this.lowestCount = 0; + this.items = {}; + } + + addFront(element) { + if (this.isEmpty()) { + this.addBack(element); + } else if (this.lowestCount > 0) { + this.lowestCount--; + this.items[this.lowestCount] = element; + } else { + for (let i = this.count; i > 0; i--) { + this.items[i] = this.items[i - 1]; + } + this.count++; + this.items[0] = element; + } + } + + addBack(element) { + this.items[this.count] = element; + this.count++; + } + + removeFront() { + if (this.isEmpty()) { + return undefined; + } + const result = this.items[this.lowestCount]; + delete this.items[this.lowestCount]; + this.lowestCount++; + return result; + } + + removeBack() { + if (this.isEmpty()) { + return undefined; + } + this.count--; + const result = this.items[this.count]; + delete this.items[this.count]; + return result; + } + + peekFront() { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.lowestCount]; + } + + peekBack() { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.count - 1]; + } + + isEmpty() { + return this.size() === 0; + } + + clear() { + this.items = {}; + this.count = 0; + this.lowestCount = 0; + } + + size() { + return this.count - this.lowestCount; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + let objString = `${this.items[this.lowestCount]}`; + for (let i = this.lowestCount + 1; i < this.count; i++) { + objString = `${objString},${this.items[i]}`; + } + return objString; + } +} diff --git a/src/js/data-structures/dictionary.js b/src/js/data-structures/dictionary.js new file mode 100644 index 00000000..4ec6212a --- /dev/null +++ b/src/js/data-structures/dictionary.js @@ -0,0 +1,69 @@ +import { defaultToString } from '../util'; +import { ValuePair } from './models/value-pair'; + +export default class Dictionary { + constructor(toStrFn = defaultToString) { + this.toStrFn = toStrFn; + this.table = {}; + } + set(key, value) { + if (key != null && value != null) { + const tableKey = this.toStrFn(key); + this.table[tableKey] = new ValuePair(key, value); + return true; + } + return false; + } + get(key) { + const valuePair = this.table[this.toStrFn(key)]; + return valuePair == null ? undefined : valuePair.value; + } + hasKey(key) { + return this.table[this.toStrFn(key)] != null; + } + remove(key) { + if (this.hasKey(key)) { + delete this.table[this.toStrFn(key)]; + return true; + } + return false; + } + values() { + return this.keyValues().map(valuePair => valuePair.value); + } + keys() { + return this.keyValues().map(valuePair => valuePair.key); + } + keyValues() { + return Object.values(this.table); + } + forEach(callbackFn) { + const valuePairs = this.keyValues(); + for (let i = 0; i < valuePairs.length; i++) { + const result = callbackFn(valuePairs[i].key, valuePairs[i].value); + if (result === false) { + break; + } + } + } + isEmpty() { + return this.size() === 0; + } + size() { + return Object.keys(this.table).length; + } + clear() { + this.table = {}; + } + toString() { + if (this.isEmpty()) { + return ''; + } + const valuePairs = this.keyValues(); + let objString = `${valuePairs[0].toString()}`; + for (let i = 1; i < valuePairs.length; i++) { + objString = `${objString},${valuePairs[i].toString()}`; + } + return objString; + } +} diff --git a/src/js/data-structures/doubly-linked-list.js b/src/js/data-structures/doubly-linked-list.js new file mode 100644 index 00000000..8960e173 --- /dev/null +++ b/src/js/data-structures/doubly-linked-list.js @@ -0,0 +1,129 @@ +import { defaultEquals } from '../util'; +import LinkedList from './linked-list'; +import { DoublyNode } from './models/linked-list-models'; + +export default class DoublyLinkedList extends LinkedList { + constructor(equalsFn = defaultEquals) { + super(equalsFn); + this.tail = undefined; + } + push(element) { + const node = new DoublyNode(element); + if (this.head == null) { + this.head = node; + this.tail = node; // NEW + } else { + // attach to the tail node // NEW + this.tail.next = node; + node.prev = this.tail; + this.tail = node; + } + this.count++; + } + insert(element, index) { + if (index >= 0 && index <= this.count) { + const node = new DoublyNode(element); + let current = this.head; + if (index === 0) { + if (this.head == null) { // NEW + this.head = node; + this.tail = node; // NEW + } else { + node.next = this.head; + this.head.prev = node; // NEW + this.head = node; + } + } else if (index === this.count) { // last item NEW + current = this.tail; + current.next = node; + node.prev = current; + this.tail = node; + } else { + const previous = this.getElementAt(index - 1); + current = previous.next; + node.next = current; + previous.next = node; + current.prev = node; // NEW + node.prev = previous; // NEW + } + this.count++; + return true; + } + return false; + } + removeAt(index) { + if (index >= 0 && index < this.count) { + let current = this.head; + if (index === 0) { + this.head = this.head.next; + // if there is only one item, then we update tail as well //NEW + if (this.count === 1) { + // {2} + this.tail = undefined; + } else { + this.head.prev = undefined; + } + } else if (index === this.count - 1) { + // last item //NEW + current = this.tail; + this.tail = current.prev; + this.tail.next = undefined; + } else { + current = this.getElementAt(index); + const previous = current.prev; + // link previous with current's next - skip it to remove + previous.next = current.next; + current.next.prev = previous; // NEW + } + this.count--; + return current.element; + } + return undefined; + } + indexOf(element) { + let current = this.head; + let index = 0; + while (current != null) { + if (this.equalsFn(element, current.element)) { + return index; + } + index++; + current = current.next; + } + return -1; + } + getHead() { + return this.head; + } + getTail() { + return this.tail; + } + clear() { + super.clear(); + this.tail = undefined; + } + toString() { + if (this.head == null) { + return ''; + } + let objString = `${this.head.element}`; + let current = this.head.next; + while (current != null) { + objString = `${objString},${current.element}`; + current = current.next; + } + return objString; + } + inverseToString() { + if (this.tail == null) { + return ''; + } + let objString = `${this.tail.element}`; + let previous = this.tail.prev; + while (previous != null) { + objString = `${objString},${previous.element}`; + previous = previous.prev; + } + return objString; + } +} diff --git a/src/js/data-structures/graph.js b/src/js/data-structures/graph.js new file mode 100644 index 00000000..d838c4f6 --- /dev/null +++ b/src/js/data-structures/graph.js @@ -0,0 +1,45 @@ +import Dictionary from './dictionary'; + +export default class Graph { + constructor(isDirected = false) { + this.isDirected = isDirected; + this.vertices = []; + this.adjList = new Dictionary(); + } + addVertex(v) { + if (!this.vertices.includes(v)) { + this.vertices.push(v); + this.adjList.set(v, []); // initialize adjacency list with array as well; + } + } + addEdge(a, b) { + if (!this.adjList.get(a)) { + this.addVertex(a); + } + if (!this.adjList.get(b)) { + this.addVertex(b); + } + this.adjList.get(a).push(b); + if (!this.isDirected) { + this.adjList.get(b).push(a); + } + } + getVertices() { + return this.vertices; + } + getAdjList() { + return this.adjList; + } + toString() { + let s = ''; + for (let i = 0; i < this.vertices.length; i++) { + s += `${this.vertices[i]} -> `; + const neighbors = this.adjList.get(this.vertices[i]); + for (let j = 0; j < neighbors.length; j++) { + s += `${neighbors[j]} `; + } + s += '\n'; + } + return s; + } +} diff --git a/src/js/data-structures/hash-table-linear-probing-lazy.js b/src/js/data-structures/hash-table-linear-probing-lazy.js new file mode 100644 index 00000000..57038d0f --- /dev/null +++ b/src/js/data-structures/hash-table-linear-probing-lazy.js @@ -0,0 +1,122 @@ +import { defaultToString } from '../util'; +import { ValuePairLazy } from './models/value-pair-lazy'; + +export default class HashTableLinearProbingLazy { + constructor(toStrFn = defaultToString) { + this.toStrFn = toStrFn; + this.table = {}; + } + loseloseHashCode(key) { + if (typeof key === 'number') { + return key; + } + const tableKey = this.toStrFn(key); + let hash = 0; + for (let i = 0; i < tableKey.length; i++) { + hash += tableKey.charCodeAt(i); + } + return hash % 37; + } + hashCode(key) { + return this.loseloseHashCode(key); + } + put(key, value) { + if (key != null && value != null) { + const position = this.hashCode(key); + if ( + this.table[position] == null || + (this.table[position] != null && this.table[position].isDeleted) + ) { + this.table[position] = new ValuePairLazy(key, value); + } else { + let index = position + 1; + while (this.table[index] != null && !this.table[position].isDeleted) { + index++; + } + this.table[index] = new ValuePairLazy(key, value); + } + return true; + } + return false; + } + get(key) { + const position = this.hashCode(key); + if (this.table[position] != null) { + if (this.table[position].key === key && !this.table[position].isDeleted) { + return this.table[position].value; + } + let index = position + 1; + while ( + this.table[index] != null && + (this.table[index].key !== key || this.table[index].isDeleted) + ) { + if (this.table[index].key === key && this.table[index].isDeleted) { + return undefined; + } + index++; + } + if ( + this.table[index] != null && + this.table[index].key === key && + !this.table[index].isDeleted + ) { + return this.table[position].value; + } + } + return undefined; + } + remove(key) { + const position = this.hashCode(key); + if (this.table[position] != null) { + if (this.table[position].key === key && !this.table[position].isDeleted) { + this.table[position].isDeleted = true; + return true; + } + let index = position + 1; + while ( + this.table[index] != null && + (this.table[index].key !== key || this.table[index].isDeleted) + ) { + index++; + } + if ( + this.table[index] != null && + this.table[index].key === key && + !this.table[index].isDeleted + ) { + this.table[index].isDeleted = true; + return true; + } + } + return false; + } + isEmpty() { + return this.size() === 0; + } + size() { + let count = 0; + Object.values(this.table).forEach(valuePair => { + count += valuePair.isDeleted === true ? 0 : 1; + }); + return count; + } + clear() { + this.table = {}; + } + getTable() { + return this.table; + } + toString() { + if (this.isEmpty()) { + return ''; + } + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + objString = `${objString},{${keys[i]} => ${this.table[ + keys[i] + ].toString()}}`; + } + return objString; + } +} diff --git a/src/js/data-structures/hash-table-linear-probing.js b/src/js/data-structures/hash-table-linear-probing.js new file mode 100644 index 00000000..940d764a --- /dev/null +++ b/src/js/data-structures/hash-table-linear-probing.js @@ -0,0 +1,113 @@ +import { defaultToString } from '../util'; +import { ValuePair } from './models/value-pair'; + +export default class HashTableLinearProbing { + constructor(toStrFn = defaultToString) { + this.toStrFn = toStrFn; + this.table = {}; + } + loseloseHashCode(key) { + if (typeof key === 'number') { + return key; + } + const tableKey = this.toStrFn(key); + let hash = 0; + for (let i = 0; i < tableKey.length; i++) { + hash += tableKey.charCodeAt(i); + } + return hash % 37; + } + hashCode(key) { + return this.loseloseHashCode(key); + } + put(key, value) { + if (key != null && value != null) { + const position = this.hashCode(key); + if (this.table[position] == null) { + this.table[position] = new ValuePair(key, value); + } else { + let index = position + 1; + while (this.table[index] != null) { + index++; + } + this.table[index] = new ValuePair(key, value); + } + return true; + } + return false; + } + get(key) { + const position = this.hashCode(key); + if (this.table[position] != null) { + if (this.table[position].key === key) { + return this.table[position].value; + } + let index = position + 1; + while (this.table[index] != null && this.table[index].key !== key) { + index++; + } + if (this.table[index] != null && this.table[index].key === key) { + return this.table[position].value; + } + } + return undefined; + } + remove(key) { + const position = this.hashCode(key); + if (this.table[position] != null) { + if (this.table[position].key === key) { + delete this.table[position]; + this.verifyRemoveSideEffect(key, position); + return true; + } + let index = position + 1; + while (this.table[index] != null && this.table[index].key !== key) { + index++; + } + if (this.table[index] != null && this.table[index].key === key) { + delete this.table[index]; + this.verifyRemoveSideEffect(key, index); + return true; + } + } + return false; + } + verifyRemoveSideEffect(key, removedPosition) { + const hash = this.hashCode(key); + let index = removedPosition + 1; + while (this.table[index] != null) { + const posHash = this.hashCode(this.table[index].key); + if (posHash <= hash || posHash <= removedPosition) { + this.table[removedPosition] = this.table[index]; + delete this.table[index]; + removedPosition = index; + } + index++; + } + } + isEmpty() { + return this.size() === 0; + } + size() { + return Object.keys(this.table).length; + } + clear() { + this.table = {}; + } + getTable() { + return this.table; + } + toString() { + if (this.isEmpty()) { + return ''; + } + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + objString = `${objString},{${keys[i]} => ${this.table[ + keys[i] + ].toString()}}`; + } + return objString; + } +} diff --git a/src/js/data-structures/hash-table-separate-chaining.js b/src/js/data-structures/hash-table-separate-chaining.js new file mode 100644 index 00000000..c00c277c --- /dev/null +++ b/src/js/data-structures/hash-table-separate-chaining.js @@ -0,0 +1,96 @@ +import { defaultToString } from '../util'; +import LinkedList from './linked-list'; +import { ValuePair } from './models/value-pair'; + +export default class HashTableSeparateChaining { + constructor(toStrFn = defaultToString) { + this.toStrFn = toStrFn; + this.table = {}; + } + loseloseHashCode(key) { + if (typeof key === 'number') { + return key; + } + const tableKey = this.toStrFn(key); + let hash = 0; + for (let i = 0; i < tableKey.length; i++) { + hash += tableKey.charCodeAt(i); + } + return hash % 37; + } + hashCode(key) { + return this.loseloseHashCode(key); + } + put(key, value) { + if (key != null && value != null) { + const position = this.hashCode(key); + if (this.table[position] == null) { + this.table[position] = new LinkedList(); + } + this.table[position].push(new ValuePair(key, value)); + return true; + } + return false; + } + get(key) { + const position = this.hashCode(key); + const linkedList = this.table[position]; + if (linkedList != null && !linkedList.isEmpty()) { + let current = linkedList.getHead(); + while (current != null) { + if (current.element.key === key) { + return current.element.value; + } + current = current.next; + } + } + return undefined; + } + remove(key) { + const position = this.hashCode(key); + const linkedList = this.table[position]; + if (linkedList != null && !linkedList.isEmpty()) { + let current = linkedList.getHead(); + while (current != null) { + if (current.element.key === key) { + linkedList.remove(current.element); + if (linkedList.isEmpty()) { + delete this.table[position]; + } + return true; + } + current = current.next; + } + } + return false; + } + isEmpty() { + return this.size() === 0; + } + size() { + let count = 0; + Object.values(this.table).forEach(linkedList => { + count += linkedList.size(); + }); + return count; + } + clear() { + this.table = {}; + } + getTable() { + return this.table; + } + toString() { + if (this.isEmpty()) { + return ''; + } + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + objString = `${objString},{${keys[i]} => ${this.table[ + keys[i] + ].toString()}}`; + } + return objString; + } +} diff --git a/src/js/data-structures/hash-table.js b/src/js/data-structures/hash-table.js new file mode 100644 index 00000000..725ac0b5 --- /dev/null +++ b/src/js/data-structures/hash-table.js @@ -0,0 +1,75 @@ +import { defaultToString } from '../util'; +import { ValuePair } from './models/value-pair'; + +export default class HashTable { + constructor(toStrFn = defaultToString) { + this.toStrFn = toStrFn; + this.table = {}; + } + loseloseHashCode(key) { + if (typeof key === 'number') { + return key; + } + const tableKey = this.toStrFn(key); + let hash = 0; + for (let i = 0; i < tableKey.length; i++) { + hash += tableKey.charCodeAt(i); + } + return hash % 37; + } + /* djb2HashCode(key) { + const tableKey = this.toStrFn(key); + let hash = 5381; + for (let i = 0; i < tableKey.length; i++) { + hash = (hash * 33) + tableKey.charCodeAt(i); + } + return hash % 1013; + } */ + hashCode(key) { + return this.loseloseHashCode(key); + } + put(key, value) { + if (key != null && value != null) { + const position = this.hashCode(key); + this.table[position] = new ValuePair(key, value); + return true; + } + return false; + } + get(key) { + const valuePair = this.table[this.hashCode(key)]; + return valuePair == null ? undefined : valuePair.value; + } + remove(key) { + const hash = this.hashCode(key); + const valuePair = this.table[hash]; + if (valuePair != null) { + delete this.table[hash]; + return true; + } + return false; + } + getTable() { + return this.table; + } + isEmpty() { + return this.size() === 0; + } + size() { + return Object.keys(this.table).length; + } + clear() { + this.table = {}; + } + toString() { + if (this.isEmpty()) { + return ''; + } + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + objString = `${objString},{${keys[i]} => ${this.table[keys[i]].toString()}}`; + } + return objString; + } +} diff --git a/src/js/data-structures/heap.js b/src/js/data-structures/heap.js new file mode 100644 index 00000000..edce1608 --- /dev/null +++ b/src/js/data-structures/heap.js @@ -0,0 +1,95 @@ +import { Compare, defaultCompare, reverseCompare, swap } from '../util'; + +export class MinHeap { + constructor(compareFn = defaultCompare) { + this.compareFn = compareFn; + this.heap = []; + } + getLeftIndex(index) { + return (2 * index) + 1; + } + getRightIndex(index) { + return (2 * index) + 2; + } + getParentIndex(index) { + if (index === 0) { + return undefined; + } + return Math.floor((index - 1) / 2); + } + size() { + return this.heap.length; + } + isEmpty() { + return this.size() <= 0; + } + clear() { + this.heap = []; + } + find() { + return this.isEmpty() ? undefined : this.heap[0]; + } + insert(value) { + if (value != null) { + const index = this.heap.length; + this.heap.push(value); + this.siftUp(index); + return true; + } + return false; + } + siftDown(index) { + let element = index; + const left = this.getLeftIndex(index); + const right = this.getRightIndex(index); + const size = this.size(); + if (left < size && this.compareFn(this.heap[element], this.heap[left]) > Compare.BIGGER_THAN) { + element = left; + } + if ( + right < size && + this.compareFn(this.heap[element], this.heap[right]) > Compare.BIGGER_THAN + ) { + element = right; + } + if (index !== element) { + swap(this.heap, index, element); + this.siftDown(element); + } + } + siftUp(index) { + let parent = this.getParentIndex(index); + while (index > 0 && this.compareFn(this.heap[parent], this.heap[index]) > Compare.BIGGER_THAN) { + swap(this.heap, parent, index); + index = parent; + parent = this.getParentIndex(index); + } + } + extract() { + if (this.isEmpty()) { + return undefined; + } + if (this.size() === 1) { + return this.heap.shift(); + } + const removedValue = this.heap.shift(); + this.siftDown(0); + return removedValue; + } + heapify(array) { + if (array) { + this.heap = array; + this.heap.unshift(null); // remove all null elements + } + for (let i = this.size() - 1; i > 0; i--) { + this.siftDown(i); + } + } +} +export class MaxHeap extends MinHeap { + constructor(compareFn = defaultCompare) { + super(compareFn); + this.compareFn = compareFn; + this.compareFn = reverseCompare(compareFn); + } +} diff --git a/src/js/data-structures/linked-list.js b/src/js/data-structures/linked-list.js new file mode 100644 index 00000000..e9b2e16b --- /dev/null +++ b/src/js/data-structures/linked-list.js @@ -0,0 +1,106 @@ +import { defaultEquals } from '../util'; +import { Node } from './models/linked-list-models'; + +export default class LinkedList { + constructor(equalsFn = defaultEquals) { + this.equalsFn = equalsFn; + this.count = 0; + this.head = undefined; + } + push(element) { + const node = new Node(element); + let current; + if (this.head == null) { + // catches null && undefined + this.head = node; + } else { + current = this.head; + while (current.next != null) { + current = current.next; + } + current.next = node; + } + this.count++; + } + getElementAt(index) { + if (index >= 0 && index <= this.count) { + let node = this.head; + for (let i = 0; i < index && node != null; i++) { + node = node.next; + } + return node; + } + return undefined; + } + insert(element, index) { + if (index >= 0 && index <= this.count) { + const node = new Node(element); + if (index === 0) { + const current = this.head; + node.next = current; + this.head = node; + } else { + const previous = this.getElementAt(index - 1); + node.next = previous.next; + previous.next = node; + } + this.count++; + return true; + } + return false; + } + removeAt(index) { + if (index >= 0 && index < this.count) { + let current = this.head; + if (index === 0) { + this.head = current.next; + } else { + const previous = this.getElementAt(index - 1); + current = previous.next; + previous.next = current.next; + } + this.count--; + return current.element; + } + return undefined; + } + remove(element) { + const index = this.indexOf(element); + return this.removeAt(index); + } + indexOf(element) { + let current = this.head; + for (let i = 0; i < this.size() && current != null; i++) { + if (this.equalsFn(element, current.element)) { + return i; + } + current = current.next; + } + return -1; + } + isEmpty() { + return this.size() === 0; + } + size() { + return this.count; + } + getHead() { + return this.head; + } + clear() { + this.head = undefined; + this.count = 0; + } + toString() { + if (this.head == null) { + return ''; + } + let objString = `${this.head.element}`; + let current = this.head.next; + for (let i = 1; i < this.size() && current != null; i++) { + objString = `${objString},${current.element}`; + current = current.next; + } + return objString; + } +} diff --git a/src/js/data-structures/models/linked-list-models.js b/src/js/data-structures/models/linked-list-models.js new file mode 100644 index 00000000..31f5dd95 --- /dev/null +++ b/src/js/data-structures/models/linked-list-models.js @@ -0,0 +1,12 @@ +export class Node { + constructor(element, next) { + this.element = element; + this.next = next; + } +} +export class DoublyNode extends Node { + constructor(element, next, prev) { + super(element, next); + this.prev = prev; + } +} diff --git a/src/js/data-structures/models/node.js b/src/js/data-structures/models/node.js new file mode 100644 index 00000000..6d3b54a7 --- /dev/null +++ b/src/js/data-structures/models/node.js @@ -0,0 +1,10 @@ +export class Node { + constructor(key) { + this.key = key; + this.left = null; + this.right = null; + } + toString() { + return `${this.key}`; + } +} diff --git a/src/js/data-structures/models/value-pair-lazy.js b/src/js/data-structures/models/value-pair-lazy.js new file mode 100644 index 00000000..ff2e4398 --- /dev/null +++ b/src/js/data-structures/models/value-pair-lazy.js @@ -0,0 +1,10 @@ +import { ValuePair } from './value-pair'; + +export class ValuePairLazy extends ValuePair { + constructor(key, value, isDeleted = false) { + super(key, value); + this.key = key; + this.value = value; + this.isDeleted = isDeleted; + } +} diff --git a/src/js/data-structures/models/value-pair.js b/src/js/data-structures/models/value-pair.js new file mode 100644 index 00000000..b35a936a --- /dev/null +++ b/src/js/data-structures/models/value-pair.js @@ -0,0 +1,9 @@ +export class ValuePair { + constructor(key, value) { + this.key = key; + this.value = value; + } + toString() { + return `[#${this.key}: ${this.value}]`; + } +} diff --git a/src/js/data-structures/queue.js b/src/js/data-structures/queue.js new file mode 100644 index 00000000..6c879004 --- /dev/null +++ b/src/js/data-structures/queue.js @@ -0,0 +1,56 @@ +// @ts-check + +export default class Queue { + constructor() { + this.count = 0; + this.lowestCount = 0; + this.items = {}; + } + + enqueue(element) { + this.items[this.count] = element; + this.count++; + } + + dequeue() { + if (this.isEmpty()) { + return undefined; + } + const result = this.items[this.lowestCount]; + delete this.items[this.lowestCount]; + this.lowestCount++; + return result; + } + + peek() { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.lowestCount]; + } + + isEmpty() { + return this.size() === 0; + } + + clear() { + this.items = {}; + this.count = 0; + this.lowestCount = 0; + } + + size() { + return this.count - this.lowestCount; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + let objString = `${this.items[this.lowestCount]}`; + for (let i = this.lowestCount + 1; i < this.count; i++) { + objString = `${objString},${this.items[i]}`; + } + return objString; + } +} diff --git a/src/js/data-structures/set.js b/src/js/data-structures/set.js new file mode 100644 index 00000000..0203bb8b --- /dev/null +++ b/src/js/data-structures/set.js @@ -0,0 +1,91 @@ +export default class Set { + constructor() { + this.items = {}; + } + add(element) { + if (!this.has(element)) { + this.items[element] = element; + return true; + } + return false; + } + delete(element) { + if (this.has(element)) { + delete this.items[element]; + return true; + } + return false; + } + has(element) { + return Object.prototype.hasOwnProperty.call(this.items, element); + } + values() { + return Object.values(this.items); + } + union(otherSet) { + const unionSet = new Set(); + this.values().forEach(value => unionSet.add(value)); + otherSet.values().forEach(value => unionSet.add(value)); + return unionSet; + } + intersection(otherSet) { + const intersectionSet = new Set(); + const values = this.values(); + const otherValues = otherSet.values(); + let biggerSet = values; + let smallerSet = otherValues; + if (otherValues.length - values.length > 0) { + biggerSet = otherValues; + smallerSet = values; + } + smallerSet.forEach(value => { + if (biggerSet.includes(value)) { + intersectionSet.add(value); + } + }); + return intersectionSet; + } + difference(otherSet) { + const differenceSet = new Set(); + this.values().forEach(value => { + if (!otherSet.has(value)) { + differenceSet.add(value); + } + }); + return differenceSet; + } + isSubsetOf(otherSet) { + if (this.size() > otherSet.size()) { + return false; + } + let isSubset = true; + this.values().every(value => { + if (!otherSet.has(value)) { + isSubset = false; + return false; + } + return true; + }); + return isSubset; + } + isEmpty() { + return this.size() === 0; + } + size() { + return Object.keys(this.items).length; + } + clear() { + this.items = {}; + } + toString() { + if (this.isEmpty()) { + return ''; + } + const values = this.values(); + let objString = `${values[0]}`; + for (let i = 1; i < values.length; i++) { + objString = `${objString},${values[i].toString()}`; + } + return objString; + } +} diff --git a/src/js/data-structures/sorted-linked-list.js b/src/js/data-structures/sorted-linked-list.js new file mode 100644 index 00000000..7b3bd468 --- /dev/null +++ b/src/js/data-structures/sorted-linked-list.js @@ -0,0 +1,37 @@ +import { Compare, defaultCompare, defaultEquals } from '../util'; +import LinkedList from './linked-list'; + +export default class SortedLinkedList extends LinkedList { + constructor(equalsFn = defaultEquals, compareFn = defaultCompare) { + super(equalsFn); + this.equalsFn = equalsFn; + this.compareFn = compareFn; + } + push(element) { + if (this.isEmpty()) { + super.push(element); + } else { + const index = this.getIndexNextSortedElement(element); + super.insert(element, index); + } + } + insert(element, index = 0) { + if (this.isEmpty()) { + return super.insert(element, index === 0 ? index : 0); + } + const pos = this.getIndexNextSortedElement(element); + return super.insert(element, pos); + } + getIndexNextSortedElement(element) { + let current = this.head; + let i = 0; + for (; i < this.size() && current; i++) { + const comp = this.compareFn(element, current.element); + if (comp === Compare.LESS_THAN) { + return i; + } + current = current.next; + } + return i; + } +} diff --git a/src/js/data-structures/stack-array.js b/src/js/data-structures/stack-array.js new file mode 100644 index 00000000..ec8642ad --- /dev/null +++ b/src/js/data-structures/stack-array.js @@ -0,0 +1,38 @@ +// @ts-check + +export default class StackArray { + constructor() { + this.items = []; + } + push(element) { + this.items.push(element); + } + + pop() { + return this.items.pop(); + } + + peek() { + return this.items[this.items.length - 1]; + } + + isEmpty() { + return this.items.length === 0; + } + + size() { + return this.items.length; + } + + clear() { + this.items = []; + } + + toArray() { + return this.items; + } + + toString() { + return this.items.toString(); + } +} diff --git a/src/js/data-structures/stack-linked-list.js b/src/js/data-structures/stack-linked-list.js new file mode 100644 index 00000000..cab8c5d5 --- /dev/null +++ b/src/js/data-structures/stack-linked-list.js @@ -0,0 +1,35 @@ +import DoublyLinkedList from './doubly-linked-list'; + +export default class StackLinkedList { + constructor() { + this.items = new DoublyLinkedList(); + } + push(element) { + this.items.push(element); + } + pop() { + if (this.isEmpty()) { + return undefined; + } + const result = this.items.removeAt(this.size() - 1); + return result; + } + peek() { + if (this.isEmpty()) { + return undefined; + } + return this.items.getElementAt(this.size() - 1).element; + } + isEmpty() { + return this.items.isEmpty(); + } + size() { + return this.items.size(); + } + clear() { + this.items.clear(); + } + toString() { + return this.items.toString(); + } +} diff --git a/src/js/data-structures/stack.js b/src/js/data-structures/stack.js new file mode 100644 index 00000000..081bab33 --- /dev/null +++ b/src/js/data-structures/stack.js @@ -0,0 +1,50 @@ +// @ts-check + +export default class Stack { + constructor() { + this.count = 0; + this.items = {}; + } + push(element) { + this.items[this.count] = element; + this.count++; + } + pop() { + if (this.isEmpty()) { + return undefined; + } + this.count--; + const result = this.items[this.count]; + delete this.items[this.count]; + return result; + } + peek() { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.count - 1]; + } + isEmpty() { + return this.count === 0; + } + size() { + return this.count; + } + clear() { + /* while (!this.isEmpty()) { + this.pop(); + } */ + this.items = {}; + this.count = 0; + } + toString() { + if (this.isEmpty()) { + return ''; + } + let objString = `${this.items[0]}`; + for (let i = 1; i < this.count; i++) { + objString = `${objString},${this.items[i]}`; + } + return objString; + } +} diff --git a/src/js/index.js b/src/js/index.js new file mode 100644 index 00000000..5e16442e --- /dev/null +++ b/src/js/index.js @@ -0,0 +1,83 @@ +import * as _util from './util'; + +// chapters 05 and 07 +export const util = _util; + +// chapter 03 +export { default as StackArray } from './data-structures/stack-array'; +export { default as Stack } from './data-structures/stack'; +export { default as hanoi } from './others/hanoi'; +export { default as hanoiStack } from './others/hanoi'; +export { default as baseConverter } from './others/base-converter'; +export { default as decimalToBinary } from './others/base-converter'; +export { default as parenthesesChecker } from './others/balanced-symbols'; + +// chapter 04 +export { default as Queue } from './data-structures/queue'; +export { default as Deque } from './data-structures/deque'; +export { default as hotPotato } from './others/hot-potato'; +export { default as palindromeChecker } from './others/palindrome-checker'; + +// chapter 05 +export { default as LinkedList } from './data-structures/linked-list'; +export { default as DoublyLinkedList } from './data-structures/doubly-linked-list'; +export { default as CircularLinkedList } from './data-structures/circular-linked-list'; +export { default as SortedLinkedList } from './data-structures/sorted-linked-list'; +export { default as StackLinkedList } from './data-structures/stack-linked-list'; + +// chapter 06 +export { default as Set } from './data-structures/set'; + +// chapter 07 +export { default as Dictionary } from './data-structures/dictionary'; +export { default as HashTable } from './data-structures/hash-table'; +export { default as HashTableSeparateChaining } from './data-structures/hash-table-separate-chaining'; +export { default as HashTableLinearProbing } from './data-structures/hash-table-linear-probing'; +export { default as HashTableLinearProbingLazy } from './data-structures/hash-table-linear-probing-lazy'; + +// chapter 08 +export { default as factorialIterative } from './others/factorial'; +export { default as factorial } from './others/factorial'; +export { default as fibonacci } from './others/fibonacci'; +export { default as fibonacciIterative } from './others/fibonacci'; +export { default as fibonacciMemoization } from './others/fibonacci'; + +// chapter 09 +export { default as BinarySearchTree } from './data-structures/binary-search-tree'; +export { default as AVLTree } from './data-structures/avl-tree'; + +// chapter 10 +export { MinHeap } from './data-structures/heap'; +export { MaxHeap } from './data-structures/heap'; +export { default as heapSort } from './algorithms/sorting/heap-sort'; + +// chapter 11 +export { default as Graph } from './data-structures/graph'; +export { breadthFirstSearch } from './algorithms/graph/breadth-first-search'; +export { BFS } from './algorithms/graph/breadth-first-search'; +export { depthFirstSearch } from './algorithms/graph/depth-first-search'; +export { DFS } from './algorithms/graph/depth-first-search'; +export { dijkstra } from './algorithms/graph/dijkstra'; +export { floydWarshall } from './algorithms/graph/floyd-warshall'; +export { prim } from './algorithms/graph/prim'; +export { kruskal } from './algorithms/graph/kruskal'; + +// chapter 12 +export { shuffle } from './algorithms/shuffle/fisher–yates'; + +export { bubbleSort } from './algorithms/sorting/bubble-sort'; +export { modifiedBubbleSort } from './algorithms/sorting/bubble-sort-improved'; +export { bucketSort } from './algorithms/sorting/bucket-sort'; +export { countingSort } from './algorithms/sorting/counting-sort'; +export { insertionSort } from './algorithms/sorting/insertion-sort'; +export { mergeSort } from './algorithms/sorting/merge-sort'; +export { quickSort } from './algorithms/sorting/quicksort'; +export { radixSort } from './algorithms/sorting/radix-sort'; +export { selectionSort } from './algorithms/sorting/selection-sort'; +export { shellSort } from './algorithms/sorting/shell-sort'; + +export { binarySearch } from './algorithms/search/binary-search'; +export { interpolationSearch } from './algorithms/search/interpolation-search'; +export { sequentialSearch } from './algorithms/search/sequential-search'; +export { findMaxValue } from './algorithms/search/min-max-search'; +export { findMinValue } from './algorithms/search/min-max-search'; diff --git a/src/js/others/balanced-symbols.js b/src/js/others/balanced-symbols.js new file mode 100644 index 00000000..ef1435ad --- /dev/null +++ b/src/js/others/balanced-symbols.js @@ -0,0 +1,31 @@ +// @ts-check +import Stack from '../data-structures/stack'; + +export function parenthesesChecker(symbols) { + const stack = new Stack(); + const opens = '([{'; + const closers = ')]}'; + let balanced = true; + let index = 0; + let symbol; + let top; + + while (index < symbols.length && balanced) { + symbol = symbols.charAt(index); + if (opens.indexOf(symbol) >= 0) { + stack.push(symbol); + } else if (stack.isEmpty()) { + balanced = false; + } else { + top = stack.pop(); + if (!(opens.indexOf(top) === closers.indexOf(symbol))) { + balanced = false; + } + } + index++; + } + if (balanced && stack.isEmpty()) { + return true; + } + return false; +} diff --git a/src/js/others/base-converter.js b/src/js/others/base-converter.js new file mode 100644 index 00000000..0213b1d7 --- /dev/null +++ b/src/js/others/base-converter.js @@ -0,0 +1,45 @@ +// @ts-check +import Stack from '../data-structures/stack'; + +export function decimalToBinary(decNumber) { + const remStack = new Stack(); + let number = decNumber; + let rem; + let binaryString = ''; + + while (number > 0) { + rem = Math.floor(number % 2); + remStack.push(rem); + number = Math.floor(number / 2); + } + + while (!remStack.isEmpty()) { + binaryString += remStack.pop().toString(); + } + + return binaryString; +} + +export function baseConverter(decNumber, base) { + const remStack = new Stack(); + const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let number = decNumber; + let rem; + let baseString = ''; + + if (!(base >= 2 && base <= 36)) { + return ''; + } + + while (number > 0) { + rem = Math.floor(number % base); + remStack.push(rem); + number = Math.floor(number / base); + } + + while (!remStack.isEmpty()) { + baseString += digits[remStack.pop()]; + } + + return baseString; +} diff --git a/src/js/others/factorial.js b/src/js/others/factorial.js new file mode 100644 index 00000000..8592c36e --- /dev/null +++ b/src/js/others/factorial.js @@ -0,0 +1,20 @@ +export function factorialIterative(number) { + if (number < 0) { + return undefined; + } + let total = 1; + for (let n = number; n > 1; n--) { + total *= n; + } + return total; +} + +export function factorial(n) { + if (n < 0) { + return undefined; + } + if (n === 1 || n === 0) { + return 1; + } + return n * factorial(n - 1); +} diff --git a/src/js/others/fibonacci.js b/src/js/others/fibonacci.js new file mode 100644 index 00000000..04de5e68 --- /dev/null +++ b/src/js/others/fibonacci.js @@ -0,0 +1,33 @@ +export function fibonacci(n) { + if (n < 1) { + return 0; + } + if (n <= 2) { + return 1; + } + return fibonacci(n - 1) + fibonacci(n - 2); +} + +export function fibonacciIterative(n) { + if (n < 1) { return 0; } + let fibNMinus2 = 0; + let fibNMinus1 = 1; + let fibN = n; + for (let i = 2; i <= n; i++) { + fibN = fibNMinus1 + fibNMinus2; + fibNMinus2 = fibNMinus1; + fibNMinus1 = fibN; + } + return fibN; +} + +export function fibonacciMemoization(n) { + if (n < 1) { return 0; } + const memo = [0, 1]; + const fibonacciMem = num => { + if (memo[num] != null) { return memo[num]; } + memo[num] = fibonacciMem(num - 1) + fibonacciMem(num - 2); + return (memo[num] = fibonacciMem(num - 1) + fibonacciMem(num - 2)); + }; + return fibonacciMem(n); +} diff --git a/src/js/others/hanoi.js b/src/js/others/hanoi.js new file mode 100644 index 00000000..b1ab2d0e --- /dev/null +++ b/src/js/others/hanoi.js @@ -0,0 +1,52 @@ +// @ts-check +import Stack from '../data-structures/stack'; + +function towerOfHanoi(plates, source, helper, dest, sourceName, helperName, destName, moves = []) { + if (plates <= 0) { + return moves; + } + if (plates === 1) { + dest.push(source.pop()); + const move = {}; + move[sourceName] = source.toString(); + move[helperName] = helper.toString(); + move[destName] = dest.toString(); + moves.push(move); + } else { + towerOfHanoi(plates - 1, source, dest, helper, sourceName, destName, helperName, moves); + dest.push(source.pop()); + const move = {}; + move[sourceName] = source.toString(); + move[helperName] = helper.toString(); + move[destName] = dest.toString(); + moves.push(move); + towerOfHanoi(plates - 1, helper, source, dest, helperName, sourceName, destName, moves); + } + return moves; +} + +export function hanoiStack(plates) { + const source = new Stack(); + const dest = new Stack(); + const helper = new Stack(); + + for (let i = plates; i > 0; i--) { + source.push(i); + } + + return towerOfHanoi(plates, source, helper, dest, 'source', 'helper', 'dest'); +} + +export function hanoi(plates, source, helper, dest, moves = []) { + if (plates <= 0) { + return moves; + } + if (plates === 1) { + moves.push([source, dest]); + } else { + hanoi(plates - 1, source, dest, helper, moves); + moves.push([source, dest]); + hanoi(plates - 1, helper, source, dest, moves); + } + return moves; +} diff --git a/src/js/others/hot-potato.js b/src/js/others/hot-potato.js new file mode 100644 index 00000000..b34fcc64 --- /dev/null +++ b/src/js/others/hot-potato.js @@ -0,0 +1,22 @@ +import Queue from '../data-structures/queue'; + +export function hotPotato(elementsList, num) { + const queue = new Queue(); + const elimitatedList = []; + + for (let i = 0; i < elementsList.length; i++) { + queue.enqueue(elementsList[i]); + } + + while (queue.size() > 1) { + for (let i = 0; i < num; i++) { + queue.enqueue(queue.dequeue()); + } + elimitatedList.push(queue.dequeue()); + } + + return { + eliminated: elimitatedList, + winner: queue.dequeue() + }; +} diff --git a/src/js/others/palindrome-checker.js b/src/js/others/palindrome-checker.js new file mode 100644 index 00000000..17a0c581 --- /dev/null +++ b/src/js/others/palindrome-checker.js @@ -0,0 +1,30 @@ +import Deque from '../data-structures/deque'; + +export function palindromeChecker(aString) { + if ( + aString === undefined || + aString === null || + (aString !== null && aString.length === 0) + ) { + return false; + } + const deque = new Deque(); + const lowerString = aString.toLocaleLowerCase().split(' ').join(''); + let isEqual = true; + let firstChar; + let lastChar; + + for (let i = 0; i < lowerString.length; i++) { + deque.addBack(lowerString.charAt(i)); + } + + while (deque.size() > 1 && isEqual) { + firstChar = deque.removeFront(); + lastChar = deque.removeBack(); + if (firstChar !== lastChar) { + isEqual = false; + } + } + + return isEqual; +} diff --git a/src/js/util.js b/src/js/util.js new file mode 100644 index 00000000..70e85181 --- /dev/null +++ b/src/js/util.js @@ -0,0 +1,53 @@ +export const Compare = { + LESS_THAN: -1, + BIGGER_THAN: 1, + EQUALS: 0 +}; + +export const DOES_NOT_EXIST = -1; + +export function lesserEquals(a, b, compareFn) { + const comp = compareFn(a, b); + return comp === Compare.LESS_THAN || comp === Compare.EQUALS; +} + +export function biggerEquals(a, b, compareFn) { + const comp = compareFn(a, b); + return comp === Compare.BIGGER_THAN || comp === Compare.EQUALS; +} + +export function defaultCompare(a, b) { + if (a === b) { + return 0; + } + return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; +} + +export function defaultEquals(a, b) { + return a === b; +} + +export function defaultToString(item) { + if (item === null) { + return 'NULL'; + } else if (item === undefined) { + return 'UNDEFINED'; + } else if (typeof item === 'string' || item instanceof String) { + return `${item}`; + } + return item.toString(); +} + +export function swap(array, a, b) { + /* const temp = array[a]; + array[a] = array[b]; + array[b] = temp; */ + [array[a], array[b]] = [array[b], array[a]]; +} +export function reverseCompare(compareFn) { + return (a, b) => compareFn(b, a); +} + +export function defaultDiff(a, b) { + return Number(a) - Number(b); +} diff --git a/src/ts/algorithms/backtracking/rat-in-maze.ts b/src/ts/algorithms/backtracking/rat-in-maze.ts new file mode 100644 index 00000000..915c8b63 --- /dev/null +++ b/src/ts/algorithms/backtracking/rat-in-maze.ts @@ -0,0 +1,54 @@ +export function ratInAMaze(maze: Array>) { + const solution: Array> = []; + + for (let i = 0; i < maze.length; i++) { + solution[i] = []; + for (let j = 0; j < maze[i].length; j++) { + solution[i][j] = 0; + } + } + + if (findPath(maze, 0, 0, solution) === false) { + return solution; + } else { + return 'NO PATH FOUND'; + } +} + +function findPath( + maze: Array>, + x: number, + y: number, + solution: Array> +) { + const n = maze.length; + if (x === n - 1 && y === n - 1) { + solution[x][y] = 1; + return true; + } + + if (isSafe(maze, x, y) === true) { + solution[x][y] = 1; + + if (findPath(maze, x + 1, y, solution)) { + return true; + } + + if (findPath(maze, x, y + 1, solution)) { + return true; + } + + solution[x][y] = 0; + return false; + } + + return false; +} + +function isSafe(maze: Array>, x: number, y: number) { + const n = maze.length; + if (x >= 0 && y >= 0 && x < n && y < n && maze[x][y] !== 0) { + return true; + } + return false; +} diff --git a/src/ts/algorithms/backtracking/sudoku-solver.ts b/src/ts/algorithms/backtracking/sudoku-solver.ts new file mode 100644 index 00000000..3ada1069 --- /dev/null +++ b/src/ts/algorithms/backtracking/sudoku-solver.ts @@ -0,0 +1,87 @@ +const UNASSIGNED = 0; + +export function sudokuSolver(grid: Array>) { + if (solveSudoku(grid) === true) { + return grid; + } else { + return 'NO SOLUTION EXISTS!'; + } +} + +function solveSudoku(grid: Array>) { + let row = 0; + let col = 0; + let checkBlankSpaces = false; + + for (row = 0; row < grid.length; row++) { + for (col = 0; col < grid[row].length; col++) { + if (grid[row][col] === UNASSIGNED) { + checkBlankSpaces = true; + break; + } + } + if (checkBlankSpaces === true) { + break; + } + } + + if (checkBlankSpaces === false) { + return true; + } + + for (let num = 1; num <= 9; num++) { + if (isSafe(grid, row, col, num)) { + grid[row][col] = num; + + if (solveSudoku(grid)) { + return true; + } + + grid[row][col] = UNASSIGNED; + } + } + + return false; +} + +function usedInRow(grid: Array>, row: number, num: number) { + for (let col = 0; col < grid.length; col++) { + if (grid[row][col] === num) { + return true; + } + } + return false; +} + +function usedInCol(grid: Array>, col: number, num: number) { + for (let row = 0; row < grid.length; row++) { + if (grid[row][col] === num) { + return true; + } + } + return false; +} + +function usedInBox( + grid: Array>, + boxStartRow: number, + boxStartCol: number, + num: number +) { + for (let row = 0; row < 3; row++) { + for (let col = 0; col < 3; col++) { + if (grid[row + boxStartRow][col + boxStartCol] === num) { + return true; + } + } + } + return false; +} + +function isSafe(grid: Array>, row: number, col: number, num: number) { + return ( + !usedInRow(grid, row, num) && + !usedInCol(grid, col, num) && + !usedInBox(grid, row - row % 3, col - col % 3, num) + ); +} diff --git a/src/ts/algorithms/dynamic-programing/knapsack-recursive.ts b/src/ts/algorithms/dynamic-programing/knapsack-recursive.ts new file mode 100644 index 00000000..0fce59a5 --- /dev/null +++ b/src/ts/algorithms/dynamic-programing/knapsack-recursive.ts @@ -0,0 +1,13 @@ +export function knapSack(capacity: number, weights: number[], values: number[], n: number): number { + if (n === 0 || capacity === 0) { + return 0; + } + + if (weights[n - 1] > capacity) { + return knapSack(capacity, weights, values, n - 1); + } else { + const a: number = values[n - 1] + knapSack(capacity - weights[n - 1], weights, values, n - 1); + const b: number = knapSack(capacity, weights, values, n - 1); + return a > b ? a : b; + } +} diff --git a/src/ts/algorithms/dynamic-programing/knapsack.ts b/src/ts/algorithms/dynamic-programing/knapsack.ts new file mode 100644 index 00000000..bfa42398 --- /dev/null +++ b/src/ts/algorithms/dynamic-programing/knapsack.ts @@ -0,0 +1,51 @@ +export function knapSack(capacity: number, weights: number[], values: number[], n: number) { + const kS: Array> = []; + + for (let i = 0; i <= n; i++) { + kS[i] = []; + } + + for (let i = 0; i <= n; i++) { + for (let w = 0; w <= capacity; w++) { + if (i === 0 || w === 0) { + kS[i][w] = 0; + } else if (weights[i - 1] <= w) { + const a = values[i - 1] + kS[i - 1][w - weights[i - 1]]; + const b = kS[i - 1][w]; + kS[i][w] = a > b ? a : b; // max(a,b) + // console.log(a + ' can be part of the solution'); + } else { + kS[i][w] = kS[i - 1][w]; + } + } + // console.log(kS[i].join()); + } + + // extra algorithm to find the items that are part of the solution + findValues(n, capacity, kS); + + return kS[n][capacity]; +} + +function findValues( + n: number, + capacity: number, + kS: Array> +) { + let i = n; + let k = capacity; + + // console.log('Items that are part of the solution:'); + + while (i > 0 && k > 0) { + if (kS[i][k] !== kS[i - 1][k]) { + /* console.log( + 'item ' + i + ' can be part of solution w,v: ' + weights[i - 1] + ',' + values[i - 1] + ); */ + i--; + k = k - kS[i][k]; + } else { + i--; + } + } +} diff --git a/src/ts/algorithms/dynamic-programing/longest-common-subsequence-print.ts b/src/ts/algorithms/dynamic-programing/longest-common-subsequence-print.ts new file mode 100644 index 00000000..3fbbab8e --- /dev/null +++ b/src/ts/algorithms/dynamic-programing/longest-common-subsequence-print.ts @@ -0,0 +1,59 @@ +export function lcs(wordX: string, wordY: string) { + const m = wordX.length; + const n = wordY.length; + const l: Array> = []; + const solution: Array> = []; + + for (let i = 0; i <= m; i++) { + l[i] = []; + solution[i] = []; + for (let j = 0; j <= n; j++) { + l[i][j] = 0; + solution[i][j] = '0'; + } + } + + for (let i = 0; i <= m; i++) { + for (let j = 0; j <= n; j++) { + if (i === 0 || j === 0) { + l[i][j] = 0; + } else if (wordX[i - 1] === wordY[j - 1]) { + l[i][j] = l[i - 1][j - 1] + 1; + solution[i][j] = 'diagonal'; + } else { + const a = l[i - 1][j]; + const b = l[i][j - 1]; + l[i][j] = a > b ? a : b; // max(a,b) + solution[i][j] = l[i][j] === l[i - 1][j] ? 'top' : 'left'; + } + } + // console.log(l[i].join()); + // console.log(solution[i].join()); + } + + printSolution(solution, wordX, m, n); + + return l[m][n]; +} + +function printSolution(solution: Array>, wordX: string, m: number, n: number) { + let a = m; + let b = n; + let x = solution[a][b]; + let answer = ''; + + while (x !== '0') { + if (solution[a][b] === 'diagonal') { + answer = wordX[a - 1] + answer; + a--; + b--; + } else if (solution[a][b] === 'left') { + b--; + } else if (solution[a][b] === 'top') { + a--; + } + x = solution[a][b]; + } + + // console.log('lcs: ' + answer); +} diff --git a/src/ts/algorithms/dynamic-programing/longest-common-subsequence.ts b/src/ts/algorithms/dynamic-programing/longest-common-subsequence.ts new file mode 100644 index 00000000..9aa48054 --- /dev/null +++ b/src/ts/algorithms/dynamic-programing/longest-common-subsequence.ts @@ -0,0 +1,29 @@ +export function lcs(wordX: string, wordY: string) { + const m = wordX.length; + const n = wordY.length; + const l: Array> = []; + + for (let i = 0; i <= m; i++) { + l[i] = []; + for (let j = 0; j <= n; j++) { + l[i][j] = 0; + } + } + + for (let i = 0; i <= m; i++) { + for (let j = 0; j <= n; j++) { + if (i === 0 || j === 0) { + l[i][j] = 0; + } else if (wordX[i - 1] === wordY[j - 1]) { + l[i][j] = l[i - 1][j - 1] + 1; + } else { + const a = l[i - 1][j]; + const b = l[i][j - 1]; + l[i][j] = a > b ? a : b; // max(a,b) + } + } + // console.log(l[i].join()); + } + + return l[m][n]; +} diff --git a/src/ts/algorithms/dynamic-programing/matrix-chain-multiplication.ts b/src/ts/algorithms/dynamic-programing/matrix-chain-multiplication.ts new file mode 100644 index 00000000..c2743ec5 --- /dev/null +++ b/src/ts/algorithms/dynamic-programing/matrix-chain-multiplication.ts @@ -0,0 +1,52 @@ +export function matrixChainOrder(p: number[]): number { + const n = p.length; + + const m: Array> = []; + const s: Array> = []; + + for (let i = 1; i <= n; i++) { + m[i] = []; + m[i][i] = 0; + } + + for (let i = 0; i <= n; i++) { + // to help printing the optimal solution + s[i] = []; // auxiliary + for (let j = 0; j <= n; j++) { + s[i][j] = 0; + } + } + + for (let l = 2; l < n; l++) { + for (let i = 1; i <= n - l + 1; i++) { + const j = i + l - 1; + m[i][j] = Number.MAX_SAFE_INTEGER; + for (let k = i; k <= j - 1; k++) { + // q = cost/scalar multiplications + const q = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]; + if (q < m[i][j]) { + m[i][j] = q; + s[i][j] = k; // s[i,j] = Second auxiliary table that stores k + } + } + } + } + + // console.log(m); + // console.log(s); + + printOptimalParenthesis(s, 1, n - 1); + + return m[1][n - 1]; +} + +function printOptimalParenthesis(s: Array>, i: number, j: number) { + if (i === j) { + // console.log('A[' + i + ']'); + } else { + // console.log('('); + printOptimalParenthesis(s, i, s[i][j]); + printOptimalParenthesis(s, s[i][j] + 1, j); + // console.log(')'); + } +} diff --git a/src/ts/algorithms/dynamic-programing/min-coin-change.ts b/src/ts/algorithms/dynamic-programing/min-coin-change.ts new file mode 100644 index 00000000..85baeb83 --- /dev/null +++ b/src/ts/algorithms/dynamic-programing/min-coin-change.ts @@ -0,0 +1,34 @@ +export function minCoinChange(coins: number[], amount: number) { + const cache: Array> = []; + + // tslint:disable-next-line:no-shadowed-variable + const makeChange = function(amount: number) { + if (!amount) { + return []; + } + if (cache[amount]) { + return cache[amount]; + } + let min: number[] = [], + newMin, + newAmount; + for (let i = 0; i < coins.length; i++) { + const coin = coins[i]; + newAmount = amount - coin; + if (newAmount >= 0) { + newMin = makeChange(newAmount); + } + if ( + newAmount >= 0 && + (newMin.length < min.length - 1 || !min.length) && + (newMin.length || !newAmount) + ) { + min = [coin].concat(newMin); + // console.log('new Min ' + min + ' for ' + amount); + } + } + return (cache[amount] = min); + }; + + return makeChange(amount); +} diff --git a/src/ts/algorithms/graph/breadth-first-search.ts b/src/ts/algorithms/graph/breadth-first-search.ts new file mode 100644 index 00000000..147bebe7 --- /dev/null +++ b/src/ts/algorithms/graph/breadth-first-search.ts @@ -0,0 +1,77 @@ +import Graph from '../../data-structures/graph'; +import Queue from '../../data-structures/queue'; + +enum Colors { + WHITE = 0, + GREY = 1, + BLACK = 2 +} + +const initializeColor = (vertices: (string | number)[]) => { + const color: any = {}; + for (let i = 0; i < vertices.length; i++) { + color[vertices[i]] = Colors.WHITE; + } + return color; +}; + +export const breadthFirstSearch = (graph: Graph, callback: Function) => { + const vertices = graph.getVertices(); + const adjList = graph.getAdjList(); + const color = initializeColor(vertices); + const queue = new Queue(); + queue.enqueue(vertices); + + while (!queue.isEmpty()) { + const u = queue.dequeue(), + neighbors = adjList.get(u); + color[u] = Colors.GREY; + for (let i = 0; i < neighbors.length; i++) { + const w = neighbors[i]; + if (color[w] === Colors.WHITE) { + color[w] = Colors.GREY; + queue.enqueue(w); + } + } + color[u] = Colors.BLACK; + if (callback) { + callback(u); + } + } +}; + +export const bfs = (graph: Graph, startVertex: number) => { + const vertices = graph.getVertices(); + const adjList = graph.getAdjList(); + const color = initializeColor(vertices); + const queue = new Queue(); + const distances: any = {}; + const predecessors: any = {}; + queue.enqueue(startVertex); + + for (let i = 0; i < vertices.length; i++) { + distances[vertices[i]] = 0; + predecessors[vertices[i]] = null; + } + + while (!queue.isEmpty()) { + const u = queue.dequeue(), + neighbors = adjList.get(u); + color[u] = Colors.GREY; + for (let i = 0; i < neighbors.length; i++) { + const w = neighbors[i]; + if (color[w] === Colors.WHITE) { + color[w] = Colors.GREY; + distances[w] = distances[u] + 1; + predecessors[w] = u; + queue.enqueue(w); + } + } + color[u] = Colors.BLACK; + } + + return { + distances: distances, + predecessors: predecessors + }; +}; diff --git a/src/ts/algorithms/graph/depth-first-search.ts b/src/ts/algorithms/graph/depth-first-search.ts new file mode 100644 index 00000000..e47ef54c --- /dev/null +++ b/src/ts/algorithms/graph/depth-first-search.ts @@ -0,0 +1,103 @@ +import Graph from '../../data-structures/graph'; + +enum Colors { + WHITE = 0, + GREY = 1, + BLACK = 2 +} + +const initializeColor = (vertices: (string | number)[]) => { + const color: any = {}; + for (let i = 0; i < vertices.length; i++) { + color[vertices[i]] = Colors.WHITE; + } + return color; +}; + +const depthFirstSearchVisit = ( + u: string | number, + color: any, + adjList: any, + callback: Function +) => { + color[u] = 'grey'; + if (callback) { + callback(u); + } + // console.log('Discovered ' + u); + const neighbors = adjList.get(u); + for (let i = 0; i < neighbors.length; i++) { + const w = neighbors[i]; + if (color[w] === Colors.WHITE) { + depthFirstSearchVisit(w, color, adjList, callback); + } + } + color[u] = Colors.BLACK; + // console.log('explored ' + u); +}; + +export const depthFirstSearch = (graph: Graph, callback: Function) => { + const vertices = graph.getVertices(); + const adjList = graph.getAdjList(); + const color = initializeColor(vertices); + + for (let i = 0; i < vertices.length; i++) { + if (color[vertices[i]] === Colors.WHITE) { + depthFirstSearchVisit(vertices[i], color, adjList, callback); + } + } +}; + +const DFSVisit = ( + u: string | number, + color: any, + d: any, + f: any, + p: any, + time: number, + adjList: any +) => { + // console.log('discovered ' + u); + color[u] = Colors.GREY; + d[u] = ++time; + const neighbors = adjList.get(u); + for (let i = 0; i < neighbors.length; i++) { + const w = neighbors[i]; + if (color[w] === Colors.WHITE) { + p[w] = u; + DFSVisit(w, color, d, f, p, time, adjList); + } + } + color[u] = Colors.BLACK; + f[u] = ++time; + // console.log('explored ' + u); +}; + +export const DFS = (graph: Graph) => { + + const vertices = graph.getVertices(); + const adjList = graph.getAdjList(); + const color = initializeColor(vertices); + const d: any = {}; + const f: any = {}; + const p: any = {}; + const time = 0; + + for (let i = 0; i < vertices.length; i++) { + f[vertices[i]] = 0; + d[vertices[i]] = 0; + p[vertices[i]] = null; + } + + for (let i = 0; i < vertices.length; i++) { + if (color[vertices[i]] === Colors.WHITE) { + DFSVisit(vertices[i], color, d, f, p, time, adjList); + } + } + + return { + discovery: d, + finished: f, + predecessors: p + }; +}; diff --git a/src/ts/algorithms/graph/dijkstra.ts b/src/ts/algorithms/graph/dijkstra.ts new file mode 100644 index 00000000..a3dd4752 --- /dev/null +++ b/src/ts/algorithms/graph/dijkstra.ts @@ -0,0 +1,42 @@ +const INF = Number.MAX_SAFE_INTEGER; + +const minDistance = (dist: number[], visited: boolean[]) => { + let min = INF; + let minIndex = -1; + + for (let v = 0; v < dist.length; v++) { + if (visited[v] === false && dist[v] <= min) { + min = dist[v]; + minIndex = v; + } + } + + return minIndex; +}; + +export const dijkstra = (graph: number[][], src: number) => { + const dist: number[] = []; + const visited: boolean[] = []; + const length = graph.length; + + for (let i = 0; i < length; i++) { + dist[i] = INF; + visited[i] = false; + } + + dist[src] = 0; + + for (let i = 0; i < length - 1; i++) { + const u = minDistance(dist, visited); + + visited[u] = true; + + for (let v = 0; v < length; v++) { + if (!visited[v] && graph[u][v] !== 0 && dist[u] !== INF && dist[u] + graph[u][v] < dist[v]) { + dist[v] = dist[u] + graph[u][v]; + } + } + } + + return dist; +}; diff --git a/src/ts/algorithms/graph/floyd-warshall.ts b/src/ts/algorithms/graph/floyd-warshall.ts new file mode 100644 index 00000000..7699c6e8 --- /dev/null +++ b/src/ts/algorithms/graph/floyd-warshall.ts @@ -0,0 +1,29 @@ +export const floydWarshall = (graph: number[][]) => { + const dist: number[][] = []; + const length = graph.length; + + for (let i = 0; i < length; i++) { + dist[i] = []; + for (let j = 0; j < length; j++) { + if (i === j) { + dist[i][j] = 0; + } else if (!isFinite(graph[i][j])) { + dist[i][j] = Infinity; + } else { + dist[i][j] = graph[i][j]; + } + } + } + + for (let k = 0; k < length; k++) { + for (let i = 0; i < length; i++) { + for (let j = 0; j < length; j++) { + if (dist[i][k] + dist[k][j] < dist[i][j]) { + dist[i][j] = dist[i][k] + dist[k][j]; + } + } + } + } + + return dist; +}; diff --git a/src/ts/algorithms/graph/kruskal.ts b/src/ts/algorithms/graph/kruskal.ts new file mode 100644 index 00000000..978b1e3b --- /dev/null +++ b/src/ts/algorithms/graph/kruskal.ts @@ -0,0 +1,67 @@ +const INF = Number.MAX_SAFE_INTEGER; + +const find = (i: number, parent: number[]) => { + while (parent[i]) { + i = parent[i]; + } + return i; +}; + +const union = (i: number, j: number, parent: number[]) => { + if (i !== j) { + parent[j] = i; + return true; + } + return false; +}; + +const initializeCost = (graph: number[][]) => { + const cost: number[][] = []; + const length = graph.length; + for (let i = 0; i < length; i++) { + cost[i] = []; + for (let j = 0; j < length; j++) { + if (graph[i][j] === 0) { + cost[i][j] = INF; + } else { + cost[i][j] = graph[i][j]; + } + } + } + return cost; +}; + +export const kruskal = (graph: number[][]) => { + const length = graph.length; + const parent: number[] = []; + let ne = 0; + let a; + let b; + let u; + let v; + + const cost = initializeCost(graph); + + while (ne < length - 1) { + for (let i = 0, min = INF; i < length; i++) { + for (let j = 0; j < length; j++) { + if (cost[i][j] < min) { + min = cost[i][j]; + a = u = i; + b = v = j; + } + } + } + + u = find(u, parent); + v = find(v, parent); + + if (union(u, v, parent)) { + ne++; + } + + cost[a][b] = cost[b][a] = INF; + } + + return parent; +}; diff --git a/src/ts/algorithms/graph/prim.ts b/src/ts/algorithms/graph/prim.ts new file mode 100644 index 00000000..85892d03 --- /dev/null +++ b/src/ts/algorithms/graph/prim.ts @@ -0,0 +1,45 @@ +const INF = Number.MAX_SAFE_INTEGER; + +const minKey = (graph: number[][], key: number[], visited: boolean[]) => { + // Initialize min value + let min = INF; + let minIndex = 0; + + for (let v = 0; v < graph.length; v++) { + if (visited[v] === false && key[v] < min) { + min = key[v]; + minIndex = v; + } + } + + return minIndex; +}; + +export const prim = (graph: number[][]) => { + const parent: number[] = []; + const key: number[] = []; + const visited: boolean[] = []; + const length = graph.length; + + for (let i = 0; i < length; i++) { + key[i] = INF; + visited[i] = false; + } + + key[0] = 0; + parent[0] = -1; + + for (let i = 0; i < length - 1; i++) { + const u = minKey(graph, key, visited); + visited[u] = true; + + for (let v = 0; v < length; v++) { + if (graph[u][v] && visited[v] === false && graph[u][v] < key[v]) { + parent[v] = u; + key[v] = graph[u][v]; + } + } + } + + return parent; +}; diff --git a/src/ts/algorithms/greedy/knapsack.ts b/src/ts/algorithms/greedy/knapsack.ts new file mode 100644 index 00000000..68b235ec --- /dev/null +++ b/src/ts/algorithms/greedy/knapsack.ts @@ -0,0 +1,19 @@ +function knapSack(capacity: number, weights: number[], values: number[]) { + const n = values.length; + let load = 0; + let val = 0; + + for (let i = 0; i < n && load < capacity; i++) { + if (weights[i] <= capacity - load) { + val += values[i]; + load += weights[i]; + // console.log('using item ' + (i + 1) + ' for the solution'); + } else { + const r = (capacity - load) / weights[i]; + val += r * values[i]; + load += weights[i]; + // console.log('using ratio of ' + r + ' for item ' + (i + 1) + ' for the solution'); + } + } + return val; +} diff --git a/src/ts/algorithms/greedy/longest-common-subsequence.ts b/src/ts/algorithms/greedy/longest-common-subsequence.ts new file mode 100644 index 00000000..45ce0ac9 --- /dev/null +++ b/src/ts/algorithms/greedy/longest-common-subsequence.ts @@ -0,0 +1,13 @@ +function lcs(wordX: string, wordY: string, m = wordX.length, n = wordY.length): number { + if (m === 0 || n === 0) { + return 0; + } + + if (wordX[m - 1] === wordY[n - 1]) { + return 1 + lcs(wordX, wordY, m - 1, n - 1); + } else { + const a = lcs(wordX, wordY, m, n - 1); + const b = lcs(wordX, wordY, m - 1, n); + return a > b ? a : b; + } +} diff --git a/src/ts/algorithms/greedy/matrix-chain-multiplication.ts b/src/ts/algorithms/greedy/matrix-chain-multiplication.ts new file mode 100644 index 00000000..fc23fced --- /dev/null +++ b/src/ts/algorithms/greedy/matrix-chain-multiplication.ts @@ -0,0 +1,18 @@ +export function matrixChainOrder(p: number[], i = 1, j = p.length - 1): number { + if (i === j) { + return 0; + } + + let min = Number.MAX_SAFE_INTEGER; + + for (let k = i; k < j; k++) { + const count = + matrixChainOrder(p, i, k) + matrixChainOrder(p, k + 1, j) + p[i - 1] * p[k] * p[j]; + + if (count < min) { + min = count; + } + } + + return min; +} diff --git a/src/ts/algorithms/greedy/min-coin-change.ts b/src/ts/algorithms/greedy/min-coin-change.ts new file mode 100644 index 00000000..7e8f7f72 --- /dev/null +++ b/src/ts/algorithms/greedy/min-coin-change.ts @@ -0,0 +1,12 @@ +function minCoinChange(coins: number[], amount: number) { + const change: number[] = []; + let total = 0; + for (let i = coins.length; i >= 0; i--) { + const coin = coins[i]; + while (total + coin <= amount) { + change.push(coin); + total += coin; + } + } + return change; +} diff --git a/src/ts/algorithms/math/find-divisors.ts b/src/ts/algorithms/math/find-divisors.ts new file mode 100644 index 00000000..1397d555 --- /dev/null +++ b/src/ts/algorithms/math/find-divisors.ts @@ -0,0 +1,18 @@ +export const findDivisors = (num: number) => { + const divisors = []; + + const sqrt = Math.floor(Math.sqrt(num)); + + for (let i = 1; i <= sqrt; i++) { + if (num % i === 0) { + divisors.push(i); + if (i !== sqrt) { + divisors.push(Math.floor(num / i)); + } + } + } + + divisors.sort((a, b) => a - b); + + return divisors; +}; diff --git a/src/ts/algorithms/math/gcd.ts b/src/ts/algorithms/math/gcd.ts new file mode 100644 index 00000000..35818deb --- /dev/null +++ b/src/ts/algorithms/math/gcd.ts @@ -0,0 +1,22 @@ +export const gcd = (num1: number, num2: number): number => { + if (num1 === 0 || num2 === 0) { + return 0; + } + if (num1 === num2) { + return num1; + } + if (num1 > num2) { + return gcd(num1 - num2, num2); + } + return gcd(num1, num2 - num1); +}; + +export const gcdArray = (num: number[]) => { + let result = num[0]; + + for (let i = 1; i < num.length; i++) { + result = gcd(num[i], result); + } + + return result; +}; diff --git a/src/ts/algorithms/math/greatest-difference.ts b/src/ts/algorithms/math/greatest-difference.ts new file mode 100644 index 00000000..a0412445 --- /dev/null +++ b/src/ts/algorithms/math/greatest-difference.ts @@ -0,0 +1,20 @@ +export const greatestDifference = (numbers: number[]) => { + let index = 0; + let largest = numbers[0]; + const length = numbers.length; + let number; + let smallest = numbers[0]; + + for (index; index < length; index++) { + number = numbers[index]; + + if (number > largest) { + largest = number; + } + if (number < smallest) { + smallest = number; + } + } + + return largest - smallest; +}; diff --git a/src/ts/algorithms/math/lcm.ts b/src/ts/algorithms/math/lcm.ts new file mode 100644 index 00000000..a4c4ac37 --- /dev/null +++ b/src/ts/algorithms/math/lcm.ts @@ -0,0 +1,20 @@ +import { gcd } from './gcd'; + +export const lcm = (num1: number, num2: number) => { + if (num1 === 0 || num2 === 0) { + return 0; + } + num1 = Math.abs(num1); + num2 = Math.abs(num2); + return (num1 * num2) / gcd(num1, num2); +}; + +export const lcmArray = (num: number[]) => { + let result = num[0]; + + for (let i = 1; i < num.length; i++) { + result = num[i] * result / gcd(num[i], result); + } + + return result; +}; diff --git a/src/ts/algorithms/math/primality-test.ts b/src/ts/algorithms/math/primality-test.ts new file mode 100644 index 00000000..6ab95167 --- /dev/null +++ b/src/ts/algorithms/math/primality-test.ts @@ -0,0 +1,36 @@ +export const isPrime = (n: number) => { + if (n <= 1) { + return false; + } + + const sqrt = Math.floor(Math.sqrt(n)); + for (let i = 2; i < sqrt; i++) { + if (n % i === 0) { + return false; + } + } + + return true; +}; + +export const testPrime = (n: number) => { + if (n <= 1) { + return false; + } else { + if (n === 2 || n === 3) { + return true; + } else if (n % 2 === 0) { + return false; + } else { + const sqrt = Math.floor(Math.sqrt(n)); + for (let i = 3; i <= sqrt; i += 2) { + if (n % i === 0) { + return false; + } + } + } + } + return true; +}; + +export const isPrime2 = (n: number) => ![...Array(n).keys()].slice(2).map(i => !(n % i)).includes(true) && ![0, 1].includes(n); diff --git a/src/ts/algorithms/math/sieve-eratosthenes.ts b/src/ts/algorithms/math/sieve-eratosthenes.ts new file mode 100644 index 00000000..1c2cb06d --- /dev/null +++ b/src/ts/algorithms/math/sieve-eratosthenes.ts @@ -0,0 +1,18 @@ +export const sieveOfEratosthenes = (n: number) => { + + const prime: boolean[] = []; + + for (let i = 0; i < n; i++) { + prime[i] = true; + } + + for (let p = 2; p * p <= n; p++) { + if (prime[p]) { + for (let i = p * 2; i <= n; i += p) { + prime[i] = false; + } + } + } + + return prime.filter(num => num === true); +}; diff --git a/src/ts/algorithms/search/binary-search-recursive.ts b/src/ts/algorithms/search/binary-search-recursive.ts new file mode 100644 index 00000000..6d902139 --- /dev/null +++ b/src/ts/algorithms/search/binary-search-recursive.ts @@ -0,0 +1,32 @@ +import { Compare, defaultCompare, DOES_NOT_EXIST } from '../../util'; +import { quickSort } from '../sorting/quicksort'; + +function binarySearchRecursive( + array: T[], + value: T, + low: number, + high: number, + compareFn = defaultCompare +): number { + if (low <= high) { + const mid = Math.floor((low + high) / 2); + const element = array[mid]; + + if (compareFn(element, value) === Compare.LESS_THAN) { + return binarySearchRecursive(array, value, mid + 1, high, compareFn); + } else if (compareFn(element, value) === Compare.BIGGER_THAN) { + return binarySearchRecursive(array, value, low, mid - 1, compareFn); + } else { + return mid; + } + } + return DOES_NOT_EXIST; +} + +export function binarySearch(array: T[], value: T, compareFn = defaultCompare) { + const sortedArray = quickSort(array); + const low = 0; + const high = sortedArray.length - 1; + + return binarySearchRecursive(array, value, low, high, compareFn); +} diff --git a/src/ts/algorithms/search/binary-search.ts b/src/ts/algorithms/search/binary-search.ts new file mode 100644 index 00000000..2e8513bf --- /dev/null +++ b/src/ts/algorithms/search/binary-search.ts @@ -0,0 +1,25 @@ +import { Compare, defaultCompare, DOES_NOT_EXIST } from '../../util'; +import { quickSort } from '../sorting/quicksort'; + +export function binarySearch (array: T[], value: T, compareFn = defaultCompare) { + const sortedArray = quickSort(array); + let low = 0; + let high = sortedArray.length - 1; + + while (low <= high) { + const mid = Math.floor((low + high) / 2); + const element = sortedArray[mid]; + // console.log('mid element is ' + element); + if (compareFn(element, value) === Compare.LESS_THAN) { + low = mid + 1; + // console.log('low is ' + low); + } else if (compareFn(element, value) === Compare.BIGGER_THAN) { + high = mid - 1; + // console.log('high is ' + high); + } else { + // console.log('found it'); + return mid; + } + } + return DOES_NOT_EXIST; +} diff --git a/src/ts/algorithms/search/interpolation-search.ts b/src/ts/algorithms/search/interpolation-search.ts new file mode 100644 index 00000000..aebdeb09 --- /dev/null +++ b/src/ts/algorithms/search/interpolation-search.ts @@ -0,0 +1,41 @@ +import { + biggerEquals, + Compare, + defaultCompare, + defaultEquals, + defaultDiff, + DOES_NOT_EXIST, + lesserEquals +} from '../../util'; + +export function interpolationSearch( + array: T[], + value: T, + compareFn = defaultCompare, + equalsFn = defaultEquals, + diffFn = defaultDiff +) { + const { length } = array; + let low = 0; + let high = length - 1; + let position = -1; + let delta = -1; + while ( + low <= high && + biggerEquals(value, array[low], compareFn) && + lesserEquals(value, array[high], compareFn) + ) { + delta = diffFn(value, array[low]) / diffFn(array[high], array[low]); + position = low + Math.floor((high - low) * delta); + if (equalsFn(array[position], value)) { + return position; + } + if (compareFn(array[position], value) === Compare.LESS_THAN) { + low = position + 1; + } else { + high = position - 1; + } + } + + return DOES_NOT_EXIST; +} diff --git a/src/ts/algorithms/search/min-max-search.ts b/src/ts/algorithms/search/min-max-search.ts new file mode 100644 index 00000000..1869f412 --- /dev/null +++ b/src/ts/algorithms/search/min-max-search.ts @@ -0,0 +1,27 @@ +import { defaultCompare, Compare } from '../../util'; + +export function findMaxValue(array: T[], compareFn = defaultCompare) { + if (array && array.length > 0) { + let max = array[0]; + for (let i = 1; i < array.length; i++) { + if (compareFn(max, array[i]) === Compare.LESS_THAN) { + max = array[i]; + } + } + return max; + } + return undefined; +} + +export function findMinValue(array: T[], compareFn = defaultCompare) { + if (array && array.length > 0) { + let min = array[0]; + for (let i = 1; i < array.length; i++) { + if (compareFn(min, array[i]) === Compare.BIGGER_THAN) { + min = array[i]; + } + } + return min; + } + return undefined; +} diff --git a/src/ts/algorithms/search/sequential-search.ts b/src/ts/algorithms/search/sequential-search.ts new file mode 100644 index 00000000..ba9ddf6c --- /dev/null +++ b/src/ts/algorithms/search/sequential-search.ts @@ -0,0 +1,10 @@ +import { defaultEquals, DOES_NOT_EXIST, IEqualsFunction } from '../../util'; + +export function sequentialSearch(array: T[], value: T, equalsFn: IEqualsFunction = defaultEquals) { + for (let i = 0; i < array.length; i++) { + if (equalsFn(value, array[i])) { + return i; + } + } + return DOES_NOT_EXIST; +} diff --git "a/src/ts/algorithms/shuffle/fisher\342\200\223yates.ts" "b/src/ts/algorithms/shuffle/fisher\342\200\223yates.ts" new file mode 100644 index 00000000..b5869692 --- /dev/null +++ "b/src/ts/algorithms/shuffle/fisher\342\200\223yates.ts" @@ -0,0 +1,10 @@ +import { swap } from '../../util'; + +export function shuffle(array: T[]) { + for (let i = array.length - 1; i > 0; i--) { + const randomIndex = Math.floor(Math.random() * (i + 1)); + swap(array, i, randomIndex); + } + + return array; +} diff --git a/src/ts/algorithms/sorting/bubble-sort-improved.ts b/src/ts/algorithms/sorting/bubble-sort-improved.ts new file mode 100644 index 00000000..6e016d4d --- /dev/null +++ b/src/ts/algorithms/sorting/bubble-sort-improved.ts @@ -0,0 +1,18 @@ +import { Compare, defaultCompare, swap } from '../../util'; + +export function modifiedBubbleSort(array: T[], compareFn = defaultCompare) { + const { length } = array; + + for (let i = 0; i < length; i++) { + // console.log('--- '); + for (let j = 0; j < length - 1 - i; j++) { + // console.log('compare ' + array[j] + ' with ' + array[j + 1]); + if (compareFn(array[j], array[j + 1]) === Compare.BIGGER_THAN) { + // console.log('swap ' + array[j] + ' with ' + array[j + 1]); + swap(array, j, j + 1); + } + } + } + + return array; +} diff --git a/src/ts/algorithms/sorting/bubble-sort.ts b/src/ts/algorithms/sorting/bubble-sort.ts new file mode 100644 index 00000000..41074f6f --- /dev/null +++ b/src/ts/algorithms/sorting/bubble-sort.ts @@ -0,0 +1,18 @@ +import { Compare, defaultCompare, swap } from '../../util'; + +export function bubbleSort(array: T[], compareFn = defaultCompare) { + const { length } = array; + + for (let i = 0; i < length; i++) { + // console.log('--- '); + for (let j = 0; j < length - 1; j++) { + // console.log('compare ' + array[j] + ' with ' + array[j + 1]); + if (compareFn(array[j], array[j + 1]) === Compare.BIGGER_THAN) { + // console.log('swap ' + array[j] + ' with ' + array[j + 1]); + swap(array, j, j + 1); + } + } + } + + return array; +} diff --git a/src/ts/algorithms/sorting/bucket-sort.ts b/src/ts/algorithms/sorting/bucket-sort.ts new file mode 100644 index 00000000..bcb8f61b --- /dev/null +++ b/src/ts/algorithms/sorting/bucket-sort.ts @@ -0,0 +1,48 @@ +import { insertionSort } from './insertion-sort'; + +function createBuckets(array: number[], bucketSize: number): number[][] { + let minValue = array[0]; + let maxValue = array[0]; + for (let i = 1; i < array.length; i++) { + if (array[i] < minValue) { + minValue = array[i]; + } else if (array[i] > maxValue) { + maxValue = array[i]; + } + } + + const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; + const buckets = []; + for (let i = 0; i < bucketCount; i++) { + buckets[i] = []; + } + + for (let i = 0; i < array.length; i++) { + buckets[Math.floor((array[i] - minValue) / bucketSize)].push(array[i]); + } + + return buckets; +} + +function sortBuckets(buckets: number[][]) { + const sortedArray = []; + for (let i = 0; i < buckets.length; i++) { + if (buckets[i] != null) { + insertionSort(buckets[i]); + + sortedArray.push(...buckets[i]); + } + } + + return sortedArray; +} + +export function bucketSort(array: number[], bucketSize = 5) { + if (array.length < 2) { + return array; + } + + const buckets = createBuckets(array, bucketSize); + + return sortBuckets(buckets); +} diff --git a/src/ts/algorithms/sorting/counting-sort.ts b/src/ts/algorithms/sorting/counting-sort.ts new file mode 100644 index 00000000..be3d9046 --- /dev/null +++ b/src/ts/algorithms/sorting/counting-sort.ts @@ -0,0 +1,30 @@ +import { findMaxValue } from '../search/min-max-search'; + +export function countingSort(array: number[]) { + + if (array.length < 2) { + return array; + } + + const maxValue = findMaxValue(array); + let sortedIndex = 0; + const counts = new Array(maxValue + 1); + + array.forEach(element => { + if (!counts[element]) { + counts[element] = 0; + } + counts[element]++; + }); + + // console.log('Frequencies: ' + counts.join()); + + counts.forEach((element, i) => { + while (element > 0) { + array[sortedIndex++] = i; + element--; + } + }); + + return array; +} diff --git a/src/ts/algorithms/sorting/heap-sort.ts b/src/ts/algorithms/sorting/heap-sort.ts new file mode 100644 index 00000000..7f461860 --- /dev/null +++ b/src/ts/algorithms/sorting/heap-sort.ts @@ -0,0 +1,37 @@ +import { defaultCompare, ICompareFunction, swap } from '../../util'; + +function heapify(array: any[], index: number, heapSize: number, compareFn: ICompareFunction) { + let largest = index; + const left = (2 * index) + 1; + const right = (2 * index) + 2; + + if (left < heapSize && compareFn(array[left], array[index]) > 0) { + largest = left; + } + + if (right < heapSize && compareFn(array[right], array[largest]) > 0) { + largest = right; + } + + if (largest !== index) { + swap(array, index, largest); + heapify(array, largest, heapSize, compareFn); + } +} + +function buildMaxHeap(array: any[], compareFn: ICompareFunction) { + for (let i = Math.floor(array.length / 2); i >= 0; i -= 1) { + heapify(array, i, array.length, compareFn); + } + return array; +} + +export default function heapSort(array: any[], compareFn = defaultCompare) { + let heapSize = array.length; + buildMaxHeap(array, compareFn); + while (heapSize > 1) { + swap(array, 0, --heapSize); + heapify(array, 0, heapSize, compareFn); + } + return array; +} diff --git a/src/ts/algorithms/sorting/insertion-sort.ts b/src/ts/algorithms/sorting/insertion-sort.ts new file mode 100644 index 00000000..e67c4f34 --- /dev/null +++ b/src/ts/algorithms/sorting/insertion-sort.ts @@ -0,0 +1,20 @@ +import { Compare, defaultCompare } from '../../util'; + +export const insertionSort = (array: any[], compareFn = defaultCompare) => { + const { length } = array; + let temp; + for (let i = 1; i < length; i++) { + let j = i; + temp = array[i]; + // console.log('to be inserted ' + temp); + while (j > 0 && compareFn(array[j - 1], temp) === Compare.BIGGER_THAN) { + // console.log('shift ' + array[j - 1]); + array[j] = array[j - 1]; + j--; + } + // console.log('insert ' + temp); + array[j] = temp; + } + + return array; +}; diff --git a/src/ts/algorithms/sorting/merge-sort.ts b/src/ts/algorithms/sorting/merge-sort.ts new file mode 100644 index 00000000..b4f20f65 --- /dev/null +++ b/src/ts/algorithms/sorting/merge-sort.ts @@ -0,0 +1,25 @@ +import { Compare, defaultCompare, ICompareFunction } from '../../util'; + +function merge(left: T[], right: T[], compareFn: ICompareFunction) { + let i = 0; + let j = 0; + const result = []; + + while (i < left.length && j < right.length) { + result.push(compareFn(left[i], right[j]) === Compare.LESS_THAN ? left[i++] : right[j++]); + } + + return result.concat(i < left.length ? left.slice(i) : right.slice(j)); +} + +export function mergeSort(array: T[], compareFn = defaultCompare): T[] { + if (array.length > 1) { + const { length } = array; + const middle = Math.floor(length / 2); + const left = mergeSort(array.slice(0, middle), compareFn); + const right = mergeSort(array.slice(middle, length), compareFn); + array = merge(left, right, compareFn); + } + + return array; +} diff --git a/src/ts/algorithms/sorting/quicksort.ts b/src/ts/algorithms/sorting/quicksort.ts new file mode 100644 index 00000000..97c9f9ce --- /dev/null +++ b/src/ts/algorithms/sorting/quicksort.ts @@ -0,0 +1,61 @@ +import { Compare, defaultCompare, ICompareFunction, swap } from '../../util'; + +const partition = function( + array: any[], + left: number, + right: number, + compareFn: ICompareFunction +) { + const pivot = array[Math.floor((right + left) / 2)]; + let i = left; + let j = right; + + // console.log('pivot is ' + pivot + '; left is ' + left + '; right is ' + right); + + while (i <= j) { + while (compareFn(array[i], pivot) === Compare.LESS_THAN) { + i++; + // console.log('i = ' + i); + } + + while (compareFn(array[j], pivot) === Compare.BIGGER_THAN) { + j--; + // console.log('j = ' + j); + } + + if (i <= j) { + // console.log('swap ' + array[i] + ' with ' + array[j]); + swap(array, i, j); + i++; + j--; + } + } + + return i; +}; + +const quick = function( + array: any[], + left: number, + right: number, + compareFn: ICompareFunction +) { + let index; + + if (array.length > 1) { + index = partition(array, left, right, compareFn); + + if (left < index - 1) { + quick(array, left, index - 1, compareFn); + } + + if (index < right) { + quick(array, index, right, compareFn); + } + } + return array; +}; + +export const quickSort = (array: any[], compareFn = defaultCompare) => { + return quick(array, 0, array.length - 1, compareFn); +}; diff --git a/src/ts/algorithms/sorting/radix-sort.ts b/src/ts/algorithms/sorting/radix-sort.ts new file mode 100644 index 00000000..e6d1027a --- /dev/null +++ b/src/ts/algorithms/sorting/radix-sort.ts @@ -0,0 +1,60 @@ +import { findMaxValue, findMinValue } from '../search/min-max-search'; + +const countingSortForRadix = ( + array: number[], + radixBase: number, + significantDigit: number, + minValue: any +) => { + let bucketsIndex; + const buckets: number[] = []; + const aux: number[] = []; + + for (let i = 0; i < radixBase; i++) { + buckets[i] = 0; + } + + for (let i = 0; i < array.length; i++) { + bucketsIndex = Math.floor(((array[i] - minValue) / significantDigit) % radixBase); + buckets[bucketsIndex]++; + } + + for (let i = 1; i < radixBase; i++) { + buckets[i] += buckets[i - 1]; + } + + for (let i = array.length - 1; i >= 0; i--) { + bucketsIndex = Math.floor(((array[i] - minValue) / significantDigit) % radixBase); + aux[--buckets[bucketsIndex]] = array[i]; + } + + + // array = []; + // array.push(...aux); + for (let i = 0; i < array.length; i++) { + array[i] = aux[i]; + } + + return array; +}; + +export function radixSort(array: number[], radixBase = 10) { + + if (array.length < 2) { + return array; + } + + const minValue = findMinValue(array); + const maxValue = findMaxValue(array); + + // Perform counting sort for each significant digit, starting at 1 + let significantDigit = 1; + while ((maxValue - minValue) / significantDigit >= 1) { + // console.log('radix sort for digit ' + significantDigit); + array = countingSortForRadix(array, radixBase, significantDigit, minValue); + // console.log(array.join()); + significantDigit *= radixBase; + } + + return array; +} diff --git a/src/ts/algorithms/sorting/selection-sort.ts b/src/ts/algorithms/sorting/selection-sort.ts new file mode 100644 index 00000000..465e78a1 --- /dev/null +++ b/src/ts/algorithms/sorting/selection-sort.ts @@ -0,0 +1,23 @@ +import { Compare, defaultCompare, swap } from '../../util'; + +export const selectionSort = (array: any[], compareFn = defaultCompare) => { + const { length } = array; + let indexMin; + + for (let i = 0; i < length - 1; i++) { + indexMin = i; + // console.log('index ' + array[i]); + for (let j = i; j < length; j++) { + if (compareFn(array[indexMin], array[j]) === Compare.BIGGER_THAN) { + // console.log('new index min ' + array[j]); + indexMin = j; + } + } + if (i !== indexMin) { + // console.log('swap ' + array[i] + ' with ' + array[indexMin]); + swap(array, i, indexMin); + } + } + + return array; +}; diff --git a/src/ts/algorithms/sorting/shell-sort.ts b/src/ts/algorithms/sorting/shell-sort.ts new file mode 100644 index 00000000..19e11bf9 --- /dev/null +++ b/src/ts/algorithms/sorting/shell-sort.ts @@ -0,0 +1,25 @@ +import { Compare, defaultCompare } from '../../util'; + +export function shellSort(array: T[], compareFn = defaultCompare) { + let increment = array.length / 2; + while (increment > 0) { + for (let i = increment; i < array.length; i++) { + let j = i; + const temp = array[i]; + + while (j >= increment && compareFn(array[j - increment], temp) === Compare.BIGGER_THAN) { + array[j] = array[j - increment]; + j = j - increment; + } + + array[j] = temp; + } + + if (increment === 2) { + increment = 1; + } else { + increment = Math.floor(increment * 5 / 11); + } + } + return array; +} diff --git a/src/ts/algorithms/string/brute-force.ts b/src/ts/algorithms/string/brute-force.ts new file mode 100644 index 00000000..1bf21f85 --- /dev/null +++ b/src/ts/algorithms/string/brute-force.ts @@ -0,0 +1,22 @@ +const stringSearch = (text: string, pattern: string) => { + const n = text.length; + const m = pattern.length; + + if (m > n) { + return -1; + } + + for (let i = 0; i < n; i++) { + let j = 0; + for (j = 0; j < m && (i + j) < n; j++) { + if (text.charAt(i + j) !== pattern.charAt(j)) { + break; + } + } + if (j === m) { + return i; + } + } + + return -1; +}; diff --git a/src/ts/algorithms/string/huffman.ts b/src/ts/algorithms/string/huffman.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/ts/algorithms/string/knuth-morris-pratt.ts b/src/ts/algorithms/string/knuth-morris-pratt.ts new file mode 100644 index 00000000..4b0f2759 --- /dev/null +++ b/src/ts/algorithms/string/knuth-morris-pratt.ts @@ -0,0 +1,46 @@ +const buildTable = (pattern: string) => { + const length = pattern.length; + const table = []; + let position = 2; + let cnd = 0; + + table[0] = -1; + table[1] = 0; + + while (position < length) { + if (pattern[position - 1] === pattern[cnd]) { + table[position++] = ++cnd; + } else if (cnd > 0) { + cnd = table[cnd]; + } else { + table[position++] = 0; + } + } + + return table; +}; + +const knuthMorrisPratt = (text: string, pattern: string) => { + const textLength = text.length; + const patternLength = pattern.length; + let m = 0; + let i = 0; + const table = buildTable(pattern); + + while (m + i < textLength) { + if (pattern[i] === text[m + i]) { + if (i === patternLength - 1) { + return m; + } + i++; + } else if (table[i] >= 0) { + i = table[i]; + m = m + i - table[i]; + } else { + i = 0; + m++; + } + } + + return textLength; +}; diff --git a/src/ts/algorithms/string/longest-common-substring.ts b/src/ts/algorithms/string/longest-common-substring.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/ts/algorithms/string/rabin-karp.ts b/src/ts/algorithms/string/rabin-karp.ts new file mode 100644 index 00000000..ab60fe3e --- /dev/null +++ b/src/ts/algorithms/string/rabin-karp.ts @@ -0,0 +1,39 @@ +const base = 997; + +const hash = (word: string) => { + let h = 0; + + for (let i = 0; i < word.length; i++) { + h += word.charCodeAt(i) * Math.pow(base, word.length - i - 1); + } + + return h; +}; + +const rabinKarp = (text: string, pattern: string) => { + if (pattern == null || pattern.length === 0) { + return 0; + } + + const hashPattern = hash(pattern); + let currentSubstring = text.substring(0, pattern.length); + let hashCurrentSubstring; + + for (let i = pattern.length; i <= text.length; i++) { + if (hashCurrentSubstring === undefined) { + hashCurrentSubstring = hash(currentSubstring); + } else { + hashCurrentSubstring -= currentSubstring.charCodeAt(0) * Math.pow(base, pattern.length - 1); + hashCurrentSubstring *= base; + hashCurrentSubstring += text.charCodeAt(i); + + currentSubstring = currentSubstring.substring(1) + text[i]; + } + + if (hashPattern === hashCurrentSubstring && pattern === currentSubstring) { + return i === pattern.length ? 0 : i - pattern.length + 1; + } + } + + return -1; +}; diff --git a/src/ts/data-structures/avl-tree.ts b/src/ts/data-structures/avl-tree.ts new file mode 100644 index 00000000..feb44a19 --- /dev/null +++ b/src/ts/data-structures/avl-tree.ts @@ -0,0 +1,201 @@ +import { Compare, defaultCompare, ICompareFunction } from '../util'; +import BinarySearchTree from './binary-search-tree'; +import { Node } from './models/node'; + +enum BalanceFactor { + UNBALANCED_RIGHT = 1, + SLIGHTLY_UNBALANCED_RIGHT = 2, + BALANCED = 3, + SLIGHTLY_UNBALANCED_LEFT = 4, + UNBALANCED_LEFT = 5 +} + +export default class AVLTree extends BinarySearchTree { + + constructor(protected compareFn: ICompareFunction = defaultCompare) { + super(compareFn); + } + + private getNodeHeight(node: Node): number { + if (node == null) { + return -1; + } + return Math.max(this.getNodeHeight(node.left), this.getNodeHeight(node.right)) + 1; + } + + /** + * Left left case: rotate right + * + * b a + * / \ / \ + * a e -> rotationLL(b) -> c b + * / \ / \ + * c d d e + * + * @param node Node + */ + private rotationLL(node: Node) { + const tmp = node.left; + node.left = tmp.right; + tmp.right = node; + return tmp; + } + + /** + * Right right case: rotate left + * + * a b + * / \ / \ + * c b -> rotationRR(a) -> a e + * / \ / \ + * d e c d + * + * @param node Node + */ + private rotationRR(node: Node) { + const tmp = node.right; + node.right = tmp.left; + tmp.left = node; + return tmp; + } + + /** + * Left right case: rotate left then right + * @param node Node + */ + private rotationLR(node: Node) { + node.left = this.rotationRR(node.left); + return this.rotationLL(node); + } + + /** + * Right left case: rotate right then left + * @param node Node + */ + private rotationRL(node: Node) { + node.right = this.rotationLL(node.right); + return this.rotationRR(node); + } + + private getBalanceFactor(node: Node) { + const heightDifference = this.getNodeHeight(node.left) - this.getNodeHeight(node.right); + switch (heightDifference) { + case -2: + return BalanceFactor.UNBALANCED_RIGHT; + case -1: + return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT; + case 1: + return BalanceFactor.SLIGHTLY_UNBALANCED_LEFT; + case 2: + return BalanceFactor.UNBALANCED_LEFT; + default: + return BalanceFactor.BALANCED; + } + } + + insert(key: T) { + this.root = this.insertNode(this.root, key); + } + + protected insertNode(node: Node, key: T) { + if (node == null) { + return new Node(key); + } else if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + node.left = this.insertNode(node.left, key); + } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { + node.right = this.insertNode(node.right, key); + } else { + return node; // duplicated key + } + + // verify if tree is balanced + const balanceState = this.getBalanceFactor(node); + + if (balanceState === BalanceFactor.UNBALANCED_LEFT) { + if (this.compareFn(key, node.left.key) === Compare.LESS_THAN) { + // Left left case + node = this.rotationLL(node); + } else { + // Left right case + return this.rotationLR(node); + } + } + + if (balanceState === BalanceFactor.UNBALANCED_RIGHT) { + if (this.compareFn(key, node.right.key) === Compare.BIGGER_THAN) { + // Right right case + node = this.rotationRR(node); + } else { + // Right left case + return this.rotationRL(node); + } + } + + return node; + } + + protected removeNode(node: Node, key: T) { + if (node == null) { + return null; + } + + if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + // The key to be deleted is in the left sub-tree + node.left = this.removeNode(node.left, key); + } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { + // The key to be deleted is in the right sub-tree + node.right = this.removeNode(node.right, key); + } else { + // node is the node to be deleted + if (node.left == null && node.right == null) { + node = null; + } else if (node.left == null && node.right != null) { + node = node.right; + } else if (node.left != null && node.right == null) { + node = node.left; + } else { + // node has 2 children, get the in-order successor + const inOrderSuccessor = this.minNode(node.right); + node.key = inOrderSuccessor.key; + node.right = this.removeNode(node.right, inOrderSuccessor.key); + } + } + + if (node == null) { + return node; + } + + // verify if tree is balanced + const balanceState = this.getBalanceFactor(node); + + if (balanceState === BalanceFactor.UNBALANCED_LEFT) { + // Left left case + if ( + this.getBalanceFactor(node.left) === BalanceFactor.BALANCED || + this.getBalanceFactor(node.left) === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT + ) { + return this.rotationLL(node); + } + // Left right case + if (this.getBalanceFactor(node.left) === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) { + return this.rotationLR(node.left); + } + } + + if (balanceState === BalanceFactor.UNBALANCED_RIGHT) { + // Right right case + if ( + this.getBalanceFactor(node.right) === BalanceFactor.BALANCED || + this.getBalanceFactor(node.right) === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT + ) { + return this.rotationRR(node); + } + // Right left case + if (this.getBalanceFactor(node.right) === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) { + return this.rotationRL(node.right); + } + } + + return node; + } +} diff --git a/src/ts/data-structures/binary-search-tree.ts b/src/ts/data-structures/binary-search-tree.ts new file mode 100644 index 00000000..dd7141ff --- /dev/null +++ b/src/ts/data-structures/binary-search-tree.ts @@ -0,0 +1,159 @@ +import { Compare, defaultCompare, ICompareFunction } from '../util'; +import { Node } from './models/node'; + +export default class BinarySearchTree { + protected root: Node; + + constructor(protected compareFn: ICompareFunction = defaultCompare) {} + + insert(key: T) { + // special case: first key + if (this.root == null) { + this.root = new Node(key); + } else { + this.insertNode(this.root, key); + } + } + + protected insertNode(node: Node, key: T) { + if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + if (node.left == null) { + node.left = new Node(key); + } else { + this.insertNode(node.left, key); + } + } else if (node.right == null) { + node.right = new Node(key); + } else { + this.insertNode(node.right, key); + } + } + + getRoot() { + return this.root; + } + + search(key: T) { + return this.searchNode(this.root, key); + } + + private searchNode(node: Node, key: T): boolean { + if (node == null) { + return false; + } + + if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + return this.searchNode(node.left, key); + } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { + return this.searchNode(node.right, key); + } + // key is equal to node.item + return true; + } + + inOrderTraverse(callback: Function) { + this.inOrderTraverseNode(this.root, callback); + } + + private inOrderTraverseNode(node: Node, callback: Function) { + if (node != null) { + this.inOrderTraverseNode(node.left, callback); + callback(node.key); + this.inOrderTraverseNode(node.right, callback); + } + } + + preOrderTraverse(callback: Function) { + this.preOrderTraverseNode(this.root, callback); + } + + private preOrderTraverseNode(node: Node, callback: Function) { + if (node != null) { + callback(node.key); + this.preOrderTraverseNode(node.left, callback); + this.preOrderTraverseNode(node.right, callback); + } + } + + postOrderTraverse(callback: Function) { + this.postOrderTraverseNode(this.root, callback); + } + + private postOrderTraverseNode(node: Node, callback: Function) { + if (node != null) { + this.postOrderTraverseNode(node.left, callback); + this.postOrderTraverseNode(node.right, callback); + callback(node.key); + } + } + + min() { + return this.minNode(this.root); + } + + protected minNode(node: Node) { + let current = node; + while (current != null && current.left != null) { + current = current.left; + } + return current; + } + + max() { + return this.maxNode(this.root); + } + + protected maxNode(node: Node) { + let current = node; + while (current != null && current.right != null) { + current = current.right; + } + return current; + } + + remove(key: T) { + this.root = this.removeNode(this.root, key); + } + + protected removeNode(node: Node, key: T) { + if (node == null) { + return null; + } + + if (this.compareFn(key, node.key) === Compare.LESS_THAN) { + node.left = this.removeNode(node.left, key); + return node; + } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { + node.right = this.removeNode(node.right, key); + return node; + } else { + // key is equal to node.item + + // handle 3 special conditions + // 1 - a leaf node + // 2 - a node with only 1 child + // 3 - a node with 2 children + + // case 1 + if (node.left == null && node.right == null) { + node = null; + return node; + } + + // case 2 + if (node.left == null) { + node = node.right; + return node; + } else if (node.right == null) { + node = node.left; + return node; + } + + // case 3 + const aux = this.minNode(node.right); + node.key = aux.key; + node.right = this.removeNode(node.right, aux.key); + return node; + } + } +} diff --git a/src/ts/data-structures/circular-linked-list.ts b/src/ts/data-structures/circular-linked-list.ts new file mode 100644 index 00000000..f422d276 --- /dev/null +++ b/src/ts/data-structures/circular-linked-list.ts @@ -0,0 +1,80 @@ +import { defaultEquals, IEqualsFunction } from '../util'; +import LinkedList from './linked-list'; +import { Node } from './models/linked-list-models'; + +export default class CircularLinkedList extends LinkedList { + constructor(protected equalsFn: IEqualsFunction = defaultEquals) { + super(equalsFn); + } + + push(element: T) { + const node = new Node(element); + let current; + + if (this.head == null) { + this.head = node; + } else { + current = this.getElementAt(this.size() - 1); + current.next = node; + } + + // set node.next to head - to have circular list + node.next = this.head; + + this.count++; + } + + insert(element: T, index: number) { + if (index >= 0 && index <= this.count) { + const node = new Node(element); + let current = this.head; + + if (index === 0) { + if (this.head == null) { + // if no node in list + this.head = node; + node.next = this.head; + } else { + node.next = current; + current = this.getElementAt(this.size()); + // update last element + this.head = node; + current.next = this.head; + } + } else { + const previous = this.getElementAt(index - 1); + node.next = previous.next; + previous.next = node; + } + this.count++; + return true; + } + return false; + } + + removeAt(index: number) { + if (index >= 0 && index < this.count) { + let current = this.head; + + if (index === 0) { + if (this.size() === 1) { + this.head = undefined; + } else { + const removed = this.head; + current = this.getElementAt(this.size() - 1); + this.head = this.head.next; + current.next = this.head; + current = removed; + } + } else { + // no need to update last element for circular list + const previous = this.getElementAt(index - 1); + current = previous.next; + previous.next = current.next; + } + this.count--; + return current.element; + } + return undefined; + } +} diff --git a/src/ts/data-structures/deque.ts b/src/ts/data-structures/deque.ts new file mode 100644 index 00000000..155b735c --- /dev/null +++ b/src/ts/data-structures/deque.ts @@ -0,0 +1,90 @@ +export default class Deque { + private count: number; + private lowestCount: number; + private items: any; + + constructor() { + this.count = 0; + this.lowestCount = 0; + this.items = {}; + } + + addFront(element: T) { + if (this.isEmpty()) { + this.addBack(element); + } else if (this.lowestCount > 0) { + this.lowestCount--; + this.items[this.lowestCount] = element; + } else { + for (let i = this.count; i > 0; i--) { + this.items[i] = this.items[i - 1]; + } + this.count++; + this.items[0] = element; + } + } + + addBack(element: T) { + this.items[this.count] = element; + this.count++; + } + + removeFront() { + if (this.isEmpty()) { + return undefined; + } + const result = this.items[this.lowestCount]; + delete this.items[this.lowestCount]; + this.lowestCount++; + return result; + } + + removeBack() { + if (this.isEmpty()) { + return undefined; + } + this.count--; + const result = this.items[this.count]; + delete this.items[this.count]; + return result; + } + + peekFront() { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.lowestCount]; + } + + peekBack() { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.count - 1]; + } + + isEmpty() { + return this.size() === 0; + } + + clear() { + this.items = {}; + this.count = 0; + this.lowestCount = 0; + } + + size() { + return this.count - this.lowestCount; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + let objString = `${this.items[this.lowestCount]}`; + for (let i = this.lowestCount + 1; i < this.count; i++) { + objString = `${objString},${this.items[i]}`; + } + return objString; + } +} diff --git a/src/ts/data-structures/dictionary.ts b/src/ts/data-structures/dictionary.ts new file mode 100644 index 00000000..98485718 --- /dev/null +++ b/src/ts/data-structures/dictionary.ts @@ -0,0 +1,86 @@ +import { defaultToString } from '../util'; +import { ValuePair } from './models/value-pair'; + +export default class Dictionary { + private table: { [key: string]: ValuePair }; + + constructor(private toStrFn: (key: K) => string = defaultToString) { + this.table = {}; + } + + set(key: K, value: V) { + if (key != null && value != null) { + const tableKey = this.toStrFn(key); + this.table[tableKey] = new ValuePair(key, value); + return true; + } + return false; + } + + get(key: K): V { + const valuePair = this.table[this.toStrFn(key)]; + return valuePair == null ? undefined : valuePair.value; + } + + hasKey(key: K) { + return this.table[this.toStrFn(key)] != null; + } + + remove(key: K) { + if (this.hasKey(key)) { + delete this.table[this.toStrFn(key)]; + return true; + } + return false; + } + + values(): V[] { + return this.keyValues().map( + (valuePair: ValuePair) => valuePair.value + ); + } + + keys(): K[] { + return this.keyValues().map( + (valuePair: ValuePair) => valuePair.key + ); + } + + keyValues(): ValuePair[] { + return Object.values(this.table); + } + + forEach(callbackFn: (key: K, value: V) => any) { + const valuePairs = this.keyValues(); + for (let i = 0; i < valuePairs.length; i++) { + const result = callbackFn(valuePairs[i].key, valuePairs[i].value); + if (result === false) { + break; + } + } + } + + isEmpty() { + return this.size() === 0; + } + + size() { + return Object.keys(this.table).length; + } + + clear() { + this.table = {}; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + const valuePairs = this.keyValues(); + let objString = `${valuePairs[0].toString()}`; + for (let i = 1; i < valuePairs.length; i++) { + objString = `${objString},${valuePairs[i].toString()}`; + } + return objString; + } +} diff --git a/src/ts/data-structures/doubly-linked-list.ts b/src/ts/data-structures/doubly-linked-list.ts new file mode 100644 index 00000000..a2d16e72 --- /dev/null +++ b/src/ts/data-structures/doubly-linked-list.ts @@ -0,0 +1,148 @@ +import { defaultEquals, IEqualsFunction } from '../util'; +import LinkedList from './linked-list'; +import { DoublyNode } from './models/linked-list-models'; + +export default class DoublyLinkedList extends LinkedList { + protected head: DoublyNode | undefined; + protected tail: DoublyNode | undefined; + + constructor(protected equalsFn: IEqualsFunction = defaultEquals) { + super(equalsFn); + } + + push(element: T) { + const node = new DoublyNode(element); + + if (this.head == null) { + this.head = node; + this.tail = node; // NEW + } else { + // attach to the tail node // NEW + this.tail.next = node; + node.prev = this.tail; + this.tail = node; + } + this.count++; + } + + insert(element: T, index: number) { + if (index >= 0 && index <= this.count) { + const node = new DoublyNode(element); + let current = this.head; + + if (index === 0) { + if (this.head == null) { + // NEW + this.head = node; + this.tail = node; + } else { + node.next = this.head; + this.head.prev = node; // NEW + this.head = node; + } + } else if (index === this.count) { + // last item // NEW + current = this.tail; // {2} + current.next = node; + node.prev = current; + this.tail = node; + } else { + const previous = this.getElementAt(index - 1); + current = previous.next; + node.next = current; + previous.next = node; + + current.prev = node; // NEW + node.prev = previous; // NEW + } + this.count++; + return true; + } + return false; + } + + removeAt(index: number) { + if (index >= 0 && index < this.count) { + let current = this.head; + + if (index === 0) { + this.head = this.head.next; // {1} + // if there is only one item, then we update tail as well //NEW + if (this.count === 1) { + // {2} + this.tail = undefined; + } else { + this.head.prev = undefined; // {3} + } + } else if (index === this.count - 1) { + // last item //NEW + current = this.tail; // {4} + this.tail = current.prev; + this.tail.next = undefined; + } else { + current = this.getElementAt(index); + const previous = current.prev; + // link previous with current's next - skip it to remove + previous.next = current.next; // {6} + current.next.prev = previous; // NEW + } + this.count--; + return current.element; + } + return undefined; + } + + indexOf(element: T) { + let current = this.head; + let index = 0; + + while (current != null) { + if (this.equalsFn(element, current.element)) { + return index; + } + index++; + current = current.next; + } + + return -1; + } + + getHead() { + return this.head; + } + + getTail() { + return this.tail; + } + + clear() { + super.clear(); + this.tail = undefined; + } + + toString() { + if (this.head == null) { + return ''; + } + let objString = `${this.head.element}`; + let current = this.head.next; + while (current != null) { + objString = `${objString},${current.element}`; + current = current.next; + } + return objString; + } + + inverseToString() { + if (this.tail == null) { + return ''; + } + let objString = `${this.tail.element}`; + let previous = this.tail.prev; + while (previous != null) { + objString = `${objString},${previous.element}`; + previous = previous.prev; + } + return objString; + } +} diff --git a/src/ts/data-structures/graph.ts b/src/ts/data-structures/graph.ts new file mode 100644 index 00000000..2123d407 --- /dev/null +++ b/src/ts/data-structures/graph.ts @@ -0,0 +1,52 @@ +import Dictionary from './dictionary'; + +export default class Graph { + private vertices: (string | number)[] = []; + private adjList: Dictionary = new Dictionary(); + + constructor(private isDirected = false) {} + + addVertex(v: string | number) { + if (!this.vertices.includes(v)) { + this.vertices.push(v); + this.adjList.set(v, []); // initialize adjacency list with array as well; + } + } + + addEdge(a: string | number, b: string | number) { + if (!this.adjList.get(a)) { + this.addVertex(a); + } + if (!this.adjList.get(b)) { + this.addVertex(b); + } + + this.adjList.get(a).push(b); + + if (!this.isDirected) { + this.adjList.get(b).push(a); + } + // adjList.get(w).push(v); //commented to run the improved DFS with topological sorting + } + + getVertices() { + return this.vertices; + } + + getAdjList() { + return this.adjList; + } + + toString() { + let s = ''; + for (let i = 0; i < this.vertices.length; i++) { + s += this.vertices[i] + ' -> '; + const neighbors = this.adjList.get(this.vertices[i]); + for (let j = 0; j < neighbors.length; j++) { + s += neighbors[j] + ' '; + } + s += '\n'; + } + return s; + } +} diff --git a/src/ts/data-structures/hash-table-linear-probing-lazy.ts b/src/ts/data-structures/hash-table-linear-probing-lazy.ts new file mode 100644 index 00000000..43817050 --- /dev/null +++ b/src/ts/data-structures/hash-table-linear-probing-lazy.ts @@ -0,0 +1,136 @@ +import { defaultToString } from '../util'; +import { ValuePairLazy } from './models/value-pair-lazy'; + +export default class HashTableLinearProbingLazy { + protected table: { [key: string]: ValuePairLazy }; + + constructor(protected toStrFn: (key: K) => string = defaultToString) { + this.table = {}; + } + + private loseloseHashCode(key: K) { + if (typeof key === 'number') { + return key; + } + const tableKey = this.toStrFn(key); + let hash = 0; + for (let i = 0; i < tableKey.length; i++) { + hash += tableKey.charCodeAt(i); + } + return hash % 37; + } + + hashCode(key: K) { + return this.loseloseHashCode(key); + } + + put(key: K, value: V) { + if (key != null && value != null) { + const position = this.hashCode(key); + + if ( + this.table[position] == null || + (this.table[position] != null && this.table[position].isDeleted) + ) { + this.table[position] = new ValuePairLazy(key, value); + } else { + let index = position + 1; + while (this.table[index] != null && !this.table[position].isDeleted) { + index++; + } + this.table[index] = new ValuePairLazy(key, value); + } + return true; + } + return false; + } + + get(key: K) { + const position = this.hashCode(key); + + if (this.table[position] != null) { + if (this.table[position].key === key && !this.table[position].isDeleted) { + return this.table[position].value; + } + let index = position + 1; + while ( + this.table[index] != null && + (this.table[index].key !== key || this.table[index].isDeleted) + ) { + if (this.table[index].key === key && this.table[index].isDeleted) { + return undefined; + } + index++; + } + if ( + this.table[index] != null && + this.table[index].key === key && + !this.table[index].isDeleted + ) { + return this.table[position].value; + } + } + return undefined; + } + + remove(key: K) { + const position = this.hashCode(key); + + if (this.table[position] != null) { + if (this.table[position].key === key && !this.table[position].isDeleted) { + this.table[position].isDeleted = true; + return true; + } + let index = position + 1; + while ( + this.table[index] != null && + (this.table[index].key !== key || this.table[index].isDeleted) + ) { + index++; + } + if ( + this.table[index] != null && + this.table[index].key === key && + !this.table[index].isDeleted + ) { + this.table[index].isDeleted = true; + return true; + } + } + return false; + } + + isEmpty() { + return this.size() === 0; + } + + size() { + let count = 0; + Object.values(this.table).forEach(valuePair => { + count += valuePair.isDeleted === true ? 0 : 1; + }); + return count; + } + + clear() { + this.table = {}; + } + + getTable() { + return this.table; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + objString = `${objString},{${keys[i]} => ${this.table[ + keys[i] + ].toString()}}`; + } + return objString; + } +} diff --git a/src/ts/data-structures/hash-table-linear-probing.ts b/src/ts/data-structures/hash-table-linear-probing.ts new file mode 100644 index 00000000..872b4421 --- /dev/null +++ b/src/ts/data-structures/hash-table-linear-probing.ts @@ -0,0 +1,128 @@ +import { defaultToString } from '../util'; +import { ValuePair } from './models/value-pair'; + +export default class HashTableLinearProbing { + protected table: { [key: string]: ValuePair }; + + constructor(protected toStrFn: (key: K) => string = defaultToString) { + this.table = {}; + } + + private loseloseHashCode(key: K) { + if (typeof key === 'number') { + return key; + } + const tableKey = this.toStrFn(key); + let hash = 0; + for (let i = 0; i < tableKey.length; i++) { + hash += tableKey.charCodeAt(i); + } + return hash % 37; + } + + hashCode(key: K) { + return this.loseloseHashCode(key); + } + + put(key: K, value: V) { + if (key != null && value != null) { + const position = this.hashCode(key); + + if (this.table[position] == null) { + this.table[position] = new ValuePair(key, value); + } else { + let index = position + 1; + while (this.table[index] != null) { + index++; + } + this.table[index] = new ValuePair(key, value); + } + return true; + } + return false; + } + + get(key: K) { + const position = this.hashCode(key); + + if (this.table[position] != null) { + if (this.table[position].key === key) { + return this.table[position].value; + } + let index = position + 1; + while (this.table[index] != null && this.table[index].key !== key) { + index++; + } + if (this.table[index] != null && this.table[index].key === key) { + return this.table[position].value; + } + } + return undefined; + } + + remove(key: K) { + const position = this.hashCode(key); + + if (this.table[position] != null) { + if (this.table[position].key === key) { + delete this.table[position]; + this.verifyRemoveSideEffect(key, position); + return true; + } + let index = position + 1; + while (this.table[index] != null && this.table[index].key !== key) { + index++; + } + if (this.table[index] != null && this.table[index].key === key) { + delete this.table[index]; + this.verifyRemoveSideEffect(key, index); + return true; + } + } + return false; + } + + private verifyRemoveSideEffect(key: K, removedPosition: number) { + const hash = this.hashCode(key); + let index = removedPosition + 1; + while (this.table[index] != null) { + const posHash = this.hashCode(this.table[index].key); + if (posHash <= hash || posHash <= removedPosition) { + this.table[removedPosition] = this.table[index]; + delete this.table[index]; + removedPosition = index; + } + index++; + } + } + + isEmpty() { + return this.size() === 0; + } + + size() { + return Object.keys(this.table).length; + } + + clear() { + this.table = {}; + } + + getTable() { + return this.table; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + objString = `${objString},{${keys[i]} => ${this.table[ + keys[i] + ].toString()}}`; + } + return objString; + } +} diff --git a/src/ts/data-structures/hash-table-separate-chaining.ts b/src/ts/data-structures/hash-table-separate-chaining.ts new file mode 100644 index 00000000..165ff15d --- /dev/null +++ b/src/ts/data-structures/hash-table-separate-chaining.ts @@ -0,0 +1,106 @@ +import { defaultToString } from '../util'; +import LinkedList from './linked-list'; +import { ValuePair } from './models/value-pair'; + +export default class HashTableSeparateChaining { + protected table: { [key: string]: LinkedList> }; + + constructor(protected toStrFn: (key: K) => string = defaultToString) { + this.table = {}; + } + + private loseloseHashCode(key: K) { + if (typeof key === 'number') { + return key; + } + const tableKey = this.toStrFn(key); + let hash = 0; + for (let i = 0; i < tableKey.length; i++) { + hash += tableKey.charCodeAt(i); + } + return hash % 37; + } + + hashCode(key: K) { + return this.loseloseHashCode(key); + } + + put(key: K, value: V) { + if (key != null && value != null) { + const position = this.hashCode(key); + + if (this.table[position] == null) { + this.table[position] = new LinkedList>(); + } + this.table[position].push(new ValuePair(key, value)); + return true; + } + return false; + } + + get(key: K) { + const position = this.hashCode(key); + const linkedList = this.table[position]; + if (linkedList != null && !linkedList.isEmpty()) { + let current = linkedList.getHead(); + while (current != null) { + if (current.element.key === key) { + return current.element.value; + } + current = current.next; + } + } + return undefined; + } + + remove(key: K) { + const position = this.hashCode(key); + const linkedList = this.table[position]; + if (linkedList != null && !linkedList.isEmpty()) { + let current = linkedList.getHead(); + while (current != null) { + if (current.element.key === key) { + linkedList.remove(current.element); + if (linkedList.isEmpty()) { + delete this.table[position]; + } + return true; + } + current = current.next; + } + } + return false; + } + + isEmpty() { + return this.size() === 0; + } + + size() { + let count = 0; + Object.values(this.table).forEach(linkedList => count += linkedList.size()); + return count; + } + + clear() { + this.table = {}; + } + + getTable() { + return this.table; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + objString = `${objString},{${keys[i]} => ${this.table[ + keys[i] + ].toString()}}`; + } + return objString; + } +} diff --git a/src/ts/data-structures/hash-table.ts b/src/ts/data-structures/hash-table.ts new file mode 100644 index 00000000..7b3c6983 --- /dev/null +++ b/src/ts/data-structures/hash-table.ts @@ -0,0 +1,87 @@ +import { defaultToString } from '../util'; +import { ValuePair } from './models/value-pair'; + +export default class HashTable { + protected table: { [key: string]: ValuePair }; + + constructor(protected toStrFn: (key: K) => string = defaultToString) { + this.table = {}; + } + + private loseloseHashCode(key: K) { + if (typeof key === 'number') { + return key; + } + const tableKey = this.toStrFn(key); + let hash = 0; + for (let i = 0; i < tableKey.length; i++) { + hash += tableKey.charCodeAt(i); + } + return hash % 37; + } + + /* private djb2HashCode(key: K) { + const tableKey = this.toStrFn(key); + let hash = 5381; + for (let i = 0; i < tableKey.length; i++) { + hash = (hash * 33) + tableKey.charCodeAt(i); + } + return hash % 1013; + } */ + + hashCode(key: K) { + return this.loseloseHashCode(key); + } + + put(key: K, value: V) { + if (key != null && value != null) { + const position = this.hashCode(key); + this.table[position] = new ValuePair(key, value); + return true; + } + return false; + } + + get(key: K) { + const valuePair = this.table[this.hashCode(key)]; + return valuePair == null ? undefined : valuePair.value; + } + + remove(key: K) { + const hash = this.hashCode(key); + const valuePair = this.table[hash]; + if (valuePair != null) { + delete this.table[hash]; + return true; + } + return false; + } + + getTable() { + return this.table; + } + + isEmpty() { + return this.size() === 0; + } + + size() { + return Object.keys(this.table).length; + } + + clear() { + this.table = {}; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + objString = `${objString},{${keys[i]} => ${this.table[keys[i]].toString()}}`; + } + return objString; + } +} diff --git a/src/ts/data-structures/heap.ts b/src/ts/data-structures/heap.ts new file mode 100644 index 00000000..6dc06585 --- /dev/null +++ b/src/ts/data-structures/heap.ts @@ -0,0 +1,113 @@ +import { Compare, defaultCompare, ICompareFunction, reverseCompare, swap } from '../util'; + +export class MinHeap { + protected heap: T[] = []; + + constructor(protected compareFn: ICompareFunction = defaultCompare) {} + + private getLeftIndex(index: number) { + return (2 * index) + 1; + } + + private getRightIndex(index: number) { + return (2 * index) + 2; + } + + private getParentIndex(index: number) { + if (index === 0) { + return undefined; + } + return Math.floor((index - 1) / 2); + } + + size() { + return this.heap.length; + } + + isEmpty() { + return this.size() <= 0; + } + + clear() { + this.heap = []; + } + + findMinimum() { + return this.isEmpty() ? undefined : this.heap[0]; + } + + insert(value: T) { + if (value != null) { + const index = this.heap.length; + this.heap.push(value); + this.siftUp(index); + return true; + } + return false; + } + + private siftDown(index: number) { + let element = index; + const left = this.getLeftIndex(index); + const right = this.getRightIndex(index); + const size = this.size(); + + if (left < size && this.compareFn(this.heap[element], this.heap[left]) === Compare.BIGGER_THAN) { + element = left; + } + + if ( + right < size && + this.compareFn(this.heap[element], this.heap[right]) === Compare.BIGGER_THAN + ) { + element = right; + } + + if (index !== element) { + swap(this.heap, index, element); + this.siftDown(element); + } + } + + private siftUp(index: number): void { + let parent = this.getParentIndex(index); + while (index > 0 && this.compareFn(this.heap[parent], this.heap[index]) === Compare.BIGGER_THAN) { + swap(this.heap, parent, index); + index = parent; + parent = this.getParentIndex(index); + } + } + + extract() { + if (this.isEmpty()) { + return undefined; + } + if (this.size() === 1) { + return this.heap.shift(); + } + const removedValue = this.heap.shift(); + this.siftDown(0); + return removedValue; + } + + heapify(array: T[]) { + if (array) { + this.heap = array; + } + + const maxIndex = Math.floor(this.size() / 2) - 1; + + for (let i = 0; i <= maxIndex; i++) { + this.siftDown(i); + } + + return this.heap; + } +} + +export class MaxHeap extends MinHeap { + constructor(protected compareFn: ICompareFunction = defaultCompare) { + super(compareFn); + this.compareFn = reverseCompare(compareFn); + } +} diff --git a/src/ts/data-structures/linked-list.ts b/src/ts/data-structures/linked-list.ts new file mode 100644 index 00000000..f0460c90 --- /dev/null +++ b/src/ts/data-structures/linked-list.ts @@ -0,0 +1,123 @@ +import { defaultEquals, IEqualsFunction } from '../util'; +import { Node } from './models/linked-list-models'; + +export default class LinkedList { + protected count = 0; + protected head: Node | undefined; + + constructor(protected equalsFn: IEqualsFunction = defaultEquals) {} + + push(element: T) { + const node = new Node(element); + let current; + + if (this.head == null) { + // catches null && undefined + this.head = node; + } else { + current = this.head; + + while (current.next != null) { + current = current.next; + } + + current.next = node; + } + this.count++; + } + + getElementAt(index: number) { + if (index >= 0 && index <= this.count) { + let node = this.head; + for (let i = 0; i < index && node != null; i++) { + node = node.next; + } + return node; + } + return undefined; + } + + insert(element: T, index: number) { + if (index >= 0 && index <= this.count) { + const node = new Node(element); + + if (index === 0) { + const current = this.head; + node.next = current; + this.head = node; + } else { + const previous = this.getElementAt(index - 1); + node.next = previous.next; + previous.next = node; + } + this.count++; + return true; + } + return false; + } + + removeAt(index: number) { + if (index >= 0 && index < this.count) { + let current = this.head; + + if (index === 0) { + this.head = current.next; + } else { + const previous = this.getElementAt(index - 1); + current = previous.next; + previous.next = current.next; + } + this.count--; + return current.element; + } + return undefined; + } + + remove(element: T) { + const index = this.indexOf(element); + return this.removeAt(index); + } + + indexOf(element: T) { + let current = this.head; + + for (let i = 0; i < this.size() && current != null; i++) { + if (this.equalsFn(element, current.element)) { + return i; + } + current = current.next; + } + + return -1; + } + + isEmpty() { + return this.size() === 0; + } + + size() { + return this.count; + } + + getHead() { + return this.head; + } + + clear() { + this.head = undefined; + this.count = 0; + } + + toString() { + if (this.head == null) { + return ''; + } + let objString = `${this.head.element}`; + let current = this.head.next; + for (let i = 1; i < this.size() && current != null; i++) { + objString = `${objString},${current.element}`; + current = current.next; + } + return objString; + } +} diff --git a/src/ts/data-structures/models/linked-list-models.ts b/src/ts/data-structures/models/linked-list-models.ts new file mode 100644 index 00000000..168d640e --- /dev/null +++ b/src/ts/data-structures/models/linked-list-models.ts @@ -0,0 +1,13 @@ +export class Node { + constructor(public element: T, public next?: Node) {} +} + +export class DoublyNode extends Node { + constructor( + public element: T, + public next?: DoublyNode, + public prev?: DoublyNode + ) { + super(element, next); + } +} diff --git a/src/ts/data-structures/models/node.ts b/src/ts/data-structures/models/node.ts new file mode 100644 index 00000000..f3e3f9aa --- /dev/null +++ b/src/ts/data-structures/models/node.ts @@ -0,0 +1,10 @@ +export class Node { + left: Node; + right: Node; + + constructor(public key: K) {} + + toString() { + return `${this.key}`; + } +} diff --git a/src/ts/data-structures/models/red-black-node.ts b/src/ts/data-structures/models/red-black-node.ts new file mode 100644 index 00000000..256bd7f5 --- /dev/null +++ b/src/ts/data-structures/models/red-black-node.ts @@ -0,0 +1,28 @@ +export enum Colors { + RED = 0, + BLACK = 1 +} + +export class RedBlackNode { + left: RedBlackNode; + right: RedBlackNode; + color: Colors; + + constructor(public key: K) {} + + isRed() { + return this.color === Colors.RED; + } + + flipColor() { + if (this.color === Colors.RED) { + this.color = Colors.BLACK; + } else { + this.color = Colors.RED; + } + } + + toString() { + return `${this.key}`; + } +} diff --git a/src/ts/data-structures/models/value-pair-lazy.ts b/src/ts/data-structures/models/value-pair-lazy.ts new file mode 100644 index 00000000..57b6abbd --- /dev/null +++ b/src/ts/data-structures/models/value-pair-lazy.ts @@ -0,0 +1,7 @@ +import { ValuePair } from './value-pair'; + +export class ValuePairLazy extends ValuePair { + constructor(public key: K, public value: V, public isDeleted = false) { + super(key, value); + } +} diff --git a/src/ts/data-structures/models/value-pair.ts b/src/ts/data-structures/models/value-pair.ts new file mode 100644 index 00000000..eb4cff0a --- /dev/null +++ b/src/ts/data-structures/models/value-pair.ts @@ -0,0 +1,7 @@ +export class ValuePair { + constructor(public key: K, public value: V) {} + + toString() { + return `[#${this.key}: ${this.value}]`; + } +} diff --git a/src/ts/data-structures/priority-queue-array.ts b/src/ts/data-structures/priority-queue-array.ts new file mode 100644 index 00000000..6ad0d441 --- /dev/null +++ b/src/ts/data-structures/priority-queue-array.ts @@ -0,0 +1,58 @@ +import { Compare, defaultCompare, ICompareFunction } from '../util'; + +export default class PriorityQueue { + private items: T[]; + + constructor( + private compareFn: ICompareFunction = defaultCompare, + private compare: Compare = Compare.LESS_THAN + ) { + this.items = []; + } + + enqueue(element: T) { + let added = false; + + for (let i = 0; i < this.items.length; i++) { + if (this.compareFn(element, this.items[i]) === this.compare) { + this.items.splice(i, 0, element); + added = true; + break; + } + } + + if (!added) { + this.items.push(element); + } + } + + dequeue() { + return this.items.shift(); + } + + peek() { + if (this.isEmpty()) { + return undefined; + } + return this.items[0]; + } + + isEmpty() { + return this.items.length === 0; + } + + clear() { + this.items = []; + } + + size() { + return this.items.length; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + return this.items; + } +} diff --git a/src/ts/data-structures/queue.ts b/src/ts/data-structures/queue.ts new file mode 100644 index 00000000..b6db45a5 --- /dev/null +++ b/src/ts/data-structures/queue.ts @@ -0,0 +1,58 @@ +export default class Queue { + private count: number; + private lowestCount: number; + private items: any; + + constructor() { + this.count = 0; + this.lowestCount = 0; + this.items = {}; + } + + enqueue(element: T) { + this.items[this.count] = element; + this.count++; + } + + dequeue() { + if (this.isEmpty()) { + return undefined; + } + const result = this.items[this.lowestCount]; + delete this.items[this.lowestCount]; + this.lowestCount++; + return result; + } + + peek() { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.lowestCount]; + } + + isEmpty() { + return this.size() === 0; + } + + clear() { + this.items = {}; + this.count = 0; + this.lowestCount = 0; + } + + size() { + return this.count - this.lowestCount; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + let objString = `${this.items[this.lowestCount]}`; + for (let i = this.lowestCount + 1; i < this.count; i++) { + objString = `${objString},${this.items[i]}`; + } + return objString; + } +} diff --git a/src/ts/data-structures/red-black-tree.ts b/src/ts/data-structures/red-black-tree.ts new file mode 100644 index 00000000..001a2fb4 --- /dev/null +++ b/src/ts/data-structures/red-black-tree.ts @@ -0,0 +1,11 @@ +/*import { Compare, defaultCompare, ICompareFunction } from '../util'; +import BinarySearchTree from './binary-search-tree'; +import { Node } from './models/node'; + +export default class RedBlackTree extends BinarySearchTree { + + constructor(protected compareFn: ICompareFunction = defaultCompare) { + super(compareFn); + } +} +*/ diff --git a/src/ts/data-structures/set.ts b/src/ts/data-structures/set.ts new file mode 100644 index 00000000..c67faf75 --- /dev/null +++ b/src/ts/data-structures/set.ts @@ -0,0 +1,117 @@ +export default class Set { + private items: any; + + constructor() { + this.items = {}; + } + + add(element: T) { + if (!this.has(element)) { + this.items[element] = element; + return true; + } + return false; + } + + delete(element: T) { + if (this.has(element)) { + delete this.items[element]; + return true; + } + return false; + } + + has(element: T) { + // return this.items.hasOwnProperty(element); + return Object.prototype.hasOwnProperty.call(this.items, element); + } + + values(): T[] { + return Object.values(this.items); + } + + union(otherSet: Set) { + const unionSet = new Set(); + + this.values().forEach(value => unionSet.add(value)); + otherSet.values().forEach(value => unionSet.add(value)); + + return unionSet; + } + + intersection(otherSet: Set) { + const intersectionSet = new Set(); + + const values = this.values(); + const otherValues = otherSet.values(); + + let biggerSet = values; + let smallerSet = otherValues; + + if (otherValues.length - values.length > 0) { + biggerSet = otherValues; + smallerSet = values; + } + + smallerSet.forEach(value => { + if (biggerSet.includes(value)) { + intersectionSet.add(value); + } + }); + + return intersectionSet; + } + + difference(otherSet: Set) { + const differenceSet = new Set(); + + this.values().forEach(value => { + if (!otherSet.has(value)) { + differenceSet.add(value); + } + }); + + return differenceSet; + } + + isSubsetOf(otherSet: Set) { + if (this.size() > otherSet.size()) { + return false; + } + + let isSubset = true; + this.values().every(value => { + if (!otherSet.has(value)) { + isSubset = false; + return false; + } + return true; + }); + + return isSubset; + } + + isEmpty() { + return this.size() === 0; + } + + size() { + return Object.keys(this.items).length; + } + + clear() { + this.items = {}; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + const values = this.values(); + let objString = `${values[0]}`; + for (let i = 1; i < values.length; i++) { + objString = `${objString},${values[i].toString()}`; + } + return objString; + } +} diff --git a/src/ts/data-structures/sorted-linked-list.ts b/src/ts/data-structures/sorted-linked-list.ts new file mode 100644 index 00000000..f7832e81 --- /dev/null +++ b/src/ts/data-structures/sorted-linked-list.ts @@ -0,0 +1,43 @@ +import { Compare, defaultCompare, defaultEquals, ICompareFunction, IEqualsFunction } from '../util'; +import LinkedList from './linked-list'; + +export default class SortedLinkedList extends LinkedList { + constructor( + protected equalsFn: IEqualsFunction = defaultEquals, + protected compareFn: ICompareFunction = defaultCompare + ) { + super(equalsFn); + } + + push(element: T) { + if (this.isEmpty()) { + super.push(element); + } else { + const index = this.getIndexNextSortedElement(element); + super.insert(element, index); + } + } + + insert(element: T, index: number = 0) { + if (this.isEmpty()) { + return super.insert(element, 0); + } + index = this.getIndexNextSortedElement(element); + return super.insert(element, index); + } + + private getIndexNextSortedElement(element: T) { + let current = this.head; + let i = 0; + + for (; i < this.size() && current; i++) { + const comp = this.compareFn(element, current.element); + if (comp === Compare.LESS_THAN) { + return i; + } + current = current.next; + } + + return i; + } +} diff --git a/src/ts/data-structures/stack-array.ts b/src/ts/data-structures/stack-array.ts new file mode 100644 index 00000000..44bf91da --- /dev/null +++ b/src/ts/data-structures/stack-array.ts @@ -0,0 +1,39 @@ +export default class StackArray { + private items: T[]; + + constructor() { + this.items = []; + } + + push(element: T) { + this.items.push(element); + } + + pop() { + return this.items.pop(); + } + + peek() { + return this.items[this.items.length - 1]; + } + + isEmpty() { + return this.items.length === 0; + } + + size() { + return this.items.length; + } + + clear() { + this.items = []; + } + + toArray() { + return this.items; + } + + toString() { + return this.items.toString(); + } +} diff --git a/src/ts/data-structures/stack-linked-list.ts b/src/ts/data-structures/stack-linked-list.ts new file mode 100644 index 00000000..fa39023b --- /dev/null +++ b/src/ts/data-structures/stack-linked-list.ts @@ -0,0 +1,44 @@ +import DoublyLinkedList from './doubly-linked-list'; + +export default class StackLinkedList { + private items: DoublyLinkedList; + + constructor() { + this.items = new DoublyLinkedList(); + } + + push(element: T) { + this.items.push(element); + } + + pop() { + if (this.isEmpty()) { + return undefined; + } + const result = this.items.removeAt(this.size() - 1); + return result; + } + + peek() { + if (this.isEmpty()) { + return undefined; + } + return this.items.getElementAt(this.size() - 1).element; + } + + isEmpty() { + return this.items.isEmpty(); + } + + size() { + return this.items.size(); + } + + clear() { + this.items.clear(); + } + + toString() { + return this.items.toString(); + } +} diff --git a/src/ts/data-structures/stack.ts b/src/ts/data-structures/stack.ts new file mode 100644 index 00000000..4ea30351 --- /dev/null +++ b/src/ts/data-structures/stack.ts @@ -0,0 +1,59 @@ +export default class Stack { + private count: number; + private items: any; + + constructor() { + this.count = 0; + this.items = {}; + } + + push(element: T) { + this.items[this.count] = element; + this.count++; + } + + pop() { + if (this.isEmpty()) { + return undefined; + } + this.count--; + const result = this.items[this.count]; + delete this.items[this.count]; + return result; + } + + peek() { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.count - 1]; + } + + isEmpty() { + return this.count === 0; + } + + size() { + return this.count; + } + + clear() { + /* while (!this.isEmpty()) { + this.pop(); + } */ + + this.items = {}; + this.count = 0; + } + + toString() { + if (this.isEmpty()) { + return ''; + } + let objString = `${this.items[0]}`; + for (let i = 1; i < this.count; i++) { + objString = `${objString},${this.items[i]}`; + } + return objString; + } +} diff --git a/src/ts/index.ts b/src/ts/index.ts new file mode 100644 index 00000000..661de1f2 --- /dev/null +++ b/src/ts/index.ts @@ -0,0 +1,94 @@ +import * as _util from './util'; +export const util = _util; + +export { default as LinkedList } from './data-structures/linked-list'; +export { default as DoublyLinkedList } from './data-structures/doubly-linked-list'; +export { default as CircularLinkedList } from './data-structures/circular-linked-list'; +export { default as SortedLinkedList } from './data-structures/sorted-linked-list'; +export { default as StackLinkedList } from './data-structures/stack-linked-list'; +export { default as Set } from './data-structures/set'; +export { default as Dictionary } from './data-structures/dictionary'; +export { default as HashTable } from './data-structures/hash-table'; +export { default as HashTableSeparateChaining } from './data-structures/hash-table-separate-chaining'; +export { default as HashTableLinearProbing } from './data-structures/hash-table-linear-probing'; +export { default as HashTableLinearProbingLazy } from './data-structures/hash-table-linear-probing-lazy'; + +// chapter 08 +export { factorialIterative as factorialIterative } from './others/factorial'; +export { factorial as factorial} from './others/factorial'; +export { fibonacci as fibonacci} from './others/fibonacci'; +export { fibonacciIterative as fibonacciIterative} from './others/fibonacci'; +export { fibonacciMemoization as fibonacciMemoization} from './others/fibonacci'; + +// chapter 09 +export { default as BinarySearchTree } from './data-structures/binary-search-tree'; +export { default as AVLTree } from './data-structures/avl-tree'; + +// chapter 10 +export { MinHeap as MinHeap } from './data-structures/heap'; +export { MaxHeap as MaxHeap } from './data-structures/heap'; +export { default as heapSort } from './algorithms/sorting/heap-sort'; + +// chapter 11 +export { default as Graph } from './data-structures/graph'; +export { breadthFirstSearch as breadthFirstSearch } from './algorithms/graph/breadth-first-search'; +export { bfs as BFS } from './algorithms/graph/breadth-first-search'; +export { depthFirstSearch as depthFirstSearch } from './algorithms/graph/depth-first-search'; +export { DFS as DFS } from './algorithms/graph/depth-first-search'; +export { dijkstra as dijkstra } from './algorithms/graph/dijkstra'; +export { floydWarshall as floydWarshall } from './algorithms/graph/floyd-warshall'; +export { prim as prim } from './algorithms/graph/prim'; +export { kruskal as kruskal } from './algorithms/graph/kruskal'; + +// chapter 12 +export { shuffle as shuffle } from './algorithms/shuffle/fisher–yates'; + +export { bubbleSort as bubbleSort } from './algorithms/sorting/bubble-sort'; +export { modifiedBubbleSort as modifiedBubbleSort } from './algorithms/sorting/bubble-sort-improved'; +export { bucketSort as bucketSort } from './algorithms/sorting/bucket-sort'; +export { countingSort as countingSort } from './algorithms/sorting/counting-sort'; +export { insertionSort as insertionSort } from './algorithms/sorting/insertion-sort'; +export { mergeSort as mergeSort } from './algorithms/sorting/merge-sort'; +export { quickSort as quickSort } from './algorithms/sorting/quicksort'; +export { radixSort as radixSort } from './algorithms/sorting/radix-sort'; +export { selectionSort as selectionSort } from './algorithms/sorting/selection-sort'; +export { shellSort as shellSort } from './algorithms/sorting/shell-sort'; + +export { binarySearch as binarySearch } from './algorithms/search/binary-search'; +export { interpolationSearch as interpolationSearch } from './algorithms/search/interpolation-search'; +export { sequentialSearch as sequentialSearch } from './algorithms/search/sequential-search'; +export { findMaxValue as findMaxValue } from './algorithms/search/min-max-search'; +export { findMinValue as findMinValue } from './algorithms/search/min-max-search'; + +// chapter 14 +export { binarySearch as binarySearchRecursive } from './algorithms/search/binary-search-recursive'; +export { minCoinChange as minCoinChange } from './algorithms/dynamic-programing/min-coin-change'; +export { ratInAMaze as ratInAMaze } from './algorithms/backtracking/rat-in-maze'; +export { sudokuSolver as sudokuSolver } from './algorithms/backtracking/sudoku-solver'; + + +/* import { hotPotato } from './others/hot-potato'; +import { palindromeChecker } from './others/palindrome-checker'; +import Deque from './data-structures/deque'; +import Queue from './data-structures/queue'; +import { hanoi, hanoiStack } from './others/hanoi'; +import { baseConverter, decimalToBinary } from './others/base-converter'; +import StackArray from './data-structures/stack-array'; +import Stack from './data-structures/stack'; +import { parenthesesChecker } from './others/balanced-symbols'; +import { MinHeap, MaxHeap } from './data-structures/heap'; + + +export { + Stack, + StackArray, + parenthesesChecker, + baseConverter, + decimalToBinary, + hanoi, + hanoiStack, + Queue, + Deque, + hotPotato, + palindromeChecker +}; */ diff --git a/src/ts/others/balanced-symbols.ts b/src/ts/others/balanced-symbols.ts new file mode 100644 index 00000000..cf8c2fe4 --- /dev/null +++ b/src/ts/others/balanced-symbols.ts @@ -0,0 +1,43 @@ +import Stack from '../data-structures/stack'; + +export function parenthesesChecker(symbols: string) { + const stack = new Stack(); + const opens = '([{'; + const closers = ')]}'; + let balanced = true; + let index = 0; + let symbol: string; + let top: string; + + while (index < symbols.length && balanced) { + symbol = symbols.charAt(index); + if (opens.indexOf(symbol) >= 0) { + stack.push(symbol); + // console.log(`open symbol - stacking ${symbol}`); + } else { + // console.log(`close symbol ${symbol}`); + if (stack.isEmpty()) { + balanced = false; + // console.log('Stack is empty, no more symbols to pop and compare'); + } else { + top = stack.pop(); + // if (!matches(top, symbol)){ + if (!(opens.indexOf(top) === closers.indexOf(symbol))) { + balanced = false; + /* console.log( + `poping symbol ${top} - is not a match compared to ${symbol}` + ); */ + } /* else { + console.log( + `poping symbol ${top} - is is a match compared to ${symbol}` + ); + } */ + } + } + index++; + } + if (balanced && stack.isEmpty()) { + return true; + } + return false; +} diff --git a/src/ts/others/base-converter.ts b/src/ts/others/base-converter.ts new file mode 100644 index 00000000..d65b27ef --- /dev/null +++ b/src/ts/others/base-converter.ts @@ -0,0 +1,42 @@ +import Stack from '../data-structures/stack'; + +export function decimalToBinary(decNumber: number) { + const remStack = new Stack(); + let rem: number; + let binaryString = ''; + + while (decNumber > 0) { + rem = Math.floor(decNumber % 2); + remStack.push(rem); + decNumber = Math.floor(decNumber / 2); + } + + while (!remStack.isEmpty()) { + binaryString += remStack.pop().toString(); + } + + return binaryString; +} + +export function baseConverter(decNumber: number, base: number) { + const remStack = new Stack(); + const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let rem: number; + let baseString = ''; + + if (!(base >= 2 && base <= 36)) { + return ''; + } + + while (decNumber > 0) { + rem = Math.floor(decNumber % base); + remStack.push(rem); + decNumber = Math.floor(decNumber / base); + } + + while (!remStack.isEmpty()) { + baseString += digits[remStack.pop()]; + } + + return baseString; +} diff --git a/src/ts/others/factorial.ts b/src/ts/others/factorial.ts new file mode 100644 index 00000000..5ee10414 --- /dev/null +++ b/src/ts/others/factorial.ts @@ -0,0 +1,20 @@ +export function factorialIterative(number: number) { + if (number < 0) { + return undefined; + } + let total = 1; + for (let n = number; n > 1; n--) { + total = total * n; + } + return total; +} + +export function factorial(n: number): number { + if (n < 0) { + return undefined; + } + if (n === 1 || n === 0) { + return 1; + } + return n * factorial(n - 1); +} diff --git a/src/ts/others/fibonacci.ts b/src/ts/others/fibonacci.ts new file mode 100644 index 00000000..32d8db4f --- /dev/null +++ b/src/ts/others/fibonacci.ts @@ -0,0 +1,29 @@ +export function fibonacci(n: number): number { + if (n < 1) { return 0; } // {1} + if (n <= 2) { return 1; } // {2} + return fibonacci(n - 1) + fibonacci(n - 2); // {3} +} + +export function fibonacciIterative(n: number) { + if (n < 1) { return 0; } + let fibNMinus2 = 0; + let fibNMinus1 = 1; + let fibN = n; + for (let i = 2; i <= n; i++) { + // n >= 2 + fibN = fibNMinus1 + fibNMinus2; // f(n-1) + f(n-2) + fibNMinus2 = fibNMinus1; + fibNMinus1 = fibN; + } + return fibN; +} + +export function fibonacciMemoization(n: number) { + if (n < 1) { return 0; } + const memo = [0, 1]; + const fibonacciMem = (num: number): number => { + if (memo[num] != null) { return memo[num]; } + return (memo[num] = fibonacciMem(num - 1) + fibonacciMem(num - 2)); + }; + return fibonacciMem(n); +} diff --git a/src/ts/others/hanoi.ts b/src/ts/others/hanoi.ts new file mode 100644 index 00000000..fdc166a6 --- /dev/null +++ b/src/ts/others/hanoi.ts @@ -0,0 +1,64 @@ +import Stack from '../data-structures/stack'; + +function towerOfHanoi( + plates: number, + source: Stack, + helper: Stack, + dest: Stack, + sourceName: string, helperName: string, destName: string, + moves: any[] = [] +) { + if (plates <= 0) { + return moves; + } + if (plates === 1) { + dest.push(source.pop()); + const move: any = {}; + move[sourceName] = source.toString(); + move[helperName] = helper.toString(); + move[destName] = dest.toString(); + moves.push(move); + } else { + towerOfHanoi(plates - 1, source, dest, helper, sourceName, destName, helperName, moves); + dest.push(source.pop()); + const move: any = {}; + move[sourceName] = source.toString(); + move[helperName] = helper.toString(); + move[destName] = dest.toString(); + moves.push(move); + towerOfHanoi(plates - 1, helper, source, dest, helperName, sourceName, destName, moves); + } + return moves; +} + +export function hanoiStack(plates: number) { + const source = new Stack(); + const dest = new Stack(); + const helper = new Stack(); + + for (let i = plates; i > 0; i--) { + source.push(i); + } + + return towerOfHanoi(plates, source, helper, dest, 'source', 'helper', 'dest'); +} + +export function hanoi( + plates: number, + source: string, + helper: string, + dest: string, + moves: string[][] = [] +) { + if (plates <= 0) { + return moves; + } + if (plates === 1) { + moves.push([source, dest]); + } else { + hanoi(plates - 1, source, dest, helper, moves); + moves.push([source, dest]); + hanoi(plates - 1, helper, source, dest, moves); + } + return moves; +} diff --git a/src/ts/others/hot-potato.ts b/src/ts/others/hot-potato.ts new file mode 100644 index 00000000..f6eb8349 --- /dev/null +++ b/src/ts/others/hot-potato.ts @@ -0,0 +1,22 @@ +import Queue from '../data-structures/queue'; + +export function hotPotato(elementsList: any[], num: number) { + const queue = new Queue(); + const elimitatedList = []; + + for (let i = 0; i < elementsList.length; i++) { + queue.enqueue(elementsList[i]); + } + + while (queue.size() > 1) { + for (let i = 0; i < num; i++) { + queue.enqueue(queue.dequeue()); + } + elimitatedList.push(queue.dequeue()); + } + + return { + elimitated: elimitatedList, + winner: queue.dequeue() + }; +} diff --git a/src/ts/others/palindrome-checker.ts b/src/ts/others/palindrome-checker.ts new file mode 100644 index 00000000..10c5a21a --- /dev/null +++ b/src/ts/others/palindrome-checker.ts @@ -0,0 +1,28 @@ +import Deque from '../data-structures/deque'; + +export function palindromeChecker(aString: string) { + + if (aString === undefined || aString === null || + (aString !== null && aString.length === 0)) { + return false; + } + + const deque = new Deque(); + const lowerString = aString.toLocaleLowerCase().split(' ').join(''); + let isEqual = true; + let firstChar: string, lastChar: string; + + for (let i = 0; i < lowerString.length; i++) { + deque.addBack(lowerString.charAt(i)); + } + + while (deque.size() > 1 && isEqual) { + firstChar = deque.removeFront(); + lastChar = deque.removeBack(); + if (firstChar !== lastChar) { + isEqual = false; + } + } + + return isEqual; +} diff --git a/src/ts/util.ts b/src/ts/util.ts new file mode 100644 index 00000000..3202d19d --- /dev/null +++ b/src/ts/util.ts @@ -0,0 +1,60 @@ +export type ICompareFunction = (a: T, b: T) => number; + +export type IEqualsFunction = (a: T, b: T) => boolean; + +export type IDiffFunction = (a: T, b: T) => number; + +export const DOES_NOT_EXIST = -1; + +export enum Compare { + LESS_THAN = -1, + BIGGER_THAN = 1, + EQUALS = 0 +} + +export function lesserEquals(a: T, b: T, compareFn: ICompareFunction) { + const comp = compareFn(a, b); + return comp === Compare.LESS_THAN || comp === Compare.EQUALS; +} + +export function biggerEquals(a: T, b: T, compareFn: ICompareFunction) { + const comp = compareFn(a, b); + return comp === Compare.BIGGER_THAN || comp === Compare.EQUALS; +} + +export function defaultCompare(a: T, b: T): number { + if (a === b) { + return 0; + } + return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; +} + +export function defaultEquals(a: T, b: T): boolean { + return a === b; +} + +export function defaultToString(item: any): string { + if (item === null) { + return 'NULL'; + } else if (item === undefined) { + return 'UNDEFINED'; + } else if (typeof item === 'string' || item instanceof String) { + return `${item}`; + } + return item.toString(); +} + +export function swap(array: any[], a: number, b: number) { + /* const temp = array[a]; + array[a] = array[b]; + array[b] = temp; */ + [array[a], array[b]] = [array[b], array[a]]; +} + +export function reverseCompare(compareFn: ICompareFunction): ICompareFunction { + return (a, b) => compareFn(b, a); +} + +export function defaultDiff(a: T, b: T): number { + return Number(a) - Number(b); +} diff --git a/test/js/algorithms/search/binary-search.spec.js b/test/js/algorithms/search/binary-search.spec.js new file mode 100644 index 00000000..220c1b28 --- /dev/null +++ b/test/js/algorithms/search/binary-search.spec.js @@ -0,0 +1,4 @@ +import { binarySearch } from '../../../../src/js/index'; +import { testSearchAlgorithm } from './search-algorithms-tests'; + +testSearchAlgorithm(binarySearch, 'Binary Search'); diff --git a/test/js/algorithms/search/interpolation-search.spec.js b/test/js/algorithms/search/interpolation-search.spec.js new file mode 100644 index 00000000..1daff1cc --- /dev/null +++ b/test/js/algorithms/search/interpolation-search.spec.js @@ -0,0 +1,4 @@ +import { interpolationSearch } from '../../../../src/js/index'; +import { testSearchAlgorithm } from './search-algorithms-tests'; + +testSearchAlgorithm(interpolationSearch, 'Interpolation Search', { customEquals: false }); diff --git a/test/js/algorithms/search/min-max-search.spec.js b/test/js/algorithms/search/min-max-search.spec.js new file mode 100644 index 00000000..0c789a2f --- /dev/null +++ b/test/js/algorithms/search/min-max-search.spec.js @@ -0,0 +1,31 @@ +import 'mocha'; +import { expect } from 'chai'; +import { findMinValue, findMaxValue } from '../../../../src/js/index'; + +describe('Min and Max Values Search', () => { + const SIZE = 10; + + function createSortedArray() { + const array = []; + for (let i = 1; i <= SIZE; i++) { + array.push(i); + } + return array; + } + + it('min value - works with empty arrays', () => { + expect(findMinValue([])).to.equal(undefined); + }); + + it('max value - works with empty arrays', () => { + expect(findMaxValue([])).to.equal(undefined); + }); + + it('min value', () => { + expect(findMinValue(createSortedArray())).to.equal(1); + }); + + it('max value', () => { + expect(findMaxValue(createSortedArray())).to.equal(SIZE); + }); +}); diff --git a/test/js/algorithms/search/search-algorithms-tests.js b/test/js/algorithms/search/search-algorithms-tests.js new file mode 100644 index 00000000..d3e1774e --- /dev/null +++ b/test/js/algorithms/search/search-algorithms-tests.js @@ -0,0 +1,43 @@ +import 'mocha'; +import { expect } from 'chai'; + +const customEquals = (a, b) => a.key === b.key; + +export function testSearchAlgorithm( + searchAlgorithm, + algorithmName, + config = { customEquals: true } +) { + describe(algorithmName, () => { + const SIZE = 10; + + function createSortedArray() { + const array = []; + for (let i = 1; i <= SIZE; i++) { + array.push(i); + } + return array; + } + + it('works with empty arrays', () => { + expect(searchAlgorithm([], 1)).to.equal(-1); + }); + + it('finds value at the first position', () => { + const array = createSortedArray(); + expect(searchAlgorithm(array, 1)).to.equal(0); + }); + + it('finds value at the last position', () => { + const array = createSortedArray(); + expect(searchAlgorithm(array, SIZE)).to.equal(SIZE - 1); + }); + + if (config.customEquals) { + it('finds value with custom equals function', () => { + const array = [{ key: 1 }, { key: 2 }, { key: 3 }]; + expect(searchAlgorithm(array, { key: 2 }, customEquals)).to.equal(1); + }); + } + }); +} diff --git a/test/js/algorithms/search/sequential-search.spec.js b/test/js/algorithms/search/sequential-search.spec.js new file mode 100644 index 00000000..a308474e --- /dev/null +++ b/test/js/algorithms/search/sequential-search.spec.js @@ -0,0 +1,4 @@ +import { sequentialSearch } from '../../../../src/js/index'; +import { testSearchAlgorithm } from './search-algorithms-tests'; + +testSearchAlgorithm(sequentialSearch, 'Sequential Search'); diff --git "a/test/js/algorithms/shuffle/fisher\342\200\223yates.spec.js" "b/test/js/algorithms/shuffle/fisher\342\200\223yates.spec.js" new file mode 100644 index 00000000..6e87916c --- /dev/null +++ "b/test/js/algorithms/shuffle/fisher\342\200\223yates.spec.js" @@ -0,0 +1,31 @@ +import 'mocha'; +import { expect } from 'chai'; +import { shuffle } from '../../../../src/js/index'; + +describe('Fisher-Yates Suffle', () => { + const SIZE = 100; + + function createSortedArray() { + const array = []; + for (let i = 1; i <= SIZE; i++) { + array.push(i); + } + return array; + } + + it('works with empty arrays', () => { + expect(shuffle([])).to.deep.equal([]); + }); + + it('works with arrays with a single value', () => { + const array = [1]; + expect(shuffle(array)).to.deep.equal(array); + }); + + it('works with sorted arrays', () => { + let array = createSortedArray(); + const sortedArray = createSortedArray(); + array = shuffle(array); + expect(array).to.not.deep.equal(sortedArray); + }); +}); diff --git a/test/js/algorithms/sorting/bubble-sort-improved.spec.js b/test/js/algorithms/sorting/bubble-sort-improved.spec.js new file mode 100644 index 00000000..1ad03b39 --- /dev/null +++ b/test/js/algorithms/sorting/bubble-sort-improved.spec.js @@ -0,0 +1,4 @@ +import { modifiedBubbleSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(modifiedBubbleSort, 'Bubble Sort - Improved'); diff --git a/test/js/algorithms/sorting/bubble-sort.spec.js b/test/js/algorithms/sorting/bubble-sort.spec.js new file mode 100644 index 00000000..fe3b31a2 --- /dev/null +++ b/test/js/algorithms/sorting/bubble-sort.spec.js @@ -0,0 +1,5 @@ +import { bubbleSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(bubbleSort, 'Bubble Sort'); + diff --git a/test/js/algorithms/sorting/bucket-sort.spec.js b/test/js/algorithms/sorting/bucket-sort.spec.js new file mode 100644 index 00000000..2a00d8db --- /dev/null +++ b/test/js/algorithms/sorting/bucket-sort.spec.js @@ -0,0 +1,4 @@ +import { bucketSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(bucketSort, 'Bucket Sort', { reverseCompare: false }); diff --git a/test/js/algorithms/sorting/counting-sort.spec.js b/test/js/algorithms/sorting/counting-sort.spec.js new file mode 100644 index 00000000..783a48a5 --- /dev/null +++ b/test/js/algorithms/sorting/counting-sort.spec.js @@ -0,0 +1,4 @@ +import { countingSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(countingSort, 'Counting Sort', { reverseCompare: false }); diff --git a/test/js/algorithms/sorting/heap-sort.spec.js b/test/js/algorithms/sorting/heap-sort.spec.js new file mode 100644 index 00000000..75505e4e --- /dev/null +++ b/test/js/algorithms/sorting/heap-sort.spec.js @@ -0,0 +1,5 @@ +import { heapSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(heapSort, 'Heap Sort'); + diff --git a/test/js/algorithms/sorting/insertion-sort.spec.js b/test/js/algorithms/sorting/insertion-sort.spec.js new file mode 100644 index 00000000..324e12c5 --- /dev/null +++ b/test/js/algorithms/sorting/insertion-sort.spec.js @@ -0,0 +1,5 @@ +import { insertionSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(insertionSort, 'Insertion Sort'); + diff --git a/test/js/algorithms/sorting/merge-sort.spec.js b/test/js/algorithms/sorting/merge-sort.spec.js new file mode 100644 index 00000000..f46ca6bb --- /dev/null +++ b/test/js/algorithms/sorting/merge-sort.spec.js @@ -0,0 +1,5 @@ +import { mergeSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(mergeSort, 'Merge Sort'); + diff --git a/test/js/algorithms/sorting/quicksort.spec.js b/test/js/algorithms/sorting/quicksort.spec.js new file mode 100644 index 00000000..d23694db --- /dev/null +++ b/test/js/algorithms/sorting/quicksort.spec.js @@ -0,0 +1,5 @@ +import { quickSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(quickSort, 'Quick Sort'); + diff --git a/test/js/algorithms/sorting/radix-sort.spec.js b/test/js/algorithms/sorting/radix-sort.spec.js new file mode 100644 index 00000000..b312af90 --- /dev/null +++ b/test/js/algorithms/sorting/radix-sort.spec.js @@ -0,0 +1,5 @@ +import { radixSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(radixSort, 'Radix Sort', { reverseCompare: false }); + diff --git a/test/js/algorithms/sorting/selection-sort.spec.js b/test/js/algorithms/sorting/selection-sort.spec.js new file mode 100644 index 00000000..ad372942 --- /dev/null +++ b/test/js/algorithms/sorting/selection-sort.spec.js @@ -0,0 +1,5 @@ +import { selectionSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(selectionSort, 'Selection Sort'); + diff --git a/test/js/algorithms/sorting/shell-sort.spec.js b/test/js/algorithms/sorting/shell-sort.spec.js new file mode 100644 index 00000000..eb9ba569 --- /dev/null +++ b/test/js/algorithms/sorting/shell-sort.spec.js @@ -0,0 +1,5 @@ +import { shellSort } from '../../../../src/js/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(shellSort, 'Shell Sort'); + diff --git a/test/js/algorithms/sorting/sort-algorithm-tests.js b/test/js/algorithms/sorting/sort-algorithm-tests.js new file mode 100644 index 00000000..6b883e83 --- /dev/null +++ b/test/js/algorithms/sorting/sort-algorithm-tests.js @@ -0,0 +1,69 @@ +import 'mocha'; +import { expect } from 'chai'; +import { Compare } from '../../../../src/js/util'; + +export function testSortAlgorithm(sortAlgorithm, algorithmName, config = { reverseCompare: true }) { + describe(algorithmName, () => { + const SIZE = 100; + + function createNonSortedArray() { + const array = []; + for (let i = SIZE; i > 0; i--) { + array.push(i); + } + return array; + } + + function createSortedArray() { + const array = []; + for (let i = 1; i <= SIZE; i++) { + array.push(i); + } + return array; + } + + it('works with empty arrays', () => { + expect(sortAlgorithm([])).to.deep.equal([]); + }); + + it('works with sorted arrays', () => { + let array = createSortedArray(); + const sortedArray = createSortedArray(); + array = sortAlgorithm(array); + expect(array).to.deep.equal(sortedArray); + }); + + it('works with non-sorted arrays', () => { + let array = createNonSortedArray(); + const sortedArray = createSortedArray(); + array = sortAlgorithm(array); + + expect(array).to.deep.equal(sortedArray); + + for (let i = 0; i < array.length - 1; i++) { + expect(array[i] <= array[i + 1]).to.equal(true); + } + }); + + function reverseCompare(a, b) { + if (a === b) { + return 0; + } + return a < b ? Compare.BIGGER_THAN : Compare.LESS_THAN; + } + + if (config.reverseCompare) { + it('works with reverse comparator - descending order', () => { + let array = createSortedArray(); + const sortedArray = createNonSortedArray(); + array = sortAlgorithm(array, reverseCompare); + + expect(array).to.deep.equal(sortedArray); + + for (let i = 0; i < array.length - 1; i++) { + expect(array[i] >= array[i + 1]).to.equal(true); + } + }); + } + }); +} diff --git a/test/js/data-structures/circular-linked-list.spec.js b/test/js/data-structures/circular-linked-list.spec.js new file mode 100644 index 00000000..aadb02ee --- /dev/null +++ b/test/js/data-structures/circular-linked-list.spec.js @@ -0,0 +1,342 @@ +import 'mocha'; +import { expect } from 'chai'; +import { CircularLinkedList } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('CircularLinkedList', () => { + let list; + let min; + let max; + + beforeEach(() => { + list = new CircularLinkedList(); + min = 1; + max = 3; + }); + + function pushesElements() { + for (let i = min; i <= max; i++) { + list.push(i); + } + } + + function verifyList() { + let current = list.getHead(); + for (let i = min; i <= max && current; i++) { + expect(current).to.not.be.an('undefined'); + if (current) { + // TS strictNullChecks + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(i); + if (i < max) { + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + // TS strictNullChecks + expect(current.next.element).to.equal(i + 1); + } + } else { + // circular list + expect(current.next).to.not.be.an('undefined'); + expect(current.next).to.equal(list.getHead()); + if (current.next) { + expect(current.next.element).to.equal(min); + } + } + current = current.next; + } + } + } + + it('starts empty', () => { + expect(list.size()).to.equal(0); + expect(list.isEmpty()).to.equal(true); + expect(list.getHead()).to.be.an('undefined'); + }); + + it('pushes elements', () => { + pushesElements(); + verifyList(); + }); + + it('returns element at specific index: invalid position', () => { + // list is empty + expect(list.getElementAt(3)).to.be.an('undefined'); + }); + + it('returns element at specific index', () => { + let node; + + pushesElements(); + + for (let i = min; i <= max; i++) { + node = list.getElementAt(i - 1); + expect(node).to.not.be.an('undefined'); + if (node) { + expect(node.element).to.equal(i); + } + } + }); + + it('inserts elements first position empty list', () => { + const element = 1; + max = element; + expect(list.insert(element, 0)).to.equal(true); + verifyList(); + }); + + it('inserts elements first position not empty list', () => { + max = 2; + expect(list.insert(max, 0)).to.equal(true); + + expect(list.insert(min, 0)).to.equal(true); + + verifyList(); + }); + + it('inserts elements invalid position empty list', () => { + expect(list.insert(1, 1)).to.equal(false); + }); + + it('inserts elements invalid position not empty list', () => { + const element = 1; + expect(list.insert(element, 0)).to.equal(true); + expect(list.insert(element, 2)).to.equal(false); + }); + + it('inserts elements in the middle of list', () => { + expect(list.insert(3, 0)).to.equal(true); + expect(list.insert(1, 0)).to.equal(true); + expect(list.insert(2, 1)).to.equal(true); + verifyList(); + }); + + it('inserts elements at the end of list', () => { + max = 5; + + for (let i = min; i <= max; i++) { + expect(list.insert(i, i - 1)).to.equal(true); + } + + verifyList(); + }); + + it('returns index of elements', () => { + let index; + + pushesElements(); + + for (let i = min; i <= max; i++) { + index = list.indexOf(i); + expect(index).to.equal(i - 1); + } + + expect(list.indexOf(max + 2)).to.equal(-1); + }); + + it('removes valid elements', () => { + let element; + + pushesElements(); + + const minIndex = min; + for (let i = minIndex; i <= max; i++) { + element = list.remove(i); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + min++; + verifyList(); + } + }); + + it('removes invalid elements', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.remove(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position empty list', () => { + let element; + + for (let i = min; i <= max; i++) { + element = list.removeAt(i - 1); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position not empty list', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.removeAt(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes first element list single element', () => { + const value = 1; + list.push(value); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(value); + + expect(list.getHead()).to.be.an('undefined'); + expect(list.isEmpty()).to.equal(true); + }); + + it('removes first element list multiple elements', () => { + pushesElements(); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(min); + + min = 2; + verifyList(); + }); + + it('removes element from middle of list', () => { + pushesElements(); // 1, 2, 3 + + const element = list.removeAt(1); // element 2 + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(2); + + // list needs to be [1, 3] + let current = list.getHead(); + + // element 1 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(min); + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + expect(current.next.element).to.equal(max); + current = current.next; + } + } + + // element 3 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(max); + expect(current.next).to.not.be.an('undefined'); + expect(current.next).to.equal(list.getHead()); + if (current.next) { + expect(current.next.element).to.equal(min); + } + } + }); + + it('removes element from end of list', () => { + let element; + + pushesElements(); + + const maxIndex = max; + for (let i = maxIndex; i >= min; i--) { + element = list.removeAt(i - 1); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + max--; + verifyList(); + } + }); + + it('returns the head of the list', () => { + expect(list.getHead()).to.be.an('undefined'); + + list.push(1); + expect(list.getHead()).to.not.be.an('undefined'); + }); + + it('returns the correct size', () => { + expect(list.size()).to.equal(0); + + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.size()).to.equal(i); + } + + const size = max; + for (let i = min; i <= max; i++) { + list.remove(i); + expect(list.size()).to.equal(size - i); + } + + expect(list.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(list.isEmpty()).to.equal(true); + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.isEmpty()).to.equal(false); + } + + for (let i = min; i < max; i++) { + list.remove(i); + expect(list.isEmpty()).to.equal(false); + } + list.remove(max); + expect(list.isEmpty()).to.equal(true); + + pushesElements(); + expect(list.isEmpty()).to.equal(false); + + list.clear(); + expect(list.isEmpty()).to.equal(true); + }); + + it('clears the list', () => { + expect(list.size()).to.equal(0); + list.clear(); + expect(list.size()).to.equal(0); + pushesElements(); + expect(list.size()).to.greaterThan(0); + list.clear(); + expect(list.size()).to.equal(0); + }); + + it('returns toString primitive types', () => { + expect(list.toString()).to.equal(''); + + list.push(1); + expect(list.toString()).to.equal('1'); + + list.push(2); + expect(list.toString()).to.equal('1,2'); + + list.clear(); + expect(list.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const ds = new CircularLinkedList(); + ds.push('el1'); + expect(ds.toString()).to.equal('el1'); + + ds.push('el2'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const ds = new CircularLinkedList(); + expect(ds.toString()).to.equal(''); + + ds.push(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2'); + + ds.push(new MyObj(3, 4)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/data-structures/deque.spec.js b/test/js/data-structures/deque.spec.js new file mode 100644 index 00000000..69861377 --- /dev/null +++ b/test/js/data-structures/deque.spec.js @@ -0,0 +1,207 @@ +import 'mocha'; +import { expect } from 'chai'; +import Deque from '../../../src/js/data-structures/deque'; + +describe('Deque', () => { + let deque; + + beforeEach(() => { + deque = new Deque(); + }); + + it('starts empty', () => { + expect(deque.size()).to.equal(0); + expect(deque.isEmpty()).to.equal(true); + }); + + it('add elements in the back', () => { + deque.addBack(1); + expect(deque.size()).to.equal(1); + + deque.addBack(2); + expect(deque.size()).to.equal(2); + + deque.addBack(3); + expect(deque.size()).to.equal(3); + }); + + it('add elements in the front', () => { + deque.addFront(1); + expect(deque.size()).to.equal(1); + + deque.addFront(2); + expect(deque.size()).to.equal(2); + + deque.addFront(3); + expect(deque.size()).to.equal(3); + + deque.removeFront(); + deque.addFront(4); + expect(deque.size()).to.equal(3); + }); + + it('remove elements from the back', () => { + deque.addBack(1); + deque.addBack(2); + deque.addBack(3); + deque.addFront(0); + + expect(deque.removeBack()).to.equal(3); + expect(deque.removeBack()).to.equal(2); + expect(deque.removeBack()).to.equal(1); + expect(deque.removeBack()).to.equal(0); + expect(deque.removeBack()).to.equal(undefined); + }); + + it('remove elements from the front', () => { + deque.addFront(1); + deque.addBack(2); + deque.addBack(3); + deque.addFront(0); + deque.addFront(-1); + deque.addFront(-2); + + expect(deque.removeFront()).to.equal(-2); + expect(deque.removeFront()).to.equal(-1); + expect(deque.removeFront()).to.equal(0); + expect(deque.removeFront()).to.equal(1); + expect(deque.removeFront()).to.equal(2); + expect(deque.removeFront()).to.equal(3); + expect(deque.removeFront()).to.equal(undefined); + }); + + it('allows to peek at the front element in the deque without removing it', () => { + expect(deque.peekFront()).to.equal(undefined); + + deque.addFront(1); + expect(deque.peekFront()).to.equal(1); + deque.addBack(2); + expect(deque.peekFront()).to.equal(1); + deque.addBack(3); + expect(deque.peekFront()).to.equal(1); + deque.addFront(0); + expect(deque.peekFront()).to.equal(0); + deque.addFront(-1); + expect(deque.peekFront()).to.equal(-1); + deque.addFront(-2); + expect(deque.peekFront()).to.equal(-2); + }); + + it('allows to peek at the last element in the deque without removing it', () => { + expect(deque.peekBack()).to.equal(undefined); + + deque.addFront(1); + expect(deque.peekBack()).to.equal(1); + deque.addBack(2); + expect(deque.peekBack()).to.equal(2); + deque.addBack(3); + expect(deque.peekBack()).to.equal(3); + deque.addFront(0); + expect(deque.peekBack()).to.equal(3); + deque.addFront(-1); + expect(deque.peekBack()).to.equal(3); + deque.addFront(-2); + expect(deque.peekBack()).to.equal(3); + }); + + it('returns the correct size', () => { + expect(deque.size()).to.equal(0); + + deque.addFront(1); + expect(deque.size()).to.equal(1); + deque.addBack(2); + expect(deque.size()).to.equal(2); + deque.addBack(3); + expect(deque.size()).to.equal(3); + deque.addFront(0); + expect(deque.size()).to.equal(4); + deque.addFront(-1); + expect(deque.size()).to.equal(5); + deque.addFront(-2); + expect(deque.size()).to.equal(6); + + deque.clear(); + expect(deque.size()).to.equal(0); + + deque.addFront(1); + deque.addBack(2); + expect(deque.size()).to.equal(2); + + deque.removeFront(); + deque.removeBack(); + expect(deque.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(deque.isEmpty()).to.equal(true); + + deque.addFront(1); + expect(deque.isEmpty()).to.equal(false); + deque.addBack(2); + expect(deque.isEmpty()).to.equal(false); + + deque.clear(); + expect(deque.isEmpty()).to.equal(true); + + deque.addFront(1); + deque.addBack(2); + expect(deque.isEmpty()).to.equal(false); + + deque.removeFront(); + expect(deque.isEmpty()).to.equal(false); + deque.removeBack(); + expect(deque.isEmpty()).to.equal(true); + }); + + it('clears the queue', () => { + deque.clear(); + expect(deque.isEmpty()).to.equal(true); + + deque.addFront(1); + deque.addBack(2); + expect(deque.isEmpty()).to.equal(false); + + deque.clear(); + expect(deque.isEmpty()).to.equal(true); + }); + + it('returns toString primitive types', () => { + expect(deque.toString()).to.equal(''); + + deque.addFront(1); + expect(deque.toString()).to.equal('1'); + + deque.addBack(2); + expect(deque.toString()).to.equal('1,2'); + + deque.clear(); + expect(deque.toString()).to.equal(''); + + const queueString = new Deque(); + queueString.addFront('el1'); + expect(queueString.toString()).to.equal('el1'); + + queueString.addBack('el2'); + expect(queueString.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + class MyObj { + constructor(el1, el2) { + this.el1 = el1; + this.el2 = el2; + } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } + } + const dequeMyObj = new Deque(); + expect(dequeMyObj.toString()).to.equal(''); + + dequeMyObj.addFront(new MyObj(1, 2)); + expect(dequeMyObj.toString()).to.equal('1|2'); + + dequeMyObj.addBack(new MyObj(3, 4)); + expect(dequeMyObj.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/data-structures/dictionary.spec.js b/test/js/data-structures/dictionary.spec.js new file mode 100644 index 00000000..72e78640 --- /dev/null +++ b/test/js/data-structures/dictionary.spec.js @@ -0,0 +1,266 @@ +import 'mocha'; +import { expect } from 'chai'; +import { Dictionary } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('Dictionary', () => { + let dictionary; + beforeEach(() => { + dictionary = new Dictionary(); + }); + it('starts empty', () => { + expect(dictionary.size()).to.equal(0); + expect(dictionary.isEmpty()).to.equal(true); + }); + it('sets undefined and null keys and values', () => { + const dict = new Dictionary(); + expect(dict.set('undefined', undefined)).to.equal(false); + expect(dict.get('undefined')).to.equal(undefined); + expect(dict.set('undefined', 1)).to.equal(true); + expect(dict.get('undefined')).to.equal(1); + expect(dict.set('null', null)).to.equal(false); + expect(dict.get('null')).to.equal(undefined); + expect(dict.set('null', 1)).to.equal(true); + expect(dict.get('null')).to.equal(1); + dict.clear(); + expect(dict.set(undefined, undefined)).to.equal(false); + expect(dict.get(undefined)).to.equal(undefined); + expect(dict.set(undefined, 1)).to.equal(false); + expect(dict.get(undefined)).to.equal(undefined); + expect(dict.set(null, null)).to.equal(false); + expect(dict.get(null)).to.equal(undefined); + expect(dict.set(null, 1)).to.equal(false); + expect(dict.get(null)).to.equal(undefined); + }); + it('sets values with string key', () => { + const dict = new Dictionary(); + const min = 1; + const max = 5; + const size = (max - min) + 1; + for (let i = min; i <= max; i++) { + expect(dict.set(`${i}`, i)).to.equal(true); + } + expect(dict.size()).to.equal(size); + const keys = dict.keys(); + expect(keys.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal((i + 1).toString(10)); + } + dict.set('a', 1); + expect(dict.get('a')).to.equal(1); + }); + it('sets values with number key', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + for (let i = min; i <= max; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + expect(dictionary.size()).to.equal(size); + const keys = dictionary.keys(); + expect(keys.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal(i + 1); + } + }); + it('sets values with object', () => { + const dict = new Dictionary(); + const min = 0; + const max = 5; + const size = max - min; + const myObjList = []; + for (let i = min; i < max; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + for (let i = min; i < max; i++) { + expect(dict.set(myObjList[i], myObjList[i])).to.equal(true); + } + expect(dict.size()).to.equal(size); + for (let i = min; i < max; i++) { + expect(dict.get(myObjList[i])).to.equal(myObjList[i]); + } + const keys = dict.keys(); + expect(keys.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal(myObjList[i]); + } + const values = dict.values(); + expect(values.length).to.equal(size); + for (let i = 0; i < values.length; i++) { + expect(values[i]).to.equal(myObjList[i]); + } + }); + function customToString(key) { + return `####${key.toString()}`; + } + it('sets values with custom toString function', () => { + const dict = new Dictionary(customToString); + const min = 0; + const max = 5; + const size = max - min; + const myObjList = []; + for (let i = min; i < max; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + for (let i = min; i < max; i++) { + expect(dict.set(myObjList[i], myObjList[i])).to.equal(true); + } + expect(dict.size()).to.equal(size); + for (let i = min; i < max; i++) { + expect(dict.get(myObjList[i])).to.equal(myObjList[i]); + } + const keys = dict.keys(); + expect(keys.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal(myObjList[i]); + } + const values = dict.values(); + expect(values.length).to.equal(size); + for (let i = 0; i < values.length; i++) { + expect(values[i]).to.equal(myObjList[i]); + } + }); + it('removes elements', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + for (let i = min; i <= max; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + expect(dictionary.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(dictionary.remove(i)).to.equal(true); + } + // elements do not exist + for (let i = min; i <= max; i++) { + expect(dictionary.remove(i)).to.equal(false); + } + expect(dictionary.isEmpty()).to.equal(true); + }); + it('returns the correct size', () => { + expect(dictionary.size()).to.equal(0); + const max = 5; + for (let i = 1; i < max; i++) { + dictionary.set(i, i); + expect(dictionary.size()).to.equal(i); + } + for (let i = 1; i < max; i++) { + dictionary.remove(i); + expect(dictionary.size()).to.equal(max - i - 1); + } + expect(dictionary.size()).to.equal(0); + expect(dictionary.isEmpty()).to.equal(true); + }); + it('returns if element exists', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + for (let i = min; i <= max; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + expect(dictionary.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(dictionary.hasKey(i)).to.equal(true); + expect(dictionary.remove(i)).to.equal(true); + expect(dictionary.hasKey(i)).to.equal(false); + } + }); + it('returns if it is empty', () => { + expect(dictionary.isEmpty()).to.equal(true); + for (let i = 1; i < 5; i++) { + dictionary.set(i, i); + expect(dictionary.isEmpty()).to.equal(false); + } + for (let i = 1; i < 5; i++) { + dictionary.remove(i); + expect(dictionary.isEmpty()).to.equal(!(i < 4)); + } + expect(dictionary.size()).to.equal(0); + expect(dictionary.isEmpty()).to.equal(true); + }); + it('clears the dictionary', () => { + dictionary.clear(); + expect(dictionary.isEmpty()).to.equal(true); + dictionary.set(1, 1); + dictionary.set(2, 2); + dictionary.clear(); + expect(dictionary.isEmpty()).to.equal(true); + }); + it('returns values, keys and value pairs', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + for (let i = min; i <= max; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + expect(dictionary.size()).to.equal(size); + const keys = dictionary.keys(); + const values = dictionary.values(); + const valuePairs = dictionary.keyValues(); + expect(keys.length).to.equal(size); + expect(values.length).to.equal(size); + expect(valuePairs.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal(i + 1); + expect(values[i]).to.equal(i + 1); + expect(valuePairs[i].key).to.equal(i + 1); + expect(valuePairs[i].value).to.equal(i + 1); + } + }); + it('allows to iterate with forEach', () => { + for (let i = 1; i <= 5; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + dictionary.forEach((k, v) => { + expect(dictionary.hasKey(k)).to.equal(true); + expect(dictionary.get(k)).to.equal(v); + }); + }); + it('allows to iterate with forEach and interrupt', () => { + for (let i = 1; i <= 5; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + const size = dictionary.keys().length; + let index = 1; + dictionary.forEach((k, v) => { + expect(dictionary.hasKey(k)).to.equal(true); + expect(dictionary.get(k)).to.equal(v); + index++; + }); + expect(index).to.equal(size + 1); + index = 1; + dictionary.forEach((k, v) => { + expect(dictionary.hasKey(k)).to.equal(true); + expect(dictionary.get(k)).to.equal(v); + index++; + return !(k % 3 === 0); + }); + expect(index).to.equal(size - 1); + }); + it('returns toString primitive types', () => { + expect(dictionary.toString()).to.equal(''); + dictionary.set(1, 1); + expect(dictionary.toString()).to.equal('[#1: 1]'); + dictionary.set(2, 2); + expect(dictionary.toString()).to.equal('[#1: 1],[#2: 2]'); + dictionary.clear(); + expect(dictionary.toString()).to.equal(''); + }); + it('returns toString primitive types: string', () => { + const dict = new Dictionary(); + dict.set('el1', 1); + expect(dict.toString()).to.equal('[#el1: 1]'); + dict.set('el2', 2); + expect(dict.toString()).to.equal('[#el1: 1],[#el2: 2]'); + }); + it('returns toString objects', () => { + const dict = new Dictionary(); + expect(dict.toString()).to.equal(''); + let myObj = new MyObj(1, 2); + dict.set(myObj, myObj); + expect(dict.toString()).to.equal('[#1|2: 1|2]'); + myObj = new MyObj(3, 4); + dict.set(myObj, myObj); + expect(dict.toString()).to.equal('[#1|2: 1|2],[#3|4: 3|4]'); + }); +}); diff --git a/test/js/data-structures/doubly-linked-list.spec.js b/test/js/data-structures/doubly-linked-list.spec.js new file mode 100644 index 00000000..5e7fd6a4 --- /dev/null +++ b/test/js/data-structures/doubly-linked-list.spec.js @@ -0,0 +1,406 @@ +import 'mocha'; +import { expect } from 'chai'; +import { DoublyLinkedList } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('DoublyLinkedList', () => { + let list; + let min; + let max; + + beforeEach(() => { + list = new DoublyLinkedList(); + min = 1; + max = 3; + }); + + function pushesElements() { + for (let i = min; i <= max; i++) { + list.push(i); + } + } + + function verifyNode(current, i) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(i); + + // verify next node + if (i < max) { + expect(current.next).to.not.be.an('undefined'); + // TS strictNullChecks + if (current.next) { + expect(current.next.element).to.equal(i + 1); + } + } else { + expect(current.next).to.be.an('undefined'); + } + + // verify previous node + if (i > min) { + expect(current.prev).to.not.be.an('undefined'); + if (current.prev) { + expect(current.prev.element).to.equal(i - 1); + } + } else { + expect(current.prev).to.be.an('undefined'); + } + } + + function verifyListFromTail() { + let current = list.getTail(); + for (let i = max; i >= min; i--) { + expect(current).to.not.be.an('undefined'); + // TS strictNullChecks + if (current) { + verifyNode(current, i); + current = current.prev; + } + } + } + + function verifyList() { + let current = list.getHead(); + for (let i = min; i <= max; i++) { + expect(current).to.not.be.an('undefined'); + // TS strictNullChecks + if (current) { + verifyNode(current, i); + current = current.next; + } + } + verifyListFromTail(); + } + + it('starts empty', () => { + expect(list.size()).to.equal(0); + expect(list.isEmpty()).to.equal(true); + expect(list.getHead()).to.be.an('undefined'); + expect(list.getTail()).to.be.an('undefined'); + }); + + it('pushes elements', () => { + pushesElements(); + verifyList(); + }); + + it('returns element at specific index: invalid position', () => { + // list is empty + expect(list.getElementAt(3)).to.be.an('undefined'); + }); + + it('returns element at specific index', () => { + let node; + + pushesElements(); + + for (let i = min; i <= max; i++) { + node = list.getElementAt(i - 1); + expect(node).to.not.be.an('undefined'); + if (node) { + expect(node.element).to.equal(i); + } + } + }); + + it('inserts elements first position empty list', () => { + const element = 1; + max = element; + expect(list.insert(element, 0)).to.equal(true); + verifyList(); + }); + + it('inserts elements first position not empty list', () => { + max = 2; + expect(list.insert(max, 0)).to.equal(true); + + expect(list.insert(min, 0)).to.equal(true); + + verifyList(); + }); + + it('inserts elements invalid position empty list', () => { + expect(list.insert(1, 1)).to.equal(false); + }); + + it('inserts elements invalid position not empty list', () => { + const element = 1; + expect(list.insert(element, 0)).to.equal(true); + expect(list.insert(element, 2)).to.equal(false); + }); + + it('inserts elements at the end of list', () => { + max = 5; + + for (let i = min; i <= max; i++) { + expect(list.insert(i, i - 1)).to.equal(true); + } + + verifyList(); + }); + + it('inserts elements in the middle of list', () => { + expect(list.insert(3, 0)).to.equal(true); + expect(list.insert(1, 0)).to.equal(true); + expect(list.insert(2, 1)).to.equal(true); + verifyList(); + }); + + it('returns index of elements', () => { + let index; + + pushesElements(); + + for (let i = min; i <= max; i++) { + index = list.indexOf(i); + expect(index).to.equal(i - 1); + } + + expect(list.indexOf(max + 2)).to.equal(-1); + }); + + it('removes invalid elements', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.remove(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes valid elements', () => { + let element; + + pushesElements(); + + for (let i = min; i <= max; i++) { + element = list.remove(i); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + } + }); + + it('removes element invalid position empty list', () => { + let element; + + for (let i = min; i <= max; i++) { + element = list.removeAt(i - 1); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position not empty list', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.removeAt(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes first element list single element', () => { + const value = 1; + list.push(value); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(value); + + expect(list.getHead()).to.be.an('undefined'); + expect(list.getTail()).to.be.an('undefined'); + expect(list.isEmpty()).to.equal(true); + }); + + it('removes first element list multiple elements', () => { + pushesElements(); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(min); + + min = 2; + verifyList(); + }); + + it('removes element from end of list', () => { + let element; + + pushesElements(); + + const maxIndex = max; + for (let i = maxIndex; i >= min; i--) { + element = list.removeAt(i - 1); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + max--; + verifyList(); + } + }); + + it('removes element from middle of list', () => { + pushesElements(); // 1, 2, 3 + + const element = list.removeAt(1); // element 2 + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(2); + + // list needs to be [1, 3] + let current = list.getHead(); + + // element 1 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(1); + expect(current.prev).to.be.an('undefined'); + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + expect(current.next.element).to.equal(3); + current = current.next; + } + } + + // element 3 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(3); + expect(current.next).to.be.an('undefined'); + expect(current.prev).to.not.be.an('undefined'); + if (current.prev) { + expect(current.prev.element).to.equal(1); + } + } + }); + + it('returns the head of the list', () => { + expect(list.getHead()).to.be.an('undefined'); + + list.push(1); + expect(list.getHead()).to.not.be.an('undefined'); + }); + + it('returns the tail of the list', () => { + expect(list.getTail()).to.be.an('undefined'); + + list.push(1); + expect(list.getTail()).to.not.be.an('undefined'); + }); + + it('returns the correct size', () => { + expect(list.size()).to.equal(0); + + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.size()).to.equal(i); + } + + const size = max; + for (let i = min; i <= max; i++) { + list.remove(i); + expect(list.size()).to.equal(size - i); + } + + expect(list.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(list.isEmpty()).to.equal(true); + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.isEmpty()).to.equal(false); + } + + for (let i = min; i < max; i++) { + list.remove(i); + expect(list.isEmpty()).to.equal(false); + } + list.remove(max); + expect(list.isEmpty()).to.equal(true); + + pushesElements(); + expect(list.isEmpty()).to.equal(false); + + list.clear(); + expect(list.isEmpty()).to.equal(true); + }); + + it('clears the list', () => { + expect(list.size()).to.equal(0); + list.clear(); + expect(list.size()).to.equal(0); + pushesElements(); + expect(list.size()).to.greaterThan(0); + list.clear(); + expect(list.size()).to.equal(0); + }); + + it('returns toString primitive types', () => { + expect(list.toString()).to.equal(''); + + list.push(1); + expect(list.toString()).to.equal('1'); + + list.push(2); + expect(list.toString()).to.equal('1,2'); + + list.clear(); + expect(list.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const ds = new DoublyLinkedList(); + ds.push('el1'); + expect(ds.toString()).to.equal('el1'); + + ds.push('el2'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const ds = new DoublyLinkedList(); + expect(ds.toString()).to.equal(''); + + ds.push(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2'); + + ds.push(new MyObj(3, 4)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); + + it('returns inverseToString primitive types', () => { + expect(list.inverseToString()).to.equal(''); + + list.push(1); + expect(list.inverseToString()).to.equal('1'); + + list.push(2); + expect(list.inverseToString()).to.equal('2,1'); + + list.clear(); + expect(list.inverseToString()).to.equal(''); + }); + + it('returns inverseToString primitive types: string', () => { + const ds = new DoublyLinkedList(); + ds.push('el1'); + expect(ds.inverseToString()).to.equal('el1'); + + ds.push('el2'); + expect(ds.inverseToString()).to.equal('el2,el1'); + }); + + it('returns inverseToString objects', () => { + const ds = new DoublyLinkedList(); + expect(ds.inverseToString()).to.equal(''); + + ds.push(new MyObj(1, 2)); + expect(ds.inverseToString()).to.equal('1|2'); + + ds.push(new MyObj(3, 4)); + expect(ds.inverseToString()).to.equal('3|4,1|2'); + }); +}); diff --git a/test/js/data-structures/hash-table-linear-probing-lazy.spec.js b/test/js/data-structures/hash-table-linear-probing-lazy.spec.js new file mode 100644 index 00000000..f7dd7e06 --- /dev/null +++ b/test/js/data-structures/hash-table-linear-probing-lazy.spec.js @@ -0,0 +1,300 @@ +import 'mocha'; +import { expect } from 'chai'; +import { HashTableLinearProbingLazy } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('HashTableLinearProbingLazy', () => { + const A = 'Jonathan'; + const B = 'Jamie'; + const C = 'Sue'; + it('starts empty', () => { + const hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.size()).to.equal(0); + expect(hashTable.isEmpty()).to.equal(true); + }); + it('generates hashcode', () => { + // numbers + let hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.hashCode(1)).to.equal(1); + expect(hashTable.hashCode(10)).to.equal(10); + expect(hashTable.hashCode(100)).to.equal(100); + expect(hashTable.hashCode(1000)).to.equal(1000); + // strings + hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.hashCode('1')).to.equal(12); + expect(hashTable.hashCode('10')).to.equal(23); + expect(hashTable.hashCode('100')).to.equal(34); + expect(hashTable.hashCode('1000')).to.equal(8); + expect(hashTable.hashCode('a')).to.equal(23); + expect(hashTable.hashCode('A')).to.equal(28); + expect(hashTable.hashCode('Aba')).to.equal(1); + // objects + hashTable = new HashTableLinearProbingLazy(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + expect(hashTable.hashCode(myObjList[0])).to.equal(1); + expect(hashTable.hashCode(myObjList[1])).to.equal(3); + expect(hashTable.hashCode(myObjList[2])).to.equal(5); + expect(hashTable.hashCode(myObjList[3])).to.equal(7); + expect(hashTable.hashCode(myObjList[4])).to.equal(9); + }); + it('puts undefined and null keys and values', () => { + const hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.put('undefined', undefined)).to.equal(false); + expect(hashTable.get('undefined')).to.equal(undefined); + expect(hashTable.put('undefined', 1)).to.equal(true); + expect(hashTable.get('undefined')).to.equal(1); + expect(hashTable.put('null', null)).to.equal(false); + expect(hashTable.get('null')).to.equal(undefined); + expect(hashTable.put('null', 1)).to.equal(true); + expect(hashTable.get('null')).to.equal(1); + hashTable.clear(); + expect(hashTable.put(undefined, undefined)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + expect(hashTable.put(undefined, 1)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + expect(hashTable.put(null, null)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + expect(hashTable.put(null, 1)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + }); + it('puts values with number key without collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableLinearProbingLazy(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + } + }); + it('puts values with string key without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.put('1', 1)).to.equal(true); + expect(hashTable.put('10', 10)).to.equal(true); + expect(hashTable.put('100', 100)).to.equal(true); + expect(hashTable.put('1000', 1000)).to.equal(true); + const table = hashTable.getTable(); + expect(table[12].key).to.equal('1'); + expect(table[12].value).to.equal(1); + expect(table[23].key).to.equal('10'); + expect(table[23].value).to.equal(10); + expect(table[34].key).to.equal('100'); + expect(table[34].value).to.equal(100); + expect(table[8].key).to.equal('1000'); + expect(table[8].value).to.equal(1000); + }); + it('puts values with object key without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + expect(hashTable.put(myObjList[i - 1], myObjList[i - 1])).to.equal(true); + } + const table = hashTable.getTable(); + expect(table[1].key).to.equal(myObjList[0]); + expect(table[1].value).to.equal(myObjList[0]); + expect(table[3].key).to.equal(myObjList[1]); + expect(table[3].value).to.equal(myObjList[1]); + expect(table[5].key).to.equal(myObjList[2]); + expect(table[5].value).to.equal(myObjList[2]); + expect(table[7].key).to.equal(myObjList[3]); + expect(table[7].value).to.equal(myObjList[3]); + expect(table[9].key).to.equal(myObjList[4]); + expect(table[9].value).to.equal(myObjList[4]); + }); + function addValuesCollision() { + const hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.size()).to.equal(3); + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + expect(hashTable.size()).to.equal(3); + return hashTable; + } + it('puts values with collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableLinearProbingLazy(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 10)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 2); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 100)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 3); + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + expect(table[i + size].key).to.equal(i); + expect(table[i + size].value).to.equal(i + 10); + expect(table[i + (size * 2)].key).to.equal(i); + expect(table[i + (size * 2)].value).to.equal(i + 100); + } + addValuesCollision(); + }); + it('removes elements without collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableLinearProbingLazy(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(true); + } + // elements do not exist + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(false); + } + expect(hashTable.isEmpty()).to.equal(true); + }); + function removeWithCollision(a, b, c) { + const hashTable = addValuesCollision(); + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + expect(hashTable.isEmpty()).to.equal(true); + } + it('removes elements with collisions: scenario 1', () => { + // test all possibilities for removal + removeWithCollision(A, B, C); + removeWithCollision(A, C, B); + removeWithCollision(B, A, C); + removeWithCollision(B, C, A); + removeWithCollision(C, A, B); + removeWithCollision(C, B, A); + }); + function addValuesCollision2() { + const hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.put(')', 'parenthesis@email.com')).to.equal(true); + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put('+', 'plus@email.com')).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(',', 'comma@email.com')).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.put('-', 'minus@email.com')).to.equal(true); + expect(hashTable.put('0', 'zero@email.com')).to.equal(true); + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + expect(hashTable.hashCode(')')).to.equal(4); + expect(hashTable.hashCode('+')).to.equal(6); + expect(hashTable.hashCode(',')).to.equal(7); + expect(hashTable.hashCode('-')).to.equal(8); + expect(hashTable.hashCode('0')).to.equal(11); + expect(hashTable.size()).to.equal(8); + const table = hashTable.getTable(); + expect(table[4].key).to.equal(')'); + expect(table[5].key).to.equal(A); + expect(table[6].key).to.equal('+'); + expect(table[7].key).to.equal(B); + expect(table[8].key).to.equal(','); + expect(table[9].key).to.equal(C); + expect(table[10].key).to.equal('-'); + expect(table[11].key).to.equal('0'); + return hashTable; + } + function verifyOtherKeys(hashTable) { + expect(hashTable.get(')')).to.not.equal(undefined); + expect(hashTable.get('+')).to.not.equal(undefined); + expect(hashTable.get(',')).to.not.equal(undefined); + expect(hashTable.get('-')).to.not.equal(undefined); + expect(hashTable.get('0')).to.not.equal(undefined); + } + function removeWithCollision2(a, b, c) { + const hashTable = addValuesCollision2(); + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + verifyOtherKeys(hashTable); + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + verifyOtherKeys(hashTable); + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + verifyOtherKeys(hashTable); + } + it('removes elements with collisions: scenario 2', () => { + // test all possibilities for removal + removeWithCollision2(A, B, C); + removeWithCollision2(A, C, B); + removeWithCollision2(B, A, C); + removeWithCollision2(B, C, A); + removeWithCollision2(C, A, B); + removeWithCollision2(C, B, A); + }); + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.toString()).to.equal(''); + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + hashTable.put('el1', 1); + expect(hashTable.toString()).to.equal('{36 => [#el1: 1]}'); + hashTable.put('el2', 2); + expect(hashTable.toString()).to.equal('{0 => [#el2: 2]},{36 => [#el1: 1]}'); + }); + it('returns toString objects without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + let myObj = new MyObj(1, 2); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]}'); + myObj = new MyObj(3, 4); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]},{5 => [#3|4: 3|4]}'); + }); + it('returns toString with collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.toString()).to.equal(''); + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + hashTable.put(1, 10); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]},{3 => [#1: 10]}'); + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); +}); diff --git a/test/js/data-structures/hash-table-linear-probing.spec.js b/test/js/data-structures/hash-table-linear-probing.spec.js new file mode 100644 index 00000000..bfb519f3 --- /dev/null +++ b/test/js/data-structures/hash-table-linear-probing.spec.js @@ -0,0 +1,300 @@ +import 'mocha'; +import { expect } from 'chai'; +import { HashTableLinearProbing } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('HashTableLinearProbing', () => { + const A = 'Jonathan'; + const B = 'Jamie'; + const C = 'Sue'; + it('starts empty', () => { + const hashTable = new HashTableLinearProbing(); + expect(hashTable.size()).to.equal(0); + expect(hashTable.isEmpty()).to.equal(true); + }); + it('generates hashcode', () => { + // numbers + let hashTable = new HashTableLinearProbing(); + expect(hashTable.hashCode(1)).to.equal(1); + expect(hashTable.hashCode(10)).to.equal(10); + expect(hashTable.hashCode(100)).to.equal(100); + expect(hashTable.hashCode(1000)).to.equal(1000); + // strings + hashTable = new HashTableLinearProbing(); + expect(hashTable.hashCode('1')).to.equal(12); + expect(hashTable.hashCode('10')).to.equal(23); + expect(hashTable.hashCode('100')).to.equal(34); + expect(hashTable.hashCode('1000')).to.equal(8); + expect(hashTable.hashCode('a')).to.equal(23); + expect(hashTable.hashCode('A')).to.equal(28); + expect(hashTable.hashCode('Aba')).to.equal(1); + // objects + hashTable = new HashTableLinearProbing(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + expect(hashTable.hashCode(myObjList[0])).to.equal(1); + expect(hashTable.hashCode(myObjList[1])).to.equal(3); + expect(hashTable.hashCode(myObjList[2])).to.equal(5); + expect(hashTable.hashCode(myObjList[3])).to.equal(7); + expect(hashTable.hashCode(myObjList[4])).to.equal(9); + }); + it('puts undefined and null keys and values', () => { + const hashTable = new HashTableLinearProbing(); + expect(hashTable.put('undefined', undefined)).to.equal(false); + expect(hashTable.get('undefined')).to.equal(undefined); + expect(hashTable.put('undefined', 1)).to.equal(true); + expect(hashTable.get('undefined')).to.equal(1); + expect(hashTable.put('null', null)).to.equal(false); + expect(hashTable.get('null')).to.equal(undefined); + expect(hashTable.put('null', 1)).to.equal(true); + expect(hashTable.get('null')).to.equal(1); + hashTable.clear(); + expect(hashTable.put(undefined, undefined)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + expect(hashTable.put(undefined, 1)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + expect(hashTable.put(null, null)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + expect(hashTable.put(null, 1)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + }); + it('puts values with number key without collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableLinearProbing(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + } + }); + it('puts values with string key without collisions', () => { + const hashTable = new HashTableLinearProbing(); + expect(hashTable.put('1', 1)).to.equal(true); + expect(hashTable.put('10', 10)).to.equal(true); + expect(hashTable.put('100', 100)).to.equal(true); + expect(hashTable.put('1000', 1000)).to.equal(true); + const table = hashTable.getTable(); + expect(table[12].key).to.equal('1'); + expect(table[12].value).to.equal(1); + expect(table[23].key).to.equal('10'); + expect(table[23].value).to.equal(10); + expect(table[34].key).to.equal('100'); + expect(table[34].value).to.equal(100); + expect(table[8].key).to.equal('1000'); + expect(table[8].value).to.equal(1000); + }); + it('puts values with object key without collisions', () => { + const hashTable = new HashTableLinearProbing(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + expect(hashTable.put(myObjList[i - 1], myObjList[i - 1])).to.equal(true); + } + const table = hashTable.getTable(); + expect(table[1].key).to.equal(myObjList[0]); + expect(table[1].value).to.equal(myObjList[0]); + expect(table[3].key).to.equal(myObjList[1]); + expect(table[3].value).to.equal(myObjList[1]); + expect(table[5].key).to.equal(myObjList[2]); + expect(table[5].value).to.equal(myObjList[2]); + expect(table[7].key).to.equal(myObjList[3]); + expect(table[7].value).to.equal(myObjList[3]); + expect(table[9].key).to.equal(myObjList[4]); + expect(table[9].value).to.equal(myObjList[4]); + }); + function addValuesCollision() { + const hashTable = new HashTableLinearProbing(); + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.size()).to.equal(3); + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + expect(hashTable.size()).to.equal(3); + return hashTable; + } + it('puts values with collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableLinearProbing(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 10)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 2); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 100)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 3); + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + expect(table[i + size].key).to.equal(i); + expect(table[i + size].value).to.equal(i + 10); + expect(table[i + (size * 2)].key).to.equal(i); + expect(table[i + (size * 2)].value).to.equal(i + 100); + } + addValuesCollision(); + }); + it('removes elements without collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableLinearProbing(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(true); + } + // elements do not exist + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(false); + } + expect(hashTable.isEmpty()).to.equal(true); + }); + function removeWithCollision(a, b, c) { + const hashTable = addValuesCollision(); + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + expect(hashTable.isEmpty()).to.equal(true); + } + it('removes elements with collisions: scenario 1', () => { + // test all possibilities for removal + removeWithCollision(A, B, C); + removeWithCollision(A, C, B); + removeWithCollision(B, A, C); + removeWithCollision(B, C, A); + removeWithCollision(C, A, B); + removeWithCollision(C, B, A); + }); + function addValuesCollision2() { + const hashTable = new HashTableLinearProbing(); + expect(hashTable.put(')', 'parenthesis@email.com')).to.equal(true); + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put('+', 'plus@email.com')).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(',', 'comma@email.com')).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.put('-', 'minus@email.com')).to.equal(true); + expect(hashTable.put('0', 'zero@email.com')).to.equal(true); + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + expect(hashTable.hashCode(')')).to.equal(4); + expect(hashTable.hashCode('+')).to.equal(6); + expect(hashTable.hashCode(',')).to.equal(7); + expect(hashTable.hashCode('-')).to.equal(8); + expect(hashTable.hashCode('0')).to.equal(11); + expect(hashTable.size()).to.equal(8); + const table = hashTable.getTable(); + expect(table[4].key).to.equal(')'); + expect(table[5].key).to.equal(A); + expect(table[6].key).to.equal('+'); + expect(table[7].key).to.equal(B); + expect(table[8].key).to.equal(','); + expect(table[9].key).to.equal(C); + expect(table[10].key).to.equal('-'); + expect(table[11].key).to.equal('0'); + return hashTable; + } + function verifyOtherKeys(hashTable) { + expect(hashTable.get(')')).to.not.equal(undefined); + expect(hashTable.get('+')).to.not.equal(undefined); + expect(hashTable.get(',')).to.not.equal(undefined); + expect(hashTable.get('-')).to.not.equal(undefined); + expect(hashTable.get('0')).to.not.equal(undefined); + } + function removeWithCollision2(a, b, c) { + const hashTable = addValuesCollision2(); + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + verifyOtherKeys(hashTable); + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + verifyOtherKeys(hashTable); + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + verifyOtherKeys(hashTable); + } + it('removes elements with collisions: scenario 2', () => { + // test all possibilities for removal + removeWithCollision2(A, B, C); + removeWithCollision2(A, C, B); + removeWithCollision2(B, A, C); + removeWithCollision2(B, C, A); + removeWithCollision2(C, A, B); + removeWithCollision2(C, B, A); + }); + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableLinearProbing(); + expect(hashTable.toString()).to.equal(''); + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableLinearProbing(); + hashTable.put('el1', 1); + expect(hashTable.toString()).to.equal('{36 => [#el1: 1]}'); + hashTable.put('el2', 2); + expect(hashTable.toString()).to.equal('{0 => [#el2: 2]},{36 => [#el1: 1]}'); + }); + it('returns toString objects without collisions', () => { + const hashTable = new HashTableLinearProbing(); + let myObj = new MyObj(1, 2); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]}'); + myObj = new MyObj(3, 4); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]},{5 => [#3|4: 3|4]}'); + }); + it('returns toString with collisions', () => { + const hashTable = new HashTableLinearProbing(); + expect(hashTable.toString()).to.equal(''); + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + hashTable.put(1, 10); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]},{3 => [#1: 10]}'); + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); +}); diff --git a/test/js/data-structures/hash-table-separate-chaining.spec.js b/test/js/data-structures/hash-table-separate-chaining.spec.js new file mode 100644 index 00000000..2066c2d0 --- /dev/null +++ b/test/js/data-structures/hash-table-separate-chaining.spec.js @@ -0,0 +1,269 @@ +import 'mocha'; +import { expect } from 'chai'; +import { HashTableSeparateChaining } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('HashTableSeparateChaining', () => { + const A = 'Jonathan'; + const B = 'Jamie'; + const C = 'Sue'; + it('starts empty', () => { + const hashTable = new HashTableSeparateChaining(); + expect(hashTable.size()).to.equal(0); + expect(hashTable.isEmpty()).to.equal(true); + }); + it('generates hashcode', () => { + // numbers + let hashTable = new HashTableSeparateChaining(); + expect(hashTable.hashCode(1)).to.equal(1); + expect(hashTable.hashCode(10)).to.equal(10); + expect(hashTable.hashCode(100)).to.equal(100); + expect(hashTable.hashCode(1000)).to.equal(1000); + // strings + hashTable = new HashTableSeparateChaining(); + expect(hashTable.hashCode('1')).to.equal(12); + expect(hashTable.hashCode('10')).to.equal(23); + expect(hashTable.hashCode('100')).to.equal(34); + expect(hashTable.hashCode('1000')).to.equal(8); + expect(hashTable.hashCode('a')).to.equal(23); + expect(hashTable.hashCode('A')).to.equal(28); + expect(hashTable.hashCode('Aba')).to.equal(1); + // objects + hashTable = new HashTableSeparateChaining(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + expect(hashTable.hashCode(myObjList[0])).to.equal(1); + expect(hashTable.hashCode(myObjList[1])).to.equal(3); + expect(hashTable.hashCode(myObjList[2])).to.equal(5); + expect(hashTable.hashCode(myObjList[3])).to.equal(7); + expect(hashTable.hashCode(myObjList[4])).to.equal(9); + }); + it('puts undefined and null keys and values', () => { + const hashTable = new HashTableSeparateChaining(); + expect(hashTable.put('undefined', undefined)).to.equal(false); + expect(hashTable.get('undefined')).to.equal(undefined); + expect(hashTable.put('undefined', 1)).to.equal(true); + expect(hashTable.get('undefined')).to.equal(1); + expect(hashTable.put('null', null)).to.equal(false); + expect(hashTable.get('null')).to.equal(undefined); + expect(hashTable.put('null', 1)).to.equal(true); + expect(hashTable.get('null')).to.equal(1); + hashTable.clear(); + expect(hashTable.put(undefined, undefined)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + expect(hashTable.put(undefined, 1)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + expect(hashTable.put(null, null)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + expect(hashTable.put(null, 1)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + }); + it('puts values with number key without collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableSeparateChaining(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + const linkedList = table[i]; + expect(linkedList.size()).to.equal(1); + const valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(i); + expect(valuePair.element.value).to.equal(i); + } + }); + it('puts values with string key without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + expect(hashTable.put('1', 1)).to.equal(true); + expect(hashTable.put('10', 10)).to.equal(true); + expect(hashTable.put('100', 100)).to.equal(true); + expect(hashTable.put('1000', 1000)).to.equal(true); + const table = hashTable.getTable(); + let linkedList = table[12]; + expect(linkedList.size()).to.equal(1); + let valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal('1'); + expect(valuePair.element.value).to.equal(1); + linkedList = table[23]; // eslint-disable-line prefer-destructuring + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal('10'); + expect(valuePair.element.value).to.equal(10); + linkedList = table[34]; // eslint-disable-line prefer-destructuring + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal('100'); + expect(valuePair.element.value).to.equal(100); + linkedList = table[8]; // eslint-disable-line prefer-destructuring + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal('1000'); + expect(valuePair.element.value).to.equal(1000); + }); + it('puts values with object key without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + expect(hashTable.put(myObjList[i - 1], myObjList[i - 1])).to.equal(true); + } + const table = hashTable.getTable(); + let linkedList = table[1]; + expect(linkedList.size()).to.equal(1); + let valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[0]); + expect(valuePair.element.value).to.equal(myObjList[0]); + linkedList = table[3]; // eslint-disable-line prefer-destructuring + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[1]); + expect(valuePair.element.value).to.equal(myObjList[1]); + linkedList = table[5]; // eslint-disable-line prefer-destructuring + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[2]); + expect(valuePair.element.value).to.equal(myObjList[2]); + linkedList = table[7]; // eslint-disable-line prefer-destructuring + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[3]); + expect(valuePair.element.value).to.equal(myObjList[3]); + linkedList = table[9]; // eslint-disable-line prefer-destructuring + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[4]); + expect(valuePair.element.value).to.equal(myObjList[4]); + }); + it('puts values with collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableSeparateChaining(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 10)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 2); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 100)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 3); + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + const linkedList = table[i]; + expect(linkedList.size()).to.equal(3); + let valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(i); + expect(valuePair.element.value).to.equal(i); + valuePair = valuePair.next; + expect(valuePair.element.key).to.equal(i); + expect(valuePair.element.value).to.equal(i + 10); + valuePair = valuePair.next; + expect(valuePair.element.key).to.equal(i); + expect(valuePair.element.value).to.equal(i + 100); + } + }); + it('removes elements without collisions', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTableSeparateChaining(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(true); + } + // elements do not exist + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(false); + } + expect(hashTable.isEmpty()).to.equal(true); + }); + function addValuesCollision() { + const hashTable = new HashTableSeparateChaining(); + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.size()).to.equal(3); + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + expect(hashTable.getTable()[expectedHash].size()).to.equal(3); + return hashTable; + } + function removeWithCollision(a, b, c) { + const hashTable = addValuesCollision(); + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + expect(hashTable.isEmpty()).to.equal(true); + } + it('removes elements with collisions', () => { + // test all possibilities for removal + removeWithCollision(A, B, C); + removeWithCollision(A, C, B); + removeWithCollision(B, A, C); + removeWithCollision(B, C, A); + removeWithCollision(C, A, B); + removeWithCollision(C, B, A); + }); + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + expect(hashTable.toString()).to.equal(''); + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + hashTable.put('el1', 1); + expect(hashTable.toString()).to.equal('{36 => [#el1: 1]}'); + hashTable.put('el2', 2); + expect(hashTable.toString()).to.equal('{0 => [#el2: 2]},{36 => [#el1: 1]}'); + }); + it('returns toString objects without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + let myObj = new MyObj(1, 2); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]}'); + myObj = new MyObj(3, 4); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]},{5 => [#3|4: 3|4]}'); + }); + it('returns toString with collisions', () => { + const hashTable = new HashTableSeparateChaining(); + expect(hashTable.toString()).to.equal(''); + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + hashTable.put(1, 10); + expect(hashTable.toString()).to.equal('{1 => [#1: 1],[#1: 10]},{2 => [#2: 2]}'); + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); +}); diff --git a/test/js/data-structures/hash-table.spec.js b/test/js/data-structures/hash-table.spec.js new file mode 100644 index 00000000..862c9be0 --- /dev/null +++ b/test/js/data-structures/hash-table.spec.js @@ -0,0 +1,161 @@ +import 'mocha'; +import { expect } from 'chai'; +import { HashTable } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('HashTable', () => { + it('starts empty', () => { + const hashTable = new HashTable(); + expect(hashTable.size()).to.equal(0); + expect(hashTable.isEmpty()).to.equal(true); + }); + it('generates hashcode', () => { + // numbers + let hashTable = new HashTable(); + expect(hashTable.hashCode(1)).to.equal(1); + expect(hashTable.hashCode(10)).to.equal(10); + expect(hashTable.hashCode(100)).to.equal(100); + expect(hashTable.hashCode(1000)).to.equal(1000); + // strings + hashTable = new HashTable(); + expect(hashTable.hashCode('1')).to.equal(12); + expect(hashTable.hashCode('10')).to.equal(23); + expect(hashTable.hashCode('100')).to.equal(34); + expect(hashTable.hashCode('1000')).to.equal(8); + expect(hashTable.hashCode('a')).to.equal(23); + expect(hashTable.hashCode('A')).to.equal(28); + expect(hashTable.hashCode('Aba')).to.equal(1); + // objects + hashTable = new HashTable(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + expect(hashTable.hashCode(myObjList[0])).to.equal(1); + expect(hashTable.hashCode(myObjList[1])).to.equal(3); + expect(hashTable.hashCode(myObjList[2])).to.equal(5); + expect(hashTable.hashCode(myObjList[3])).to.equal(7); + expect(hashTable.hashCode(myObjList[4])).to.equal(9); + }); + it('puts undefined and null keys and values', () => { + const hashTable = new HashTable(); + expect(hashTable.put('undefined', undefined)).to.equal(false); + expect(hashTable.get('undefined')).to.equal(undefined); + expect(hashTable.put('undefined', 1)).to.equal(true); + expect(hashTable.get('undefined')).to.equal(1); + expect(hashTable.put('null', null)).to.equal(false); + expect(hashTable.get('null')).to.equal(undefined); + expect(hashTable.put('null', 1)).to.equal(true); + expect(hashTable.get('null')).to.equal(1); + hashTable.clear(); + expect(hashTable.put(undefined, undefined)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + expect(hashTable.put(undefined, 1)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + expect(hashTable.put(null, null)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + expect(hashTable.put(null, 1)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + }); + it('puts values with number key', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTable(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + } + }); + it('puts values with string key', () => { + const hashTable = new HashTable(); + expect(hashTable.put('1', 1)).to.equal(true); + expect(hashTable.put('10', 10)).to.equal(true); + expect(hashTable.put('100', 100)).to.equal(true); + expect(hashTable.put('1000', 1000)).to.equal(true); + const table = hashTable.getTable(); + expect(table[12].key).to.equal('1'); + expect(table[12].value).to.equal(1); + expect(table[23].key).to.equal('10'); + expect(table[23].value).to.equal(10); + expect(table[34].key).to.equal('100'); + expect(table[34].value).to.equal(100); + expect(table[8].key).to.equal('1000'); + expect(table[8].value).to.equal(1000); + }); + it('puts values with object key', () => { + const hashTable = new HashTable(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + expect(hashTable.put(myObjList[i - 1], myObjList[i - 1])).to.equal(true); + } + const table = hashTable.getTable(); + expect(table[1].key).to.equal(myObjList[0]); + expect(table[1].value).to.equal(myObjList[0]); + expect(table[3].key).to.equal(myObjList[1]); + expect(table[3].value).to.equal(myObjList[1]); + expect(table[5].key).to.equal(myObjList[2]); + expect(table[5].value).to.equal(myObjList[2]); + expect(table[7].key).to.equal(myObjList[3]); + expect(table[7].value).to.equal(myObjList[3]); + expect(table[9].key).to.equal(myObjList[4]); + expect(table[9].value).to.equal(myObjList[4]); + }); + it('does NOT handle collision, replaces values', () => { + const hashTable = new HashTable(); + for (let i = 0; i < 5; i++) { + expect(hashTable.put(1, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(1); + }); + it('removes elements', () => { + const min = 1; + const max = 5; + const size = (max - min) + 1; + const hashTable = new HashTable(); + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(true); + } + // elements do not exist + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(false); + } + expect(hashTable.isEmpty()).to.equal(true); + }); + it('returns toString primitive types', () => { + const hashTable = new HashTable(); + expect(hashTable.toString()).to.equal(''); + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); + it('returns toString primitive types', () => { + const hashTable = new HashTable(); + hashTable.put('el1', 1); + expect(hashTable.toString()).to.equal('{36 => [#el1: 1]}'); + hashTable.put('el2', 2); + expect(hashTable.toString()).to.equal('{0 => [#el2: 2]},{36 => [#el1: 1]}'); + }); + it('returns toString objects', () => { + const hashTable = new HashTable(); + let myObj = new MyObj(1, 2); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]}'); + myObj = new MyObj(3, 4); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]},{5 => [#3|4: 3|4]}'); + }); +}); diff --git a/test/js/data-structures/linked-list.spec.js b/test/js/data-structures/linked-list.spec.js new file mode 100644 index 00000000..4d9f19c6 --- /dev/null +++ b/test/js/data-structures/linked-list.spec.js @@ -0,0 +1,330 @@ +import 'mocha'; +import { expect } from 'chai'; +import { LinkedList, util } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('LinkedList', () => { + let list; + let min; + let max; + + beforeEach(() => { + list = new LinkedList(util.defaultEquals); + min = 1; + max = 3; + }); + + function pushesElements() { + for (let i = min; i <= max; i++) { + list.push(i); + } + } + + function verifyList() { + let current = list.getHead(); + for (let i = min; i <= max && current; i++) { + expect(current).to.not.be.an('undefined'); + if (current) { + // TS strictNullChecks + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(i); + if (i < max) { + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + // TS strictNullChecks + expect(current.next.element).to.equal(i + 1); + } + } else { + expect(current.next).to.be.an('undefined'); + } + current = current.next; + } + } + } + + it('starts empty', () => { + expect(list.size()).to.equal(0); + expect(list.isEmpty()).to.equal(true); + expect(list.getHead()).to.be.an('undefined'); + }); + + it('pushes elements', () => { + pushesElements(); + verifyList(); + }); + + it('returns element at specific index: invalid position', () => { + // list is empty + expect(list.getElementAt(3)).to.be.an('undefined'); + }); + + it('returns element at specific index', () => { + let node; + + pushesElements(); + + for (let i = min; i <= max; i++) { + node = list.getElementAt(i - 1); + expect(node).to.not.be.an('undefined'); + if (node) { + expect(node.element).to.equal(i); + } + } + }); + + it('inserts elements first position empty list', () => { + const element = 1; + max = element; + expect(list.insert(element, 0)).to.equal(true); + verifyList(); + }); + + it('inserts elements first position not empty list', () => { + max = 2; + expect(list.insert(max, 0)).to.equal(true); + + expect(list.insert(min, 0)).to.equal(true); + + verifyList(); + }); + + it('inserts elements invalid position empty list', () => { + expect(list.insert(1, 1)).to.equal(false); + }); + + it('inserts elements invalid position not empty list', () => { + const element = 1; + expect(list.insert(element, 0)).to.equal(true); + expect(list.insert(element, 2)).to.equal(false); + }); + + it('inserts elements in the middle of list', () => { + expect(list.insert(3, 0)).to.equal(true); + expect(list.insert(1, 0)).to.equal(true); + expect(list.insert(2, 1)).to.equal(true); + verifyList(); + }); + + it('inserts elements at the end of list', () => { + max = 5; + + for (let i = min; i <= max; i++) { + expect(list.insert(i, i - 1)).to.equal(true); + } + + verifyList(); + }); + + it('returns index of elements', () => { + let index; + + pushesElements(); + + for (let i = min; i <= max; i++) { + index = list.indexOf(i); + expect(index).to.equal(i - 1); + } + + expect(list.indexOf(max + 2)).to.equal(-1); + }); + + it('removes valid elements', () => { + let element; + + pushesElements(); + + for (let i = min; i <= max; i++) { + element = list.remove(i); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + } + }); + + it('removes invalid elements', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.remove(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position empty list', () => { + let element; + + for (let i = min; i <= max; i++) { + element = list.removeAt(i - 1); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position not empty list', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.removeAt(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes first element list single element', () => { + const value = 1; + list.push(value); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(value); + + expect(list.getHead()).to.be.an('undefined'); + expect(list.isEmpty()).to.equal(true); + }); + + it('removes first element list multiple elements', () => { + pushesElements(); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(min); + + min = 2; + verifyList(); + }); + + it('removes element from middle of list', () => { + pushesElements(); // 1, 2, 3 + + const element = list.removeAt(1); // element 2 + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(2); + + // list needs to be [1, 3] + let current = list.getHead(); + + // element 1 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(1); + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + expect(current.next.element).to.equal(3); + current = current.next; + } + } + + // element 3 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(3); + expect(current.next).to.be.an('undefined'); + } + }); + + it('removes element from end of list', () => { + let element; + + pushesElements(); + + const maxIndex = max; + for (let i = maxIndex; i >= min; i--) { + element = list.removeAt(i - 1); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + max--; + verifyList(); + } + }); + + it('returns the head of the list', () => { + expect(list.getHead()).to.be.an('undefined'); + + list.push(1); + expect(list.getHead()).to.not.be.an('undefined'); + }); + + it('returns the correct size', () => { + expect(list.size()).to.equal(0); + + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.size()).to.equal(i); + } + + const size = max; + for (let i = min; i <= max; i++) { + list.remove(i); + expect(list.size()).to.equal(size - i); + } + + expect(list.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(list.isEmpty()).to.equal(true); + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.isEmpty()).to.equal(false); + } + + for (let i = min; i < max; i++) { + list.remove(i); + expect(list.isEmpty()).to.equal(false); + } + list.remove(max); + expect(list.isEmpty()).to.equal(true); + + pushesElements(); + expect(list.isEmpty()).to.equal(false); + + list.clear(); + expect(list.isEmpty()).to.equal(true); + }); + + it('clears the list', () => { + expect(list.size()).to.equal(0); + list.clear(); + expect(list.size()).to.equal(0); + pushesElements(); + expect(list.size()).to.greaterThan(0); + list.clear(); + expect(list.size()).to.equal(0); + }); + + it('returns toString primitive types', () => { + expect(list.toString()).to.equal(''); + + list.push(1); + expect(list.toString()).to.equal('1'); + + list.push(2); + expect(list.toString()).to.equal('1,2'); + + list.clear(); + expect(list.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const ds = new LinkedList(); + ds.push('el1'); + expect(ds.toString()).to.equal('el1'); + + ds.push('el2'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const ds = new LinkedList(); + expect(ds.toString()).to.equal(''); + + ds.push(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2'); + + ds.push(new MyObj(3, 4)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/data-structures/my-obj.js b/test/js/data-structures/my-obj.js new file mode 100644 index 00000000..a4d2f5f3 --- /dev/null +++ b/test/js/data-structures/my-obj.js @@ -0,0 +1,9 @@ +export default class MyObj { + constructor(el1, el2) { + this.el1 = el1; + this.el2 = el2; + } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } +} diff --git a/test/js/data-structures/queue.spec.js b/test/js/data-structures/queue.spec.js new file mode 100644 index 00000000..ec113683 --- /dev/null +++ b/test/js/data-structures/queue.spec.js @@ -0,0 +1,171 @@ +import 'mocha'; +import { expect } from 'chai'; +import Queue from '../../../src/js/data-structures/queue'; + +describe('Queue', () => { + let queue; + + beforeEach(() => { + queue = new Queue(); + }); + + it('starts empty', () => { + expect(queue.size()).to.equal(0); + expect(queue.isEmpty()).to.equal(true); + }); + + it('enqueues elements', () => { + queue.enqueue(1); + expect(queue.size()).to.equal(1); + queue.enqueue(2); + expect(queue.size()).to.equal(2); + queue.enqueue(3); + expect(queue.size()).to.equal(3); + + expect(queue.isEmpty()).to.equal(false); + }); + + it('dequeue elements', () => { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + + expect(queue.dequeue()).to.equal(1); + expect(queue.dequeue()).to.equal(2); + expect(queue.dequeue()).to.equal(3); + expect(queue.dequeue()).to.equal(undefined); + }); + + it('implements FIFO logic', () => { + queue.enqueue(1); + expect(queue.peek()).to.equal(1); + queue.enqueue(2); + expect(queue.peek()).to.equal(1); + queue.enqueue(3); + expect(queue.peek()).to.equal(1); + + expect(queue.dequeue()).to.equal(1); + expect(queue.dequeue()).to.equal(2); + expect(queue.dequeue()).to.equal(3); + expect(queue.dequeue()).to.equal(undefined); + }); + + it('allows to peek at the front element in the queue without dequeuing it', () => { + expect(queue.peek()).to.equal(undefined); + + queue.enqueue(1); + expect(queue.peek()).to.equal(1); + + queue.enqueue(2); + expect(queue.peek()).to.equal(1); + + queue.dequeue(); + expect(queue.peek()).to.equal(2); + }); + + it('returns the correct size', () => { + expect(queue.size()).to.equal(0); + queue.enqueue(1); + expect(queue.size()).to.equal(1); + queue.enqueue(2); + expect(queue.size()).to.equal(2); + queue.enqueue(3); + expect(queue.size()).to.equal(3); + + queue.clear(); + expect(queue.isEmpty()).to.equal(true); + + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + expect(queue.size()).to.equal(3); + + queue.dequeue(); + expect(queue.size()).to.equal(2); + queue.dequeue(); + expect(queue.size()).to.equal(1); + queue.dequeue(); + expect(queue.size()).to.equal(0); + queue.dequeue(); + expect(queue.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(queue.isEmpty()).to.equal(true); + queue.enqueue(1); + expect(queue.isEmpty()).to.equal(false); + queue.enqueue(2); + expect(queue.isEmpty()).to.equal(false); + queue.enqueue(3); + expect(queue.isEmpty()).to.equal(false); + + queue.clear(); + expect(queue.isEmpty()).to.equal(true); + + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + expect(queue.isEmpty()).to.equal(false); + + queue.dequeue(); + expect(queue.isEmpty()).to.equal(false); + queue.dequeue(); + expect(queue.isEmpty()).to.equal(false); + queue.dequeue(); + expect(queue.isEmpty()).to.equal(true); + queue.dequeue(); + expect(queue.isEmpty()).to.equal(true); + }); + + it('clears the queue', () => { + queue.clear(); + expect(queue.isEmpty()).to.equal(true); + + queue.enqueue(1); + queue.enqueue(2); + expect(queue.isEmpty()).to.equal(false); + + queue.clear(); + expect(queue.isEmpty()).to.equal(true); + }); + + it('returns toString primitive types', () => { + expect(queue.toString()).to.equal(''); + + queue.enqueue(1); + expect(queue.toString()).to.equal('1'); + + queue.enqueue(2); + expect(queue.toString()).to.equal('1,2'); + + queue.clear(); + expect(queue.toString()).to.equal(''); + + const queueString = new Queue(); + queueString.enqueue('el1'); + expect(queueString.toString()).to.equal('el1'); + + queueString.enqueue('el2'); + expect(queueString.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + class MyObj { + constructor(el1, el2) { + this.el1 = el1; + this.el2 = el2; + } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } + } + const queueMyObj = new Queue(); + expect(queueMyObj.toString()).to.equal(''); + + queueMyObj.enqueue(new MyObj(1, 2)); + expect(queueMyObj.toString()).to.equal('1|2'); + + queueMyObj.enqueue(new MyObj(3, 4)); + expect(queueMyObj.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/data-structures/set.spec.js b/test/js/data-structures/set.spec.js new file mode 100644 index 00000000..f8e2bac1 --- /dev/null +++ b/test/js/data-structures/set.spec.js @@ -0,0 +1,352 @@ +import 'mocha'; +import { expect } from 'chai'; +import { Set } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('Set', () => { + let set; + + beforeEach(() => { + set = new Set(); + }); + + it('starts empty', () => { + expect(set.size()).to.equal(0); + expect(set.isEmpty()).to.equal(true); + }); + + it('adds elements', () => { + for (let i = 1; i < 5; i++) { + set.add(i); + expect(set.size()).to.equal(i); + } + + expect(set.isEmpty()).to.equal(false); + }); + + it('does not allow duplicated elements', () => { + let expected = true; + for (let i = 1; i < 5; i++) { + expect(set.add(i)).to.equal(expected); + } + + expected = false; + for (let i = 1; i < 5; i++) { + expect(set.add(i)).to.equal(expected); + } + }); + + it('deletes elements', () => { + for (let i = 1; i < 5; i++) { + set.add(i); + } + + for (let i = 1; i < 5; i++) { + expect(set.delete(i)).to.equal(true); + } + + // elements do not exist + for (let i = 1; i < 5; i++) { + expect(set.delete(i)).to.equal(false); + } + + expect(set.isEmpty()).to.equal(true); + }); + + it('returns if element exists', () => { + for (let i = 1; i < 5; i++) { + set.add(i); + expect(set.has(i)).to.equal(true); + } + + for (let i = 1; i < 5; i++) { + expect(set.delete(i)).to.equal(true); + expect(set.has(i)).to.equal(false); + } + }); + + it('returns the correct size', () => { + expect(set.size()).to.equal(0); + + for (let i = 1; i < 5; i++) { + set.add(i); + expect(set.size()).to.equal(i); + } + + const max = 5; + for (let i = 1; i < max; i++) { + set.delete(i); + expect(set.size()).to.equal(max - i - 1); + } + + expect(set.size()).to.equal(0); + expect(set.isEmpty()).to.equal(true); + }); + + it('returns if it is empty', () => { + expect(set.isEmpty()).to.equal(true); + + for (let i = 1; i < 5; i++) { + set.add(i); + expect(set.isEmpty()).to.equal(false); + } + + for (let i = 1; i < 5; i++) { + set.delete(i); + expect(set.isEmpty()).to.equal(!(i < 4)); + } + + expect(set.size()).to.equal(0); + expect(set.isEmpty()).to.equal(true); + }); + + it('clears the set', () => { + set.clear(); + expect(set.isEmpty()).to.equal(true); + + set.add(1); + set.add(2); + + set.clear(); + expect(set.isEmpty()).to.equal(true); + }); + + function addValues(min, max) { + set = new Set(); + + for (let i = min; i <= max; i++) { + set.add(i); + } + + return set; + } + + it('union between empty sets', () => { + const set1 = new Set(); + const set2 = new Set(); + + let setResult = set1.union(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.union(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('union between equal sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(1, 5); + + let setResult = set1.union(set2); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.union(set1); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('union between different sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(6, 10); + + let setResult = set1.union(set2); + for (let i = 1; i <= 10; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.union(set1); + for (let i = 1; i <= 10; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('union between sets with common values', () => { + const set1 = addValues(1, 5); + const set2 = addValues(3, 6); + + let setResult = set1.union(set2); + for (let i = 1; i <= 6; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.union(set1); + for (let i = 1; i <= 6; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('intersection between empty sets', () => { + const set1 = new Set(); + const set2 = new Set(); + + let setResult = set1.intersection(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.intersection(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('intersection between equal sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(1, 5); + + let setResult = set1.intersection(set2); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.intersection(set1); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('intersection different sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(6, 10); + + let setResult = set1.intersection(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.intersection(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('intersection between sets with common values', () => { + const set1 = addValues(1, 5); + const set2 = addValues(3, 6); + + let setResult = set1.intersection(set2); + for (let i = 3; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.intersection(set1); + for (let i = 3; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('difference between empty sets', () => { + const set1 = new Set(); + const set2 = new Set(); + + let setResult = set1.difference(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.difference(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('difference between equal sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(1, 5); + + let setResult = set1.difference(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.difference(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('difference different sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(6, 10); + + let setResult = set1.difference(set2); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.difference(set1); + for (let i = 6; i <= 10; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('difference between sets with common values', () => { + const set1 = addValues(1, 5); + const set2 = addValues(3, 6); + + let setResult = set1.difference(set2); + for (let i = 1; i <= 2; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.difference(set1); + for (let i = 6; i <= 6; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('isSubsetOf between empty sets', () => { + const set1 = new Set(); + const set2 = new Set(); + + expect(set1.isSubsetOf(set2)).to.equal(true); + expect(set2.isSubsetOf(set1)).to.equal(true); + }); + + it('isSubsetOf between equal sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(1, 5); + + expect(set1.isSubsetOf(set2)).to.equal(true); + expect(set2.isSubsetOf(set1)).to.equal(true); + }); + + it('isSubsetOf different sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(6, 10); + + expect(set1.isSubsetOf(set2)).to.equal(false); + expect(set2.isSubsetOf(set1)).to.equal(false); + }); + + it('isSubsetOf between sets with common values', () => { + const set1 = addValues(1, 8); + const set2 = addValues(3, 6); + expect(set1.isSubsetOf(set2)).to.equal(false); + expect(set2.isSubsetOf(set1)).to.equal(true); + + const set3 = addValues(1, 5); + const set4 = addValues(3, 6); + expect(set3.isSubsetOf(set4)).to.equal(false); + expect(set4.isSubsetOf(set3)).to.equal(false); + }); + + it('returns toString primitive types', () => { + expect(set.toString()).to.equal(''); + + set.add(1); + expect(set.toString()).to.equal('1'); + + set.add(2); + expect(set.toString()).to.equal('1,2'); + + set.clear(); + expect(set.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const ds = new Set(); + ds.add('el1'); + expect(ds.toString()).to.equal('el1'); + + ds.add('el2'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const ds = new Set(); + expect(ds.toString()).to.equal(''); + + ds.add(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2'); + + ds.add(new MyObj(3, 4)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/data-structures/sorted-linked-list.spec.js b/test/js/data-structures/sorted-linked-list.spec.js new file mode 100644 index 00000000..5ccaaff9 --- /dev/null +++ b/test/js/data-structures/sorted-linked-list.spec.js @@ -0,0 +1,340 @@ +import 'mocha'; +import { expect } from 'chai'; +import { SortedLinkedList, util } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('SortedLinkedList', () => { + let list; + let min; + let max; + + beforeEach(() => { + list = new SortedLinkedList(); + min = 1; + max = 3; + }); + + function pushesElements() { + for (let i = max; i >= min; i--) { + list.push(i); + } + } + + function verifyList() { + let current = list.getHead(); + for (let i = min; i <= max && current; i++) { + expect(current).to.not.be.an('undefined'); + if (current) { + // TS strictNullChecks + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(i); + if (i < max) { + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + // TS strictNullChecks + expect(current.next.element).to.equal(i + 1); + } + } else { + expect(current.next).to.be.an('undefined'); + } + current = current.next; + } + } + } + + it('starts empty', () => { + expect(list.size()).to.equal(0); + expect(list.isEmpty()).to.equal(true); + expect(list.getHead()).to.be.an('undefined'); + }); + + it('pushes elements', () => { + pushesElements(); + verifyList(); + }); + + it('returns element at specific index: invalid position', () => { + // list is empty + expect(list.getElementAt(3)).to.be.an('undefined'); + }); + + it('returns element at specific index', () => { + let node; + + pushesElements(); + + for (let i = min; i <= max; i++) { + node = list.getElementAt(i - 1); + expect(node).to.not.be.an('undefined'); + if (node) { + expect(node.element).to.equal(i); + } + } + }); + + it('inserts elements first position empty list', () => { + const element = 1; + max = element; + expect(list.insert(element, 0)).to.equal(true); + verifyList(); + }); + + it('inserts elements first position not empty list', () => { + max = 2; + expect(list.insert(max)).to.equal(true); + + expect(list.insert(min, 0)).to.equal(true); + + verifyList(); + }); + + it('inserts elements invalid position empty list', () => { + // sorted list will ignore the index position + expect(list.insert(1, 1)).to.equal(true); + }); + + it('inserts elements invalid position not empty list', () => { + // sorted list will ignore the index position + const element = 1; + expect(list.insert(element, 0)).to.equal(true); + expect(list.insert(element, 2)).to.equal(true); + }); + + it('inserts elements in the middle of list', () => { + expect(list.insert(3, 0)).to.equal(true); + expect(list.insert(1, 0)).to.equal(true); + expect(list.insert(2, 1)).to.equal(true); + verifyList(); + }); + + it('inserts elements at the end of list', () => { + max = 5; + + for (let i = min; i <= max; i++) { + expect(list.insert(i, i - 1)).to.equal(true); + } + + verifyList(); + }); + + it('returns index of elements', () => { + let index; + + pushesElements(); + + for (let i = min; i <= max; i++) { + index = list.indexOf(i); + expect(index).to.equal(i - 1); + } + + expect(list.indexOf(max + 2)).to.equal(-1); + }); + + it('removes valid elements', () => { + let element; + + pushesElements(); + + for (let i = min; i <= max; i++) { + element = list.remove(i); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + } + }); + + it('removes invalid elements', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.remove(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position empty list', () => { + let element; + + for (let i = min; i <= max; i++) { + element = list.removeAt(i - 1); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position not empty list', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.removeAt(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes first element list single element', () => { + const value = 1; + list.push(value); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(value); + + expect(list.getHead()).to.be.an('undefined'); + expect(list.isEmpty()).to.equal(true); + }); + + it('removes first element list multiple elements', () => { + pushesElements(); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(min); + + min = 2; + verifyList(); + }); + + it('removes element from middle of list', () => { + pushesElements(); // 1, 2, 3 + + const element = list.removeAt(1); // element 2 + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(2); + + // list needs to be [1, 3] + let current = list.getHead(); + + // element 1 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(1); + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + expect(current.next.element).to.equal(3); + current = current.next; + } + } + + // element 3 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(3); + expect(current.next).to.be.an('undefined'); + } + }); + + it('removes element from end of list', () => { + let element; + + pushesElements(); + + const maxIndex = max; + for (let i = maxIndex; i >= min; i--) { + element = list.removeAt(i - 1); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + max--; + verifyList(); + } + }); + + it('returns the head of the list', () => { + expect(list.getHead()).to.be.an('undefined'); + + list.push(1); + expect(list.getHead()).to.not.be.an('undefined'); + }); + + it('returns the correct size', () => { + expect(list.size()).to.equal(0); + + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.size()).to.equal(i); + } + + const size = max; + for (let i = min; i <= max; i++) { + list.remove(i); + expect(list.size()).to.equal(size - i); + } + + expect(list.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(list.isEmpty()).to.equal(true); + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.isEmpty()).to.equal(false); + } + + for (let i = min; i < max; i++) { + list.remove(i); + expect(list.isEmpty()).to.equal(false); + } + list.remove(max); + expect(list.isEmpty()).to.equal(true); + + pushesElements(); + expect(list.isEmpty()).to.equal(false); + + list.clear(); + expect(list.isEmpty()).to.equal(true); + }); + + it('clears the list', () => { + expect(list.size()).to.equal(0); + list.clear(); + expect(list.size()).to.equal(0); + pushesElements(); + expect(list.size()).to.greaterThan(0); + list.clear(); + expect(list.size()).to.equal(0); + }); + + it('returns toString primitive types', () => { + expect(list.toString()).to.equal(''); + + list.push(1); + expect(list.toString()).to.equal('1'); + + list.push(2); + expect(list.toString()).to.equal('1,2'); + + list.clear(); + expect(list.toString()).to.equal(''); + }); + + function stringCompare(a, b) { + return a.localeCompare(b); + } + + it('returns toString primitive types: string', () => { + const ds = new SortedLinkedList(util.defaultEquals, stringCompare); + ds.push('el2'); + expect(ds.toString()).to.equal('el2'); + + ds.push('el1'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + function myObjCompare(a, b) { + return a.toString().localeCompare(b.toString()); + } + + it('returns toString objects', () => { + const ds = new SortedLinkedList(util.defaultEquals, myObjCompare); + expect(ds.toString()).to.equal(''); + + ds.push(new MyObj(3, 4)); + expect(ds.toString()).to.equal('3|4'); + + ds.push(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/data-structures/stack-array.spec.js b/test/js/data-structures/stack-array.spec.js new file mode 100644 index 00000000..615fca68 --- /dev/null +++ b/test/js/data-structures/stack-array.spec.js @@ -0,0 +1,180 @@ +import 'mocha'; +import { expect } from 'chai'; +import StackArray from '../../../src/js/data-structures/stack-array'; + +describe('StackArray', () => { + let stack; + + beforeEach(() => { + stack = new StackArray(); + }); + + it('starts empty', () => { + expect(stack.size()).to.equal(0); + expect(stack.isEmpty()).to.equal(true); + }); + + it('pushes elements', () => { + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + expect(stack.isEmpty()).to.equal(false); + }); + + it('pops elements', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('implements LIFO logic', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('allows to peek at the top element in he stack without popping it', () => { + expect(stack.peek()).to.equal(undefined); + + stack.push(1); + expect(stack.peek()).to.equal(1); + + stack.push(2); + expect(stack.peek()).to.equal(2); + + stack.pop(); + expect(stack.peek()).to.equal(1); + }); + + it('returns the correct size', () => { + expect(stack.size()).to.equal(0); + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.size()).to.equal(2); + stack.pop(); + expect(stack.size()).to.equal(1); + stack.pop(); + expect(stack.size()).to.equal(0); + stack.pop(); + expect(stack.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(stack.isEmpty()).to.equal(true); + stack.push(1); + expect(stack.isEmpty()).to.equal(false); + stack.push(2); + expect(stack.isEmpty()).to.equal(false); + stack.push(3); + expect(stack.isEmpty()).to.equal(false); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('clears the stack', () => { + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('returns an Array', () => { + let stackArray = stack.toArray(); + expect(stackArray.length).to.equal(0); + + stack.push(1); + stack.push(2); + + stackArray = stack.toArray(); + expect(stackArray.length).to.equal(2); + + let i = 1; + stackArray.forEach(e => { + expect(e).to.equal(i); + i++; + }); + }); + + it('returns toString primitive types', () => { + expect(stack.toString()).to.equal(''); + + stack.push(1); + expect(stack.toString()).to.equal('1'); + + stack.push(2); + expect(stack.toString()).to.equal('1,2'); + + stack.clear(); + expect(stack.toString()).to.equal(''); + + stack.push('el1'); + expect(stack.toString()).to.equal('el1'); + + stack.push('el2'); + expect(stack.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + class MyObj { + constructor(el1, el2) { + this.el1 = el1; + this.el2 = el2; + } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } + } + expect(stack.toString()).to.equal(''); + + stack.push(new MyObj(1, 2)); + expect(stack.toString()).to.equal('1|2'); + + stack.push(new MyObj(3, 4)); + expect(stack.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/data-structures/stack-linked-list.spec.js b/test/js/data-structures/stack-linked-list.spec.js new file mode 100644 index 00000000..19549713 --- /dev/null +++ b/test/js/data-structures/stack-linked-list.spec.js @@ -0,0 +1,157 @@ +import 'mocha'; +import { expect } from 'chai'; +import { StackLinkedList } from '../../../src/js/index'; +import MyObj from './my-obj'; + +describe('StackLinkedList', () => { + let stack; + + beforeEach(() => { + stack = new StackLinkedList(); + }); + + it('starts empty', () => { + expect(stack.size()).to.equal(0); + expect(stack.isEmpty()).to.equal(true); + }); + + it('pushes elements', () => { + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + expect(stack.isEmpty()).to.equal(false); + }); + + it('pops elements', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('implements LIFO logic', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('allows to peek at the top element in he stack without popping it', () => { + expect(stack.peek()).to.equal(undefined); + + stack.push(1); + expect(stack.peek()).to.equal(1); + + stack.push(2); + expect(stack.peek()).to.equal(2); + + stack.pop(); + expect(stack.peek()).to.equal(1); + }); + + it('returns the correct size', () => { + expect(stack.size()).to.equal(0); + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.size()).to.equal(2); + stack.pop(); + expect(stack.size()).to.equal(1); + stack.pop(); + expect(stack.size()).to.equal(0); + stack.pop(); + expect(stack.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(stack.isEmpty()).to.equal(true); + stack.push(1); + expect(stack.isEmpty()).to.equal(false); + stack.push(2); + expect(stack.isEmpty()).to.equal(false); + stack.push(3); + expect(stack.isEmpty()).to.equal(false); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('clears the stack', () => { + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('returns toString primitive types', () => { + expect(stack.toString()).to.equal(''); + + stack.push(1); + expect(stack.toString()).to.equal('1'); + + stack.push(2); + expect(stack.toString()).to.equal('1,2'); + + stack.clear(); + expect(stack.toString()).to.equal(''); + + const stackString = new StackLinkedList(); + stackString.push('el1'); + expect(stackString.toString()).to.equal('el1'); + + stackString.push('el2'); + expect(stackString.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const stackMyObj = new StackLinkedList(); + expect(stackMyObj.toString()).to.equal(''); + + stackMyObj.push(new MyObj(1, 2)); + expect(stackMyObj.toString()).to.equal('1|2'); + + stackMyObj.push(new MyObj(3, 4)); + expect(stackMyObj.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/data-structures/stack.spec.js b/test/js/data-structures/stack.spec.js new file mode 100644 index 00000000..7c77703b --- /dev/null +++ b/test/js/data-structures/stack.spec.js @@ -0,0 +1,163 @@ +import 'mocha'; +import { expect } from 'chai'; +import Stack from '../../../src/js/data-structures/stack'; + +describe('Stack', () => { + let stack; + + beforeEach(() => { + stack = new Stack(); + }); + + it('starts empty', () => { + expect(stack.size()).to.equal(0); + expect(stack.isEmpty()).to.equal(true); + }); + + it('pushes elements', () => { + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + expect(stack.isEmpty()).to.equal(false); + }); + + it('pops elements', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('implements LIFO logic', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('allows to peek at the top element in he stack without popping it', () => { + expect(stack.peek()).to.equal(undefined); + + stack.push(1); + expect(stack.peek()).to.equal(1); + + stack.push(2); + expect(stack.peek()).to.equal(2); + + stack.pop(); + expect(stack.peek()).to.equal(1); + }); + + it('returns the correct size', () => { + expect(stack.size()).to.equal(0); + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.size()).to.equal(2); + stack.pop(); + expect(stack.size()).to.equal(1); + stack.pop(); + expect(stack.size()).to.equal(0); + stack.pop(); + expect(stack.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(stack.isEmpty()).to.equal(true); + stack.push(1); + expect(stack.isEmpty()).to.equal(false); + stack.push(2); + expect(stack.isEmpty()).to.equal(false); + stack.push(3); + expect(stack.isEmpty()).to.equal(false); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('clears the stack', () => { + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('returns toString primitive types', () => { + expect(stack.toString()).to.equal(''); + + stack.push(1); + expect(stack.toString()).to.equal('1'); + + stack.push(2); + expect(stack.toString()).to.equal('1,2'); + + stack.clear(); + expect(stack.toString()).to.equal(''); + + stack.push('el1'); + expect(stack.toString()).to.equal('el1'); + + stack.push('el2'); + expect(stack.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + class MyObj { + constructor(el1, el2) { + this.el1 = el1; + this.el2 = el2; + } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } + } + expect(stack.toString()).to.equal(''); + + stack.push(new MyObj(1, 2)); + expect(stack.toString()).to.equal('1|2'); + + stack.push(new MyObj(3, 4)); + expect(stack.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/js/others/balanced-symbols.spec.js b/test/js/others/balanced-symbols.spec.js new file mode 100644 index 00000000..6a2ce6e4 --- /dev/null +++ b/test/js/others/balanced-symbols.spec.js @@ -0,0 +1,41 @@ +import 'mocha'; +import { expect } from 'chai'; +import { parenthesesChecker } from '../../../src/js/others/balanced-symbols'; + +describe('Balanced Symbols', () => { + it('empty to be falsy', () => { + expect(parenthesesChecker('')).to.equal(true); + }); + + it('{ to be falsy', () => { + expect(parenthesesChecker('{')).to.equal(false); + }); + + it('} to be falsy', () => { + expect(parenthesesChecker('}')).to.equal(false); + }); + + it('11 to be falsy', () => { + expect(parenthesesChecker('11')).to.equal(false); + }); + + it('{11 to be falsy', () => { + expect(parenthesesChecker('{11')).to.equal(false); + }); + + it('{([1])} to be falsy', () => { + expect(parenthesesChecker('{([1])}')).to.equal(false); + }); + + it('{([])} to be truthy', () => { + expect(parenthesesChecker('{([])}')).to.equal(true); + }); + + it('{{([][])}()} to be truthy', () => { + expect(parenthesesChecker('{{([][])}()}')).to.equal(true); + }); + + it('[{()] to be falsy', () => { + expect(parenthesesChecker('[{()]')).to.equal(false); + }); +}); diff --git a/test/js/others/base-converter.spec.js b/test/js/others/base-converter.spec.js new file mode 100644 index 00000000..ed93a0dc --- /dev/null +++ b/test/js/others/base-converter.spec.js @@ -0,0 +1,57 @@ +import 'mocha'; +import { expect } from 'chai'; +import { baseConverter, decimalToBinary } from '../../../src/js/others/base-converter'; + +describe('Base Converter', () => { + it('decimalToBinary 1 -> 1', () => { + expect(decimalToBinary(1)).to.equal('1'); + }); + + it('decimalToBinary 2 -> 11', () => { + expect(decimalToBinary(2)).to.equal('10'); + }); + + it('decimalToBinary 233 -> 11101001', () => { + expect(decimalToBinary(233)).to.equal('11101001'); + }); + + it('decimalToBinary 10 -> 1010', () => { + expect(decimalToBinary(10)).to.equal('1010'); + }); + + it('decimalToBinary 1000 -> 1111101000', () => { + expect(decimalToBinary(1000)).to.equal('1111101000'); + }); + + it('baseConverter 100345, 2 -> 11000011111111001', () => { + expect(baseConverter(100345, 2)).to.equal('11000011111111001'); + }); + + it('baseConverter 100345, 8 -> 303771', () => { + expect(baseConverter(100345, 8)).to.equal('303771'); + }); + + it('baseConverter 100345, 16 -> 187F9', () => { + expect(baseConverter(100345, 16)).to.equal('187F9'); + }); + + it('baseConverter 100345, 7 -> 565360', () => { + expect(baseConverter(100345, 7)).to.equal('565360'); + }); + + it('baseConverter 100345, 20 -> CAH5', () => { + expect(baseConverter(100345, 20)).to.equal('CAH5'); + }); + + it('baseConverter 100345, 35 -> 2BW0', () => { + expect(baseConverter(100345, 35)).to.equal('2BW0'); + }); + + it('baseConverter 100345, 36 -> 25FD', () => { + expect(baseConverter(100345, 36)).to.equal('25FD'); + }); + + it('baseConverter 100345, 37 -> ', () => { + expect(baseConverter(100345, 37)).to.equal(''); + }); +}); diff --git a/test/js/others/factorial.spec.js b/test/js/others/factorial.spec.js new file mode 100644 index 00000000..975f6404 --- /dev/null +++ b/test/js/others/factorial.spec.js @@ -0,0 +1,25 @@ +import 'mocha'; +import { expect } from 'chai'; +import { factorialIterative, factorial } from './../../../src/js/others/factorial'; + +describe('Factorial', () => { + it('Iterative Factorial', () => { + expect(factorialIterative(-1)).to.equal(undefined); + expect(factorialIterative(0)).to.equal(1); + expect(factorialIterative(1)).to.equal(1); + expect(factorialIterative(2)).to.equal(2); + expect(factorialIterative(3)).to.equal(6); + expect(factorialIterative(4)).to.equal(24); + expect(factorialIterative(5)).to.equal(120); + }); + + it('Recursive Factorial', () => { + expect(factorial(-1)).to.equal(undefined); + expect(factorial(0)).to.equal(1); + expect(factorial(1)).to.equal(1); + expect(factorial(2)).to.equal(2); + expect(factorial(3)).to.equal(6); + expect(factorial(4)).to.equal(24); + expect(factorial(5)).to.equal(120); + }); +}); diff --git a/test/js/others/fibonacci.spec.js b/test/js/others/fibonacci.spec.js new file mode 100644 index 00000000..5eea4888 --- /dev/null +++ b/test/js/others/fibonacci.spec.js @@ -0,0 +1,36 @@ +import 'mocha'; +import { expect } from 'chai'; +import { + fibonacci, + fibonacciIterative, + fibonacciMemoization +} from '../../../src/js/others/fibonacci'; + +describe('Fibonacci', () => { + it('Fibonacci Recursive', () => { + expect(fibonacci(-1)).to.equal(0); + expect(fibonacci(0)).to.equal(0); + expect(fibonacci(1)).to.equal(1); + expect(fibonacci(2)).to.equal(1); + expect(fibonacci(3)).to.equal(2); + expect(fibonacci(4)).to.equal(3); + }); + + it('Fibonacci Iterative', () => { + expect(fibonacciIterative(-1)).to.equal(0); + expect(fibonacciIterative(0)).to.equal(0); + expect(fibonacciIterative(1)).to.equal(1); + expect(fibonacciIterative(2)).to.equal(1); + expect(fibonacciIterative(3)).to.equal(2); + expect(fibonacciIterative(4)).to.equal(3); + }); + + it('Fibonacci with Memoization', () => { + expect(fibonacciMemoization(-1)).to.equal(0); + expect(fibonacciMemoization(0)).to.equal(0); + expect(fibonacciMemoization(1)).to.equal(1); + expect(fibonacciMemoization(2)).to.equal(1); + expect(fibonacciMemoization(3)).to.equal(2); + expect(fibonacciMemoization(4)).to.equal(3); + }); +}); diff --git a/test/js/others/hanoi.spec.js b/test/js/others/hanoi.spec.js new file mode 100644 index 00000000..cca28431 --- /dev/null +++ b/test/js/others/hanoi.spec.js @@ -0,0 +1,19 @@ +import 'mocha'; +import { expect } from 'chai'; +import { hanoi, hanoiStack } from '../../../src/js/others/hanoi'; + +describe('Tower of Hanoi', () => { + it('Hanoi', () => { + for (let i = 0; i < 10; i++) { + const result = hanoi(i, 'a', 'b', 'c'); + expect(result.length).to.equal((2 ** i) - 1); + } + }); + + it('Hanoi with Stack', () => { + for (let i = 0; i < 10; i++) { + const result = hanoiStack(i); + expect(result.length).to.equal((2 ** i) - 1); + } + }); +}); diff --git a/test/js/others/hot-potato.spec.js b/test/js/others/hot-potato.spec.js new file mode 100644 index 00000000..dfbb2751 --- /dev/null +++ b/test/js/others/hot-potato.spec.js @@ -0,0 +1,14 @@ +import 'mocha'; +import { expect } from 'chai'; +import { hotPotato } from '../../../src/js/others/hot-potato'; + +describe('Hot Potato with Queue', () => { + it('Hot potato game', () => { + const names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl']; + expect(hotPotato(names, 6).winner).to.equal('Ingrid'); + expect(hotPotato(names, 7).winner).to.equal('John'); + expect(hotPotato(names, 8).winner).to.equal('Jack'); + expect(hotPotato(names, 9).winner).to.equal('Ingrid'); + expect(hotPotato(names, 10).winner).to.equal('Carl'); + }); +}); diff --git a/test/js/others/palindrome-checker.spec.js b/test/js/others/palindrome-checker.spec.js new file mode 100644 index 00000000..c5664a31 --- /dev/null +++ b/test/js/others/palindrome-checker.spec.js @@ -0,0 +1,19 @@ +import 'mocha'; +import { expect } from 'chai'; +import { palindromeChecker } from '../../../src/js/others/palindrome-checker'; + +describe('Palindrome', () => { + it('Palindrome Checker', () => { + expect(palindromeChecker('')).to.equal(false); + expect(palindromeChecker('a')).to.equal(true); + expect(palindromeChecker('aa')).to.equal(true); + expect(palindromeChecker('aba')).to.equal(true); + expect(palindromeChecker('ab')).to.equal(false); + expect(palindromeChecker('kayak')).to.equal(true); + expect(palindromeChecker('radar')).to.equal(true); + expect(palindromeChecker('level')).to.equal(true); + expect(palindromeChecker('Was it a car or a cat I saw')).to.equal(true); + expect(palindromeChecker('Step on no pets')).to.equal(true); + expect(palindromeChecker('Able was I ere I saw Elba')).to.equal(true); + }); +}); diff --git a/test/ts/algorithms/backtracking/sudoku-solver.spec.ts b/test/ts/algorithms/backtracking/sudoku-solver.spec.ts new file mode 100644 index 00000000..2b78a551 --- /dev/null +++ b/test/ts/algorithms/backtracking/sudoku-solver.spec.ts @@ -0,0 +1,31 @@ +import 'mocha'; +import { expect } from 'chai'; +import { sudokuSolver } from '../../../../src/ts/index'; + +describe('Sudoku Solver', () => { + it('sudoku solver', () => { + const grid = [ + [3, 0, 6, 5, 0, 8, 4, 0, 0], + [5, 2, 0, 0, 0, 0, 0, 0, 0], + [0, 8, 7, 0, 0, 0, 0, 3, 1], + [0, 0, 3, 0, 1, 0, 0, 8, 0], + [9, 0, 0, 8, 6, 3, 0, 0, 5], + [0, 5, 0, 0, 9, 0, 6, 0, 0], + [1, 3, 0, 0, 0, 0, 2, 5, 0], + [0, 0, 0, 0, 0, 0, 0, 7, 4], + [0, 0, 5, 2, 0, 6, 3, 0, 0] + ]; + const solution = [ + [3, 1, 6, 5, 7, 8, 4, 9, 2], + [5, 2, 9, 1, 3, 4, 7, 6, 8], + [4, 8, 7, 6, 2, 9, 5, 3, 1], + [2, 6, 3, 4, 1, 5, 9, 8, 7], + [9, 7, 4, 8, 6, 3, 1, 2, 5], + [8, 5, 1, 7, 9, 2, 6, 4, 3], + [1, 3, 8, 9, 4, 7, 2, 5, 6], + [6, 9, 2, 3, 5, 1, 8, 7, 4], + [7, 4, 5, 2, 8, 6, 3, 1, 9] + ]; + expect(sudokuSolver(grid)).to.deep.equal(solution); + }); +}); diff --git a/test/ts/algorithms/dynamic-programming/min-coin-change.spec.ts b/test/ts/algorithms/dynamic-programming/min-coin-change.spec.ts new file mode 100644 index 00000000..26821b06 --- /dev/null +++ b/test/ts/algorithms/dynamic-programming/min-coin-change.spec.ts @@ -0,0 +1,30 @@ +import 'mocha'; +import { expect } from 'chai'; +import { minCoinChange } from '../../../../src/ts/index'; + +describe('Dynamic Programming: Min Coin Change', () => { + + it('works with amount 0', () => { + expect(minCoinChange([1, 2, 3], 0)).to.deep.equal([]); + }); + + it('works with amount 1', () => { + expect(minCoinChange([1, 2, 3], 1)).to.deep.equal([1]); + }); + + it('works with amount 2', () => { + expect(minCoinChange([1, 2, 3], 2)).to.deep.equal([2]); + }); + + it('works with amount 3', () => { + expect(minCoinChange([1, 2, 3], 3)).to.deep.equal([3]); + }); + + it('works with amount 4', () => { + expect(minCoinChange([1, 2, 3], 4)).to.deep.equal([1, 3]); + }); + + it('works with amount 6', () => { + expect(minCoinChange([1, 2, 3], 6)).to.deep.equal([3, 3]); + }); +}); diff --git a/test/ts/algorithms/search/binary-search-recursive.spec.ts b/test/ts/algorithms/search/binary-search-recursive.spec.ts new file mode 100644 index 00000000..b06ce773 --- /dev/null +++ b/test/ts/algorithms/search/binary-search-recursive.spec.ts @@ -0,0 +1,5 @@ +import { binarySearchRecursive } from '../../../../src/ts/index'; +import { testSearchAlgorithm } from './search-algorithms-tests'; + +testSearchAlgorithm(binarySearchRecursive, 'Binary Search Recursive'); + diff --git a/test/ts/algorithms/search/binary-search.spec.ts b/test/ts/algorithms/search/binary-search.spec.ts new file mode 100644 index 00000000..f62d0e96 --- /dev/null +++ b/test/ts/algorithms/search/binary-search.spec.ts @@ -0,0 +1,5 @@ +import { binarySearch } from '../../../../src/ts/index'; +import { testSearchAlgorithm } from './search-algorithms-tests'; + +testSearchAlgorithm(binarySearch, 'Binary Search'); + diff --git a/test/ts/algorithms/search/interpolation-search.spec.ts b/test/ts/algorithms/search/interpolation-search.spec.ts new file mode 100644 index 00000000..287351f2 --- /dev/null +++ b/test/ts/algorithms/search/interpolation-search.spec.ts @@ -0,0 +1,5 @@ +import { interpolationSearch } from '../../../../src/ts/index'; +import { testSearchAlgorithm } from './search-algorithms-tests'; + +testSearchAlgorithm(interpolationSearch, 'Interpolation Search', { customEquals: false }); + diff --git a/test/ts/algorithms/search/min-max-search.spec.ts b/test/ts/algorithms/search/min-max-search.spec.ts new file mode 100644 index 00000000..d0dd76c3 --- /dev/null +++ b/test/ts/algorithms/search/min-max-search.spec.ts @@ -0,0 +1,32 @@ +import 'mocha'; +import { expect } from 'chai'; +import { findMinValue, findMaxValue } from '../../../../src/ts/index'; + +describe('Min and Max Values Search', () => { + + const SIZE = 10; + + function createSortedArray() { + const array: number[] = []; + for (let i = 1; i <= SIZE; i++) { + array.push(i); + } + return array; + } + + it('min value - works with empty arrays', () => { + expect(findMinValue([])).to.equal(undefined); + }); + + it('max value - works with empty arrays', () => { + expect(findMaxValue([])).to.equal(undefined); + }); + + it('min value', () => { + expect(findMinValue(createSortedArray())).to.equal(1); + }); + + it('max value', () => { + expect(findMaxValue(createSortedArray())).to.equal(SIZE); + }); +}); diff --git a/test/ts/algorithms/search/search-algorithms-tests.ts b/test/ts/algorithms/search/search-algorithms-tests.ts new file mode 100644 index 00000000..05d1d88d --- /dev/null +++ b/test/ts/algorithms/search/search-algorithms-tests.ts @@ -0,0 +1,49 @@ +import 'mocha'; +import { expect } from 'chai'; +import { IEqualsFunction } from '../../../../src/ts/util'; + +interface CustomObject { + key: number; +} + +const customEquals: IEqualsFunction = (a: CustomObject, b: CustomObject) => + a.key === b.key; + +export function testSearchAlgorithm( + searchAlgorithm: Function, + algorithmName: string, + config = { customEquals: true } +) { + describe(algorithmName, () => { + const SIZE = 10; + + function createSortedArray() { + const array: number[] = []; + for (let i = 1; i <= SIZE; i++) { + array.push(i); + } + return array; + } + + it('works with empty arrays', () => { + expect(searchAlgorithm([], 1)).to.equal(-1); + }); + + it('finds value at the first position', () => { + const array = createSortedArray(); + expect(searchAlgorithm(array, 1)).to.equal(0); + }); + + it('finds value at the last position', () => { + const array = createSortedArray(); + expect(searchAlgorithm(array, SIZE)).to.equal(SIZE - 1); + }); + + if (config.customEquals) { + it('finds value with custom equals function', () => { + const array = [{ key: 1 }, { key: 2 }, { key: 3 }]; + expect(searchAlgorithm(array, { key: 2 }, customEquals)).to.equal(1); + }); + } + }); +} diff --git a/test/ts/algorithms/search/sequential-search.spec.ts b/test/ts/algorithms/search/sequential-search.spec.ts new file mode 100644 index 00000000..94ddf885 --- /dev/null +++ b/test/ts/algorithms/search/sequential-search.spec.ts @@ -0,0 +1,5 @@ +import { sequentialSearch } from '../../../../src/ts/index'; +import { testSearchAlgorithm } from './search-algorithms-tests'; + +testSearchAlgorithm(sequentialSearch, 'Sequential Search'); + diff --git "a/test/ts/algorithms/shuffle/fisher\342\200\223yates.spec.ts" "b/test/ts/algorithms/shuffle/fisher\342\200\223yates.spec.ts" new file mode 100644 index 00000000..da450f41 --- /dev/null +++ "b/test/ts/algorithms/shuffle/fisher\342\200\223yates.spec.ts" @@ -0,0 +1,33 @@ +import 'mocha'; +import { expect } from 'chai'; +import { shuffle } from '../../../../src/ts/index'; + +describe('Fisher-Yates Suffle', () => { + + const SIZE = 100; + + function createSortedArray() { + const array: number[] = []; + for (let i = 1; i <= SIZE; i++) { + array.push(i); + } + return array; + } + + it('works with empty arrays', () => { + expect(shuffle([])).to.deep.equal([]); + }); + + it('works with arrays with a single value', () => { + const array = [1]; + expect(shuffle(array)).to.deep.equal(array); + }); + + it('works with sorted arrays', () => { + let array = createSortedArray(); + const sortedArray = createSortedArray(); + array = shuffle(array); + expect(array).to.not.deep.equal(sortedArray); + }); + +}); diff --git a/test/ts/algorithms/sorting/bubble-sort-improved.spec.ts b/test/ts/algorithms/sorting/bubble-sort-improved.spec.ts new file mode 100644 index 00000000..7686a5fd --- /dev/null +++ b/test/ts/algorithms/sorting/bubble-sort-improved.spec.ts @@ -0,0 +1,5 @@ +import { modifiedBubbleSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(modifiedBubbleSort, 'Bubble Sort - Improved'); + diff --git a/test/ts/algorithms/sorting/bubble-sort.spec.ts b/test/ts/algorithms/sorting/bubble-sort.spec.ts new file mode 100644 index 00000000..2804fe9c --- /dev/null +++ b/test/ts/algorithms/sorting/bubble-sort.spec.ts @@ -0,0 +1,5 @@ +import { bubbleSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(bubbleSort, 'Bubble Sort'); + diff --git a/test/ts/algorithms/sorting/bucket-sort.spec.ts b/test/ts/algorithms/sorting/bucket-sort.spec.ts new file mode 100644 index 00000000..1a4ec458 --- /dev/null +++ b/test/ts/algorithms/sorting/bucket-sort.spec.ts @@ -0,0 +1,4 @@ +import { bucketSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(bucketSort, 'Bucket Sort', {reverseCompare: false}); diff --git a/test/ts/algorithms/sorting/counting-sort.spec.ts b/test/ts/algorithms/sorting/counting-sort.spec.ts new file mode 100644 index 00000000..c3fee9a5 --- /dev/null +++ b/test/ts/algorithms/sorting/counting-sort.spec.ts @@ -0,0 +1,4 @@ +import { countingSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(countingSort, 'Counting Sort', {reverseCompare: false}); diff --git a/test/ts/algorithms/sorting/heap-sort.spec.ts b/test/ts/algorithms/sorting/heap-sort.spec.ts new file mode 100644 index 00000000..b0b95da9 --- /dev/null +++ b/test/ts/algorithms/sorting/heap-sort.spec.ts @@ -0,0 +1,5 @@ +import { heapSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(heapSort, 'Heap Sort'); + diff --git a/test/ts/algorithms/sorting/insertion-sort.spec.ts b/test/ts/algorithms/sorting/insertion-sort.spec.ts new file mode 100644 index 00000000..86beb941 --- /dev/null +++ b/test/ts/algorithms/sorting/insertion-sort.spec.ts @@ -0,0 +1,5 @@ +import { insertionSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(insertionSort, 'Insertion Sort'); + diff --git a/test/ts/algorithms/sorting/merge-sort.spec.ts b/test/ts/algorithms/sorting/merge-sort.spec.ts new file mode 100644 index 00000000..cf4a7418 --- /dev/null +++ b/test/ts/algorithms/sorting/merge-sort.spec.ts @@ -0,0 +1,5 @@ +import { mergeSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(mergeSort, 'Merge Sort'); + diff --git a/test/ts/algorithms/sorting/quicksort.spec.ts b/test/ts/algorithms/sorting/quicksort.spec.ts new file mode 100644 index 00000000..348c58f4 --- /dev/null +++ b/test/ts/algorithms/sorting/quicksort.spec.ts @@ -0,0 +1,5 @@ +import { quickSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(quickSort, 'Quick Sort'); + diff --git a/test/ts/algorithms/sorting/radix-sort.spec.ts b/test/ts/algorithms/sorting/radix-sort.spec.ts new file mode 100644 index 00000000..36f909ce --- /dev/null +++ b/test/ts/algorithms/sorting/radix-sort.spec.ts @@ -0,0 +1,5 @@ +import { radixSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(radixSort, 'Radix Sort', {reverseCompare: false}); + diff --git a/test/ts/algorithms/sorting/selection-sort.spec.ts b/test/ts/algorithms/sorting/selection-sort.spec.ts new file mode 100644 index 00000000..a1359074 --- /dev/null +++ b/test/ts/algorithms/sorting/selection-sort.spec.ts @@ -0,0 +1,5 @@ +import { selectionSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(selectionSort, 'Selection Sort'); + diff --git a/test/ts/algorithms/sorting/shell-sort.spec.ts b/test/ts/algorithms/sorting/shell-sort.spec.ts new file mode 100644 index 00000000..4683a70c --- /dev/null +++ b/test/ts/algorithms/sorting/shell-sort.spec.ts @@ -0,0 +1,5 @@ +import { shellSort } from '../../../../src/ts/index'; +import { testSortAlgorithm } from './sort-algorithm-tests'; + +testSortAlgorithm(shellSort, 'Shell Sort'); + diff --git a/test/ts/algorithms/sorting/sort-algorithm-tests.ts b/test/ts/algorithms/sorting/sort-algorithm-tests.ts new file mode 100644 index 00000000..c1bacf3d --- /dev/null +++ b/test/ts/algorithms/sorting/sort-algorithm-tests.ts @@ -0,0 +1,73 @@ +import 'mocha'; +import { expect } from 'chai'; +import { Compare } from '../../../../src/ts/util'; + +export function testSortAlgorithm( + sortAlgorithm: Function, + algorithmName: string, + config = { reverseCompare: true } +) { + describe(algorithmName, () => { + const SIZE = 100; + + function createNonSortedArray() { + const array: number[] = []; + for (let i = SIZE; i > 0; i--) { + array.push(i); + } + return array; + } + + function createSortedArray() { + const array: number[] = []; + for (let i = 1; i <= SIZE; i++) { + array.push(i); + } + return array; + } + + it('works with empty arrays', () => { + expect(sortAlgorithm([])).to.deep.equal([]); + }); + + it('works with sorted arrays', () => { + let array = createSortedArray(); + const sortedArray = createSortedArray(); + array = sortAlgorithm(array); + expect(array).to.deep.equal(sortedArray); + }); + + it('works with non-sorted arrays', () => { + let array = createNonSortedArray(); + const sortedArray = createSortedArray(); + array = sortAlgorithm(array); + + expect(array).to.deep.equal(sortedArray); + + for (let i = 0; i < array.length - 1; i++) { + expect(array[i] <= array[i + 1]).to.equal(true); + } + }); + + function reverseCompare(a: T, b: T): number { + if (a === b) { + return 0; + } + return a < b ? Compare.BIGGER_THAN : Compare.LESS_THAN; + } + + if (config.reverseCompare) { + it('works with reverse comparator - descending order', () => { + let array = createSortedArray(); + const sortedArray = createNonSortedArray(); + array = sortAlgorithm(array, reverseCompare); + + expect(array).to.deep.equal(sortedArray); + + for (let i = 0; i < array.length - 1; i++) { + expect(array[i] >= array[i + 1]).to.equal(true); + } + }); + } + }); +} diff --git a/test/ts/data-structures/avl-tree.spec.ts b/test/ts/data-structures/avl-tree.spec.ts new file mode 100644 index 00000000..95562b95 --- /dev/null +++ b/test/ts/data-structures/avl-tree.spec.ts @@ -0,0 +1,32 @@ +import 'mocha'; +import { expect } from 'chai'; +import { AVLTree } from '../../../src/ts/index'; + +describe('AVLTree', () => { + let tree: AVLTree; + + beforeEach(() => { + tree = new AVLTree(); + }); + + it('starts empty', () => { + expect(tree.getRoot()).to.equal(undefined); + }); + + it('inserts elements in the AVLTree', () => { + expect(tree.getRoot()).to.equal(undefined); + + tree.insert(1); + tree.insert(2); + tree.insert(3); + tree.insert(4); + tree.insert(5); + tree.insert(6); + tree.insert(7); + tree.insert(14); + tree.insert(15); + tree.insert(13); + tree.insert(12); + tree.insert(11); + }); +}); diff --git a/test/ts/data-structures/binary-search-tree.spec.ts b/test/ts/data-structures/binary-search-tree.spec.ts new file mode 100644 index 00000000..419f8e10 --- /dev/null +++ b/test/ts/data-structures/binary-search-tree.spec.ts @@ -0,0 +1,109 @@ +import 'mocha'; +import { expect } from 'chai'; +import { BinarySearchTree } from '../../../src/ts/index'; + +describe('BinarySearchTree', () => { + let tree: BinarySearchTree; + + beforeEach(() => { + tree = new BinarySearchTree(); + }); + + it('starts empty', () => { + expect(tree.getRoot()).to.equal(undefined); + }); + + function assertNode(node: any, key: number, left: number, right: number) { + + if (key != null) { + expect(node.key).to.equal(key); + } else { + expect(node).to.equal(key); + return; + } + + if (left != null) { + expect(node.left.key).to.equal(left); + } else { + expect(node.left).to.equal(left); + } + + if (right != null) { + expect(node.right.key).to.equal(right); + } else { + expect(node.right).to.equal(right); + } + } + + it('inserts elements in the BST', () => { + expect(tree.getRoot()).to.equal(undefined); + + tree.insert(11); + tree.insert(7); + tree.insert(15); + tree.insert(5); + tree.insert(3); + tree.insert(9); + tree.insert(8); + tree.insert(10); + tree.insert(13); + tree.insert(12); + tree.insert(14); + tree.insert(20); + tree.insert(18); + tree.insert(25); + + let node = tree.getRoot(); + assertNode(node, 11, 7, 15); + + node = node.left; + assertNode(node, 7, 5, 9); + + node = node.left; + assertNode(node, 5, 3, undefined); + + node = node.left; + assertNode(node, 3, undefined, undefined); + + node = tree.getRoot().left.left.right; + assertNode(node, undefined, undefined, undefined); + + node = tree.getRoot().left.right; + assertNode(node, 9, 8, 10); + + node = node.left; + assertNode(node, 8, undefined, undefined); + + node = tree.getRoot().left.right.right; + assertNode(node, 10, undefined, undefined); + + node = tree.getRoot().right; + assertNode(node, 15, 13, 20); + + node = node.left; + assertNode(node, 13, 12, 14); + + node = node.left; + assertNode(node, 12, undefined, undefined); + + node = tree.getRoot().right.left.right; + assertNode(node, 14, undefined, undefined); + + node = tree.getRoot().right.right; + assertNode(node, 20, 18, 25); + + node = node.left; + assertNode(node, 18, undefined, undefined); + + node = tree.getRoot().right.right.right; + assertNode(node, 25, undefined, undefined); + }); + + it('verifies if element exists', () => { + expect(tree.getRoot()).to.equal(undefined); + }); + + it('removes a leaf', () => { + expect(tree.getRoot()).to.equal(undefined); + }); +}); diff --git a/test/ts/data-structures/circular-linked-list.spec.ts b/test/ts/data-structures/circular-linked-list.spec.ts new file mode 100644 index 00000000..78312614 --- /dev/null +++ b/test/ts/data-structures/circular-linked-list.spec.ts @@ -0,0 +1,342 @@ +import 'mocha'; +import { expect } from 'chai'; +import { CircularLinkedList } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('CircularLinkedList', () => { + let list: CircularLinkedList; + let min: number; + let max: number; + + beforeEach(() => { + list = new CircularLinkedList(); + min = 1; + max = 3; + }); + + function pushesElements() { + for (let i = min; i <= max; i++) { + list.push(i); + } + } + + function verifyList() { + let current = list.getHead(); + for (let i = min; i <= max && current; i++) { + expect(current).to.not.be.an('undefined'); + if (current) { + // TS strictNullChecks + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(i); + if (i < max) { + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + // TS strictNullChecks + expect(current.next.element).to.equal(i + 1); + } + } else { + // circular list + expect(current.next).to.not.be.an('undefined'); + expect(current.next).to.equal(list.getHead()); + if (current.next) { + expect(current.next.element).to.equal(min); + } + } + current = current.next; + } + } + } + + it('starts empty', () => { + expect(list.size()).to.equal(0); + expect(list.isEmpty()).to.equal(true); + expect(list.getHead()).to.be.an('undefined'); + }); + + it('pushes elements', () => { + pushesElements(); + verifyList(); + }); + + it('returns element at specific index: invalid position', () => { + // list is empty + expect(list.getElementAt(3)).to.be.an('undefined'); + }); + + it('returns element at specific index', () => { + let node; + + pushesElements(); + + for (let i = min; i <= max; i++) { + node = list.getElementAt(i - 1); + expect(node).to.not.be.an('undefined'); + if (node) { + expect(node.element).to.equal(i); + } + } + }); + + it('inserts elements first position empty list', () => { + const element = 1; + max = element; + expect(list.insert(element, 0)).to.equal(true); + verifyList(); + }); + + it('inserts elements first position not empty list', () => { + max = 2; + expect(list.insert(max, 0)).to.equal(true); + + expect(list.insert(min, 0)).to.equal(true); + + verifyList(); + }); + + it('inserts elements invalid position empty list', () => { + expect(list.insert(1, 1)).to.equal(false); + }); + + it('inserts elements invalid position not empty list', () => { + const element = 1; + expect(list.insert(element, 0)).to.equal(true); + expect(list.insert(element, 2)).to.equal(false); + }); + + it('inserts elements in the middle of list', () => { + expect(list.insert(3, 0)).to.equal(true); + expect(list.insert(1, 0)).to.equal(true); + expect(list.insert(2, 1)).to.equal(true); + verifyList(); + }); + + it('inserts elements at the end of list', () => { + max = 5; + + for (let i = min; i <= max; i++) { + expect(list.insert(i , i - 1)).to.equal(true); + } + + verifyList(); + }); + + it('returns index of elements', () => { + let index; + + pushesElements(); + + for (let i = min; i <= max; i++) { + index = list.indexOf(i); + expect(index).to.equal(i - 1); + } + + expect(list.indexOf(max + 2)).to.equal(-1); + }); + + it('removes valid elements', () => { + let element; + + pushesElements(); + + const minIndex = min; + for (let i = minIndex; i <= max; i++) { + element = list.remove(i); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + min++; + verifyList(); + } + }); + + it('removes invalid elements', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.remove(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position empty list', () => { + let element; + + for (let i = min; i <= max; i++) { + element = list.removeAt(i - 1); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position not empty list', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.removeAt(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes first element list single element', () => { + const value = 1; + list.push(value); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(value); + + expect(list.getHead()).to.be.an('undefined'); + expect(list.isEmpty()).to.equal(true); + }); + + it('removes first element list multiple elements', () => { + pushesElements(); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(min); + + min = 2; + verifyList(); + }); + + it('removes element from middle of list', () => { + pushesElements(); // 1, 2, 3 + + const element = list.removeAt(1); // element 2 + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(2); + + // list needs to be [1, 3] + let current = list.getHead(); + + // element 1 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(min); + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + expect(current.next.element).to.equal(max); + current = current.next; + } + } + + // element 3 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(max); + expect(current.next).to.not.be.an('undefined'); + expect(current.next).to.equal(list.getHead()); + if (current.next) { + expect(current.next.element).to.equal(min); + } + } + }); + + it('removes element from end of list', () => { + let element; + + pushesElements(); + + const maxIndex = max; + for (let i = maxIndex; i >= min; i--) { + element = list.removeAt(i - 1); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + max--; + verifyList(); + } + }); + + it('returns the head of the list', () => { + expect(list.getHead()).to.be.an('undefined'); + + list.push(1); + expect(list.getHead()).to.not.be.an('undefined'); + }); + + it('returns the correct size', () => { + expect(list.size()).to.equal(0); + + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.size()).to.equal(i); + } + + const size = max; + for (let i = min; i <= max; i++) { + list.remove(i); + expect(list.size()).to.equal(size - i); + } + + expect(list.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(list.isEmpty()).to.equal(true); + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.isEmpty()).to.equal(false); + } + + for (let i = min; i < max; i++) { + list.remove(i); + expect(list.isEmpty()).to.equal(false); + } + list.remove(max); + expect(list.isEmpty()).to.equal(true); + + pushesElements(); + expect(list.isEmpty()).to.equal(false); + + list.clear(); + expect(list.isEmpty()).to.equal(true); + }); + + it('clears the list', () => { + expect(list.size()).to.equal(0); + list.clear(); + expect(list.size()).to.equal(0); + pushesElements(); + expect(list.size()).to.greaterThan(0); + list.clear(); + expect(list.size()).to.equal(0); + }); + + it('returns toString primitive types', () => { + expect(list.toString()).to.equal(''); + + list.push(1); + expect(list.toString()).to.equal('1'); + + list.push(2); + expect(list.toString()).to.equal('1,2'); + + list.clear(); + expect(list.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const ds = new CircularLinkedList(); + ds.push('el1'); + expect(ds.toString()).to.equal('el1'); + + ds.push('el2'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const ds = new CircularLinkedList(); + expect(ds.toString()).to.equal(''); + + ds.push(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2'); + + ds.push(new MyObj(3, 4)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/data-structures/deque.spec.ts b/test/ts/data-structures/deque.spec.ts new file mode 100644 index 00000000..7dcb4c8a --- /dev/null +++ b/test/ts/data-structures/deque.spec.ts @@ -0,0 +1,204 @@ +import 'mocha'; +import { expect } from 'chai'; +import Deque from '../../../src/ts/data-structures/deque'; + +describe('Deque', () => { + let deque: Deque; + + beforeEach(() => { + deque = new Deque(); + }); + + it('starts empty', () => { + expect(deque.size()).to.equal(0); + expect(deque.isEmpty()).to.equal(true); + }); + + it('add elements in the back', () => { + deque.addBack(1); + expect(deque.size()).to.equal(1); + + deque.addBack(2); + expect(deque.size()).to.equal(2); + + deque.addBack(3); + expect(deque.size()).to.equal(3); + }); + + it('add elements in the front', () => { + deque.addFront(1); + expect(deque.size()).to.equal(1); + + deque.addFront(2); + expect(deque.size()).to.equal(2); + + deque.addFront(3); + expect(deque.size()).to.equal(3); + + deque.removeFront(); + deque.addFront(4); + expect(deque.size()).to.equal(3); + }); + + it('remove elements from the back', () => { + deque.addBack(1); + deque.addBack(2); + deque.addBack(3); + deque.addFront(0); + + expect(deque.removeBack()).to.equal(3); + expect(deque.removeBack()).to.equal(2); + expect(deque.removeBack()).to.equal(1); + expect(deque.removeBack()).to.equal(0); + expect(deque.removeBack()).to.equal(undefined); + }); + + it('remove elements from the front', () => { + deque.addFront(1); + deque.addBack(2); + deque.addBack(3); + deque.addFront(0); + deque.addFront(-1); + deque.addFront(-2); + + expect(deque.removeFront()).to.equal(-2); + expect(deque.removeFront()).to.equal(-1); + expect(deque.removeFront()).to.equal(0); + expect(deque.removeFront()).to.equal(1); + expect(deque.removeFront()).to.equal(2); + expect(deque.removeFront()).to.equal(3); + expect(deque.removeFront()).to.equal(undefined); + }); + + it('allows to peek at the front element in the deque without removing it', () => { + expect(deque.peekFront()).to.equal(undefined); + + deque.addFront(1); + expect(deque.peekFront()).to.equal(1); + deque.addBack(2); + expect(deque.peekFront()).to.equal(1); + deque.addBack(3); + expect(deque.peekFront()).to.equal(1); + deque.addFront(0); + expect(deque.peekFront()).to.equal(0); + deque.addFront(-1); + expect(deque.peekFront()).to.equal(-1); + deque.addFront(-2); + expect(deque.peekFront()).to.equal(-2); + }); + + it('allows to peek at the last element in the deque without removing it', () => { + expect(deque.peekBack()).to.equal(undefined); + + deque.addFront(1); + expect(deque.peekBack()).to.equal(1); + deque.addBack(2); + expect(deque.peekBack()).to.equal(2); + deque.addBack(3); + expect(deque.peekBack()).to.equal(3); + deque.addFront(0); + expect(deque.peekBack()).to.equal(3); + deque.addFront(-1); + expect(deque.peekBack()).to.equal(3); + deque.addFront(-2); + expect(deque.peekBack()).to.equal(3); + }); + + it('returns the correct size', () => { + expect(deque.size()).to.equal(0); + + deque.addFront(1); + expect(deque.size()).to.equal(1); + deque.addBack(2); + expect(deque.size()).to.equal(2); + deque.addBack(3); + expect(deque.size()).to.equal(3); + deque.addFront(0); + expect(deque.size()).to.equal(4); + deque.addFront(-1); + expect(deque.size()).to.equal(5); + deque.addFront(-2); + expect(deque.size()).to.equal(6); + + deque.clear(); + expect(deque.size()).to.equal(0); + + deque.addFront(1); + deque.addBack(2); + expect(deque.size()).to.equal(2); + + deque.removeFront(); + deque.removeBack(); + expect(deque.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(deque.isEmpty()).to.equal(true); + + deque.addFront(1); + expect(deque.isEmpty()).to.equal(false); + deque.addBack(2); + expect(deque.isEmpty()).to.equal(false); + + deque.clear(); + expect(deque.isEmpty()).to.equal(true); + + deque.addFront(1); + deque.addBack(2); + expect(deque.isEmpty()).to.equal(false); + + deque.removeFront(); + expect(deque.isEmpty()).to.equal(false); + deque.removeBack(); + expect(deque.isEmpty()).to.equal(true); + }); + + it('clears the queue', () => { + deque.clear(); + expect(deque.isEmpty()).to.equal(true); + + deque.addFront(1); + deque.addBack(2); + expect(deque.isEmpty()).to.equal(false); + + deque.clear(); + expect(deque.isEmpty()).to.equal(true); + }); + + it('returns toString primitive types', () => { + expect(deque.toString()).to.equal(''); + + deque.addFront(1); + expect(deque.toString()).to.equal('1'); + + deque.addBack(2); + expect(deque.toString()).to.equal('1,2'); + + deque.clear(); + expect(deque.toString()).to.equal(''); + + const queueString = new Deque(); + queueString.addFront('el1'); + expect(queueString.toString()).to.equal('el1'); + + queueString.addBack('el2'); + expect(queueString.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + class MyObj { + constructor(public el1: any, public el2: any) {} + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } + } + const dequeMyObj = new Deque(); + expect(dequeMyObj.toString()).to.equal(''); + + dequeMyObj.addFront(new MyObj(1, 2)); + expect(dequeMyObj.toString()).to.equal('1|2'); + + dequeMyObj.addBack(new MyObj(3, 4)); + expect(dequeMyObj.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/data-structures/dictionary.spec.ts b/test/ts/data-structures/dictionary.spec.ts new file mode 100644 index 00000000..28bf6615 --- /dev/null +++ b/test/ts/data-structures/dictionary.spec.ts @@ -0,0 +1,335 @@ +import 'mocha'; +import { expect } from 'chai'; +import { Dictionary } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('Dictionary', () => { + let dictionary: Dictionary; + + beforeEach(() => { + dictionary = new Dictionary(); + }); + + it('starts empty', () => { + expect(dictionary.size()).to.equal(0); + expect(dictionary.isEmpty()).to.equal(true); + }); + + it('sets undefined and null keys and values', () => { + const dict = new Dictionary(); + + expect(dict.set('undefined', undefined)).to.equal(false); + expect(dict.get('undefined')).to.equal(undefined); + + expect(dict.set('undefined', 1)).to.equal(true); + expect(dict.get('undefined')).to.equal(1); + + expect(dict.set('null', null)).to.equal(false); + expect(dict.get('null')).to.equal(undefined); + + expect(dict.set('null', 1)).to.equal(true); + expect(dict.get('null')).to.equal(1); + + dict.clear(); + expect(dict.set(undefined, undefined)).to.equal(false); + expect(dict.get(undefined)).to.equal(undefined); + + expect(dict.set(undefined, 1)).to.equal(false); + expect(dict.get(undefined)).to.equal(undefined); + + expect(dict.set(null, null)).to.equal(false); + expect(dict.get(null)).to.equal(undefined); + + expect(dict.set(null, 1)).to.equal(false); + expect(dict.get(null)).to.equal(undefined); + }); + + it('sets values with string key', () => { + const dict = new Dictionary(); + const min = 1; + const max = 5; + const size = max - min + 1; + + for (let i = min; i <= max; i++) { + expect(dict.set(`${i}`, i)).to.equal(true); + } + expect(dict.size()).to.equal(size); + + const keys = dict.keys(); + expect(keys.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal((i + 1).toString(10)); + } + + dict.set('a', 1); + expect(dict.get('a')).to.equal(1); + }); + + it('sets values with number key', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + + for (let i = min; i <= max; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + expect(dictionary.size()).to.equal(size); + + const keys = dictionary.keys(); + expect(keys.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal((i + 1)); + } + }); + + it('sets values with object', () => { + const dict = new Dictionary(); + const min = 0; + const max = 5; + const size = max - min; + const myObjList = []; + + for (let i = min; i < max; i++) { + myObjList.push(new MyObj(i, (i + 1))); + } + + for (let i = min; i < max; i++) { + expect(dict.set(myObjList[i], myObjList[i])).to.equal(true); + } + expect(dict.size()).to.equal(size); + + for (let i = min; i < max; i++) { + expect(dict.get(myObjList[i])).to.equal(myObjList[i]); + } + + const keys = dict.keys(); + expect(keys.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal(myObjList[i]); + } + + const values = dict.values(); + expect(values.length).to.equal(size); + for (let i = 0; i < values.length; i++) { + expect(values[i]).to.equal(myObjList[i]); + } + }); + + function customToString(key: MyObj) { + return `####${key.toString()}`; + } + + it('sets values with custom toString function', () => { + const dict = new Dictionary(customToString); + const min = 0; + const max = 5; + const size = max - min; + const myObjList = []; + + for (let i = min; i < max; i++) { + myObjList.push(new MyObj(i, (i + 1))); + } + + for (let i = min; i < max; i++) { + expect(dict.set(myObjList[i], myObjList[i])).to.equal(true); + } + expect(dict.size()).to.equal(size); + + for (let i = min; i < max; i++) { + expect(dict.get(myObjList[i])).to.equal(myObjList[i]); + } + + const keys = dict.keys(); + expect(keys.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal(myObjList[i]); + } + + const values = dict.values(); + expect(values.length).to.equal(size); + for (let i = 0; i < values.length; i++) { + expect(values[i]).to.equal(myObjList[i]); + } + }); + + it('removes elements', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + + for (let i = min; i <= max; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + expect(dictionary.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(dictionary.remove(i)).to.equal(true); + } + + // elements do not exist + for (let i = min; i <= max; i++) { + expect(dictionary.remove(i)).to.equal(false); + } + + expect(dictionary.isEmpty()).to.equal(true); + }); + + it('returns the correct size', () => { + expect(dictionary.size()).to.equal(0); + + const max = 5; + + for (let i = 1; i < max; i++) { + dictionary.set(i, i); + expect(dictionary.size()).to.equal(i); + } + for (let i = 1; i < max; i++) { + dictionary.remove(i); + expect(dictionary.size()).to.equal(max - i - 1); + } + + expect(dictionary.size()).to.equal(0); + expect(dictionary.isEmpty()).to.equal(true); + }); + + it('returns if element exists', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + + for (let i = min; i <= max; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + expect(dictionary.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(dictionary.hasKey(i)).to.equal(true); + expect(dictionary.remove(i)).to.equal(true); + expect(dictionary.hasKey(i)).to.equal(false); + } + }); + + it('returns if it is empty', () => { + expect(dictionary.isEmpty()).to.equal(true); + + for (let i = 1; i < 5; i++) { + dictionary.set(i, i); + expect(dictionary.isEmpty()).to.equal(false); + } + + for (let i = 1; i < 5; i++) { + dictionary.remove(i); + expect(dictionary.isEmpty()).to.equal(!(i < 4)); + } + + expect(dictionary.size()).to.equal(0); + expect(dictionary.isEmpty()).to.equal(true); + }); + + it('clears the dictionary', () => { + dictionary.clear(); + expect(dictionary.isEmpty()).to.equal(true); + + dictionary.set(1, 1); + dictionary.set(2, 2); + + dictionary.clear(); + expect(dictionary.isEmpty()).to.equal(true); + }); + + it('returns values, keys and value pairs', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + + for (let i = min; i <= max; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + expect(dictionary.size()).to.equal(size); + + const keys = dictionary.keys(); + const values = dictionary.values(); + const valuePairs = dictionary.keyValues(); + expect(keys.length).to.equal(size); + expect(values.length).to.equal(size); + expect(valuePairs.length).to.equal(size); + for (let i = 0; i < keys.length; i++) { + expect(keys[i]).to.equal((i + 1)); + expect(values[i]).to.equal((i + 1)); + expect(valuePairs[i].key).to.equal((i + 1)); + expect(valuePairs[i].value).to.equal((i + 1)); + } + }); + + it('allows to iterate with forEach', () => { + for (let i = 1; i <= 5; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + + dictionary.forEach((k, v) => { + expect(dictionary.hasKey(k)).to.equal(true); + expect(dictionary.get(k)).to.equal(v); + }); + }); + + it('allows to iterate with forEach and interrupt', () => { + for (let i = 1; i <= 5; i++) { + expect(dictionary.set(i, i)).to.equal(true); + } + + const size = dictionary.keys().length; + + let index = 1; + dictionary.forEach((k, v) => { + expect(dictionary.hasKey(k)).to.equal(true); + expect(dictionary.get(k)).to.equal(v); + index++; + }); + expect(index).to.equal(size + 1); + + index = 1; + dictionary.forEach((k, v) => { + expect(dictionary.hasKey(k)).to.equal(true); + expect(dictionary.get(k)).to.equal(v); + index++; + return !(k % 3 === 0); + }); + expect(index).to.equal(size - 1); + }); + + it('returns toString primitive types', () => { + expect(dictionary.toString()).to.equal(''); + + dictionary.set(1, 1); + expect(dictionary.toString()).to.equal('[#1: 1]'); + + dictionary.set(2, 2); + expect(dictionary.toString()).to.equal('[#1: 1],[#2: 2]'); + + dictionary.clear(); + expect(dictionary.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const dict = new Dictionary(); + dict.set('el1', 1); + expect(dict.toString()).to.equal('[#el1: 1]'); + + dict.set('el2', 2); + expect(dict.toString()).to.equal('[#el1: 1],[#el2: 2]'); + }); + + it('returns toString objects', () => { + const dict = new Dictionary(); + expect(dict.toString()).to.equal(''); + + let myObj = new MyObj(1, 2); + dict.set(myObj, myObj); + expect(dict.toString()).to.equal('[#1|2: 1|2]'); + + myObj = new MyObj(3, 4); + dict.set(myObj, myObj); + expect(dict.toString()).to.equal('[#1|2: 1|2],[#3|4: 3|4]'); + }); + +}); diff --git a/test/ts/data-structures/doubly-linked-list.spec.ts b/test/ts/data-structures/doubly-linked-list.spec.ts new file mode 100644 index 00000000..76abb29a --- /dev/null +++ b/test/ts/data-structures/doubly-linked-list.spec.ts @@ -0,0 +1,406 @@ +import 'mocha'; +import { expect } from 'chai'; +import { DoublyLinkedList } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('DoublyLinkedList', () => { + let list: DoublyLinkedList; + let min: number; + let max: number; + + beforeEach(() => { + list = new DoublyLinkedList(); + min = 1; + max = 3; + }); + + function pushesElements() { + for (let i = min; i <= max; i++) { + list.push(i); + } + } + + function verifyNode(current: any, i: number) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(i); + + // verify next node + if (i < max) { + expect(current.next).to.not.be.an('undefined'); + // TS strictNullChecks + if (current.next) { + expect(current.next.element).to.equal(i + 1); + } + } else { + expect(current.next).to.be.an('undefined'); + } + + // verify previous node + if (i > min) { + expect(current.prev).to.not.be.an('undefined'); + if (current.prev) { + expect(current.prev.element).to.equal(i - 1); + } + } else { + expect(current.prev).to.be.an('undefined'); + } + } + + function verifyList() { + let current = list.getHead(); + for (let i = min; i <= max; i++) { + expect(current).to.not.be.an('undefined'); + // TS strictNullChecks + if (current) { + verifyNode(current, i); + current = current.next; + } + } + verifyListFromTail(); + } + + function verifyListFromTail() { + let current = list.getTail(); + for (let i = max; i >= min; i--) { + expect(current).to.not.be.an('undefined'); + // TS strictNullChecks + if (current) { + verifyNode(current, i); + current = current.prev; + } + } + } + + it('starts empty', () => { + expect(list.size()).to.equal(0); + expect(list.isEmpty()).to.equal(true); + expect(list.getHead()).to.be.an('undefined'); + expect(list.getTail()).to.be.an('undefined'); + }); + + it('pushes elements', () => { + pushesElements(); + verifyList(); + }); + + it('returns element at specific index: invalid position', () => { + // list is empty + expect(list.getElementAt(3)).to.be.an('undefined'); + }); + + it('returns element at specific index', () => { + let node; + + pushesElements(); + + for (let i = min; i <= max; i++) { + node = list.getElementAt(i - 1); + expect(node).to.not.be.an('undefined'); + if (node) { + expect(node.element).to.equal(i); + } + } + }); + + it('inserts elements first position empty list', () => { + const element = 1; + max = element; + expect(list.insert(element, 0)).to.equal(true); + verifyList(); + }); + + it('inserts elements first position not empty list', () => { + max = 2; + expect(list.insert(max, 0)).to.equal(true); + + expect(list.insert(min, 0)).to.equal(true); + + verifyList(); + }); + + it('inserts elements invalid position empty list', () => { + expect(list.insert(1, 1)).to.equal(false); + }); + + it('inserts elements invalid position not empty list', () => { + const element = 1; + expect(list.insert(element, 0)).to.equal(true); + expect(list.insert(element, 2)).to.equal(false); + }); + + it('inserts elements at the end of list', () => { + max = 5; + + for (let i = min; i <= max; i++) { + expect(list.insert(i , i - 1)).to.equal(true); + } + + verifyList(); + }); + + it('inserts elements in the middle of list', () => { + expect(list.insert(3, 0)).to.equal(true); + expect(list.insert(1, 0)).to.equal(true); + expect(list.insert(2, 1)).to.equal(true); + verifyList(); + }); + + it('returns index of elements', () => { + let index; + + pushesElements(); + + for (let i = min; i <= max; i++) { + index = list.indexOf(i); + expect(index).to.equal(i - 1); + } + + expect(list.indexOf(max + 2)).to.equal(-1); + }); + + it('removes invalid elements', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.remove(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes valid elements', () => { + let element; + + pushesElements(); + + for (let i = min; i <= max; i++) { + element = list.remove(i); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + } + }); + + it('removes element invalid position empty list', () => { + let element; + + for (let i = min; i <= max; i++) { + element = list.removeAt(i - 1); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position not empty list', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.removeAt(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes first element list single element', () => { + const value = 1; + list.push(value); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(value); + + expect(list.getHead()).to.be.an('undefined'); + expect(list.getTail()).to.be.an('undefined'); + expect(list.isEmpty()).to.equal(true); + }); + + it('removes first element list multiple elements', () => { + pushesElements(); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(min); + + min = 2; + verifyList(); + }); + + it('removes element from end of list', () => { + let element; + + pushesElements(); + + const maxIndex = max; + for (let i = maxIndex; i >= min; i--) { + element = list.removeAt(i - 1); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + max--; + verifyList(); + } + }); + + it('removes element from middle of list', () => { + pushesElements(); // 1, 2, 3 + + const element = list.removeAt(1); // element 2 + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(2); + + // list needs to be [1, 3] + let current = list.getHead(); + + // element 1 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(1); + expect(current.prev).to.be.an('undefined'); + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + expect(current.next.element).to.equal(3); + current = current.next; + } + } + + // element 3 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(3); + expect(current.next).to.be.an('undefined'); + expect(current.prev).to.not.be.an('undefined'); + if (current.prev) { + expect(current.prev.element).to.equal(1); + } + } + }); + + it('returns the head of the list', () => { + expect(list.getHead()).to.be.an('undefined'); + + list.push(1); + expect(list.getHead()).to.not.be.an('undefined'); + }); + + it('returns the tail of the list', () => { + expect(list.getTail()).to.be.an('undefined'); + + list.push(1); + expect(list.getTail()).to.not.be.an('undefined'); + }); + + it('returns the correct size', () => { + expect(list.size()).to.equal(0); + + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.size()).to.equal(i); + } + + const size = max; + for (let i = min; i <= max; i++) { + list.remove(i); + expect(list.size()).to.equal(size - i); + } + + expect(list.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(list.isEmpty()).to.equal(true); + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.isEmpty()).to.equal(false); + } + + for (let i = min; i < max; i++) { + list.remove(i); + expect(list.isEmpty()).to.equal(false); + } + list.remove(max); + expect(list.isEmpty()).to.equal(true); + + pushesElements(); + expect(list.isEmpty()).to.equal(false); + + list.clear(); + expect(list.isEmpty()).to.equal(true); + }); + + it('clears the list', () => { + expect(list.size()).to.equal(0); + list.clear(); + expect(list.size()).to.equal(0); + pushesElements(); + expect(list.size()).to.greaterThan(0); + list.clear(); + expect(list.size()).to.equal(0); + }); + + it('returns toString primitive types', () => { + expect(list.toString()).to.equal(''); + + list.push(1); + expect(list.toString()).to.equal('1'); + + list.push(2); + expect(list.toString()).to.equal('1,2'); + + list.clear(); + expect(list.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const ds = new DoublyLinkedList(); + ds.push('el1'); + expect(ds.toString()).to.equal('el1'); + + ds.push('el2'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const ds = new DoublyLinkedList(); + expect(ds.toString()).to.equal(''); + + ds.push(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2'); + + ds.push(new MyObj(3, 4)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); + + it('returns inverseToString primitive types', () => { + expect(list.inverseToString()).to.equal(''); + + list.push(1); + expect(list.inverseToString()).to.equal('1'); + + list.push(2); + expect(list.inverseToString()).to.equal('2,1'); + + list.clear(); + expect(list.inverseToString()).to.equal(''); + }); + + it('returns inverseToString primitive types: string', () => { + const ds = new DoublyLinkedList(); + ds.push('el1'); + expect(ds.inverseToString()).to.equal('el1'); + + ds.push('el2'); + expect(ds.inverseToString()).to.equal('el2,el1'); + }); + + it('returns inverseToString objects', () => { + const ds = new DoublyLinkedList(); + expect(ds.inverseToString()).to.equal(''); + + ds.push(new MyObj(1, 2)); + expect(ds.inverseToString()).to.equal('1|2'); + + ds.push(new MyObj(3, 4)); + expect(ds.inverseToString()).to.equal('3|4,1|2'); + }); +}); diff --git a/test/ts/data-structures/hash-table-linear-probing-lazy.spec.ts b/test/ts/data-structures/hash-table-linear-probing-lazy.spec.ts new file mode 100644 index 00000000..7d004349 --- /dev/null +++ b/test/ts/data-structures/hash-table-linear-probing-lazy.spec.ts @@ -0,0 +1,388 @@ +import 'mocha'; +import { expect } from 'chai'; +import { HashTableLinearProbingLazy } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('HashTableLinearProbingLazy', () => { + const A = 'Jonathan'; + const B = 'Jamie'; + const C = 'Sue'; + + it('starts empty', () => { + const hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.size()).to.equal(0); + expect(hashTable.isEmpty()).to.equal(true); + }); + + it('generates hashcode', () => { + // numbers + let hashTable: any = new HashTableLinearProbingLazy(); + expect(hashTable.hashCode(1)).to.equal(1); + expect(hashTable.hashCode(10)).to.equal(10); + expect(hashTable.hashCode(100)).to.equal(100); + expect(hashTable.hashCode(1000)).to.equal(1000); + + // strings + hashTable = new HashTableLinearProbingLazy(); + expect(hashTable.hashCode('1')).to.equal(12); + expect(hashTable.hashCode('10')).to.equal(23); + expect(hashTable.hashCode('100')).to.equal(34); + expect(hashTable.hashCode('1000')).to.equal(8); + expect(hashTable.hashCode('a')).to.equal(23); + expect(hashTable.hashCode('A')).to.equal(28); + expect(hashTable.hashCode('Aba')).to.equal(1); + + // objects + hashTable = new HashTableLinearProbingLazy(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + expect(hashTable.hashCode(myObjList[0])).to.equal(1); + expect(hashTable.hashCode(myObjList[1])).to.equal(3); + expect(hashTable.hashCode(myObjList[2])).to.equal(5); + expect(hashTable.hashCode(myObjList[3])).to.equal(7); + expect(hashTable.hashCode(myObjList[4])).to.equal(9); + }); + + it('puts undefined and null keys and values', () => { + const hashTable = new HashTableLinearProbingLazy(); + + expect(hashTable.put('undefined', undefined)).to.equal(false); + expect(hashTable.get('undefined')).to.equal(undefined); + + expect(hashTable.put('undefined', 1)).to.equal(true); + expect(hashTable.get('undefined')).to.equal(1); + + expect(hashTable.put('null', null)).to.equal(false); + expect(hashTable.get('null')).to.equal(undefined); + + expect(hashTable.put('null', 1)).to.equal(true); + expect(hashTable.get('null')).to.equal(1); + + hashTable.clear(); + expect(hashTable.put(undefined, undefined)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + + expect(hashTable.put(undefined, 1)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + + expect(hashTable.put(null, null)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + + expect(hashTable.put(null, 1)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + }); + + it('puts values with number key without collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableLinearProbingLazy(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + } + }); + + it('puts values with string key without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + + expect(hashTable.put('1', 1)).to.equal(true); + expect(hashTable.put('10', 10)).to.equal(true); + expect(hashTable.put('100', 100)).to.equal(true); + expect(hashTable.put('1000', 1000)).to.equal(true); + + const table = hashTable.getTable(); + + expect(table[12].key).to.equal('1'); + expect(table[12].value).to.equal(1); + + expect(table[23].key).to.equal('10'); + expect(table[23].value).to.equal(10); + + expect(table[34].key).to.equal('100'); + expect(table[34].value).to.equal(100); + + expect(table[8].key).to.equal('1000'); + expect(table[8].value).to.equal(1000); + }); + + it('puts values with object key without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + expect(hashTable.put(myObjList[i - 1], myObjList[i - 1])).to.equal(true); + } + + const table = hashTable.getTable(); + + expect(table[1].key).to.equal(myObjList[0]); + expect(table[1].value).to.equal(myObjList[0]); + + expect(table[3].key).to.equal(myObjList[1]); + expect(table[3].value).to.equal(myObjList[1]); + + expect(table[5].key).to.equal(myObjList[2]); + expect(table[5].value).to.equal(myObjList[2]); + + expect(table[7].key).to.equal(myObjList[3]); + expect(table[7].value).to.equal(myObjList[3]); + + expect(table[9].key).to.equal(myObjList[4]); + expect(table[9].value).to.equal(myObjList[4]); + }); + + function addValuesCollision() { + const hashTable = new HashTableLinearProbingLazy(); + + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.size()).to.equal(3); + + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + + expect(hashTable.size()).to.equal(3); + + return hashTable; + } + + it('puts values with collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableLinearProbingLazy(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 10)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 2); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 100)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 3); + + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + + expect(table[i + size].key).to.equal(i); + expect(table[i + size].value).to.equal(i + 10); + + expect(table[i + size * 2].key).to.equal(i); + expect(table[i + size * 2].value).to.equal(i + 100); + } + + addValuesCollision(); + }); + + it('removes elements without collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableLinearProbingLazy(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(true); + } + + // elements do not exist + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(false); + } + + expect(hashTable.isEmpty()).to.equal(true); + }); + + function removeWithCollision(a: string, b: string, c: string) { + const hashTable = addValuesCollision(); + + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + + expect(hashTable.isEmpty()).to.equal(true); + } + + it('removes elements with collisions: scenario 1', () => { + // test all possibilities for removal + removeWithCollision(A, B, C); + removeWithCollision(A, C, B); + removeWithCollision(B, A, C); + removeWithCollision(B, C, A); + removeWithCollision(C, A, B); + removeWithCollision(C, B, A); + }); + + function addValuesCollision2() { + const hashTable = new HashTableLinearProbingLazy(); + + expect(hashTable.put(')', `parenthesis@email.com`)).to.equal(true); + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put('+', `plus@email.com`)).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(',', `comma@email.com`)).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.put('-', `minus@email.com`)).to.equal(true); + expect(hashTable.put('0', `zero@email.com`)).to.equal(true); + + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + expect(hashTable.hashCode(')')).to.equal(4); + expect(hashTable.hashCode('+')).to.equal(6); + expect(hashTable.hashCode(',')).to.equal(7); + expect(hashTable.hashCode('-')).to.equal(8); + expect(hashTable.hashCode('0')).to.equal(11); + + expect(hashTable.size()).to.equal(8); + + const table = hashTable.getTable(); + expect(table[4].key).to.equal(')'); + expect(table[5].key).to.equal(A); + expect(table[6].key).to.equal('+'); + expect(table[7].key).to.equal(B); + expect(table[8].key).to.equal(','); + expect(table[9].key).to.equal(C); + expect(table[10].key).to.equal('-'); + expect(table[11].key).to.equal('0'); + + return hashTable; + } + + function verifyOtherKeys(hashTable: HashTableLinearProbingLazy) { + expect(hashTable.get(')')).to.not.equal(undefined); + expect(hashTable.get('+')).to.not.equal(undefined); + expect(hashTable.get(',')).to.not.equal(undefined); + expect(hashTable.get('-')).to.not.equal(undefined); + expect(hashTable.get('0')).to.not.equal(undefined); + } + + function removeWithCollision2(a: string, b: string, c: string) { + const hashTable = addValuesCollision2(); + + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + verifyOtherKeys(hashTable); + + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + verifyOtherKeys(hashTable); + + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + verifyOtherKeys(hashTable); + } + + it('removes elements with collisions: scenario 2', () => { + // test all possibilities for removal + removeWithCollision2(A, B, C); + removeWithCollision2(A, C, B); + removeWithCollision2(B, A, C); + removeWithCollision2(B, C, A); + removeWithCollision2(C, A, B); + removeWithCollision2(C, B, A); + }); + + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + + expect(hashTable.toString()).to.equal(''); + + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); + + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + + hashTable.put('el1', 1); + expect(hashTable.toString()).to.equal('{36 => [#el1: 1]}'); + + hashTable.put('el2', 2); + expect(hashTable.toString()).to.equal('{0 => [#el2: 2]},{36 => [#el1: 1]}'); + }); + + it('returns toString objects without collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + + let myObj = new MyObj(1, 2); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]}'); + + myObj = new MyObj(3, 4); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal( + '{1 => [#1|2: 1|2]},{5 => [#3|4: 3|4]}' + ); + }); + + it('returns toString with collisions', () => { + const hashTable = new HashTableLinearProbingLazy(); + + expect(hashTable.toString()).to.equal(''); + + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + + hashTable.put(1, 10); + expect(hashTable.toString()).to.equal( + '{1 => [#1: 1]},{2 => [#2: 2]},{3 => [#1: 10]}' + ); + + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); +}); diff --git a/test/ts/data-structures/hash-table-linear-probing.spec.ts b/test/ts/data-structures/hash-table-linear-probing.spec.ts new file mode 100644 index 00000000..77ed340f --- /dev/null +++ b/test/ts/data-structures/hash-table-linear-probing.spec.ts @@ -0,0 +1,388 @@ +import 'mocha'; +import { expect } from 'chai'; +import { HashTableLinearProbing } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('HashTableLinearProbing', () => { + const A = 'Jonathan'; + const B = 'Jamie'; + const C = 'Sue'; + + it('starts empty', () => { + const hashTable = new HashTableLinearProbing(); + expect(hashTable.size()).to.equal(0); + expect(hashTable.isEmpty()).to.equal(true); + }); + + it('generates hashcode', () => { + // numbers + let hashTable: any = new HashTableLinearProbing(); + expect(hashTable.hashCode(1)).to.equal(1); + expect(hashTable.hashCode(10)).to.equal(10); + expect(hashTable.hashCode(100)).to.equal(100); + expect(hashTable.hashCode(1000)).to.equal(1000); + + // strings + hashTable = new HashTableLinearProbing(); + expect(hashTable.hashCode('1')).to.equal(12); + expect(hashTable.hashCode('10')).to.equal(23); + expect(hashTable.hashCode('100')).to.equal(34); + expect(hashTable.hashCode('1000')).to.equal(8); + expect(hashTable.hashCode('a')).to.equal(23); + expect(hashTable.hashCode('A')).to.equal(28); + expect(hashTable.hashCode('Aba')).to.equal(1); + + // objects + hashTable = new HashTableLinearProbing(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + expect(hashTable.hashCode(myObjList[0])).to.equal(1); + expect(hashTable.hashCode(myObjList[1])).to.equal(3); + expect(hashTable.hashCode(myObjList[2])).to.equal(5); + expect(hashTable.hashCode(myObjList[3])).to.equal(7); + expect(hashTable.hashCode(myObjList[4])).to.equal(9); + }); + + it('puts undefined and null keys and values', () => { + const hashTable = new HashTableLinearProbing(); + + expect(hashTable.put('undefined', undefined)).to.equal(false); + expect(hashTable.get('undefined')).to.equal(undefined); + + expect(hashTable.put('undefined', 1)).to.equal(true); + expect(hashTable.get('undefined')).to.equal(1); + + expect(hashTable.put('null', null)).to.equal(false); + expect(hashTable.get('null')).to.equal(undefined); + + expect(hashTable.put('null', 1)).to.equal(true); + expect(hashTable.get('null')).to.equal(1); + + hashTable.clear(); + expect(hashTable.put(undefined, undefined)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + + expect(hashTable.put(undefined, 1)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + + expect(hashTable.put(null, null)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + + expect(hashTable.put(null, 1)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + }); + + it('puts values with number key without collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableLinearProbing(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + } + }); + + it('puts values with string key without collisions', () => { + const hashTable = new HashTableLinearProbing(); + + expect(hashTable.put('1', 1)).to.equal(true); + expect(hashTable.put('10', 10)).to.equal(true); + expect(hashTable.put('100', 100)).to.equal(true); + expect(hashTable.put('1000', 1000)).to.equal(true); + + const table = hashTable.getTable(); + + expect(table[12].key).to.equal('1'); + expect(table[12].value).to.equal(1); + + expect(table[23].key).to.equal('10'); + expect(table[23].value).to.equal(10); + + expect(table[34].key).to.equal('100'); + expect(table[34].value).to.equal(100); + + expect(table[8].key).to.equal('1000'); + expect(table[8].value).to.equal(1000); + }); + + it('puts values with object key without collisions', () => { + const hashTable = new HashTableLinearProbing(); + + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + expect(hashTable.put(myObjList[i - 1], myObjList[i - 1])).to.equal(true); + } + + const table = hashTable.getTable(); + + expect(table[1].key).to.equal(myObjList[0]); + expect(table[1].value).to.equal(myObjList[0]); + + expect(table[3].key).to.equal(myObjList[1]); + expect(table[3].value).to.equal(myObjList[1]); + + expect(table[5].key).to.equal(myObjList[2]); + expect(table[5].value).to.equal(myObjList[2]); + + expect(table[7].key).to.equal(myObjList[3]); + expect(table[7].value).to.equal(myObjList[3]); + + expect(table[9].key).to.equal(myObjList[4]); + expect(table[9].value).to.equal(myObjList[4]); + }); + + function addValuesCollision() { + const hashTable = new HashTableLinearProbing(); + + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.size()).to.equal(3); + + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + + expect(hashTable.size()).to.equal(3); + + return hashTable; + } + + it('puts values with collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableLinearProbing(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 10)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 2); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 100)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 3); + + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + + expect(table[i + size].key).to.equal(i); + expect(table[i + size].value).to.equal(i + 10); + + expect(table[i + size * 2].key).to.equal(i); + expect(table[i + size * 2].value).to.equal(i + 100); + } + + addValuesCollision(); + }); + + it('removes elements without collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableLinearProbing(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(true); + } + + // elements do not exist + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(false); + } + + expect(hashTable.isEmpty()).to.equal(true); + }); + + function removeWithCollision(a: string, b: string, c: string) { + const hashTable = addValuesCollision(); + + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + + expect(hashTable.isEmpty()).to.equal(true); + } + + it('removes elements with collisions: scenario 1', () => { + // test all possibilities for removal + removeWithCollision(A, B, C); + removeWithCollision(A, C, B); + removeWithCollision(B, A, C); + removeWithCollision(B, C, A); + removeWithCollision(C, A, B); + removeWithCollision(C, B, A); + }); + + function addValuesCollision2() { + const hashTable = new HashTableLinearProbing(); + + expect(hashTable.put(')', `parenthesis@email.com`)).to.equal(true); + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put('+', `plus@email.com`)).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(',', `comma@email.com`)).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.put('-', `minus@email.com`)).to.equal(true); + expect(hashTable.put('0', `zero@email.com`)).to.equal(true); + + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + expect(hashTable.hashCode(')')).to.equal(4); + expect(hashTable.hashCode('+')).to.equal(6); + expect(hashTable.hashCode(',')).to.equal(7); + expect(hashTable.hashCode('-')).to.equal(8); + expect(hashTable.hashCode('0')).to.equal(11); + + expect(hashTable.size()).to.equal(8); + + const table = hashTable.getTable(); + expect(table[4].key).to.equal(')'); + expect(table[5].key).to.equal(A); + expect(table[6].key).to.equal('+'); + expect(table[7].key).to.equal(B); + expect(table[8].key).to.equal(','); + expect(table[9].key).to.equal(C); + expect(table[10].key).to.equal('-'); + expect(table[11].key).to.equal('0'); + + return hashTable; + } + + function verifyOtherKeys(hashTable: HashTableLinearProbing) { + expect(hashTable.get(')')).to.not.equal(undefined); + expect(hashTable.get('+')).to.not.equal(undefined); + expect(hashTable.get(',')).to.not.equal(undefined); + expect(hashTable.get('-')).to.not.equal(undefined); + expect(hashTable.get('0')).to.not.equal(undefined); + } + + function removeWithCollision2(a: string, b: string, c: string) { + const hashTable = addValuesCollision2(); + + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + verifyOtherKeys(hashTable); + + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + verifyOtherKeys(hashTable); + + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + verifyOtherKeys(hashTable); + } + + it('removes elements with collisions: scenario 2', () => { + // test all possibilities for removal + removeWithCollision2(A, B, C); + removeWithCollision2(A, C, B); + removeWithCollision2(B, A, C); + removeWithCollision2(B, C, A); + removeWithCollision2(C, A, B); + removeWithCollision2(C, B, A); + }); + + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableLinearProbing(); + + expect(hashTable.toString()).to.equal(''); + + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); + + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableLinearProbing(); + + hashTable.put('el1', 1); + expect(hashTable.toString()).to.equal('{36 => [#el1: 1]}'); + + hashTable.put('el2', 2); + expect(hashTable.toString()).to.equal('{0 => [#el2: 2]},{36 => [#el1: 1]}'); + }); + + it('returns toString objects without collisions', () => { + const hashTable = new HashTableLinearProbing(); + + let myObj = new MyObj(1, 2); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]}'); + + myObj = new MyObj(3, 4); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal( + '{1 => [#1|2: 1|2]},{5 => [#3|4: 3|4]}' + ); + }); + + it('returns toString with collisions', () => { + const hashTable = new HashTableLinearProbing(); + + expect(hashTable.toString()).to.equal(''); + + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + + hashTable.put(1, 10); + expect(hashTable.toString()).to.equal( + '{1 => [#1: 1]},{2 => [#2: 2]},{3 => [#1: 10]}' + ); + + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); +}); diff --git a/test/ts/data-structures/hash-table-separate-chaining.spec.ts b/test/ts/data-structures/hash-table-separate-chaining.spec.ts new file mode 100644 index 00000000..b7f72524 --- /dev/null +++ b/test/ts/data-structures/hash-table-separate-chaining.spec.ts @@ -0,0 +1,343 @@ +import 'mocha'; +import { expect } from 'chai'; +import { HashTableSeparateChaining } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('HashTableSeparateChaining', () => { + const A = 'Jonathan'; + const B = 'Jamie'; + const C = 'Sue'; + + it('starts empty', () => { + const hashTable = new HashTableSeparateChaining(); + expect(hashTable.size()).to.equal(0); + expect(hashTable.isEmpty()).to.equal(true); + }); + + it('generates hashcode', () => { + // numbers + let hashTable: any = new HashTableSeparateChaining(); + expect(hashTable.hashCode(1)).to.equal(1); + expect(hashTable.hashCode(10)).to.equal(10); + expect(hashTable.hashCode(100)).to.equal(100); + expect(hashTable.hashCode(1000)).to.equal(1000); + + // strings + hashTable = new HashTableSeparateChaining(); + expect(hashTable.hashCode('1')).to.equal(12); + expect(hashTable.hashCode('10')).to.equal(23); + expect(hashTable.hashCode('100')).to.equal(34); + expect(hashTable.hashCode('1000')).to.equal(8); + expect(hashTable.hashCode('a')).to.equal(23); + expect(hashTable.hashCode('A')).to.equal(28); + expect(hashTable.hashCode('Aba')).to.equal(1); + + // objects + hashTable = new HashTableSeparateChaining(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + expect(hashTable.hashCode(myObjList[0])).to.equal(1); + expect(hashTable.hashCode(myObjList[1])).to.equal(3); + expect(hashTable.hashCode(myObjList[2])).to.equal(5); + expect(hashTable.hashCode(myObjList[3])).to.equal(7); + expect(hashTable.hashCode(myObjList[4])).to.equal(9); + }); + + it('puts undefined and null keys and values', () => { + const hashTable = new HashTableSeparateChaining(); + + expect(hashTable.put('undefined', undefined)).to.equal(false); + expect(hashTable.get('undefined')).to.equal(undefined); + + expect(hashTable.put('undefined', 1)).to.equal(true); + expect(hashTable.get('undefined')).to.equal(1); + + expect(hashTable.put('null', null)).to.equal(false); + expect(hashTable.get('null')).to.equal(undefined); + + expect(hashTable.put('null', 1)).to.equal(true); + expect(hashTable.get('null')).to.equal(1); + + hashTable.clear(); + expect(hashTable.put(undefined, undefined)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + + expect(hashTable.put(undefined, 1)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + + expect(hashTable.put(null, null)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + + expect(hashTable.put(null, 1)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + }); + + it('puts values with number key without collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableSeparateChaining(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + const linkedList = table[i]; + expect(linkedList.size()).to.equal(1); + const valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(i); + expect(valuePair.element.value).to.equal(i); + } + }); + + it('puts values with string key without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + + expect(hashTable.put('1', 1)).to.equal(true); + expect(hashTable.put('10', 10)).to.equal(true); + expect(hashTable.put('100', 100)).to.equal(true); + expect(hashTable.put('1000', 1000)).to.equal(true); + + const table = hashTable.getTable(); + + let linkedList = table[12]; + expect(linkedList.size()).to.equal(1); + let valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal('1'); + expect(valuePair.element.value).to.equal(1); + + linkedList = table[23]; + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal('10'); + expect(valuePair.element.value).to.equal(10); + + linkedList = table[34]; + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal('100'); + expect(valuePair.element.value).to.equal(100); + + linkedList = table[8]; + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal('1000'); + expect(valuePair.element.value).to.equal(1000); + }); + + it('puts values with object key without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + expect(hashTable.put(myObjList[i - 1], myObjList[i - 1])).to.equal(true); + } + + const table = hashTable.getTable(); + + let linkedList = table[1]; + expect(linkedList.size()).to.equal(1); + let valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[0]); + expect(valuePair.element.value).to.equal(myObjList[0]); + + linkedList = table[3]; + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[1]); + expect(valuePair.element.value).to.equal(myObjList[1]); + + linkedList = table[5]; + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[2]); + expect(valuePair.element.value).to.equal(myObjList[2]); + + linkedList = table[7]; + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[3]); + expect(valuePair.element.value).to.equal(myObjList[3]); + + linkedList = table[9]; + expect(linkedList.size()).to.equal(1); + valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(myObjList[4]); + expect(valuePair.element.value).to.equal(myObjList[4]); + }); + + it('puts values with collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableSeparateChaining(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 10)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 2); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i + 100)).to.equal(true); + } + expect(hashTable.size()).to.equal(size * 3); + + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + const linkedList = table[i]; + expect(linkedList.size()).to.equal(3); + + let valuePair = linkedList.getHead(); + expect(valuePair.element.key).to.equal(i); + expect(valuePair.element.value).to.equal(i); + + valuePair = valuePair.next; + expect(valuePair.element.key).to.equal(i); + expect(valuePair.element.value).to.equal(i + 10); + + valuePair = valuePair.next; + expect(valuePair.element.key).to.equal(i); + expect(valuePair.element.value).to.equal(i + 100); + } + }); + + it('removes elements without collisions', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTableSeparateChaining(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(true); + } + + // elements do not exist + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(false); + } + + expect(hashTable.isEmpty()).to.equal(true); + }); + + function addValuesCollision() { + const hashTable = new HashTableSeparateChaining(); + + expect(hashTable.put(A, `${A}@email.com`)).to.equal(true); + expect(hashTable.put(B, `${B}@email.com`)).to.equal(true); + expect(hashTable.put(C, `${C}@email.com`)).to.equal(true); + expect(hashTable.size()).to.equal(3); + + const expectedHash = 5; + expect(hashTable.hashCode(A)).to.equal(expectedHash); + expect(hashTable.hashCode(B)).to.equal(expectedHash); + expect(hashTable.hashCode(C)).to.equal(expectedHash); + + expect(hashTable.getTable()[expectedHash].size()).to.equal(3); + + return hashTable; + } + + function removeWithCollision(a: string, b: string, c: string) { + const hashTable = addValuesCollision(); + + expect(hashTable.remove(a)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.not.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + + expect(hashTable.remove(b)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.not.equal(undefined); + + expect(hashTable.remove(c)).to.equal(true); + expect(hashTable.get(a)).to.equal(undefined); + expect(hashTable.get(b)).to.equal(undefined); + expect(hashTable.get(c)).to.equal(undefined); + + expect(hashTable.isEmpty()).to.equal(true); + } + + it('removes elements with collisions', () => { + // test all possibilities for removal + removeWithCollision(A, B, C); + removeWithCollision(A, C, B); + removeWithCollision(B, A, C); + removeWithCollision(B, C, A); + removeWithCollision(C, A, B); + removeWithCollision(C, B, A); + }); + + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + + expect(hashTable.toString()).to.equal(''); + + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); + + it('returns toString primitive types without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + + hashTable.put('el1', 1); + expect(hashTable.toString()).to.equal('{36 => [#el1: 1]}'); + + hashTable.put('el2', 2); + expect(hashTable.toString()).to.equal('{0 => [#el2: 2]},{36 => [#el1: 1]}'); + }); + + it('returns toString objects without collisions', () => { + const hashTable = new HashTableSeparateChaining(); + + let myObj = new MyObj(1, 2); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]}'); + + myObj = new MyObj(3, 4); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal( + '{1 => [#1|2: 1|2]},{5 => [#3|4: 3|4]}' + ); + }); + + it('returns toString with collisions', () => { + const hashTable = new HashTableSeparateChaining(); + + expect(hashTable.toString()).to.equal(''); + + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + + hashTable.put(1, 10); + expect(hashTable.toString()).to.equal('{1 => [#1: 1],[#1: 10]},{2 => [#2: 2]}'); + + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); +}); diff --git a/test/ts/data-structures/hash-table.spec.ts b/test/ts/data-structures/hash-table.spec.ts new file mode 100644 index 00000000..2dd56a3f --- /dev/null +++ b/test/ts/data-structures/hash-table.spec.ts @@ -0,0 +1,211 @@ +import 'mocha'; +import { expect } from 'chai'; +import { HashTable } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('HashTable', () => { + it('starts empty', () => { + const hashTable = new HashTable(); + expect(hashTable.size()).to.equal(0); + expect(hashTable.isEmpty()).to.equal(true); + }); + + it('generates hashcode', () => { + // numbers + let hashTable: any = new HashTable(); + expect(hashTable.hashCode(1)).to.equal(1); + expect(hashTable.hashCode(10)).to.equal(10); + expect(hashTable.hashCode(100)).to.equal(100); + expect(hashTable.hashCode(1000)).to.equal(1000); + + // strings + hashTable = new HashTable(); + expect(hashTable.hashCode('1')).to.equal(12); + expect(hashTable.hashCode('10')).to.equal(23); + expect(hashTable.hashCode('100')).to.equal(34); + expect(hashTable.hashCode('1000')).to.equal(8); + expect(hashTable.hashCode('a')).to.equal(23); + expect(hashTable.hashCode('A')).to.equal(28); + expect(hashTable.hashCode('Aba')).to.equal(1); + + // objects + hashTable = new HashTable(); + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + } + expect(hashTable.hashCode(myObjList[0])).to.equal(1); + expect(hashTable.hashCode(myObjList[1])).to.equal(3); + expect(hashTable.hashCode(myObjList[2])).to.equal(5); + expect(hashTable.hashCode(myObjList[3])).to.equal(7); + expect(hashTable.hashCode(myObjList[4])).to.equal(9); + }); + + it('puts undefined and null keys and values', () => { + const hashTable = new HashTable(); + + expect(hashTable.put('undefined', undefined)).to.equal(false); + expect(hashTable.get('undefined')).to.equal(undefined); + + expect(hashTable.put('undefined', 1)).to.equal(true); + expect(hashTable.get('undefined')).to.equal(1); + + expect(hashTable.put('null', null)).to.equal(false); + expect(hashTable.get('null')).to.equal(undefined); + + expect(hashTable.put('null', 1)).to.equal(true); + expect(hashTable.get('null')).to.equal(1); + + hashTable.clear(); + expect(hashTable.put(undefined, undefined)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + + expect(hashTable.put(undefined, 1)).to.equal(false); + expect(hashTable.get(undefined)).to.equal(undefined); + + expect(hashTable.put(null, null)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + + expect(hashTable.put(null, 1)).to.equal(false); + expect(hashTable.get(null)).to.equal(undefined); + }); + + it('puts values with number key', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTable(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + const table = hashTable.getTable(); + for (let i = min; i <= max; i++) { + expect(table[i].key).to.equal(i); + expect(table[i].value).to.equal(i); + } + }); + + it('puts values with string key', () => { + const hashTable = new HashTable(); + + expect(hashTable.put('1', 1)).to.equal(true); + expect(hashTable.put('10', 10)).to.equal(true); + expect(hashTable.put('100', 100)).to.equal(true); + expect(hashTable.put('1000', 1000)).to.equal(true); + + const table = hashTable.getTable(); + + expect(table[12].key).to.equal('1'); + expect(table[12].value).to.equal(1); + + expect(table[23].key).to.equal('10'); + expect(table[23].value).to.equal(10); + + expect(table[34].key).to.equal('100'); + expect(table[34].value).to.equal(100); + + expect(table[8].key).to.equal('1000'); + expect(table[8].value).to.equal(1000); + }); + + it('puts values with object key', () => { + const hashTable = new HashTable(); + + const myObjList = []; + for (let i = 1; i <= 5; i++) { + myObjList.push(new MyObj(i, i + 1)); + expect(hashTable.put(myObjList[i - 1], myObjList[i - 1])).to.equal(true); + } + + const table = hashTable.getTable(); + + expect(table[1].key).to.equal(myObjList[0]); + expect(table[1].value).to.equal(myObjList[0]); + + expect(table[3].key).to.equal(myObjList[1]); + expect(table[3].value).to.equal(myObjList[1]); + + expect(table[5].key).to.equal(myObjList[2]); + expect(table[5].value).to.equal(myObjList[2]); + + expect(table[7].key).to.equal(myObjList[3]); + expect(table[7].value).to.equal(myObjList[3]); + + expect(table[9].key).to.equal(myObjList[4]); + expect(table[9].value).to.equal(myObjList[4]); + }); + + it('does NOT handle collision, replaces values', () => { + const hashTable = new HashTable(); + + for (let i = 0; i < 5; i++) { + expect(hashTable.put(1, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(1); + }); + + it('removes elements', () => { + const min = 1; + const max = 5; + const size = max - min + 1; + const hashTable = new HashTable(); + + for (let i = min; i <= max; i++) { + expect(hashTable.put(i, i)).to.equal(true); + } + expect(hashTable.size()).to.equal(size); + + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(true); + } + + // elements do not exist + for (let i = min; i <= max; i++) { + expect(hashTable.remove(i)).to.equal(false); + } + + expect(hashTable.isEmpty()).to.equal(true); + }); + + it('returns toString primitive types', () => { + const hashTable = new HashTable(); + + expect(hashTable.toString()).to.equal(''); + + hashTable.put(1, 1); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]}'); + + hashTable.put(2, 2); + expect(hashTable.toString()).to.equal('{1 => [#1: 1]},{2 => [#2: 2]}'); + + hashTable.clear(); + expect(hashTable.toString()).to.equal(''); + }); + + it('returns toString primitive types', () => { + const hashTable = new HashTable(); + + hashTable.put('el1', 1); + expect(hashTable.toString()).to.equal('{36 => [#el1: 1]}'); + + hashTable.put('el2', 2); + expect(hashTable.toString()).to.equal('{0 => [#el2: 2]},{36 => [#el1: 1]}'); + }); + + it('returns toString objects', () => { + const hashTable = new HashTable(); + + let myObj = new MyObj(1, 2); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal('{1 => [#1|2: 1|2]}'); + + myObj = new MyObj(3, 4); + hashTable.put(myObj, myObj); + expect(hashTable.toString()).to.equal( + '{1 => [#1|2: 1|2]},{5 => [#3|4: 3|4]}' + ); + }); +}); diff --git a/test/ts/data-structures/heap.spec.ts b/test/ts/data-structures/heap.spec.ts new file mode 100644 index 00000000..7296bf63 --- /dev/null +++ b/test/ts/data-structures/heap.spec.ts @@ -0,0 +1,51 @@ +import 'mocha'; +// import { expect } from 'chai'; +import { MinHeap } from '../../../src/ts/index'; +import { MaxHeap } from '../../../src/ts/data-structures/heap'; +import heapSort from '../../../src/ts/algorithms/sorting/heap-sort'; + +describe('Heap', () => { + let heap: MinHeap; + + beforeEach(() => { + heap = new MinHeap(); + }); + + it('starts empty', () => { + + }); + + it('inserts elements in the AVLTree', () => { + + heap.insert(3); + heap.insert(2); + heap.insert(1); + heap.insert(4); + + heap.extract(); + heap.extract(); + heap.extract(); + heap.extract(); + + heap.isEmpty(); + + const maxHeap = new MaxHeap(); + maxHeap.insert(3); + maxHeap.insert(2); + maxHeap.insert(1); + maxHeap.insert(4); + + maxHeap.extract(); + maxHeap.extract(); + maxHeap.extract(); + maxHeap.extract(); + + maxHeap.isEmpty(); + }); + + it('Heap Sort', () => { + const array = [3, 2, 5, 6, 1, 7, 8, 9]; + + heapSort(array); + }); +}); diff --git a/test/ts/data-structures/linked-list.spec.ts b/test/ts/data-structures/linked-list.spec.ts new file mode 100644 index 00000000..116abbd9 --- /dev/null +++ b/test/ts/data-structures/linked-list.spec.ts @@ -0,0 +1,330 @@ +import 'mocha'; +import { expect } from 'chai'; +import { LinkedList, util } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('LinkedList', () => { + let list: LinkedList; + let min: number; + let max: number; + + beforeEach(() => { + list = new LinkedList(util.defaultEquals); + min = 1; + max = 3; + }); + + function pushesElements() { + for (let i = min; i <= max; i++) { + list.push(i); + } + } + + function verifyList() { + let current = list.getHead(); + for (let i = min; i <= max && current; i++) { + expect(current).to.not.be.an('undefined'); + if (current) { + // TS strictNullChecks + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(i); + if (i < max) { + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + // TS strictNullChecks + expect(current.next.element).to.equal(i + 1); + } + } else { + expect(current.next).to.be.an('undefined'); + } + current = current.next; + } + } + } + + it('starts empty', () => { + expect(list.size()).to.equal(0); + expect(list.isEmpty()).to.equal(true); + expect(list.getHead()).to.be.an('undefined'); + }); + + it('pushes elements', () => { + pushesElements(); + verifyList(); + }); + + it('returns element at specific index: invalid position', () => { + // list is empty + expect(list.getElementAt(3)).to.be.an('undefined'); + }); + + it('returns element at specific index', () => { + let node; + + pushesElements(); + + for (let i = min; i <= max; i++) { + node = list.getElementAt(i - 1); + expect(node).to.not.be.an('undefined'); + if (node) { + expect(node.element).to.equal(i); + } + } + }); + + it('inserts elements first position empty list', () => { + const element = 1; + max = element; + expect(list.insert(element, 0)).to.equal(true); + verifyList(); + }); + + it('inserts elements first position not empty list', () => { + max = 2; + expect(list.insert(max, 0)).to.equal(true); + + expect(list.insert(min, 0)).to.equal(true); + + verifyList(); + }); + + it('inserts elements invalid position empty list', () => { + expect(list.insert(1, 1)).to.equal(false); + }); + + it('inserts elements invalid position not empty list', () => { + const element = 1; + expect(list.insert(element, 0)).to.equal(true); + expect(list.insert(element, 2)).to.equal(false); + }); + + it('inserts elements in the middle of list', () => { + expect(list.insert(3, 0)).to.equal(true); + expect(list.insert(1, 0)).to.equal(true); + expect(list.insert(2, 1)).to.equal(true); + verifyList(); + }); + + it('inserts elements at the end of list', () => { + max = 5; + + for (let i = min; i <= max; i++) { + expect(list.insert(i , i - 1)).to.equal(true); + } + + verifyList(); + }); + + it('returns index of elements', () => { + let index; + + pushesElements(); + + for (let i = min; i <= max; i++) { + index = list.indexOf(i); + expect(index).to.equal(i - 1); + } + + expect(list.indexOf(max + 2)).to.equal(-1); + }); + + it('removes valid elements', () => { + let element; + + pushesElements(); + + for (let i = min; i <= max; i++) { + element = list.remove(i); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + } + }); + + it('removes invalid elements', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.remove(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position empty list', () => { + let element; + + for (let i = min; i <= max; i++) { + element = list.removeAt(i - 1); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position not empty list', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.removeAt(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes first element list single element', () => { + const value = 1; + list.push(value); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(value); + + expect(list.getHead()).to.be.an('undefined'); + expect(list.isEmpty()).to.equal(true); + }); + + it('removes first element list multiple elements', () => { + pushesElements(); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(min); + + min = 2; + verifyList(); + }); + + it('removes element from middle of list', () => { + pushesElements(); // 1, 2, 3 + + const element = list.removeAt(1); // element 2 + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(2); + + // list needs to be [1, 3] + let current = list.getHead(); + + // element 1 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(1); + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + expect(current.next.element).to.equal(3); + current = current.next; + } + } + + // element 3 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(3); + expect(current.next).to.be.an('undefined'); + } + }); + + it('removes element from end of list', () => { + let element; + + pushesElements(); + + const maxIndex = max; + for (let i = maxIndex; i >= min; i--) { + element = list.removeAt(i - 1); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + max--; + verifyList(); + } + }); + + it('returns the head of the list', () => { + expect(list.getHead()).to.be.an('undefined'); + + list.push(1); + expect(list.getHead()).to.not.be.an('undefined'); + }); + + it('returns the correct size', () => { + expect(list.size()).to.equal(0); + + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.size()).to.equal(i); + } + + const size = max; + for (let i = min; i <= max; i++) { + list.remove(i); + expect(list.size()).to.equal(size - i); + } + + expect(list.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(list.isEmpty()).to.equal(true); + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.isEmpty()).to.equal(false); + } + + for (let i = min; i < max; i++) { + list.remove(i); + expect(list.isEmpty()).to.equal(false); + } + list.remove(max); + expect(list.isEmpty()).to.equal(true); + + pushesElements(); + expect(list.isEmpty()).to.equal(false); + + list.clear(); + expect(list.isEmpty()).to.equal(true); + }); + + it('clears the list', () => { + expect(list.size()).to.equal(0); + list.clear(); + expect(list.size()).to.equal(0); + pushesElements(); + expect(list.size()).to.greaterThan(0); + list.clear(); + expect(list.size()).to.equal(0); + }); + + it('returns toString primitive types', () => { + expect(list.toString()).to.equal(''); + + list.push(1); + expect(list.toString()).to.equal('1'); + + list.push(2); + expect(list.toString()).to.equal('1,2'); + + list.clear(); + expect(list.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const ds = new LinkedList(); + ds.push('el1'); + expect(ds.toString()).to.equal('el1'); + + ds.push('el2'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const ds = new LinkedList(); + expect(ds.toString()).to.equal(''); + + ds.push(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2'); + + ds.push(new MyObj(3, 4)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/data-structures/my-obj.ts b/test/ts/data-structures/my-obj.ts new file mode 100644 index 00000000..ff633f2b --- /dev/null +++ b/test/ts/data-structures/my-obj.ts @@ -0,0 +1,7 @@ +export default class MyObj { + constructor(public el1: any, public el2: any) { } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } +} + diff --git a/test/ts/data-structures/queue.spec.ts b/test/ts/data-structures/queue.spec.ts new file mode 100644 index 00000000..132f7280 --- /dev/null +++ b/test/ts/data-structures/queue.spec.ts @@ -0,0 +1,168 @@ +import 'mocha'; +import { expect } from 'chai'; +import Queue from '../../../src/ts/data-structures/queue'; + +describe('Queue', () => { + let queue: Queue; + + beforeEach(() => { + queue = new Queue(); + }); + + it('starts empty', () => { + expect(queue.size()).to.equal(0); + expect(queue.isEmpty()).to.equal(true); + }); + + it('enqueues elements', () => { + queue.enqueue(1); + expect(queue.size()).to.equal(1); + queue.enqueue(2); + expect(queue.size()).to.equal(2); + queue.enqueue(3); + expect(queue.size()).to.equal(3); + + expect(queue.isEmpty()).to.equal(false); + }); + + it('dequeue elements', () => { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + + expect(queue.dequeue()).to.equal(1); + expect(queue.dequeue()).to.equal(2); + expect(queue.dequeue()).to.equal(3); + expect(queue.dequeue()).to.equal(undefined); + }); + + it('implements FIFO logic', () => { + queue.enqueue(1); + expect(queue.peek()).to.equal(1); + queue.enqueue(2); + expect(queue.peek()).to.equal(1); + queue.enqueue(3); + expect(queue.peek()).to.equal(1); + + expect(queue.dequeue()).to.equal(1); + expect(queue.dequeue()).to.equal(2); + expect(queue.dequeue()).to.equal(3); + expect(queue.dequeue()).to.equal(undefined); + }); + + it('allows to peek at the front element in the queue without dequeuing it', () => { + expect(queue.peek()).to.equal(undefined); + + queue.enqueue(1); + expect(queue.peek()).to.equal(1); + + queue.enqueue(2); + expect(queue.peek()).to.equal(1); + + queue.dequeue(); + expect(queue.peek()).to.equal(2); + }); + + it('returns the correct size', () => { + expect(queue.size()).to.equal(0); + queue.enqueue(1); + expect(queue.size()).to.equal(1); + queue.enqueue(2); + expect(queue.size()).to.equal(2); + queue.enqueue(3); + expect(queue.size()).to.equal(3); + + queue.clear(); + expect(queue.isEmpty()).to.equal(true); + + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + expect(queue.size()).to.equal(3); + + queue.dequeue(); + expect(queue.size()).to.equal(2); + queue.dequeue(); + expect(queue.size()).to.equal(1); + queue.dequeue(); + expect(queue.size()).to.equal(0); + queue.dequeue(); + expect(queue.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(queue.isEmpty()).to.equal(true); + queue.enqueue(1); + expect(queue.isEmpty()).to.equal(false); + queue.enqueue(2); + expect(queue.isEmpty()).to.equal(false); + queue.enqueue(3); + expect(queue.isEmpty()).to.equal(false); + + queue.clear(); + expect(queue.isEmpty()).to.equal(true); + + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + expect(queue.isEmpty()).to.equal(false); + + queue.dequeue(); + expect(queue.isEmpty()).to.equal(false); + queue.dequeue(); + expect(queue.isEmpty()).to.equal(false); + queue.dequeue(); + expect(queue.isEmpty()).to.equal(true); + queue.dequeue(); + expect(queue.isEmpty()).to.equal(true); + }); + + it('clears the queue', () => { + queue.clear(); + expect(queue.isEmpty()).to.equal(true); + + queue.enqueue(1); + queue.enqueue(2); + expect(queue.isEmpty()).to.equal(false); + + queue.clear(); + expect(queue.isEmpty()).to.equal(true); + }); + + it('returns toString primitive types', () => { + expect(queue.toString()).to.equal(''); + + queue.enqueue(1); + expect(queue.toString()).to.equal('1'); + + queue.enqueue(2); + expect(queue.toString()).to.equal('1,2'); + + queue.clear(); + expect(queue.toString()).to.equal(''); + + const queueString = new Queue(); + queueString.enqueue('el1'); + expect(queueString.toString()).to.equal('el1'); + + queueString.enqueue('el2'); + expect(queueString.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + class MyObj { + constructor(public el1: any, public el2: any) {} + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } + } + const queueMyObj = new Queue(); + expect(queueMyObj.toString()).to.equal(''); + + queueMyObj.enqueue(new MyObj(1, 2)); + expect(queueMyObj.toString()).to.equal('1|2'); + + queueMyObj.enqueue(new MyObj(3, 4)); + expect(queueMyObj.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/data-structures/set.spec.ts b/test/ts/data-structures/set.spec.ts new file mode 100644 index 00000000..7320cdfc --- /dev/null +++ b/test/ts/data-structures/set.spec.ts @@ -0,0 +1,352 @@ +import 'mocha'; +import { expect } from 'chai'; +import { Set } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('Set', () => { + let set: Set; + + beforeEach(() => { + set = new Set(); + }); + + it('starts empty', () => { + expect(set.size()).to.equal(0); + expect(set.isEmpty()).to.equal(true); + }); + + it('adds elements', () => { + for (let i = 1; i < 5; i++) { + set.add(i); + expect(set.size()).to.equal(i); + } + + expect(set.isEmpty()).to.equal(false); + }); + + it('does not allow duplicated elements', () => { + let expected = true; + for (let i = 1; i < 5; i++) { + expect(set.add(i)).to.equal(expected); + } + + expected = false; + for (let i = 1; i < 5; i++) { + expect(set.add(i)).to.equal(expected); + } + }); + + it('deletes elements', () => { + for (let i = 1; i < 5; i++) { + set.add(i); + } + + for (let i = 1; i < 5; i++) { + expect(set.delete(i)).to.equal(true); + } + + // elements do not exist + for (let i = 1; i < 5; i++) { + expect(set.delete(i)).to.equal(false); + } + + expect(set.isEmpty()).to.equal(true); + }); + + it('returns if element exists', () => { + for (let i = 1; i < 5; i++) { + set.add(i); + expect(set.has(i)).to.equal(true); + } + + for (let i = 1; i < 5; i++) { + expect(set.delete(i)).to.equal(true); + expect(set.has(i)).to.equal(false); + } + }); + + it('returns the correct size', () => { + expect(set.size()).to.equal(0); + + for (let i = 1; i < 5; i++) { + set.add(i); + expect(set.size()).to.equal(i); + } + + const max = 5; + for (let i = 1; i < max; i++) { + set.delete(i); + expect(set.size()).to.equal(max - i - 1); + } + + expect(set.size()).to.equal(0); + expect(set.isEmpty()).to.equal(true); + }); + + it('returns if it is empty', () => { + expect(set.isEmpty()).to.equal(true); + + for (let i = 1; i < 5; i++) { + set.add(i); + expect(set.isEmpty()).to.equal(false); + } + + for (let i = 1; i < 5; i++) { + set.delete(i); + expect(set.isEmpty()).to.equal(!(i < 4)); + } + + expect(set.size()).to.equal(0); + expect(set.isEmpty()).to.equal(true); + }); + + it('clears the set', () => { + set.clear(); + expect(set.isEmpty()).to.equal(true); + + set.add(1); + set.add(2); + + set.clear(); + expect(set.isEmpty()).to.equal(true); + }); + + function addValues(min: number, max: number) { + set = new Set(); + + for (let i = min; i <= max; i++) { + set.add(i); + } + + return set; + } + + it('union between empty sets', () => { + const set1 = new Set(); + const set2 = new Set(); + + let setResult = set1.union(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.union(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('union between equal sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(1, 5); + + let setResult = set1.union(set2); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.union(set1); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('union between different sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(6, 10); + + let setResult = set1.union(set2); + for (let i = 1; i <= 10; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.union(set1); + for (let i = 1; i <= 10; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('union between sets with common values', () => { + const set1 = addValues(1, 5); + const set2 = addValues(3, 6); + + let setResult = set1.union(set2); + for (let i = 1; i <= 6; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.union(set1); + for (let i = 1; i <= 6; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('intersection between empty sets', () => { + const set1 = new Set(); + const set2 = new Set(); + + let setResult = set1.intersection(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.intersection(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('intersection between equal sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(1, 5); + + let setResult = set1.intersection(set2); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.intersection(set1); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('intersection different sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(6, 10); + + let setResult = set1.intersection(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.intersection(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('intersection between sets with common values', () => { + const set1 = addValues(1, 5); + const set2 = addValues(3, 6); + + let setResult = set1.intersection(set2); + for (let i = 3; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.intersection(set1); + for (let i = 3; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('difference between empty sets', () => { + const set1 = new Set(); + const set2 = new Set(); + + let setResult = set1.difference(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.difference(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('difference between equal sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(1, 5); + + let setResult = set1.difference(set2); + expect(setResult.isEmpty()).to.equal(true); + + setResult = set2.difference(set1); + expect(setResult.isEmpty()).to.equal(true); + }); + + it('difference different sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(6, 10); + + let setResult = set1.difference(set2); + for (let i = 1; i <= 5; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.difference(set1); + for (let i = 6; i <= 10; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('difference between sets with common values', () => { + const set1 = addValues(1, 5); + const set2 = addValues(3, 6); + + let setResult = set1.difference(set2); + for (let i = 1; i <= 2; i++) { + expect(setResult.has(i)).to.equal(true); + } + + setResult = set2.difference(set1); + for (let i = 6; i <= 6; i++) { + expect(setResult.has(i)).to.equal(true); + } + }); + + it('isSubsetOf between empty sets', () => { + const set1 = new Set(); + const set2 = new Set(); + + expect(set1.isSubsetOf(set2)).to.equal(true); + expect(set2.isSubsetOf(set1)).to.equal(true); + }); + + it('isSubsetOf between equal sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(1, 5); + + expect(set1.isSubsetOf(set2)).to.equal(true); + expect(set2.isSubsetOf(set1)).to.equal(true); + }); + + it('isSubsetOf different sets', () => { + const set1 = addValues(1, 5); + const set2 = addValues(6, 10); + + expect(set1.isSubsetOf(set2)).to.equal(false); + expect(set2.isSubsetOf(set1)).to.equal(false); + }); + + it('isSubsetOf between sets with common values', () => { + const set1 = addValues(1, 8); + const set2 = addValues(3, 6); + expect(set1.isSubsetOf(set2)).to.equal(false); + expect(set2.isSubsetOf(set1)).to.equal(true); + + const set3 = addValues(1, 5); + const set4 = addValues(3, 6); + expect(set3.isSubsetOf(set4)).to.equal(false); + expect(set4.isSubsetOf(set3)).to.equal(false); + }); + + it('returns toString primitive types', () => { + expect(set.toString()).to.equal(''); + + set.add(1); + expect(set.toString()).to.equal('1'); + + set.add(2); + expect(set.toString()).to.equal('1,2'); + + set.clear(); + expect(set.toString()).to.equal(''); + }); + + it('returns toString primitive types: string', () => { + const ds = new Set(); + ds.add('el1'); + expect(ds.toString()).to.equal('el1'); + + ds.add('el2'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + const ds = new Set(); + expect(ds.toString()).to.equal(''); + + ds.add(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2'); + + ds.add(new MyObj(3, 4)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/data-structures/sorted-linked-list.spec.ts b/test/ts/data-structures/sorted-linked-list.spec.ts new file mode 100644 index 00000000..02634f72 --- /dev/null +++ b/test/ts/data-structures/sorted-linked-list.spec.ts @@ -0,0 +1,341 @@ +import 'mocha'; +import { expect } from 'chai'; +import { SortedLinkedList, util } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('SortedLinkedList', () => { + let list: SortedLinkedList; + let min: number; + let max: number; + + beforeEach(() => { + list = new SortedLinkedList(); + min = 1; + max = 3; + }); + + function pushesElements() { + for (let i = max; i >= min; i--) { + list.push(i); + } + } + + function verifyList() { + let current = list.getHead(); + for (let i = min; i <= max && current; i++) { + expect(current).to.not.be.an('undefined'); + if (current) { + // TS strictNullChecks + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(i); + if (i < max) { + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + // TS strictNullChecks + expect(current.next.element).to.equal(i + 1); + } + } else { + expect(current.next).to.be.an('undefined'); + } + current = current.next; + } + } + } + + it('starts empty', () => { + expect(list.size()).to.equal(0); + expect(list.isEmpty()).to.equal(true); + expect(list.getHead()).to.be.an('undefined'); + }); + + it('pushes elements', () => { + pushesElements(); + verifyList(); + }); + + it('returns element at specific index: invalid position', () => { + // list is empty + expect(list.getElementAt(3)).to.be.an('undefined'); + }); + + it('returns element at specific index', () => { + let node; + + pushesElements(); + + for (let i = min; i <= max; i++) { + node = list.getElementAt(i - 1); + expect(node).to.not.be.an('undefined'); + if (node) { + expect(node.element).to.equal(i); + } + } + }); + + it('inserts elements first position empty list', () => { + const element = 1; + max = element; + expect(list.insert(element, 0)).to.equal(true); + verifyList(); + }); + + it('inserts elements first position not empty list', () => { + max = 2; + expect(list.insert(max)).to.equal(true); + + expect(list.insert(min, 0)).to.equal(true); + + verifyList(); + }); + + it('inserts elements invalid position empty list', () => { + // sorted list will ignore the index position + expect(list.insert(1, 1)).to.equal(true); + }); + + it('inserts elements invalid position not empty list', () => { + // sorted list will ignore the index position + const element = 1; + expect(list.insert(element, 0)).to.equal(true); + expect(list.insert(element, 2)).to.equal(true); + }); + + it('inserts elements in the middle of list', () => { + expect(list.insert(3, 0)).to.equal(true); + expect(list.insert(1, 0)).to.equal(true); + expect(list.insert(2, 1)).to.equal(true); + verifyList(); + }); + + it('inserts elements at the end of list', () => { + max = 5; + + for (let i = min; i <= max; i++) { + expect(list.insert(i , i - 1)).to.equal(true); + } + + verifyList(); + }); + + it('returns index of elements', () => { + let index; + + pushesElements(); + + for (let i = min; i <= max; i++) { + index = list.indexOf(i); + expect(index).to.equal(i - 1); + } + + expect(list.indexOf(max + 2)).to.equal(-1); + }); + + it('removes valid elements', () => { + let element; + + pushesElements(); + + for (let i = min; i <= max; i++) { + element = list.remove(i); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + } + }); + + it('removes invalid elements', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.remove(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position empty list', () => { + let element; + + for (let i = min; i <= max; i++) { + element = list.removeAt(i - 1); + expect(element).to.be.an('undefined'); + } + }); + + it('removes element invalid position not empty list', () => { + let element; + + pushesElements(); + + for (let i = max + 2; i <= max + 4; i++) { + element = list.removeAt(i); + expect(element).to.be.an('undefined'); + } + }); + + it('removes first element list single element', () => { + const value = 1; + list.push(value); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(value); + + expect(list.getHead()).to.be.an('undefined'); + expect(list.isEmpty()).to.equal(true); + }); + + it('removes first element list multiple elements', () => { + pushesElements(); + + const element = list.removeAt(0); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(min); + + min = 2; + verifyList(); + }); + + it('removes element from middle of list', () => { + pushesElements(); // 1, 2, 3 + + const element = list.removeAt(1); // element 2 + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(2); + + // list needs to be [1, 3] + let current = list.getHead(); + + // element 1 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(1); + expect(current.next).to.not.be.an('undefined'); + if (current.next) { + expect(current.next.element).to.equal(3); + current = current.next; + } + } + + // element 3 + expect(current).to.not.be.an('undefined'); + if (current) { + expect(current.element).to.not.be.an('undefined'); + expect(current.element).to.equal(3); + expect(current.next).to.be.an('undefined'); + } + }); + + it('removes element from end of list', () => { + let element; + + pushesElements(); + + const maxIndex = max; + for (let i = maxIndex; i >= min; i--) { + element = list.removeAt(i - 1); + expect(element).to.not.be.an('undefined'); + expect(element).to.equal(i); + max--; + verifyList(); + } + }); + + it('returns the head of the list', () => { + expect(list.getHead()).to.be.an('undefined'); + + list.push(1); + expect(list.getHead()).to.not.be.an('undefined'); + }); + + it('returns the correct size', () => { + expect(list.size()).to.equal(0); + + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.size()).to.equal(i); + } + + const size = max; + for (let i = min; i <= max; i++) { + list.remove(i); + expect(list.size()).to.equal(size - i); + } + + expect(list.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(list.isEmpty()).to.equal(true); + for (let i = min; i <= max; i++) { + list.push(i); + expect(list.isEmpty()).to.equal(false); + } + + for (let i = min; i < max; i++) { + list.remove(i); + expect(list.isEmpty()).to.equal(false); + } + list.remove(max); + expect(list.isEmpty()).to.equal(true); + + pushesElements(); + expect(list.isEmpty()).to.equal(false); + + list.clear(); + expect(list.isEmpty()).to.equal(true); + }); + + it('clears the list', () => { + expect(list.size()).to.equal(0); + list.clear(); + expect(list.size()).to.equal(0); + pushesElements(); + expect(list.size()).to.greaterThan(0); + list.clear(); + expect(list.size()).to.equal(0); + }); + + it('returns toString primitive types', () => { + expect(list.toString()).to.equal(''); + + list.push(1); + expect(list.toString()).to.equal('1'); + + list.push(2); + expect(list.toString()).to.equal('1,2'); + + list.clear(); + expect(list.toString()).to.equal(''); + }); + + function stringCompare(a: string, b: string): number { + return a.localeCompare(b); + } + + it('returns toString primitive types: string', () => { + + const ds = new SortedLinkedList(util.defaultEquals, stringCompare); + ds.push('el2'); + expect(ds.toString()).to.equal('el2'); + + ds.push('el1'); + expect(ds.toString()).to.equal('el1,el2'); + }); + + function myObjCompare(a: MyObj, b: MyObj): number { + return a.toString().localeCompare(b.toString()); + } + + it('returns toString objects', () => { + const ds = new SortedLinkedList(util.defaultEquals, myObjCompare); + expect(ds.toString()).to.equal(''); + + ds.push(new MyObj(3, 4)); + expect(ds.toString()).to.equal('3|4'); + + ds.push(new MyObj(1, 2)); + expect(ds.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/data-structures/stack-array.spec.ts b/test/ts/data-structures/stack-array.spec.ts new file mode 100644 index 00000000..0894d7de --- /dev/null +++ b/test/ts/data-structures/stack-array.spec.ts @@ -0,0 +1,180 @@ +import 'mocha'; +import { expect } from 'chai'; +import StackArray from '../../../src/ts/data-structures/stack-array'; + +describe('StackArray', () => { + let stack: StackArray; + + beforeEach(() => { + stack = new StackArray(); + }); + + it('starts empty', () => { + expect(stack.size()).to.equal(0); + expect(stack.isEmpty()).to.equal(true); + }); + + it('pushes elements', () => { + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + expect(stack.isEmpty()).to.equal(false); + }); + + it('pops elements', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('implements LIFO logic', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('allows to peek at the top element in he stack without popping it', () => { + expect(stack.peek()).to.equal(undefined); + + stack.push(1); + expect(stack.peek()).to.equal(1); + + stack.push(2); + expect(stack.peek()).to.equal(2); + + stack.pop(); + expect(stack.peek()).to.equal(1); + }); + + it('returns the correct size', () => { + expect(stack.size()).to.equal(0); + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.size()).to.equal(2); + stack.pop(); + expect(stack.size()).to.equal(1); + stack.pop(); + expect(stack.size()).to.equal(0); + stack.pop(); + expect(stack.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(stack.isEmpty()).to.equal(true); + stack.push(1); + expect(stack.isEmpty()).to.equal(false); + stack.push(2); + expect(stack.isEmpty()).to.equal(false); + stack.push(3); + expect(stack.isEmpty()).to.equal(false); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('clears the stack', () => { + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('returns an Array', () => { + let stackArray = stack.toArray(); + expect(stackArray.length).to.equal(0); + + stack.push(1); + stack.push(2); + + stackArray = stack.toArray(); + expect(stackArray.length).to.equal(2); + + let i = 1; + stackArray.forEach(e => { + expect(e).to.equal(i); + i++; + }); + }); + + it('returns toString primitive types', () => { + expect(stack.toString()).to.equal(''); + + stack.push(1); + expect(stack.toString()).to.equal('1'); + + stack.push(2); + expect(stack.toString()).to.equal('1,2'); + + stack.clear(); + expect(stack.toString()).to.equal(''); + + const stackString = new StackArray(); + stackString.push('el1'); + expect(stackString.toString()).to.equal('el1'); + + stackString.push('el2'); + expect(stackString.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + + class MyObj { + constructor(public el1: any, public el2: any) { } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } + } + const stackMyObj = new StackArray(); + expect(stackMyObj.toString()).to.equal(''); + + stackMyObj.push(new MyObj(1, 2)); + expect(stackMyObj.toString()).to.equal('1|2'); + + stackMyObj.push(new MyObj(3, 4)); + expect(stackMyObj.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/data-structures/stack-linked-list.spec.ts b/test/ts/data-structures/stack-linked-list.spec.ts new file mode 100644 index 00000000..9889fb17 --- /dev/null +++ b/test/ts/data-structures/stack-linked-list.spec.ts @@ -0,0 +1,159 @@ +import 'mocha'; +import { expect } from 'chai'; +import { StackLinkedList } from '../../../src/ts/index'; +import MyObj from './my-obj'; + +describe('StackLinkedList', () => { + let stack: StackLinkedList; + + beforeEach(() => { + stack = new StackLinkedList(); + }); + + it('starts empty', () => { + expect(stack.size()).to.equal(0); + expect(stack.isEmpty()).to.equal(true); + }); + + it('pushes elements', () => { + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + expect(stack.isEmpty()).to.equal(false); + }); + + it('pops elements', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('implements LIFO logic', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('allows to peek at the top element in he stack without popping it', () => { + expect(stack.peek()).to.equal(undefined); + + stack.push(1); + expect(stack.peek()).to.equal(1); + + stack.push(2); + expect(stack.peek()).to.equal(2); + + stack.pop(); + expect(stack.peek()).to.equal(1); + }); + + it('returns the correct size', () => { + expect(stack.size()).to.equal(0); + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.size()).to.equal(2); + stack.pop(); + expect(stack.size()).to.equal(1); + stack.pop(); + expect(stack.size()).to.equal(0); + stack.pop(); + expect(stack.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(stack.isEmpty()).to.equal(true); + stack.push(1); + expect(stack.isEmpty()).to.equal(false); + stack.push(2); + expect(stack.isEmpty()).to.equal(false); + stack.push(3); + expect(stack.isEmpty()).to.equal(false); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('clears the stack', () => { + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + }); + + + it('returns toString primitive types', () => { + expect(stack.toString()).to.equal(''); + + stack.push(1); + expect(stack.toString()).to.equal('1'); + + stack.push(2); + expect(stack.toString()).to.equal('1,2'); + + stack.clear(); + expect(stack.toString()).to.equal(''); + + const stackString = new StackLinkedList(); + stackString.push('el1'); + expect(stackString.toString()).to.equal('el1'); + + stackString.push('el2'); + expect(stackString.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + + const stackMyObj = new StackLinkedList(); + expect(stackMyObj.toString()).to.equal(''); + + stackMyObj.push(new MyObj(1, 2)); + expect(stackMyObj.toString()).to.equal('1|2'); + + stackMyObj.push(new MyObj(3, 4)); + expect(stackMyObj.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/data-structures/stack.spec.ts b/test/ts/data-structures/stack.spec.ts new file mode 100644 index 00000000..731e586c --- /dev/null +++ b/test/ts/data-structures/stack.spec.ts @@ -0,0 +1,163 @@ +import 'mocha'; +import { expect } from 'chai'; +import Stack from '../../../src/ts/data-structures/stack'; + +describe('Stack', () => { + let stack: Stack; + + beforeEach(() => { + stack = new Stack(); + }); + + it('starts empty', () => { + expect(stack.size()).to.equal(0); + expect(stack.isEmpty()).to.equal(true); + }); + + it('pushes elements', () => { + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + expect(stack.isEmpty()).to.equal(false); + }); + + it('pops elements', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('implements LIFO logic', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.pop()).to.equal(3); + expect(stack.pop()).to.equal(2); + expect(stack.pop()).to.equal(1); + expect(stack.pop()).to.equal(undefined); + }); + + it('allows to peek at the top element in the stack without popping it', () => { + expect(stack.peek()).to.equal(undefined); + + stack.push(1); + expect(stack.peek()).to.equal(1); + + stack.push(2); + expect(stack.peek()).to.equal(2); + + stack.pop(); + expect(stack.peek()).to.equal(1); + }); + + it('returns the correct size', () => { + expect(stack.size()).to.equal(0); + stack.push(1); + expect(stack.size()).to.equal(1); + stack.push(2); + expect(stack.size()).to.equal(2); + stack.push(3); + expect(stack.size()).to.equal(3); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.size()).to.equal(2); + stack.pop(); + expect(stack.size()).to.equal(1); + stack.pop(); + expect(stack.size()).to.equal(0); + stack.pop(); + expect(stack.size()).to.equal(0); + }); + + it('returns if it is empty', () => { + expect(stack.isEmpty()).to.equal(true); + stack.push(1); + expect(stack.isEmpty()).to.equal(false); + stack.push(2); + expect(stack.isEmpty()).to.equal(false); + stack.push(3); + expect(stack.isEmpty()).to.equal(false); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(false); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + stack.pop(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('clears the stack', () => { + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + + stack.push(1); + stack.push(2); + + stack.clear(); + expect(stack.isEmpty()).to.equal(true); + }); + + it('returns toString primitive types', () => { + expect(stack.toString()).to.equal(''); + + stack.push(1); + expect(stack.toString()).to.equal('1'); + + stack.push(2); + expect(stack.toString()).to.equal('1,2'); + + stack.clear(); + expect(stack.toString()).to.equal(''); + + const stackString = new Stack(); + stackString.push('el1'); + expect(stackString.toString()).to.equal('el1'); + + stackString.push('el2'); + expect(stackString.toString()).to.equal('el1,el2'); + }); + + it('returns toString objects', () => { + + class MyObj { + constructor(public el1: any, public el2: any) { } + toString() { + return `${this.el1.toString()}|${this.el2.toString()}`; + } + } + const stackMyObj = new Stack(); + expect(stackMyObj.toString()).to.equal(''); + + stackMyObj.push(new MyObj(1, 2)); + expect(stackMyObj.toString()).to.equal('1|2'); + + stackMyObj.push(new MyObj(3, 4)); + expect(stackMyObj.toString()).to.equal('1|2,3|4'); + }); +}); diff --git a/test/ts/others/balanced-symbols.spec.ts b/test/ts/others/balanced-symbols.spec.ts new file mode 100644 index 00000000..576432dd --- /dev/null +++ b/test/ts/others/balanced-symbols.spec.ts @@ -0,0 +1,42 @@ +import 'mocha'; +import { expect } from 'chai'; +import { parenthesesChecker } from '../../../src/ts/others/balanced-symbols'; + +describe('Balanced Symbols', () => { + + it('empty to be falsy', () => { + expect(parenthesesChecker('')).to.equal(true); + }); + + it('{ to be falsy', () => { + expect(parenthesesChecker('{')).to.equal(false); + }); + + it('} to be falsy', () => { + expect(parenthesesChecker('}')).to.equal(false); + }); + + it('11 to be falsy', () => { + expect(parenthesesChecker('11')).to.equal(false); + }); + + it('{11 to be falsy', () => { + expect(parenthesesChecker('{11')).to.equal(false); + }); + + it('{([1])} to be falsy', () => { + expect(parenthesesChecker('{([1])}')).to.equal(false); + }); + + it('{([])} to be truthy', () => { + expect(parenthesesChecker('{([])}')).to.equal(true); + }); + + it('{{([][])}()} to be truthy', () => { + expect(parenthesesChecker('{{([][])}()}')).to.equal(true); + }); + + it('[{()] to be falsy', () => { + expect(parenthesesChecker('[{()]')).to.equal(false); + }); +}); diff --git a/test/ts/others/base-converter.spec.ts b/test/ts/others/base-converter.spec.ts new file mode 100644 index 00000000..cec16bbe --- /dev/null +++ b/test/ts/others/base-converter.spec.ts @@ -0,0 +1,57 @@ +import 'mocha'; +import { expect } from 'chai'; +import { baseConverter, decimalToBinary } from '../../../src/ts/others/base-converter'; + +describe('Base Converter', () => { + it('decimalToBinary 1 -> 1', () => { + expect(decimalToBinary(1)).to.equal('1'); + }); + + it('decimalToBinary 2 -> 11', () => { + expect(decimalToBinary(2)).to.equal('10'); + }); + + it('decimalToBinary 233 -> 11101001', () => { + expect(decimalToBinary(233)).to.equal('11101001'); + }); + + it('decimalToBinary 10 -> 1010', () => { + expect(decimalToBinary(10)).to.equal('1010'); + }); + + it('decimalToBinary 1000 -> 1111101000', () => { + expect(decimalToBinary(1000)).to.equal('1111101000'); + }); + + it('baseConverter 100345, 2 -> 11000011111111001', () => { + expect(baseConverter(100345, 2)).to.equal('11000011111111001'); + }); + + it('baseConverter 100345, 8 -> 303771', () => { + expect(baseConverter(100345, 8)).to.equal('303771'); + }); + + it('baseConverter 100345, 16 -> 187F9', () => { + expect(baseConverter(100345, 16)).to.equal('187F9'); + }); + + it('baseConverter 100345, 7 -> 565360', () => { + expect(baseConverter(100345, 7)).to.equal('565360'); + }); + + it('baseConverter 100345, 20 -> CAH5', () => { + expect(baseConverter(100345, 20)).to.equal('CAH5'); + }); + + it('baseConverter 100345, 35 -> 2BW0', () => { + expect(baseConverter(100345, 35)).to.equal('2BW0'); + }); + + it('baseConverter 100345, 36 -> 25FD', () => { + expect(baseConverter(100345, 36)).to.equal('25FD'); + }); + + it('baseConverter 100345, 37 -> ', () => { + expect(baseConverter(100345, 37)).to.equal(''); + }); +}); diff --git a/test/ts/others/factorial.spec.ts b/test/ts/others/factorial.spec.ts new file mode 100644 index 00000000..d785d11c --- /dev/null +++ b/test/ts/others/factorial.spec.ts @@ -0,0 +1,26 @@ +import 'mocha'; +import { expect } from 'chai'; +import { factorialIterative, factorial } from '../../../src/ts/index'; + +describe('Factorial', () => { + + it('Iterative Factorial', () => { + expect(factorialIterative(-1)).to.equal(undefined); + expect(factorialIterative(0)).to.equal(1); + expect(factorialIterative(1)).to.equal(1); + expect(factorialIterative(2)).to.equal(2); + expect(factorialIterative(3)).to.equal(6); + expect(factorialIterative(4)).to.equal(24); + expect(factorialIterative(5)).to.equal(120); + }); + + it('Recursive Factorial', () => { + expect(factorial(-1)).to.equal(undefined); + expect(factorial(0)).to.equal(1); + expect(factorial(1)).to.equal(1); + expect(factorial(2)).to.equal(2); + expect(factorial(3)).to.equal(6); + expect(factorial(4)).to.equal(24); + expect(factorial(5)).to.equal(120); + }); +}); diff --git a/test/ts/others/fibonacci.spec.ts b/test/ts/others/fibonacci.spec.ts new file mode 100644 index 00000000..27ad81f0 --- /dev/null +++ b/test/ts/others/fibonacci.spec.ts @@ -0,0 +1,34 @@ +import 'mocha'; +import { expect } from 'chai'; +import { fibonacci, fibonacciIterative, fibonacciMemoization } from '../../../src/ts/index'; + +describe('Fibonacci', () => { + + it('Fibonacci Recursive', () => { + expect(fibonacci(-1)).to.equal(0); + expect(fibonacci(0)).to.equal(0); + expect(fibonacci(1)).to.equal(1); + expect(fibonacci(2)).to.equal(1); + expect(fibonacci(3)).to.equal(2); + expect(fibonacci(4)).to.equal(3); + }); + + it('Fibonacci Iterative', () => { + expect(fibonacciIterative(-1)).to.equal(0); + expect(fibonacciIterative(0)).to.equal(0); + expect(fibonacciIterative(1)).to.equal(1); + expect(fibonacciIterative(2)).to.equal(1); + expect(fibonacciIterative(3)).to.equal(2); + expect(fibonacciIterative(4)).to.equal(3); + }); + + it('Fibonacci with Memoization', () => { + expect(fibonacciMemoization(-1)).to.equal(0); + expect(fibonacciMemoization(0)).to.equal(0); + expect(fibonacciMemoization(1)).to.equal(1); + expect(fibonacciMemoization(2)).to.equal(1); + expect(fibonacciMemoization(3)).to.equal(2); + expect(fibonacciMemoization(4)).to.equal(3); + }); + +}); diff --git a/test/ts/others/hanoi.spec.ts b/test/ts/others/hanoi.spec.ts new file mode 100644 index 00000000..70f43fcd --- /dev/null +++ b/test/ts/others/hanoi.spec.ts @@ -0,0 +1,20 @@ +import 'mocha'; +import { expect } from 'chai'; +import { hanoi, hanoiStack } from '../../../src/ts/others/hanoi'; + +describe('Tower of Hanoi', () => { + + it('Hanoi', () => { + for (let i = 0; i < 10; i++) { + const result = hanoi(i, 'a', 'b', 'c'); + expect(result.length).to.equal(2 ** i - 1); + } + }); + + it('Hanoi with Stack', () => { + for (let i = 0; i < 10; i++) { + const result = hanoiStack(i); + expect(result.length).to.equal(2 ** i - 1); + } + }); +}); diff --git a/test/ts/others/hot-potato.spec.ts b/test/ts/others/hot-potato.spec.ts new file mode 100644 index 00000000..7ac9b827 --- /dev/null +++ b/test/ts/others/hot-potato.spec.ts @@ -0,0 +1,16 @@ +import { hotPotato } from '../../../src/ts/others/hot-potato'; +import 'mocha'; +import { expect } from 'chai'; + +describe('Hot Potato with Queue', () => { + + it('Hot potato game', () => { + const names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl']; + expect(hotPotato(names, 6).winner).to.equal('Ingrid'); + expect(hotPotato(names, 7).winner).to.equal('John'); + expect(hotPotato(names, 8).winner).to.equal('Jack'); + expect(hotPotato(names, 9).winner).to.equal('Ingrid'); + expect(hotPotato(names, 10).winner).to.equal('Carl'); + }); + +}); diff --git a/test/ts/others/palindrome-checker.spec.ts b/test/ts/others/palindrome-checker.spec.ts new file mode 100644 index 00000000..82451113 --- /dev/null +++ b/test/ts/others/palindrome-checker.spec.ts @@ -0,0 +1,20 @@ +import 'mocha'; +import { expect } from 'chai'; +import { palindromeChecker } from '../../../src/ts/others/palindrome-checker'; + +describe('Palindrome', () => { + + it('Palindrome Checker', () => { + expect(palindromeChecker('')).to.equal(false); + expect(palindromeChecker('a')).to.equal(true); + expect(palindromeChecker('aa')).to.equal(true); + expect(palindromeChecker('aba')).to.equal(true); + expect(palindromeChecker('ab')).to.equal(false); + expect(palindromeChecker('kayak')).to.equal(true); + expect(palindromeChecker('radar')).to.equal(true); + expect(palindromeChecker('level')).to.equal(true); + expect(palindromeChecker('Was it a car or a cat I saw')).to.equal(true); + expect(palindromeChecker('Step on no pets')).to.equal(true); + expect(palindromeChecker('Able was I ere I saw Elba')).to.equal(true); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..0acebfa5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,57 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "module": "es2015", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "lib": ["es2017"], /* Specify library files to be included in the compilation: */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist/ts", /* Redirect output structure to the directory. */ + // "rootDir": "./src/ts", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + // "strict": true /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + "noUnusedLocals": true, /* Report errors on unused locals. */ + "noUnusedParameters": true, /* Report errors on unused parameters. */ + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + }, + "exclude": ["node_modules", "dist"], + "include": ["src/ts/**/*"] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 00000000..449d62a0 --- /dev/null +++ b/tslint.json @@ -0,0 +1,115 @@ +{ + "rules": { + "arrow-return-shorthand": true, + "callable-types": true, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "import-blacklist": [ + true, + "rxjs" + ], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-misused-new": true, + "no-non-null-assertion": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unnecessary-initializer": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "typeof-compare": true, + "unified-signatures": true, + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..f7cdebe0 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,46 @@ +// @ts-check +/* eslint-disable */ +const webpack = require('webpack'); +const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; +const path = require('path'); +const env = require('yargs').argv.env; + +let libraryName = 'PacktDataStructuresAlgorithms'; + +let plugins = [], + outputFile; + +if (env === 'build') { + plugins.push(new UglifyJsPlugin({ minimize: true })); + outputFile = libraryName + '.min.js'; +} else { + outputFile = libraryName + '.js'; +} + +const config = { + entry: __dirname + '/src/js/index.js', + devtool: 'source-map', + output: { + path: __dirname + '/examples', + filename: outputFile, + library: libraryName, + libraryTarget: 'umd', + umdNamedDefine: true + }, + module: { + rules: [ + { + test: /(\.jsx|\.js)$/, + loader: 'babel-loader', + exclude: /(node_modules|bower_components)/ + } + ] + }, + resolve: { + modules: [path.resolve('./node_modules'), path.resolve('./src/js')], + extensions: ['.json', '.js'] + }, + plugins: plugins +}; + +module.exports = config;