├── README.markdown ├── _vendor ├── chai.js ├── mocha.css ├── mocha.js └── underscore.js ├── a_star └── README.markdown ├── big_o_notation └── README.MD ├── binary_trees ├── README.markdown ├── javascript │ ├── README.md │ ├── bst.js │ ├── bst_test.js │ └── index.html └── ruby │ ├── Guardfile │ ├── lib │ └── .gitkeep │ └── test │ └── binary_search_tree_test.rb ├── bitwise_binary_operators ├── README.md └── javascript │ ├── README.md │ ├── hexToDecimal-test.js │ ├── index.html │ └── rgbToHex-test.js ├── bloom_filters └── README.markdown ├── dungeon_generation └── README.md ├── graphs └── README.md ├── hash_array_mapped_tries └── README.markdown ├── hash_tables └── README.md ├── heaps └── README.md ├── huffman_coding ├── javascript │ ├── compression.js │ ├── index.html │ └── test.js └── readme.md ├── linked_lists ├── README.markdown ├── javascript │ ├── ListNode.js │ ├── ListNode_test.js │ ├── README.md │ ├── linkedList.js │ ├── linkedList_test.js │ └── test.html └── ruby │ └── linked_list_test.rb ├── luhn_algorithm ├── README.markdown └── luhn_test.rb ├── md5 ├── README.markdown ├── md5.rb └── md5_test.rb ├── pathfinding ├── Guardfile ├── lib │ ├── landscape.rb │ └── pathfinder.rb └── test │ ├── fixtures │ ├── landscape1.txt │ └── landscape2.txt │ ├── landscape_test.rb │ └── pathfinder_test.rb ├── red_black_trees └── README.markdown ├── resources ├── number_generator.rb └── sample_numbers.txt ├── sha_1 └── README.markdown ├── topics_wishlist.markdown └── tries └── README.md /README.markdown: -------------------------------------------------------------------------------- 1 | ## Data Structures & Algorithms 2 | 3 | In the process of becoming or practicing as a web application developer, it's easy to lose sight of Computer Science. 4 | 5 | This collection of exercises and experiments seeks to help you build an understanding of those foundations. 6 | 7 | ## Local Setup 8 | 9 | - Fork this repo 10 | - Clone your forked repo 11 | - `cd data_structures_and_algorithms` 12 | - Set the remote 13 | - `git remote add upstream https://github.com/turingschool/data_structures_and_algorithms.git` 14 | 15 | If there have been changes to this main repo - you can pull them down in the following way 16 | 17 | - Make sure you are on a master branch, with no uncommitted work (makes this easier) 18 | - `git fetch upstream master` 19 | 20 | ### Included Topics 21 | 22 | * binary trees - README|Ruby|JavaScript 23 | * Pathfinding Algorithm with A* - README|Ruby? 24 | * luhn algorithm (checksumming) - README|Ruby 25 | * graphs - README 26 | * linked lists - README|Ruby|JavaScript 27 | * bloom filters - README 28 | * Hash Array Mapped Trie - README 29 | * Hash Maps - README 30 | * Heaps - README 31 | * Huffman Coding - README|JavaScript 32 | * MD5 Hashing - README|Ruby 33 | * Red/Black Trees - README 34 | * SHA-1 - README 35 | * tries - README 36 | 37 | #### To-Do / Would like to cover 38 | 39 | * Skip Lists 40 | * Persistent Data Structures 41 | * Tries (more detail) 42 | * Hashing algorithm implementation (maybe start with MurmurHash?) 43 | * Dungeon Generation 44 | -------------------------------------------------------------------------------- /_vendor/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | #mocha .test .html-error { 140 | overflow: auto; 141 | color: black; 142 | line-height: 1.5; 143 | display: block; 144 | float: left; 145 | clear: left; 146 | font: 12px/1.5 monaco, monospace; 147 | margin: 5px; 148 | padding: 15px; 149 | border: 1px solid #eee; 150 | max-width: 85%; /*(1)*/ 151 | max-width: calc(100% - 42px); /*(2)*/ 152 | max-height: 300px; 153 | word-wrap: break-word; 154 | border-bottom-color: #ddd; 155 | -webkit-border-radius: 3px; 156 | -webkit-box-shadow: 0 1px 3px #eee; 157 | -moz-border-radius: 3px; 158 | -moz-box-shadow: 0 1px 3px #eee; 159 | border-radius: 3px; 160 | } 161 | 162 | #mocha .test .html-error pre.error { 163 | border: none; 164 | -webkit-border-radius: none; 165 | -webkit-box-shadow: none; 166 | -moz-border-radius: none; 167 | -moz-box-shadow: none; 168 | padding: 0; 169 | margin: 0; 170 | margin-top: 18px; 171 | max-height: none; 172 | } 173 | 174 | /** 175 | * (1): approximate for browsers not supporting calc 176 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 177 | * ^^ seriously 178 | */ 179 | #mocha .test pre { 180 | display: block; 181 | float: left; 182 | clear: left; 183 | font: 12px/1.5 monaco, monospace; 184 | margin: 5px; 185 | padding: 15px; 186 | border: 1px solid #eee; 187 | max-width: 85%; /*(1)*/ 188 | max-width: calc(100% - 42px); /*(2)*/ 189 | word-wrap: break-word; 190 | border-bottom-color: #ddd; 191 | -webkit-border-radius: 3px; 192 | -webkit-box-shadow: 0 1px 3px #eee; 193 | -moz-border-radius: 3px; 194 | -moz-box-shadow: 0 1px 3px #eee; 195 | border-radius: 3px; 196 | } 197 | 198 | #mocha .test h2 { 199 | position: relative; 200 | } 201 | 202 | #mocha .test a.replay { 203 | position: absolute; 204 | top: 3px; 205 | right: 0; 206 | text-decoration: none; 207 | vertical-align: middle; 208 | display: block; 209 | width: 15px; 210 | height: 15px; 211 | line-height: 15px; 212 | text-align: center; 213 | background: #eee; 214 | font-size: 15px; 215 | -moz-border-radius: 15px; 216 | border-radius: 15px; 217 | -webkit-transition: opacity 200ms; 218 | -moz-transition: opacity 200ms; 219 | transition: opacity 200ms; 220 | opacity: 0.3; 221 | color: #888; 222 | } 223 | 224 | #mocha .test:hover a.replay { 225 | opacity: 1; 226 | } 227 | 228 | #mocha-report.pass .test.fail { 229 | display: none; 230 | } 231 | 232 | #mocha-report.fail .test.pass { 233 | display: none; 234 | } 235 | 236 | #mocha-report.pending .test.pass, 237 | #mocha-report.pending .test.fail { 238 | display: none; 239 | } 240 | #mocha-report.pending .test.pass.pending { 241 | display: block; 242 | } 243 | 244 | #mocha-error { 245 | color: #c00; 246 | font-size: 1.5em; 247 | font-weight: 100; 248 | letter-spacing: 1px; 249 | } 250 | 251 | #mocha-stats { 252 | position: fixed; 253 | top: 15px; 254 | right: 10px; 255 | font-size: 12px; 256 | margin: 0; 257 | color: #888; 258 | z-index: 1; 259 | } 260 | 261 | #mocha-stats .progress { 262 | float: right; 263 | padding-top: 0; 264 | 265 | /** 266 | * Set safe initial values, so mochas .progress does not inherit these 267 | * properties from Bootstrap .progress (which causes .progress height to 268 | * equal line height set in Bootstrap). 269 | */ 270 | height: auto; 271 | box-shadow: none; 272 | background-color: initial; 273 | } 274 | 275 | #mocha-stats em { 276 | color: black; 277 | } 278 | 279 | #mocha-stats a { 280 | text-decoration: none; 281 | color: inherit; 282 | } 283 | 284 | #mocha-stats a:hover { 285 | border-bottom: 1px solid #eee; 286 | } 287 | 288 | #mocha-stats li { 289 | display: inline-block; 290 | margin: 0 5px; 291 | list-style: none; 292 | padding-top: 11px; 293 | } 294 | 295 | #mocha-stats canvas { 296 | width: 40px; 297 | height: 40px; 298 | } 299 | 300 | #mocha code .comment { color: #ddd; } 301 | #mocha code .init { color: #2f6fad; } 302 | #mocha code .string { color: #5890ad; } 303 | #mocha code .keyword { color: #8a6343; } 304 | #mocha code .number { color: #2f6fad; } 305 | 306 | @media screen and (max-device-width: 480px) { 307 | #mocha { 308 | margin: 60px 0px; 309 | } 310 | 311 | #mocha #stats { 312 | position: absolute; 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /_vendor/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.8.3 2 | // http://underscorejs.org 3 | // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | 6 | (function() { 7 | 8 | // Baseline setup 9 | // -------------- 10 | 11 | // Establish the root object, `window` in the browser, or `exports` on the server. 12 | var root = this; 13 | 14 | // Save the previous value of the `_` variable. 15 | var previousUnderscore = root._; 16 | 17 | // Save bytes in the minified (but not gzipped) version: 18 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; 19 | 20 | // Create quick reference variables for speed access to core prototypes. 21 | var 22 | push = ArrayProto.push, 23 | slice = ArrayProto.slice, 24 | toString = ObjProto.toString, 25 | hasOwnProperty = ObjProto.hasOwnProperty; 26 | 27 | // All **ECMAScript 5** native function implementations that we hope to use 28 | // are declared here. 29 | var 30 | nativeIsArray = Array.isArray, 31 | nativeKeys = Object.keys, 32 | nativeBind = FuncProto.bind, 33 | nativeCreate = Object.create; 34 | 35 | // Naked function reference for surrogate-prototype-swapping. 36 | var Ctor = function(){}; 37 | 38 | // Create a safe reference to the Underscore object for use below. 39 | var _ = function(obj) { 40 | if (obj instanceof _) return obj; 41 | if (!(this instanceof _)) return new _(obj); 42 | this._wrapped = obj; 43 | }; 44 | 45 | // Export the Underscore object for **Node.js**, with 46 | // backwards-compatibility for the old `require()` API. If we're in 47 | // the browser, add `_` as a global object. 48 | if (typeof exports !== 'undefined') { 49 | if (typeof module !== 'undefined' && module.exports) { 50 | exports = module.exports = _; 51 | } 52 | exports._ = _; 53 | } else { 54 | root._ = _; 55 | } 56 | 57 | // Current version. 58 | _.VERSION = '1.8.3'; 59 | 60 | // Internal function that returns an efficient (for current engines) version 61 | // of the passed-in callback, to be repeatedly applied in other Underscore 62 | // functions. 63 | var optimizeCb = function(func, context, argCount) { 64 | if (context === void 0) return func; 65 | switch (argCount == null ? 3 : argCount) { 66 | case 1: return function(value) { 67 | return func.call(context, value); 68 | }; 69 | case 2: return function(value, other) { 70 | return func.call(context, value, other); 71 | }; 72 | case 3: return function(value, index, collection) { 73 | return func.call(context, value, index, collection); 74 | }; 75 | case 4: return function(accumulator, value, index, collection) { 76 | return func.call(context, accumulator, value, index, collection); 77 | }; 78 | } 79 | return function() { 80 | return func.apply(context, arguments); 81 | }; 82 | }; 83 | 84 | // A mostly-internal function to generate callbacks that can be applied 85 | // to each element in a collection, returning the desired result — either 86 | // identity, an arbitrary callback, a property matcher, or a property accessor. 87 | var cb = function(value, context, argCount) { 88 | if (value == null) return _.identity; 89 | if (_.isFunction(value)) return optimizeCb(value, context, argCount); 90 | if (_.isObject(value)) return _.matcher(value); 91 | return _.property(value); 92 | }; 93 | _.iteratee = function(value, context) { 94 | return cb(value, context, Infinity); 95 | }; 96 | 97 | // An internal function for creating assigner functions. 98 | var createAssigner = function(keysFunc, undefinedOnly) { 99 | return function(obj) { 100 | var length = arguments.length; 101 | if (length < 2 || obj == null) return obj; 102 | for (var index = 1; index < length; index++) { 103 | var source = arguments[index], 104 | keys = keysFunc(source), 105 | l = keys.length; 106 | for (var i = 0; i < l; i++) { 107 | var key = keys[i]; 108 | if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; 109 | } 110 | } 111 | return obj; 112 | }; 113 | }; 114 | 115 | // An internal function for creating a new object that inherits from another. 116 | var baseCreate = function(prototype) { 117 | if (!_.isObject(prototype)) return {}; 118 | if (nativeCreate) return nativeCreate(prototype); 119 | Ctor.prototype = prototype; 120 | var result = new Ctor; 121 | Ctor.prototype = null; 122 | return result; 123 | }; 124 | 125 | var property = function(key) { 126 | return function(obj) { 127 | return obj == null ? void 0 : obj[key]; 128 | }; 129 | }; 130 | 131 | // Helper for collection methods to determine whether a collection 132 | // should be iterated as an array or as an object 133 | // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength 134 | // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 135 | var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; 136 | var getLength = property('length'); 137 | var isArrayLike = function(collection) { 138 | var length = getLength(collection); 139 | return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; 140 | }; 141 | 142 | // Collection Functions 143 | // -------------------- 144 | 145 | // The cornerstone, an `each` implementation, aka `forEach`. 146 | // Handles raw objects in addition to array-likes. Treats all 147 | // sparse array-likes as if they were dense. 148 | _.each = _.forEach = function(obj, iteratee, context) { 149 | iteratee = optimizeCb(iteratee, context); 150 | var i, length; 151 | if (isArrayLike(obj)) { 152 | for (i = 0, length = obj.length; i < length; i++) { 153 | iteratee(obj[i], i, obj); 154 | } 155 | } else { 156 | var keys = _.keys(obj); 157 | for (i = 0, length = keys.length; i < length; i++) { 158 | iteratee(obj[keys[i]], keys[i], obj); 159 | } 160 | } 161 | return obj; 162 | }; 163 | 164 | // Return the results of applying the iteratee to each element. 165 | _.map = _.collect = function(obj, iteratee, context) { 166 | iteratee = cb(iteratee, context); 167 | var keys = !isArrayLike(obj) && _.keys(obj), 168 | length = (keys || obj).length, 169 | results = Array(length); 170 | for (var index = 0; index < length; index++) { 171 | var currentKey = keys ? keys[index] : index; 172 | results[index] = iteratee(obj[currentKey], currentKey, obj); 173 | } 174 | return results; 175 | }; 176 | 177 | // Create a reducing function iterating left or right. 178 | function createReduce(dir) { 179 | // Optimized iterator function as using arguments.length 180 | // in the main function will deoptimize the, see #1991. 181 | function iterator(obj, iteratee, memo, keys, index, length) { 182 | for (; index >= 0 && index < length; index += dir) { 183 | var currentKey = keys ? keys[index] : index; 184 | memo = iteratee(memo, obj[currentKey], currentKey, obj); 185 | } 186 | return memo; 187 | } 188 | 189 | return function(obj, iteratee, memo, context) { 190 | iteratee = optimizeCb(iteratee, context, 4); 191 | var keys = !isArrayLike(obj) && _.keys(obj), 192 | length = (keys || obj).length, 193 | index = dir > 0 ? 0 : length - 1; 194 | // Determine the initial value if none is provided. 195 | if (arguments.length < 3) { 196 | memo = obj[keys ? keys[index] : index]; 197 | index += dir; 198 | } 199 | return iterator(obj, iteratee, memo, keys, index, length); 200 | }; 201 | } 202 | 203 | // **Reduce** builds up a single result from a list of values, aka `inject`, 204 | // or `foldl`. 205 | _.reduce = _.foldl = _.inject = createReduce(1); 206 | 207 | // The right-associative version of reduce, also known as `foldr`. 208 | _.reduceRight = _.foldr = createReduce(-1); 209 | 210 | // Return the first value which passes a truth test. Aliased as `detect`. 211 | _.find = _.detect = function(obj, predicate, context) { 212 | var key; 213 | if (isArrayLike(obj)) { 214 | key = _.findIndex(obj, predicate, context); 215 | } else { 216 | key = _.findKey(obj, predicate, context); 217 | } 218 | if (key !== void 0 && key !== -1) return obj[key]; 219 | }; 220 | 221 | // Return all the elements that pass a truth test. 222 | // Aliased as `select`. 223 | _.filter = _.select = function(obj, predicate, context) { 224 | var results = []; 225 | predicate = cb(predicate, context); 226 | _.each(obj, function(value, index, list) { 227 | if (predicate(value, index, list)) results.push(value); 228 | }); 229 | return results; 230 | }; 231 | 232 | // Return all the elements for which a truth test fails. 233 | _.reject = function(obj, predicate, context) { 234 | return _.filter(obj, _.negate(cb(predicate)), context); 235 | }; 236 | 237 | // Determine whether all of the elements match a truth test. 238 | // Aliased as `all`. 239 | _.every = _.all = function(obj, predicate, context) { 240 | predicate = cb(predicate, context); 241 | var keys = !isArrayLike(obj) && _.keys(obj), 242 | length = (keys || obj).length; 243 | for (var index = 0; index < length; index++) { 244 | var currentKey = keys ? keys[index] : index; 245 | if (!predicate(obj[currentKey], currentKey, obj)) return false; 246 | } 247 | return true; 248 | }; 249 | 250 | // Determine if at least one element in the object matches a truth test. 251 | // Aliased as `any`. 252 | _.some = _.any = function(obj, predicate, context) { 253 | predicate = cb(predicate, context); 254 | var keys = !isArrayLike(obj) && _.keys(obj), 255 | length = (keys || obj).length; 256 | for (var index = 0; index < length; index++) { 257 | var currentKey = keys ? keys[index] : index; 258 | if (predicate(obj[currentKey], currentKey, obj)) return true; 259 | } 260 | return false; 261 | }; 262 | 263 | // Determine if the array or object contains a given item (using `===`). 264 | // Aliased as `includes` and `include`. 265 | _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { 266 | if (!isArrayLike(obj)) obj = _.values(obj); 267 | if (typeof fromIndex != 'number' || guard) fromIndex = 0; 268 | return _.indexOf(obj, item, fromIndex) >= 0; 269 | }; 270 | 271 | // Invoke a method (with arguments) on every item in a collection. 272 | _.invoke = function(obj, method) { 273 | var args = slice.call(arguments, 2); 274 | var isFunc = _.isFunction(method); 275 | return _.map(obj, function(value) { 276 | var func = isFunc ? method : value[method]; 277 | return func == null ? func : func.apply(value, args); 278 | }); 279 | }; 280 | 281 | // Convenience version of a common use case of `map`: fetching a property. 282 | _.pluck = function(obj, key) { 283 | return _.map(obj, _.property(key)); 284 | }; 285 | 286 | // Convenience version of a common use case of `filter`: selecting only objects 287 | // containing specific `key:value` pairs. 288 | _.where = function(obj, attrs) { 289 | return _.filter(obj, _.matcher(attrs)); 290 | }; 291 | 292 | // Convenience version of a common use case of `find`: getting the first object 293 | // containing specific `key:value` pairs. 294 | _.findWhere = function(obj, attrs) { 295 | return _.find(obj, _.matcher(attrs)); 296 | }; 297 | 298 | // Return the maximum element (or element-based computation). 299 | _.max = function(obj, iteratee, context) { 300 | var result = -Infinity, lastComputed = -Infinity, 301 | value, computed; 302 | if (iteratee == null && obj != null) { 303 | obj = isArrayLike(obj) ? obj : _.values(obj); 304 | for (var i = 0, length = obj.length; i < length; i++) { 305 | value = obj[i]; 306 | if (value > result) { 307 | result = value; 308 | } 309 | } 310 | } else { 311 | iteratee = cb(iteratee, context); 312 | _.each(obj, function(value, index, list) { 313 | computed = iteratee(value, index, list); 314 | if (computed > lastComputed || computed === -Infinity && result === -Infinity) { 315 | result = value; 316 | lastComputed = computed; 317 | } 318 | }); 319 | } 320 | return result; 321 | }; 322 | 323 | // Return the minimum element (or element-based computation). 324 | _.min = function(obj, iteratee, context) { 325 | var result = Infinity, lastComputed = Infinity, 326 | value, computed; 327 | if (iteratee == null && obj != null) { 328 | obj = isArrayLike(obj) ? obj : _.values(obj); 329 | for (var i = 0, length = obj.length; i < length; i++) { 330 | value = obj[i]; 331 | if (value < result) { 332 | result = value; 333 | } 334 | } 335 | } else { 336 | iteratee = cb(iteratee, context); 337 | _.each(obj, function(value, index, list) { 338 | computed = iteratee(value, index, list); 339 | if (computed < lastComputed || computed === Infinity && result === Infinity) { 340 | result = value; 341 | lastComputed = computed; 342 | } 343 | }); 344 | } 345 | return result; 346 | }; 347 | 348 | // Shuffle a collection, using the modern version of the 349 | // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). 350 | _.shuffle = function(obj) { 351 | var set = isArrayLike(obj) ? obj : _.values(obj); 352 | var length = set.length; 353 | var shuffled = Array(length); 354 | for (var index = 0, rand; index < length; index++) { 355 | rand = _.random(0, index); 356 | if (rand !== index) shuffled[index] = shuffled[rand]; 357 | shuffled[rand] = set[index]; 358 | } 359 | return shuffled; 360 | }; 361 | 362 | // Sample **n** random values from a collection. 363 | // If **n** is not specified, returns a single random element. 364 | // The internal `guard` argument allows it to work with `map`. 365 | _.sample = function(obj, n, guard) { 366 | if (n == null || guard) { 367 | if (!isArrayLike(obj)) obj = _.values(obj); 368 | return obj[_.random(obj.length - 1)]; 369 | } 370 | return _.shuffle(obj).slice(0, Math.max(0, n)); 371 | }; 372 | 373 | // Sort the object's values by a criterion produced by an iteratee. 374 | _.sortBy = function(obj, iteratee, context) { 375 | iteratee = cb(iteratee, context); 376 | return _.pluck(_.map(obj, function(value, index, list) { 377 | return { 378 | value: value, 379 | index: index, 380 | criteria: iteratee(value, index, list) 381 | }; 382 | }).sort(function(left, right) { 383 | var a = left.criteria; 384 | var b = right.criteria; 385 | if (a !== b) { 386 | if (a > b || a === void 0) return 1; 387 | if (a < b || b === void 0) return -1; 388 | } 389 | return left.index - right.index; 390 | }), 'value'); 391 | }; 392 | 393 | // An internal function used for aggregate "group by" operations. 394 | var group = function(behavior) { 395 | return function(obj, iteratee, context) { 396 | var result = {}; 397 | iteratee = cb(iteratee, context); 398 | _.each(obj, function(value, index) { 399 | var key = iteratee(value, index, obj); 400 | behavior(result, value, key); 401 | }); 402 | return result; 403 | }; 404 | }; 405 | 406 | // Groups the object's values by a criterion. Pass either a string attribute 407 | // to group by, or a function that returns the criterion. 408 | _.groupBy = group(function(result, value, key) { 409 | if (_.has(result, key)) result[key].push(value); else result[key] = [value]; 410 | }); 411 | 412 | // Indexes the object's values by a criterion, similar to `groupBy`, but for 413 | // when you know that your index values will be unique. 414 | _.indexBy = group(function(result, value, key) { 415 | result[key] = value; 416 | }); 417 | 418 | // Counts instances of an object that group by a certain criterion. Pass 419 | // either a string attribute to count by, or a function that returns the 420 | // criterion. 421 | _.countBy = group(function(result, value, key) { 422 | if (_.has(result, key)) result[key]++; else result[key] = 1; 423 | }); 424 | 425 | // Safely create a real, live array from anything iterable. 426 | _.toArray = function(obj) { 427 | if (!obj) return []; 428 | if (_.isArray(obj)) return slice.call(obj); 429 | if (isArrayLike(obj)) return _.map(obj, _.identity); 430 | return _.values(obj); 431 | }; 432 | 433 | // Return the number of elements in an object. 434 | _.size = function(obj) { 435 | if (obj == null) return 0; 436 | return isArrayLike(obj) ? obj.length : _.keys(obj).length; 437 | }; 438 | 439 | // Split a collection into two arrays: one whose elements all satisfy the given 440 | // predicate, and one whose elements all do not satisfy the predicate. 441 | _.partition = function(obj, predicate, context) { 442 | predicate = cb(predicate, context); 443 | var pass = [], fail = []; 444 | _.each(obj, function(value, key, obj) { 445 | (predicate(value, key, obj) ? pass : fail).push(value); 446 | }); 447 | return [pass, fail]; 448 | }; 449 | 450 | // Array Functions 451 | // --------------- 452 | 453 | // Get the first element of an array. Passing **n** will return the first N 454 | // values in the array. Aliased as `head` and `take`. The **guard** check 455 | // allows it to work with `_.map`. 456 | _.first = _.head = _.take = function(array, n, guard) { 457 | if (array == null) return void 0; 458 | if (n == null || guard) return array[0]; 459 | return _.initial(array, array.length - n); 460 | }; 461 | 462 | // Returns everything but the last entry of the array. Especially useful on 463 | // the arguments object. Passing **n** will return all the values in 464 | // the array, excluding the last N. 465 | _.initial = function(array, n, guard) { 466 | return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); 467 | }; 468 | 469 | // Get the last element of an array. Passing **n** will return the last N 470 | // values in the array. 471 | _.last = function(array, n, guard) { 472 | if (array == null) return void 0; 473 | if (n == null || guard) return array[array.length - 1]; 474 | return _.rest(array, Math.max(0, array.length - n)); 475 | }; 476 | 477 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 478 | // Especially useful on the arguments object. Passing an **n** will return 479 | // the rest N values in the array. 480 | _.rest = _.tail = _.drop = function(array, n, guard) { 481 | return slice.call(array, n == null || guard ? 1 : n); 482 | }; 483 | 484 | // Trim out all falsy values from an array. 485 | _.compact = function(array) { 486 | return _.filter(array, _.identity); 487 | }; 488 | 489 | // Internal implementation of a recursive `flatten` function. 490 | var flatten = function(input, shallow, strict, startIndex) { 491 | var output = [], idx = 0; 492 | for (var i = startIndex || 0, length = getLength(input); i < length; i++) { 493 | var value = input[i]; 494 | if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { 495 | //flatten current level of array or arguments object 496 | if (!shallow) value = flatten(value, shallow, strict); 497 | var j = 0, len = value.length; 498 | output.length += len; 499 | while (j < len) { 500 | output[idx++] = value[j++]; 501 | } 502 | } else if (!strict) { 503 | output[idx++] = value; 504 | } 505 | } 506 | return output; 507 | }; 508 | 509 | // Flatten out an array, either recursively (by default), or just one level. 510 | _.flatten = function(array, shallow) { 511 | return flatten(array, shallow, false); 512 | }; 513 | 514 | // Return a version of the array that does not contain the specified value(s). 515 | _.without = function(array) { 516 | return _.difference(array, slice.call(arguments, 1)); 517 | }; 518 | 519 | // Produce a duplicate-free version of the array. If the array has already 520 | // been sorted, you have the option of using a faster algorithm. 521 | // Aliased as `unique`. 522 | _.uniq = _.unique = function(array, isSorted, iteratee, context) { 523 | if (!_.isBoolean(isSorted)) { 524 | context = iteratee; 525 | iteratee = isSorted; 526 | isSorted = false; 527 | } 528 | if (iteratee != null) iteratee = cb(iteratee, context); 529 | var result = []; 530 | var seen = []; 531 | for (var i = 0, length = getLength(array); i < length; i++) { 532 | var value = array[i], 533 | computed = iteratee ? iteratee(value, i, array) : value; 534 | if (isSorted) { 535 | if (!i || seen !== computed) result.push(value); 536 | seen = computed; 537 | } else if (iteratee) { 538 | if (!_.contains(seen, computed)) { 539 | seen.push(computed); 540 | result.push(value); 541 | } 542 | } else if (!_.contains(result, value)) { 543 | result.push(value); 544 | } 545 | } 546 | return result; 547 | }; 548 | 549 | // Produce an array that contains the union: each distinct element from all of 550 | // the passed-in arrays. 551 | _.union = function() { 552 | return _.uniq(flatten(arguments, true, true)); 553 | }; 554 | 555 | // Produce an array that contains every item shared between all the 556 | // passed-in arrays. 557 | _.intersection = function(array) { 558 | var result = []; 559 | var argsLength = arguments.length; 560 | for (var i = 0, length = getLength(array); i < length; i++) { 561 | var item = array[i]; 562 | if (_.contains(result, item)) continue; 563 | for (var j = 1; j < argsLength; j++) { 564 | if (!_.contains(arguments[j], item)) break; 565 | } 566 | if (j === argsLength) result.push(item); 567 | } 568 | return result; 569 | }; 570 | 571 | // Take the difference between one array and a number of other arrays. 572 | // Only the elements present in just the first array will remain. 573 | _.difference = function(array) { 574 | var rest = flatten(arguments, true, true, 1); 575 | return _.filter(array, function(value){ 576 | return !_.contains(rest, value); 577 | }); 578 | }; 579 | 580 | // Zip together multiple lists into a single array -- elements that share 581 | // an index go together. 582 | _.zip = function() { 583 | return _.unzip(arguments); 584 | }; 585 | 586 | // Complement of _.zip. Unzip accepts an array of arrays and groups 587 | // each array's elements on shared indices 588 | _.unzip = function(array) { 589 | var length = array && _.max(array, getLength).length || 0; 590 | var result = Array(length); 591 | 592 | for (var index = 0; index < length; index++) { 593 | result[index] = _.pluck(array, index); 594 | } 595 | return result; 596 | }; 597 | 598 | // Converts lists into objects. Pass either a single array of `[key, value]` 599 | // pairs, or two parallel arrays of the same length -- one of keys, and one of 600 | // the corresponding values. 601 | _.object = function(list, values) { 602 | var result = {}; 603 | for (var i = 0, length = getLength(list); i < length; i++) { 604 | if (values) { 605 | result[list[i]] = values[i]; 606 | } else { 607 | result[list[i][0]] = list[i][1]; 608 | } 609 | } 610 | return result; 611 | }; 612 | 613 | // Generator function to create the findIndex and findLastIndex functions 614 | function createPredicateIndexFinder(dir) { 615 | return function(array, predicate, context) { 616 | predicate = cb(predicate, context); 617 | var length = getLength(array); 618 | var index = dir > 0 ? 0 : length - 1; 619 | for (; index >= 0 && index < length; index += dir) { 620 | if (predicate(array[index], index, array)) return index; 621 | } 622 | return -1; 623 | }; 624 | } 625 | 626 | // Returns the first index on an array-like that passes a predicate test 627 | _.findIndex = createPredicateIndexFinder(1); 628 | _.findLastIndex = createPredicateIndexFinder(-1); 629 | 630 | // Use a comparator function to figure out the smallest index at which 631 | // an object should be inserted so as to maintain order. Uses binary search. 632 | _.sortedIndex = function(array, obj, iteratee, context) { 633 | iteratee = cb(iteratee, context, 1); 634 | var value = iteratee(obj); 635 | var low = 0, high = getLength(array); 636 | while (low < high) { 637 | var mid = Math.floor((low + high) / 2); 638 | if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; 639 | } 640 | return low; 641 | }; 642 | 643 | // Generator function to create the indexOf and lastIndexOf functions 644 | function createIndexFinder(dir, predicateFind, sortedIndex) { 645 | return function(array, item, idx) { 646 | var i = 0, length = getLength(array); 647 | if (typeof idx == 'number') { 648 | if (dir > 0) { 649 | i = idx >= 0 ? idx : Math.max(idx + length, i); 650 | } else { 651 | length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; 652 | } 653 | } else if (sortedIndex && idx && length) { 654 | idx = sortedIndex(array, item); 655 | return array[idx] === item ? idx : -1; 656 | } 657 | if (item !== item) { 658 | idx = predicateFind(slice.call(array, i, length), _.isNaN); 659 | return idx >= 0 ? idx + i : -1; 660 | } 661 | for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { 662 | if (array[idx] === item) return idx; 663 | } 664 | return -1; 665 | }; 666 | } 667 | 668 | // Return the position of the first occurrence of an item in an array, 669 | // or -1 if the item is not included in the array. 670 | // If the array is large and already in sort order, pass `true` 671 | // for **isSorted** to use binary search. 672 | _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); 673 | _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); 674 | 675 | // Generate an integer Array containing an arithmetic progression. A port of 676 | // the native Python `range()` function. See 677 | // [the Python documentation](http://docs.python.org/library/functions.html#range). 678 | _.range = function(start, stop, step) { 679 | if (stop == null) { 680 | stop = start || 0; 681 | start = 0; 682 | } 683 | step = step || 1; 684 | 685 | var length = Math.max(Math.ceil((stop - start) / step), 0); 686 | var range = Array(length); 687 | 688 | for (var idx = 0; idx < length; idx++, start += step) { 689 | range[idx] = start; 690 | } 691 | 692 | return range; 693 | }; 694 | 695 | // Function (ahem) Functions 696 | // ------------------ 697 | 698 | // Determines whether to execute a function as a constructor 699 | // or a normal function with the provided arguments 700 | var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) { 701 | if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); 702 | var self = baseCreate(sourceFunc.prototype); 703 | var result = sourceFunc.apply(self, args); 704 | if (_.isObject(result)) return result; 705 | return self; 706 | }; 707 | 708 | // Create a function bound to a given object (assigning `this`, and arguments, 709 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 710 | // available. 711 | _.bind = function(func, context) { 712 | if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 713 | if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); 714 | var args = slice.call(arguments, 2); 715 | var bound = function() { 716 | return executeBound(func, bound, context, this, args.concat(slice.call(arguments))); 717 | }; 718 | return bound; 719 | }; 720 | 721 | // Partially apply a function by creating a version that has had some of its 722 | // arguments pre-filled, without changing its dynamic `this` context. _ acts 723 | // as a placeholder, allowing any combination of arguments to be pre-filled. 724 | _.partial = function(func) { 725 | var boundArgs = slice.call(arguments, 1); 726 | var bound = function() { 727 | var position = 0, length = boundArgs.length; 728 | var args = Array(length); 729 | for (var i = 0; i < length; i++) { 730 | args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i]; 731 | } 732 | while (position < arguments.length) args.push(arguments[position++]); 733 | return executeBound(func, bound, this, this, args); 734 | }; 735 | return bound; 736 | }; 737 | 738 | // Bind a number of an object's methods to that object. Remaining arguments 739 | // are the method names to be bound. Useful for ensuring that all callbacks 740 | // defined on an object belong to it. 741 | _.bindAll = function(obj) { 742 | var i, length = arguments.length, key; 743 | if (length <= 1) throw new Error('bindAll must be passed function names'); 744 | for (i = 1; i < length; i++) { 745 | key = arguments[i]; 746 | obj[key] = _.bind(obj[key], obj); 747 | } 748 | return obj; 749 | }; 750 | 751 | // Memoize an expensive function by storing its results. 752 | _.memoize = function(func, hasher) { 753 | var memoize = function(key) { 754 | var cache = memoize.cache; 755 | var address = '' + (hasher ? hasher.apply(this, arguments) : key); 756 | if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); 757 | return cache[address]; 758 | }; 759 | memoize.cache = {}; 760 | return memoize; 761 | }; 762 | 763 | // Delays a function for the given number of milliseconds, and then calls 764 | // it with the arguments supplied. 765 | _.delay = function(func, wait) { 766 | var args = slice.call(arguments, 2); 767 | return setTimeout(function(){ 768 | return func.apply(null, args); 769 | }, wait); 770 | }; 771 | 772 | // Defers a function, scheduling it to run after the current call stack has 773 | // cleared. 774 | _.defer = _.partial(_.delay, _, 1); 775 | 776 | // Returns a function, that, when invoked, will only be triggered at most once 777 | // during a given window of time. Normally, the throttled function will run 778 | // as much as it can, without ever going more than once per `wait` duration; 779 | // but if you'd like to disable the execution on the leading edge, pass 780 | // `{leading: false}`. To disable execution on the trailing edge, ditto. 781 | _.throttle = function(func, wait, options) { 782 | var context, args, result; 783 | var timeout = null; 784 | var previous = 0; 785 | if (!options) options = {}; 786 | var later = function() { 787 | previous = options.leading === false ? 0 : _.now(); 788 | timeout = null; 789 | result = func.apply(context, args); 790 | if (!timeout) context = args = null; 791 | }; 792 | return function() { 793 | var now = _.now(); 794 | if (!previous && options.leading === false) previous = now; 795 | var remaining = wait - (now - previous); 796 | context = this; 797 | args = arguments; 798 | if (remaining <= 0 || remaining > wait) { 799 | if (timeout) { 800 | clearTimeout(timeout); 801 | timeout = null; 802 | } 803 | previous = now; 804 | result = func.apply(context, args); 805 | if (!timeout) context = args = null; 806 | } else if (!timeout && options.trailing !== false) { 807 | timeout = setTimeout(later, remaining); 808 | } 809 | return result; 810 | }; 811 | }; 812 | 813 | // Returns a function, that, as long as it continues to be invoked, will not 814 | // be triggered. The function will be called after it stops being called for 815 | // N milliseconds. If `immediate` is passed, trigger the function on the 816 | // leading edge, instead of the trailing. 817 | _.debounce = function(func, wait, immediate) { 818 | var timeout, args, context, timestamp, result; 819 | 820 | var later = function() { 821 | var last = _.now() - timestamp; 822 | 823 | if (last < wait && last >= 0) { 824 | timeout = setTimeout(later, wait - last); 825 | } else { 826 | timeout = null; 827 | if (!immediate) { 828 | result = func.apply(context, args); 829 | if (!timeout) context = args = null; 830 | } 831 | } 832 | }; 833 | 834 | return function() { 835 | context = this; 836 | args = arguments; 837 | timestamp = _.now(); 838 | var callNow = immediate && !timeout; 839 | if (!timeout) timeout = setTimeout(later, wait); 840 | if (callNow) { 841 | result = func.apply(context, args); 842 | context = args = null; 843 | } 844 | 845 | return result; 846 | }; 847 | }; 848 | 849 | // Returns the first function passed as an argument to the second, 850 | // allowing you to adjust arguments, run code before and after, and 851 | // conditionally execute the original function. 852 | _.wrap = function(func, wrapper) { 853 | return _.partial(wrapper, func); 854 | }; 855 | 856 | // Returns a negated version of the passed-in predicate. 857 | _.negate = function(predicate) { 858 | return function() { 859 | return !predicate.apply(this, arguments); 860 | }; 861 | }; 862 | 863 | // Returns a function that is the composition of a list of functions, each 864 | // consuming the return value of the function that follows. 865 | _.compose = function() { 866 | var args = arguments; 867 | var start = args.length - 1; 868 | return function() { 869 | var i = start; 870 | var result = args[start].apply(this, arguments); 871 | while (i--) result = args[i].call(this, result); 872 | return result; 873 | }; 874 | }; 875 | 876 | // Returns a function that will only be executed on and after the Nth call. 877 | _.after = function(times, func) { 878 | return function() { 879 | if (--times < 1) { 880 | return func.apply(this, arguments); 881 | } 882 | }; 883 | }; 884 | 885 | // Returns a function that will only be executed up to (but not including) the Nth call. 886 | _.before = function(times, func) { 887 | var memo; 888 | return function() { 889 | if (--times > 0) { 890 | memo = func.apply(this, arguments); 891 | } 892 | if (times <= 1) func = null; 893 | return memo; 894 | }; 895 | }; 896 | 897 | // Returns a function that will be executed at most one time, no matter how 898 | // often you call it. Useful for lazy initialization. 899 | _.once = _.partial(_.before, 2); 900 | 901 | // Object Functions 902 | // ---------------- 903 | 904 | // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed. 905 | var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); 906 | var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', 907 | 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; 908 | 909 | function collectNonEnumProps(obj, keys) { 910 | var nonEnumIdx = nonEnumerableProps.length; 911 | var constructor = obj.constructor; 912 | var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto; 913 | 914 | // Constructor is a special case. 915 | var prop = 'constructor'; 916 | if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); 917 | 918 | while (nonEnumIdx--) { 919 | prop = nonEnumerableProps[nonEnumIdx]; 920 | if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { 921 | keys.push(prop); 922 | } 923 | } 924 | } 925 | 926 | // Retrieve the names of an object's own properties. 927 | // Delegates to **ECMAScript 5**'s native `Object.keys` 928 | _.keys = function(obj) { 929 | if (!_.isObject(obj)) return []; 930 | if (nativeKeys) return nativeKeys(obj); 931 | var keys = []; 932 | for (var key in obj) if (_.has(obj, key)) keys.push(key); 933 | // Ahem, IE < 9. 934 | if (hasEnumBug) collectNonEnumProps(obj, keys); 935 | return keys; 936 | }; 937 | 938 | // Retrieve all the property names of an object. 939 | _.allKeys = function(obj) { 940 | if (!_.isObject(obj)) return []; 941 | var keys = []; 942 | for (var key in obj) keys.push(key); 943 | // Ahem, IE < 9. 944 | if (hasEnumBug) collectNonEnumProps(obj, keys); 945 | return keys; 946 | }; 947 | 948 | // Retrieve the values of an object's properties. 949 | _.values = function(obj) { 950 | var keys = _.keys(obj); 951 | var length = keys.length; 952 | var values = Array(length); 953 | for (var i = 0; i < length; i++) { 954 | values[i] = obj[keys[i]]; 955 | } 956 | return values; 957 | }; 958 | 959 | // Returns the results of applying the iteratee to each element of the object 960 | // In contrast to _.map it returns an object 961 | _.mapObject = function(obj, iteratee, context) { 962 | iteratee = cb(iteratee, context); 963 | var keys = _.keys(obj), 964 | length = keys.length, 965 | results = {}, 966 | currentKey; 967 | for (var index = 0; index < length; index++) { 968 | currentKey = keys[index]; 969 | results[currentKey] = iteratee(obj[currentKey], currentKey, obj); 970 | } 971 | return results; 972 | }; 973 | 974 | // Convert an object into a list of `[key, value]` pairs. 975 | _.pairs = function(obj) { 976 | var keys = _.keys(obj); 977 | var length = keys.length; 978 | var pairs = Array(length); 979 | for (var i = 0; i < length; i++) { 980 | pairs[i] = [keys[i], obj[keys[i]]]; 981 | } 982 | return pairs; 983 | }; 984 | 985 | // Invert the keys and values of an object. The values must be serializable. 986 | _.invert = function(obj) { 987 | var result = {}; 988 | var keys = _.keys(obj); 989 | for (var i = 0, length = keys.length; i < length; i++) { 990 | result[obj[keys[i]]] = keys[i]; 991 | } 992 | return result; 993 | }; 994 | 995 | // Return a sorted list of the function names available on the object. 996 | // Aliased as `methods` 997 | _.functions = _.methods = function(obj) { 998 | var names = []; 999 | for (var key in obj) { 1000 | if (_.isFunction(obj[key])) names.push(key); 1001 | } 1002 | return names.sort(); 1003 | }; 1004 | 1005 | // Extend a given object with all the properties in passed-in object(s). 1006 | _.extend = createAssigner(_.allKeys); 1007 | 1008 | // Assigns a given object with all the own properties in the passed-in object(s) 1009 | // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 1010 | _.extendOwn = _.assign = createAssigner(_.keys); 1011 | 1012 | // Returns the first key on an object that passes a predicate test 1013 | _.findKey = function(obj, predicate, context) { 1014 | predicate = cb(predicate, context); 1015 | var keys = _.keys(obj), key; 1016 | for (var i = 0, length = keys.length; i < length; i++) { 1017 | key = keys[i]; 1018 | if (predicate(obj[key], key, obj)) return key; 1019 | } 1020 | }; 1021 | 1022 | // Return a copy of the object only containing the whitelisted properties. 1023 | _.pick = function(object, oiteratee, context) { 1024 | var result = {}, obj = object, iteratee, keys; 1025 | if (obj == null) return result; 1026 | if (_.isFunction(oiteratee)) { 1027 | keys = _.allKeys(obj); 1028 | iteratee = optimizeCb(oiteratee, context); 1029 | } else { 1030 | keys = flatten(arguments, false, false, 1); 1031 | iteratee = function(value, key, obj) { return key in obj; }; 1032 | obj = Object(obj); 1033 | } 1034 | for (var i = 0, length = keys.length; i < length; i++) { 1035 | var key = keys[i]; 1036 | var value = obj[key]; 1037 | if (iteratee(value, key, obj)) result[key] = value; 1038 | } 1039 | return result; 1040 | }; 1041 | 1042 | // Return a copy of the object without the blacklisted properties. 1043 | _.omit = function(obj, iteratee, context) { 1044 | if (_.isFunction(iteratee)) { 1045 | iteratee = _.negate(iteratee); 1046 | } else { 1047 | var keys = _.map(flatten(arguments, false, false, 1), String); 1048 | iteratee = function(value, key) { 1049 | return !_.contains(keys, key); 1050 | }; 1051 | } 1052 | return _.pick(obj, iteratee, context); 1053 | }; 1054 | 1055 | // Fill in a given object with default properties. 1056 | _.defaults = createAssigner(_.allKeys, true); 1057 | 1058 | // Creates an object that inherits from the given prototype object. 1059 | // If additional properties are provided then they will be added to the 1060 | // created object. 1061 | _.create = function(prototype, props) { 1062 | var result = baseCreate(prototype); 1063 | if (props) _.extendOwn(result, props); 1064 | return result; 1065 | }; 1066 | 1067 | // Create a (shallow-cloned) duplicate of an object. 1068 | _.clone = function(obj) { 1069 | if (!_.isObject(obj)) return obj; 1070 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 1071 | }; 1072 | 1073 | // Invokes interceptor with the obj, and then returns obj. 1074 | // The primary purpose of this method is to "tap into" a method chain, in 1075 | // order to perform operations on intermediate results within the chain. 1076 | _.tap = function(obj, interceptor) { 1077 | interceptor(obj); 1078 | return obj; 1079 | }; 1080 | 1081 | // Returns whether an object has a given set of `key:value` pairs. 1082 | _.isMatch = function(object, attrs) { 1083 | var keys = _.keys(attrs), length = keys.length; 1084 | if (object == null) return !length; 1085 | var obj = Object(object); 1086 | for (var i = 0; i < length; i++) { 1087 | var key = keys[i]; 1088 | if (attrs[key] !== obj[key] || !(key in obj)) return false; 1089 | } 1090 | return true; 1091 | }; 1092 | 1093 | 1094 | // Internal recursive comparison function for `isEqual`. 1095 | var eq = function(a, b, aStack, bStack) { 1096 | // Identical objects are equal. `0 === -0`, but they aren't identical. 1097 | // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). 1098 | if (a === b) return a !== 0 || 1 / a === 1 / b; 1099 | // A strict comparison is necessary because `null == undefined`. 1100 | if (a == null || b == null) return a === b; 1101 | // Unwrap any wrapped objects. 1102 | if (a instanceof _) a = a._wrapped; 1103 | if (b instanceof _) b = b._wrapped; 1104 | // Compare `[[Class]]` names. 1105 | var className = toString.call(a); 1106 | if (className !== toString.call(b)) return false; 1107 | switch (className) { 1108 | // Strings, numbers, regular expressions, dates, and booleans are compared by value. 1109 | case '[object RegExp]': 1110 | // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') 1111 | case '[object String]': 1112 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 1113 | // equivalent to `new String("5")`. 1114 | return '' + a === '' + b; 1115 | case '[object Number]': 1116 | // `NaN`s are equivalent, but non-reflexive. 1117 | // Object(NaN) is equivalent to NaN 1118 | if (+a !== +a) return +b !== +b; 1119 | // An `egal` comparison is performed for other numeric values. 1120 | return +a === 0 ? 1 / +a === 1 / b : +a === +b; 1121 | case '[object Date]': 1122 | case '[object Boolean]': 1123 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their 1124 | // millisecond representations. Note that invalid dates with millisecond representations 1125 | // of `NaN` are not equivalent. 1126 | return +a === +b; 1127 | } 1128 | 1129 | var areArrays = className === '[object Array]'; 1130 | if (!areArrays) { 1131 | if (typeof a != 'object' || typeof b != 'object') return false; 1132 | 1133 | // Objects with different constructors are not equivalent, but `Object`s or `Array`s 1134 | // from different frames are. 1135 | var aCtor = a.constructor, bCtor = b.constructor; 1136 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && 1137 | _.isFunction(bCtor) && bCtor instanceof bCtor) 1138 | && ('constructor' in a && 'constructor' in b)) { 1139 | return false; 1140 | } 1141 | } 1142 | // Assume equality for cyclic structures. The algorithm for detecting cyclic 1143 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 1144 | 1145 | // Initializing stack of traversed objects. 1146 | // It's done here since we only need them for objects and arrays comparison. 1147 | aStack = aStack || []; 1148 | bStack = bStack || []; 1149 | var length = aStack.length; 1150 | while (length--) { 1151 | // Linear search. Performance is inversely proportional to the number of 1152 | // unique nested structures. 1153 | if (aStack[length] === a) return bStack[length] === b; 1154 | } 1155 | 1156 | // Add the first object to the stack of traversed objects. 1157 | aStack.push(a); 1158 | bStack.push(b); 1159 | 1160 | // Recursively compare objects and arrays. 1161 | if (areArrays) { 1162 | // Compare array lengths to determine if a deep comparison is necessary. 1163 | length = a.length; 1164 | if (length !== b.length) return false; 1165 | // Deep compare the contents, ignoring non-numeric properties. 1166 | while (length--) { 1167 | if (!eq(a[length], b[length], aStack, bStack)) return false; 1168 | } 1169 | } else { 1170 | // Deep compare objects. 1171 | var keys = _.keys(a), key; 1172 | length = keys.length; 1173 | // Ensure that both objects contain the same number of properties before comparing deep equality. 1174 | if (_.keys(b).length !== length) return false; 1175 | while (length--) { 1176 | // Deep compare each member 1177 | key = keys[length]; 1178 | if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false; 1179 | } 1180 | } 1181 | // Remove the first object from the stack of traversed objects. 1182 | aStack.pop(); 1183 | bStack.pop(); 1184 | return true; 1185 | }; 1186 | 1187 | // Perform a deep comparison to check if two objects are equal. 1188 | _.isEqual = function(a, b) { 1189 | return eq(a, b); 1190 | }; 1191 | 1192 | // Is a given array, string, or object empty? 1193 | // An "empty" object has no enumerable own-properties. 1194 | _.isEmpty = function(obj) { 1195 | if (obj == null) return true; 1196 | if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; 1197 | return _.keys(obj).length === 0; 1198 | }; 1199 | 1200 | // Is a given value a DOM element? 1201 | _.isElement = function(obj) { 1202 | return !!(obj && obj.nodeType === 1); 1203 | }; 1204 | 1205 | // Is a given value an array? 1206 | // Delegates to ECMA5's native Array.isArray 1207 | _.isArray = nativeIsArray || function(obj) { 1208 | return toString.call(obj) === '[object Array]'; 1209 | }; 1210 | 1211 | // Is a given variable an object? 1212 | _.isObject = function(obj) { 1213 | var type = typeof obj; 1214 | return type === 'function' || type === 'object' && !!obj; 1215 | }; 1216 | 1217 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. 1218 | _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { 1219 | _['is' + name] = function(obj) { 1220 | return toString.call(obj) === '[object ' + name + ']'; 1221 | }; 1222 | }); 1223 | 1224 | // Define a fallback version of the method in browsers (ahem, IE < 9), where 1225 | // there isn't any inspectable "Arguments" type. 1226 | if (!_.isArguments(arguments)) { 1227 | _.isArguments = function(obj) { 1228 | return _.has(obj, 'callee'); 1229 | }; 1230 | } 1231 | 1232 | // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, 1233 | // IE 11 (#1621), and in Safari 8 (#1929). 1234 | if (typeof /./ != 'function' && typeof Int8Array != 'object') { 1235 | _.isFunction = function(obj) { 1236 | return typeof obj == 'function' || false; 1237 | }; 1238 | } 1239 | 1240 | // Is a given object a finite number? 1241 | _.isFinite = function(obj) { 1242 | return isFinite(obj) && !isNaN(parseFloat(obj)); 1243 | }; 1244 | 1245 | // Is the given value `NaN`? (NaN is the only number which does not equal itself). 1246 | _.isNaN = function(obj) { 1247 | return _.isNumber(obj) && obj !== +obj; 1248 | }; 1249 | 1250 | // Is a given value a boolean? 1251 | _.isBoolean = function(obj) { 1252 | return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; 1253 | }; 1254 | 1255 | // Is a given value equal to null? 1256 | _.isNull = function(obj) { 1257 | return obj === null; 1258 | }; 1259 | 1260 | // Is a given variable undefined? 1261 | _.isUndefined = function(obj) { 1262 | return obj === void 0; 1263 | }; 1264 | 1265 | // Shortcut function for checking if an object has a given property directly 1266 | // on itself (in other words, not on a prototype). 1267 | _.has = function(obj, key) { 1268 | return obj != null && hasOwnProperty.call(obj, key); 1269 | }; 1270 | 1271 | // Utility Functions 1272 | // ----------------- 1273 | 1274 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 1275 | // previous owner. Returns a reference to the Underscore object. 1276 | _.noConflict = function() { 1277 | root._ = previousUnderscore; 1278 | return this; 1279 | }; 1280 | 1281 | // Keep the identity function around for default iteratees. 1282 | _.identity = function(value) { 1283 | return value; 1284 | }; 1285 | 1286 | // Predicate-generating functions. Often useful outside of Underscore. 1287 | _.constant = function(value) { 1288 | return function() { 1289 | return value; 1290 | }; 1291 | }; 1292 | 1293 | _.noop = function(){}; 1294 | 1295 | _.property = property; 1296 | 1297 | // Generates a function for a given object that returns a given property. 1298 | _.propertyOf = function(obj) { 1299 | return obj == null ? function(){} : function(key) { 1300 | return obj[key]; 1301 | }; 1302 | }; 1303 | 1304 | // Returns a predicate for checking whether an object has a given set of 1305 | // `key:value` pairs. 1306 | _.matcher = _.matches = function(attrs) { 1307 | attrs = _.extendOwn({}, attrs); 1308 | return function(obj) { 1309 | return _.isMatch(obj, attrs); 1310 | }; 1311 | }; 1312 | 1313 | // Run a function **n** times. 1314 | _.times = function(n, iteratee, context) { 1315 | var accum = Array(Math.max(0, n)); 1316 | iteratee = optimizeCb(iteratee, context, 1); 1317 | for (var i = 0; i < n; i++) accum[i] = iteratee(i); 1318 | return accum; 1319 | }; 1320 | 1321 | // Return a random integer between min and max (inclusive). 1322 | _.random = function(min, max) { 1323 | if (max == null) { 1324 | max = min; 1325 | min = 0; 1326 | } 1327 | return min + Math.floor(Math.random() * (max - min + 1)); 1328 | }; 1329 | 1330 | // A (possibly faster) way to get the current timestamp as an integer. 1331 | _.now = Date.now || function() { 1332 | return new Date().getTime(); 1333 | }; 1334 | 1335 | // List of HTML entities for escaping. 1336 | var escapeMap = { 1337 | '&': '&', 1338 | '<': '<', 1339 | '>': '>', 1340 | '"': '"', 1341 | "'": ''', 1342 | '`': '`' 1343 | }; 1344 | var unescapeMap = _.invert(escapeMap); 1345 | 1346 | // Functions for escaping and unescaping strings to/from HTML interpolation. 1347 | var createEscaper = function(map) { 1348 | var escaper = function(match) { 1349 | return map[match]; 1350 | }; 1351 | // Regexes for identifying a key that needs to be escaped 1352 | var source = '(?:' + _.keys(map).join('|') + ')'; 1353 | var testRegexp = RegExp(source); 1354 | var replaceRegexp = RegExp(source, 'g'); 1355 | return function(string) { 1356 | string = string == null ? '' : '' + string; 1357 | return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; 1358 | }; 1359 | }; 1360 | _.escape = createEscaper(escapeMap); 1361 | _.unescape = createEscaper(unescapeMap); 1362 | 1363 | // If the value of the named `property` is a function then invoke it with the 1364 | // `object` as context; otherwise, return it. 1365 | _.result = function(object, property, fallback) { 1366 | var value = object == null ? void 0 : object[property]; 1367 | if (value === void 0) { 1368 | value = fallback; 1369 | } 1370 | return _.isFunction(value) ? value.call(object) : value; 1371 | }; 1372 | 1373 | // Generate a unique integer id (unique within the entire client session). 1374 | // Useful for temporary DOM ids. 1375 | var idCounter = 0; 1376 | _.uniqueId = function(prefix) { 1377 | var id = ++idCounter + ''; 1378 | return prefix ? prefix + id : id; 1379 | }; 1380 | 1381 | // By default, Underscore uses ERB-style template delimiters, change the 1382 | // following template settings to use alternative delimiters. 1383 | _.templateSettings = { 1384 | evaluate : /<%([\s\S]+?)%>/g, 1385 | interpolate : /<%=([\s\S]+?)%>/g, 1386 | escape : /<%-([\s\S]+?)%>/g 1387 | }; 1388 | 1389 | // When customizing `templateSettings`, if you don't want to define an 1390 | // interpolation, evaluation or escaping regex, we need one that is 1391 | // guaranteed not to match. 1392 | var noMatch = /(.)^/; 1393 | 1394 | // Certain characters need to be escaped so that they can be put into a 1395 | // string literal. 1396 | var escapes = { 1397 | "'": "'", 1398 | '\\': '\\', 1399 | '\r': 'r', 1400 | '\n': 'n', 1401 | '\u2028': 'u2028', 1402 | '\u2029': 'u2029' 1403 | }; 1404 | 1405 | var escaper = /\\|'|\r|\n|\u2028|\u2029/g; 1406 | 1407 | var escapeChar = function(match) { 1408 | return '\\' + escapes[match]; 1409 | }; 1410 | 1411 | // JavaScript micro-templating, similar to John Resig's implementation. 1412 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 1413 | // and correctly escapes quotes within interpolated code. 1414 | // NB: `oldSettings` only exists for backwards compatibility. 1415 | _.template = function(text, settings, oldSettings) { 1416 | if (!settings && oldSettings) settings = oldSettings; 1417 | settings = _.defaults({}, settings, _.templateSettings); 1418 | 1419 | // Combine delimiters into one regular expression via alternation. 1420 | var matcher = RegExp([ 1421 | (settings.escape || noMatch).source, 1422 | (settings.interpolate || noMatch).source, 1423 | (settings.evaluate || noMatch).source 1424 | ].join('|') + '|$', 'g'); 1425 | 1426 | // Compile the template source, escaping string literals appropriately. 1427 | var index = 0; 1428 | var source = "__p+='"; 1429 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 1430 | source += text.slice(index, offset).replace(escaper, escapeChar); 1431 | index = offset + match.length; 1432 | 1433 | if (escape) { 1434 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 1435 | } else if (interpolate) { 1436 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 1437 | } else if (evaluate) { 1438 | source += "';\n" + evaluate + "\n__p+='"; 1439 | } 1440 | 1441 | // Adobe VMs need the match returned to produce the correct offest. 1442 | return match; 1443 | }); 1444 | source += "';\n"; 1445 | 1446 | // If a variable is not specified, place data values in local scope. 1447 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 1448 | 1449 | source = "var __t,__p='',__j=Array.prototype.join," + 1450 | "print=function(){__p+=__j.call(arguments,'');};\n" + 1451 | source + 'return __p;\n'; 1452 | 1453 | try { 1454 | var render = new Function(settings.variable || 'obj', '_', source); 1455 | } catch (e) { 1456 | e.source = source; 1457 | throw e; 1458 | } 1459 | 1460 | var template = function(data) { 1461 | return render.call(this, data, _); 1462 | }; 1463 | 1464 | // Provide the compiled source as a convenience for precompilation. 1465 | var argument = settings.variable || 'obj'; 1466 | template.source = 'function(' + argument + '){\n' + source + '}'; 1467 | 1468 | return template; 1469 | }; 1470 | 1471 | // Add a "chain" function. Start chaining a wrapped Underscore object. 1472 | _.chain = function(obj) { 1473 | var instance = _(obj); 1474 | instance._chain = true; 1475 | return instance; 1476 | }; 1477 | 1478 | // OOP 1479 | // --------------- 1480 | // If Underscore is called as a function, it returns a wrapped object that 1481 | // can be used OO-style. This wrapper holds altered versions of all the 1482 | // underscore functions. Wrapped objects may be chained. 1483 | 1484 | // Helper function to continue chaining intermediate results. 1485 | var result = function(instance, obj) { 1486 | return instance._chain ? _(obj).chain() : obj; 1487 | }; 1488 | 1489 | // Add your own custom functions to the Underscore object. 1490 | _.mixin = function(obj) { 1491 | _.each(_.functions(obj), function(name) { 1492 | var func = _[name] = obj[name]; 1493 | _.prototype[name] = function() { 1494 | var args = [this._wrapped]; 1495 | push.apply(args, arguments); 1496 | return result(this, func.apply(_, args)); 1497 | }; 1498 | }); 1499 | }; 1500 | 1501 | // Add all of the Underscore functions to the wrapper object. 1502 | _.mixin(_); 1503 | 1504 | // Add all mutator Array functions to the wrapper. 1505 | _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 1506 | var method = ArrayProto[name]; 1507 | _.prototype[name] = function() { 1508 | var obj = this._wrapped; 1509 | method.apply(obj, arguments); 1510 | if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; 1511 | return result(this, obj); 1512 | }; 1513 | }); 1514 | 1515 | // Add all accessor Array functions to the wrapper. 1516 | _.each(['concat', 'join', 'slice'], function(name) { 1517 | var method = ArrayProto[name]; 1518 | _.prototype[name] = function() { 1519 | return result(this, method.apply(this._wrapped, arguments)); 1520 | }; 1521 | }); 1522 | 1523 | // Extracts the result from a wrapped and chained object. 1524 | _.prototype.value = function() { 1525 | return this._wrapped; 1526 | }; 1527 | 1528 | // Provide unwrapping proxy for some methods used in engine operations 1529 | // such as arithmetic and JSON stringification. 1530 | _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; 1531 | 1532 | _.prototype.toString = function() { 1533 | return '' + this._wrapped; 1534 | }; 1535 | 1536 | // AMD registration happens at the end for compatibility with AMD loaders 1537 | // that may not enforce next-turn semantics on modules. Even though general 1538 | // practice for AMD registration is to be anonymous, underscore registers 1539 | // as a named module because, like jQuery, it is a base library that is 1540 | // popular enough to be bundled in a third party lib, but not be part of 1541 | // an AMD load request. Those cases could generate an error when an 1542 | // anonymous define() is called outside of a loader request. 1543 | if (typeof define === 'function' && define.amd) { 1544 | define('underscore', [], function() { 1545 | return _; 1546 | }); 1547 | } 1548 | }.call(this)); 1549 | -------------------------------------------------------------------------------- /a_star/README.markdown: -------------------------------------------------------------------------------- 1 | ## Pathfinding with A\* 2 | 3 | The A\* algorithm is a way of finding a path between two points of a graph or map. 4 | 5 | ### An Example Application 6 | 7 | Imagine you have a character `C` who wants to move to the target `T`. Blank spaces on the map are traversable, but obstructions are marked as `O`: 8 | 9 | ```plain 10 | T | 11 | OOOOO | 12 | | 13 | OOOO | 14 | C | 15 | ``` 16 | 17 | How can `C` move to `T` in the fewest steps? The A\* algorithm helps find a path. 18 | 19 | ### The Algorithm 20 | 21 | In pseudocode: 22 | 23 | ```plain 24 | function A*(start,goal) 25 | closedset := the empty set // The set of nodes already evaluated. 26 | openset := {start} // The set of tentative nodes to be evaluated, initially containing the start node 27 | came_from := the empty map // The map of navigated nodes. 28 | 29 | g_score[start] := 0 // Cost from start along best known path. 30 | // Estimated total cost from start to goal through y. 31 | f_score[start] := g_score[start] + heuristic_cost_estimate(start, goal) 32 | 33 | while openset is not empty 34 | current := the node in openset having the lowest f_score[] value 35 | if current = goal 36 | return reconstruct_path(came_from, goal) 37 | 38 | remove current from openset 39 | add current to closedset 40 | for each neighbor in neighbor_nodes(current) 41 | tentative_g_score := g_score[current] + dist_between(current,neighbor) 42 | tentative_f_score := tentative_g_score + heuristic_cost_estimate(neighbor, goal) 43 | if neighbor in closedset and tentative_f_score >= f_score[neighbor] 44 | continue 45 | 46 | if neighbor not in openset or tentative_f_score < f_score[neighbor] 47 | came_from[neighbor] := current 48 | g_score[neighbor] := tentative_g_score 49 | f_score[neighbor] := tentative_f_score 50 | if neighbor not in openset 51 | add neighbor to openset 52 | 53 | return failure 54 | ``` 55 | 56 | ### References 57 | 58 | * [Wikipedia](http://en.wikipedia.org/wiki/A*_search_algorithm) 59 | * [Ruby Quiz #98](http://rubyquiz.com/quiz98.html) 60 | -------------------------------------------------------------------------------- /big_o_notation/README.MD: -------------------------------------------------------------------------------- 1 | ## Big O Notation 2 | 3 | ### Resources 4 | 5 | - [Understand Big O](https://www.youtube.com/watch?v=v4cd1O4zkGw) 6 | - [BigOCheatsheet](http://bigocheatsheet.com/) 7 | - [A Sample Big O Test in JSPerf](https://jsperf.com/big-o/1) -------------------------------------------------------------------------------- /binary_trees/README.markdown: -------------------------------------------------------------------------------- 1 | ## Binary Search Trees 2 | 3 | A binary search tree is a fundamental data structure useful for organizing large sets of data. 4 | 5 | More on Wikipedia: http://en.wikipedia.org/wiki/Binary_search_tree 6 | 7 | ### Overview 8 | 9 | A binary tree is built from *nodes*. Each node has: 10 | 11 | * A) An element of data 12 | * B) A link to the *left*. All nodes to the left have data elements less/lower than this node's data element. 13 | * C) A link to the *right*. All nodes to the right have data elementes more/greater than this node's data element. 14 | 15 | ### Searching 16 | 17 | Your implementation should be able to confirm or deny the presence of a piece of data in the list. 18 | 19 | #### Balancing 20 | 21 | The worst case binary tree comes from a list that's already sorted before insertion to the tree. How can you avoid that problem? 22 | 23 | ### Sorting 24 | 25 | ### Implementation Tips 26 | 27 | * Start with modeling a node 28 | * Then attach one node to another 29 | * Then attach multiple nodes together 30 | * Then implement search when there's just a single node 31 | * Then implement search for multiple nodes 32 | * Then implement sorted output for a single node 33 | * Then implement sorted output for multiple nodes 34 | -------------------------------------------------------------------------------- /binary_trees/javascript/README.md: -------------------------------------------------------------------------------- 1 | # Binary Trees Problem 2 | 3 | ## Running the Tests 4 | 5 | You can run the tests in this folder in the browser. 6 | 7 | To run in the browser, open the `index.html` page and you should see your tests. 8 | -------------------------------------------------------------------------------- /binary_trees/javascript/bst.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turingschool/data_structures_and_algorithms/f9e9b47900e84e55d93be51600e51bb9297868aa/binary_trees/javascript/bst.js -------------------------------------------------------------------------------- /binary_trees/javascript/bst_test.js: -------------------------------------------------------------------------------- 1 | describe('TreeNode', function(){ 2 | it('should have data', function() { 3 | var node = new TreeNode(0); 4 | expect(node.data).to.eq(0); 5 | }); 6 | 7 | xit('should have a default null value for left and right nodes', function(){ 8 | var node = new TreeNode(0); 9 | expect(node.left).to.eq(null); 10 | expect(node.right).to.eq(null); 11 | }); 12 | 13 | xit('can have a left node', function(){ 14 | var node1 = new TreeNode(1); 15 | var node2 = new TreeNode(0, node1); 16 | expect(node2.left.data).to.eq(1); 17 | }); 18 | 19 | xit('can assign a right node', function(){ 20 | var node1 = new TreeNode(1); 21 | var node2 = new TreeNode(0, null, node1); 22 | expect(node2.right.data).to.eq(1); 23 | }); 24 | }); 25 | 26 | describe('BST', function() { 27 | var tree; 28 | 29 | beforeEach(function(){ 30 | tree = new BST(); 31 | }); 32 | 33 | context('when list is empty', function(){ 34 | xit('rootNode is null', function(){ 35 | expect(tree.rootNode).to.eq(null); 36 | }); 37 | }); 38 | 39 | describe('.push', function(){ 40 | // push adds a datum to the tree. In order to 41 | // provide a nicer interface, we will accept raw numbers as inputs to 42 | // push, and handle wrapping the data in a Node internally 43 | context('when the tree is empty', function(){ 44 | xit('creates a new root node', function(){ 45 | tree.push(1); 46 | expect(tree.rootNode instanceof TreeNode).to.be.true; 47 | expect(tree.rootNode.data).to.eq(1) 48 | }); 49 | }); 50 | 51 | context('when tree has only a rootNode', function(){ 52 | xit('smaller data is added to the left', function(){ 53 | tree.push(5); 54 | expect(tree.rootNode.data).to.eq(5); 55 | tree.push(4); 56 | expect(tree.rootNode.data).to.eq(5); 57 | expect(tree.rootNode.left instanceof TreeNode).to.eq(true, 'left node not set'); 58 | expect(tree.rootNode.left.data).to.eq(4); 59 | expect(tree.rootNode.right).to.eq(null); 60 | }); 61 | 62 | xit('larger data is added to the right', function(){ 63 | tree.push(5); 64 | expect(tree.rootNode.data).to.eq(5); 65 | tree.push(6); 66 | expect(tree.rootNode.data).to.eq(5); 67 | expect(tree.rootNode.right instanceof TreeNode).to.eq(true, 'right node not set'); 68 | expect(tree.rootNode.right.data).to.eq(6); 69 | expect(tree.rootNode.left).to.eq(null); 70 | }); 71 | 72 | xit('data equal to the rootNode is added to the left', function(){ 73 | tree.push(5); 74 | expect(tree.rootNode.data).to.eq(5); 75 | tree.push(5); 76 | expect(tree.rootNode.left instanceof TreeNode).to.eq(true, 'left node not set'); 77 | expect(tree.rootNode.left.data).to.eq(5); 78 | expect(tree.rootNode.right).to.eq(null); 79 | }); 80 | }); 81 | 82 | context('when there are multiple nodes', function(){ 83 | xit('continues adding smaller data to the left down the tree', function(){ 84 | tree.push(5); 85 | tree.push(4); 86 | tree.push(3); 87 | expect(tree.rootNode.data).to.eq(5, 'rootNode was not set correctly'); 88 | expect(tree.rootNode.left.data).to.eq(4, 'first node to the left not set correctly'); 89 | expect(tree.rootNode.left.left.data).to.eq(3, 'last node to the left not set correctly'); 90 | }); 91 | 92 | xit('continues adding larger data to the left down the tree', function(){ 93 | tree.push(5); 94 | tree.push(6); 95 | tree.push(7); 96 | expect(tree.rootNode.data).to.eq(5, 'rootNode was not set correctly'); 97 | expect(tree.rootNode.right.data).to.eq(6, 'first node to the right not set correctly'); 98 | expect(tree.rootNode.right.right.data).to.eq(7, 'last node to the right not set correctly'); 99 | }); 100 | 101 | xit('creates a tree structure with multiple nodes', function(){ 102 | // Node(1) 103 | // / 104 | // Node(2) 105 | // / \ 106 | // / Node(3) 107 | // RootNode(4) 108 | // \ Node(5) 109 | // \ / 110 | // Node(6) 111 | // \ 112 | // Node(7) 113 | tree.push(4); 114 | tree.push(6); 115 | tree.push(7); 116 | tree.push(2); 117 | tree.push(3); 118 | tree.push(5); 119 | tree.push(1); 120 | 121 | expect(tree.rootNode.data).to.eq(4) 122 | 123 | expect(tree.rootNode.left.data).to.eq(2); 124 | expect(tree.rootNode.right.data).to.eq(6); 125 | 126 | expect(tree.rootNode.left.left.data).to.eq(1); 127 | expect(tree.rootNode.left.right.data).to.eq(3); 128 | 129 | expect(tree.rootNode.right.left.data).to.eq(5); 130 | expect(tree.rootNode.right.right.data).to.eq(7); 131 | }); 132 | }); 133 | }); 134 | 135 | describe('.find', function(){ 136 | context('in an empty tree', function(){ 137 | xit('returns null', function(){ 138 | expect(tree.find(6)).to.eq(null); 139 | }); 140 | }); 141 | 142 | context('in a tree with 1 node', function(){ 143 | beforeEach(function(){ 144 | tree.push(5); 145 | }); 146 | 147 | xit('can find a match', function(){ 148 | var result = tree.find(5); 149 | expect(result instanceof TreeNode).to.eq(true, 'is not returning a TreeNode'); 150 | expect(result.data).to.eq(5); 151 | }); 152 | 153 | xit('returns null if no match', function(){ 154 | var result = tree.find(80); 155 | expect(result).to.eq(null); 156 | }); 157 | }); 158 | 159 | context('in a complex tree', function(){ 160 | beforeEach(function(){ 161 | // Node(1) 162 | // / 163 | // Node(2) 164 | // / \ 165 | // / Node(3) 166 | // RootNode(4) 167 | // \ Node(5) 168 | // \ / 169 | // Node(6) 170 | // \ 171 | // Node(7) 172 | tree.push(4); 173 | tree.push(6); 174 | tree.push(7); 175 | tree.push(2); 176 | tree.push(3); 177 | tree.push(5); 178 | tree.push(1); 179 | }); 180 | 181 | xit('can find the root', function(){ 182 | var result = tree.find(4); 183 | expect(result instanceof TreeNode).to.eq(true, 'is not returning a TreeNode'); 184 | expect(result.data).to.eq(4); 185 | }); 186 | 187 | xit('can find results to the immediate left', function(){ 188 | var result = tree.find(2); 189 | expect(result instanceof TreeNode).to.eq(true, 'is not returning a TreeNode'); 190 | expect(result.data).to.eq(2); 191 | }); 192 | 193 | xit('can find results to the far left', function(){ 194 | var result = tree.find(1); 195 | expect(result instanceof TreeNode).to.eq(true, 'is not returning a TreeNode'); 196 | expect(result.data).to.eq(1); 197 | }); 198 | 199 | xit('can find results to the immediate right', function(){ 200 | var result = tree.find(6); 201 | expect(result instanceof TreeNode).to.eq(true, 'is not returning a TreeNode'); 202 | expect(result.data).to.eq(6); 203 | }); 204 | 205 | xit('can find nested results', function(){ 206 | var result = tree.find(5); 207 | expect(result instanceof TreeNode).to.eq(true, 'is not returning a TreeNode'); 208 | expect(result.data).to.eq(5); 209 | 210 | var result = tree.find(3); 211 | expect(result instanceof TreeNode).to.eq(true, 'is not returning a TreeNode'); 212 | expect(result.data).to.eq(3); 213 | }); 214 | 215 | xit('can return null if none found', function(){ 216 | var result = tree.find(80); 217 | expect(result).to.eq(null); 218 | }); 219 | }); 220 | }); 221 | 222 | describe('.toArray', function(){ 223 | context('with an empty tree', function(){ 224 | xit('returns an empty array', function(){ 225 | expect(tree.toArray()).to.deep.eq([]); 226 | }); 227 | }); 228 | 229 | context('with nodes', function(){ 230 | xit('pulls data from the tree into an array, starting with the left branches of the tree', function(){ 231 | tree.push(5); 232 | tree.push(4); 233 | tree.push(6); 234 | tree.push(7); 235 | tree.push(3); 236 | expect(tree.toArray()).to.deep.eq([5,4,3,6,7]); 237 | }); 238 | }); 239 | }); 240 | 241 | describe('.sort', function(){ 242 | context('with an empty tree', function(){ 243 | xit('returns an empty array', function(){ 244 | expect(tree.sort()).to.deep.eq([]); 245 | }); 246 | }); 247 | 248 | context('with nodes', function(){ 249 | xit('returns a sorted array of tree elements', function(){ 250 | tree.push(5) 251 | tree.push(4) 252 | tree.push(6) 253 | tree.push(7) 254 | tree.push(3) 255 | tree.push(2) 256 | tree.push(2102) 257 | expect(tree.sort()).to.deep.eq([2,3,4,5,6,7,2102]); 258 | }); 259 | }); 260 | }); 261 | 262 | describe('.min and .max', function(){ 263 | // note that given the inherent left/right structure of a binary search tree 264 | //it is possible to find the min or max elements without searching the entire tree 265 | context('with an empty tree', function(){ 266 | xit('min returns null', function(){ 267 | expect(tree.min()).to.eq(null); 268 | }); 269 | 270 | xit('max returns null', function(){ 271 | expect(tree.max()).to.eq(null); 272 | }); 273 | }); 274 | 275 | context('with nodes', function(){ 276 | beforeEach(function(){ 277 | // Node(1) 278 | // / 279 | // Node(2) 280 | // / \ 281 | // / Node(3) 282 | // RootNode(4) 283 | // \ Node(5) 284 | // \ / 285 | // Node(6) 286 | // \ 287 | // Node(7) 288 | tree.push(4); 289 | tree.push(6); 290 | tree.push(7); 291 | tree.push(2); 292 | tree.push(3); 293 | tree.push(5); 294 | tree.push(1); 295 | }); 296 | 297 | xit('finds the min value', function(){ 298 | expect(tree.min()).to.eq(1); 299 | }); 300 | 301 | xit('finds the max value', function(){ 302 | expect(tree.max()).to.eq(7); 303 | }); 304 | }); 305 | }); 306 | 307 | describe('.postOrdered', function(){ 308 | context('with an empty tree', function(){ 309 | xit('returns an empty array', function(){ 310 | expect(tree.postOrdered()).to.deep.eq([]); 311 | }); 312 | }); 313 | context('with a complex tree', function(){ 314 | // post_order should return the elements of the tree from a 'bottom-up' perspective, 315 | // starting at the bottom-left-most element, then working right, and then progressively 316 | // up the tree 317 | // so, for example the tree: 318 | // 4 319 | // / \ 320 | // 2 5 321 | // / \ 322 | // 1 3 323 | // should produce post_ordered output [1,3,2,5,4] 324 | xit('should give back elements in post-order', function(){ 325 | tree.push(4); 326 | tree.push(2); 327 | tree.push(5); 328 | tree.push(1); 329 | tree.push(3); 330 | expect(tree.postOrdered()).to.deep.eq([1,3,2,5,4]); 331 | }); 332 | }); 333 | }); 334 | }); 335 | -------------------------------------------------------------------------------- /binary_trees/javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Linked List Test Suite 6 | 7 | 8 | 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /binary_trees/ruby/Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | guard :minitest do 5 | watch(%r{^test/(.*)\/?(.*)\.rb$}) 6 | watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" } 7 | end 8 | -------------------------------------------------------------------------------- /binary_trees/ruby/lib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turingschool/data_structures_and_algorithms/f9e9b47900e84e55d93be51600e51bb9297868aa/binary_trees/ruby/lib/.gitkeep -------------------------------------------------------------------------------- /binary_trees/ruby/test/binary_search_tree_test.rb: -------------------------------------------------------------------------------- 1 | gem 'minitest' 2 | require 'minitest/autorun' 3 | require 'minitest/pride' 4 | require 'minitest/spec' 5 | require_relative '../lib/binary_search_tree' 6 | 7 | describe Node do 8 | # A Node represents a location in the binary tree 9 | # It holds data and includes a reference to its left and right nodes 10 | 11 | it "has data" do 12 | assert_equal 0, Node.new(0).data 13 | end 14 | 15 | it "has a left node" do 16 | skip 17 | assert_equal 1, Node.new(0, Node.new(1)).left.data 18 | end 19 | 20 | it "has a right node" do 21 | skip 22 | assert_equal 2, Node.new(0, nil, Node.new(2)).right.data 23 | end 24 | end 25 | 26 | describe BinarySearchTree do 27 | before do 28 | @tree = BinarySearchTree.new 29 | end 30 | describe "#root_node" do 31 | it "is nil for an empty list" do 32 | skip 33 | assert_nil BinarySearchTree.new.root_node 34 | end 35 | 36 | it "adds a new root node when pushed" do 37 | skip 38 | @tree.push(1) 39 | assert_equal 1, @tree.root_node.data 40 | end 41 | end 42 | 43 | describe "#push" do 44 | # push adds a datum to the tree. In order to 45 | # provide a nicer interface, we will accept raw numbers as inputs to 46 | # push, and handle wrapping the data in a Node internally 47 | it "adds data smaller than the root node to the left of the root" do 48 | skip 49 | @tree.push(5) #root 50 | @tree.push(4) 51 | assert_equal 4, @tree.root_node.left.data 52 | end 53 | 54 | it "adds data equal to the root node to the left of the root" do 55 | skip 56 | @tree.push(5) #root 57 | @tree.push(5) 58 | assert_equal 5, @tree.root_node.left.data 59 | end 60 | 61 | it "adds data larger than the root node to the right of the root" do 62 | skip 63 | @tree.push(5) #root 64 | @tree.push(7) 65 | assert_equal 7, @tree.root_node.right.data 66 | end 67 | 68 | it "continues adding smaller data to the left down the tree" do 69 | skip 70 | @tree.push(5) #root 71 | @tree.push(4) 72 | @tree.push(3) 73 | assert_equal 3, @tree.root_node.left.left.data 74 | end 75 | 76 | it "continues adding larger data to the right down the tree" do 77 | skip 78 | @tree.push(5) #root 79 | @tree.push(6) 80 | @tree.push(7) 81 | assert_equal 7, @tree.root_node.right.right.data 82 | end 83 | end 84 | 85 | describe "#count" do 86 | # count tells us the number of nodes in the tree 87 | it "is 0 for an empty tree" do 88 | skip 89 | assert_equal 0, @tree.count 90 | end 91 | 92 | it "is 1 for a tree with a root node" do 93 | skip 94 | @tree.push(5) 95 | assert_equal 1, @tree.count 96 | end 97 | 98 | it "continues to count elements as they are added" do 99 | skip 100 | @tree.push(6) 101 | @tree.push(4) 102 | @tree.push(3) 103 | @tree.push(2) 104 | assert_equal 4, @tree.count 105 | end 106 | end 107 | 108 | describe "#include?" do 109 | # include? tells us whether a piece of data exists in the tree 110 | it "is false for an empty tree" do 111 | skip 112 | assert_equal false, @tree.include?(5) 113 | end 114 | 115 | it "is true for the data of the root node" do 116 | skip 117 | @tree.push(4) 118 | assert_equal true, @tree.include?(4) 119 | end 120 | 121 | it "finds data lower in the tree" do 122 | skip 123 | @tree.push(4) 124 | @tree.push(6) 125 | @tree.push(3356) 126 | @tree.push(1) 127 | assert_equal true, @tree.include?(6) 128 | assert_equal true, @tree.include?(3356) 129 | assert_equal false, @tree.include?(11111) 130 | assert_equal true, @tree.include?(1) 131 | end 132 | end 133 | 134 | describe "#to_array" do 135 | it "is empty for empty tree" do 136 | skip 137 | assert_equal [], @tree.to_array 138 | end 139 | 140 | it "pulls data from the tree into an array, starting with the left branches of the tree" do 141 | skip 142 | @tree.push(5) 143 | @tree.push(4) 144 | @tree.push(6) 145 | @tree.push(7) 146 | @tree.push(3) 147 | assert_equal [5,4,3,6,7], @tree.to_array 148 | end 149 | end 150 | 151 | describe "#sort" do 152 | it "returns empty array for empty tree" do 153 | skip 154 | assert_equal [], @tree.sort 155 | end 156 | 157 | it "returns a sorted array of the tree elements" do 158 | skip 159 | @tree.push(5) 160 | @tree.push(4) 161 | @tree.push(6) 162 | @tree.push(7) 163 | @tree.push(3) 164 | @tree.push(2) 165 | @tree.push(2102) 166 | assert_equal [2,3,4,5,6,7,2102], @tree.sort 167 | end 168 | end 169 | 170 | describe "#min" do 171 | # note that given the inherent left/right structure of a binary search tree 172 | # it is possible to find the min or max elements without searching the entire tree 173 | it "is nil for an empty tree" do 174 | skip 175 | assert_nil @tree.min 176 | end 177 | 178 | it "finds the minimum value in the tree" do 179 | skip 180 | @tree.push(5) 181 | @tree.push(4) 182 | @tree.push(6) 183 | @tree.push(7) 184 | @tree.push(3) 185 | @tree.push(1) 186 | assert_equal 1, @tree.min 187 | end 188 | end 189 | 190 | describe "#max" do 191 | # note that given the inherent left/right structure of a binary search tree 192 | # it is possible to find the min or max elements without searching the entire tree 193 | it "is nil for an empty tree" do 194 | skip 195 | assert_nil @tree.max 196 | end 197 | 198 | it "finds the maximum value in the tree" do 199 | skip 200 | @tree.push(5) 201 | @tree.push(4) 202 | @tree.push(6) 203 | @tree.push(7) 204 | @tree.push(3) 205 | @tree.push(1) 206 | assert_equal 7, @tree.max 207 | end 208 | end 209 | 210 | describe "#post_ordered" do 211 | # post_order should return the elements of the tree from a 'bottom-up' perspective, 212 | # starting at the bottom-left-most element, then working right, and then progressively 213 | # up the tree 214 | # so, for example the tree: 215 | # 4 216 | # / \ 217 | # 2 5 218 | # / \ 219 | # 1 3 220 | # should produce post_ordered output [1,3,2,5,4] 221 | 222 | it "should be empty for empty tree" do 223 | skip 224 | assert_equal [], @tree.post_ordered 225 | end 226 | 227 | it "should give back elements in post-order" do 228 | skip 229 | @tree.push(4) 230 | @tree.push(2) 231 | @tree.push(5) 232 | @tree.push(1) 233 | @tree.push(3) 234 | 235 | assert_equal [1,3,2,5,4], @tree.post_ordered 236 | end 237 | end 238 | 239 | describe "#min_height" do 240 | it "is 0 for empty tree" do 241 | skip 242 | assert_equal 0, @tree.min_height 243 | end 244 | 245 | it "is 1 for tree with root node only" do 246 | skip 247 | @tree.push(1) 248 | assert_equal 1, @tree.min_height 249 | end 250 | 251 | it "finds shortest path from root node to a leaf" do 252 | skip 253 | # 4 254 | # / \ 255 | # 2 6 256 | # / \ 257 | # 1 3 258 | @tree.push(4) 259 | @tree.push(2) 260 | @tree.push(1) 261 | @tree.push(3) 262 | @tree.push(6) 263 | assert_equal 2, @tree.min_height 264 | end 265 | end 266 | 267 | describe "#max_height" do 268 | it "is 0 for empty tree" do 269 | skip 270 | assert_equal 0, @tree.max_height 271 | end 272 | 273 | it "is 1 for tree with root node only" do 274 | skip 275 | @tree.push(1) 276 | assert_equal 1, @tree.max_height 277 | end 278 | 279 | it "finds shortest path from root node to a leaf" do 280 | skip 281 | # 4 282 | # / \ 283 | # 2 6 284 | # / \ 285 | # 1 3 286 | @tree.push(4) 287 | @tree.push(2) 288 | @tree.push(1) 289 | @tree.push(3) 290 | @tree.push(6) 291 | assert_equal 3, @tree.max_height 292 | end 293 | end 294 | 295 | describe "#balanced?" do 296 | # A binary tree is balanced if its max depth and min depth are within 297 | # 1 of each other. 298 | it "is balanced for an empty tree" do 299 | skip 300 | assert @tree.balanced? 301 | end 302 | 303 | it "is balanced for a nice tidy tree" do 304 | skip 305 | # 4 306 | # / \ 307 | # 2 6 308 | # / \ \ \ 309 | # 1 3 5 7 310 | @tree.push(4) 311 | @tree.push(2) 312 | @tree.push(1) 313 | @tree.push(3) 314 | @tree.push(6) 315 | @tree.push(5) 316 | @tree.push(7) 317 | assert_equal 3, @tree.min_height 318 | assert_equal 3, @tree.max_height 319 | assert @tree.balanced? 320 | end 321 | 322 | it "is false for tightly ordered tree" do 323 | skip 324 | @tree.push(1) 325 | @tree.push(2) 326 | @tree.push(3) 327 | @tree.push(4) 328 | refute @tree.balanced? 329 | end 330 | end 331 | 332 | describe "#balance!" do 333 | it "re-balances unbalanced tree" do 334 | skip 335 | @tree.push(1) 336 | @tree.push(2) 337 | @tree.push(3) 338 | @tree.push(4) 339 | refute @tree.balanced? 340 | @tree.balance! 341 | assert @tree.balanced? 342 | end 343 | 344 | it "balances without removing elements from the tree" do 345 | skip 346 | @tree.push(1) 347 | @tree.push(2) 348 | @tree.push(3) 349 | @tree.push(4) 350 | assert_equal [1,2,3,4], @tree.sort 351 | @tree.balance! 352 | assert_equal [1,2,3,4], @tree.sort 353 | end 354 | end 355 | end 356 | -------------------------------------------------------------------------------- /bitwise_binary_operators/README.md: -------------------------------------------------------------------------------- 1 | - Note: This explanation is currently based in JavaScript 2 | 3 | # Color & Bitwise Binary Operators 4 | 5 | ## What is a Bit 6 | 7 | A bit is a basic unit of information. AKA a binary digit. 8 | 9 | It can only have one of two values. These values are most commonly represented as a 1 or a 0. 10 | 11 | Remember taking those punch cards style tests when you were little? Where you circled in the little dots? That was where the concept of a bit came in. Paper could be fed into a computer, and depending on whether holes were punched in or not was the data was passed around. 12 | 13 | ## What is Decimal 14 | 15 | Our integer numbering system is base 10. Meaning, we use 10 different symbols to represent numbers. 16 | 17 | | Decimal | 18 | |-------------| 19 | | 0 | 20 | | 1 | 21 | | 2 | 22 | | 3 | 23 | | 4 | 24 | | 5 | 25 | | 6 | 26 | | 7 | 27 | | 8 | 28 | | 9 | 29 | 30 | So in order to get a decimal number from, say, a string - we would use the following code. 31 | 32 | ```javascript 33 | var num = '7'; 34 | parseInt(num, 10); 35 | ``` 36 | 37 | [parseInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) is a function we're going to hear about a lot in this lesson. 38 | 39 | It acceptes two parameters: 40 | - string 41 | - The value to parse. If it's not a string, the function converts it to one using `toString`. 42 | - radix 43 | - And integer between 2 and 36 that is the base in the mathematical numeral system. 44 | 45 | If you recall, we want to convert this string to a decimal integer - so we pass `10` in as our radix. 46 | 47 | `parseInt` will usually default to base ten if no radix is specified, but it is best practice to **always specify this parameter** to avoid confusion and guarantee predicatable behavior. 48 | 49 | ## What is Binary 50 | 51 | Computers count using binary, which is a number system made up of zeros and ones (bits). 52 | 53 | So if we wanted to convert a Number to binary, we could do so in the following way. 54 | 55 | ```javascript 56 | var num = 10; 57 | num.toString(2); 58 | ``` 59 | 60 | [toString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString) is a function we will also be using often in this lesson. 61 | 62 | Here you are calling the Number prototype toString method and passing it a radix of 2. 63 | 64 | Note: If you don't specify a radix, toString will default to base 10 (decimal) 65 | 66 | Putting things all together, if you wanted to write a decimal to binary converter that could handle string AND integer input - you might write something like: 67 | 68 | ```javascript 69 | parseInt(n, 10).toString(2); 70 | // if string, convert to base ten number type 71 | // if already number type, convert to string and convert back to Number 72 | // call Number's prototype function toString() and tell it base 2 73 | ``` 74 | 75 | Note: What if the decimal is a negative number? Try reading through the top answer on this [StackOverflow](http://stackoverflow.com/questions/9939760/how-do-i-convert-an-integer-to-binary-in-javascript) and using the `>>>` (right logical shift) 76 | 77 | ## What is Hexadecimal 78 | 79 | Hexadecimal is base 16, meaning it uses sixteen characters to display numbers. 80 | 81 | | Decimal | Hexadecimal | 82 | |---------|-------------| 83 | | 0 - 10 | 0 - 9 | 84 | | 10 - 15 | A - F | 85 | | 16 - 25 | 10 - 19 | 86 | | 26 | 1A | 87 | | 27 | 1B | 88 | | ... etc | ... etc | 89 | 90 | So if we wanted to convert a decimal to hex 91 | 92 | ```javascript 93 | var num = 11; 94 | num.toString(16); // "b" 95 | 96 | var num = '11'; 97 | parseInt(num, 10).toString(16) 98 | ``` 99 | 100 | If you want to see the entire graph, try running the following code in a console 101 | 102 | ```javascript 103 | for(var i = 0; i < 100; i++){ 104 | console.log(i, ' - ', i.toString(16)); 105 | } 106 | ``` 107 | Hexadecimal was widely adopted because it is so easy to convert from hexadecimal to binary (the language of computers). Basically, hexadecimal is used to display binary in a shorter string. 108 | 109 | Binary is base 2, hexadecimal is base 16. 110 | 111 | 2 to the power of 4 is 16 112 | 113 | So you need a 4 digit binary to map to a one digit hexadecimal symbol. 114 | 115 | | Hexadecimal | Binary | 116 | |-------------|--------| 117 | | 0 | 0000 | 118 | | 1 | 0001 | 119 | | 2 | 0010 | 120 | | 3 | 0011 | 121 | | 4 | 0100 | 122 | | 5 | 0101 | 123 | | 6 | 0110 | 124 | | 7 | 0111 | 125 | | 8 | 1000 | 126 | | 9 | 1001 | 127 | | A | 1010 | 128 | | B | 1011 | 129 | | C | 1100 | 130 | | D | 1101 | 131 | | E | 1110 | 132 | | F | 1111 | 133 | 134 | ## What is RGB 135 | 136 | Color or a TV or computer screen is controlled by adjusting three settings: Red, Green and Blue. 137 | 138 | Each color value can range between 0 to 255 139 | 140 | If all colors are set to zero (0, 0, 0) - the absense of light sets the screen to black. 141 | 142 | If all three colors are set as high as possible (255, 255, 255) - the screen will be white. 143 | 144 | The reason for 0 to 255 is because 8 digits in a binary system is the same as 255. 145 | 146 | RGB values are encoded as 8-bit integers. 147 | 148 | 1111 1111 in binary equals 255. 149 | 150 | - Blue: Bits from 0-7 151 | - Green: Bits from 8-15 152 | - Red: Bits from 16-23 153 | 154 | ## What is a Bitwise Operation 155 | 156 | A bitwise operation proccesses bits one at a time. In other languages, they can be refered to as bit `shifters` which might make what they do a little clearer. 157 | 158 | There is an [MDN article on how to use bitwise operators in JavaScript](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators). 159 | 160 | > Bitwise operators treat their operands as a sequence of 32 bits (zeroes and ones), rather than as decimal, hexadecimal, or octal numbers. For example, the decimal number nine has a binary representation of 1001. Bitwise operators perform their operations on such binary representations, but they return standard JavaScript numerical values. 161 | 162 | Which basically means, these operators convert numbers to their bits and work with them at the bit level. 163 | 164 | #### An Example 165 | 166 | Integers are stored, in memory, as a series of bits. For example, the number 6 when stored as a 32-bit `int` is: 167 | 168 | ``` 169 | 00000000 00000000 00000000 00000110 170 | ``` 171 | 172 | So if you shifted the bit patter to the left one position with a left shift operator `(6 << 1)`, it would be 173 | 174 | ``` 175 | 00000000 00000000 00000000 00001100 176 | ``` 177 | 178 | Which equals `12` in decimal. 179 | 180 | Take a second to scroll up and look at the hexadecimal to binary table above and compare the 2 outcomes. 181 | 182 | 0110 is 6 (in both hexadecimal and decimal symbols) 183 | 184 | 1100 is 'c' in hexadecimal 185 | 186 | If you then convert hexadecimal to decimal `parseInt('c', 16)` 187 | 188 | You get 12. 189 | 190 | #### An Example with Color 191 | 192 | Bitwise operators are not something we use every day in JavaScript - but in the case of converting rgb to hex codes, it's super effective. 193 | 194 | ```javascript 195 | var hex = 'FFEBCD'; //blanchedalmond 196 | var rgb = parseInt(hex, 16); // 16772045 197 | // if we convert to a series of bits rgb = "111111111110101111001101" 198 | 199 | // 0xFF is the same as 11111111 200 | var r = (rgb >> 16) & 0xFF; // 255 201 | var g = (rgb >> 8) & 0xFF; // 235 202 | var b = rgb & 0xFF; // 205 203 | ``` 204 | 205 | `(rbg >> 16)` = 255 206 | 207 | What happens is we right shift by 16 bits 208 | 209 | so 210 | 211 | rbg is "111111111110101111001101" 212 | 213 | The r value is "11111111" 214 | 215 | `(rgb >> 8)` = 235 216 | 217 | So we shift right 8 bits 218 | 219 | The g value is "11101011" 220 | 221 | But then for blue, we do something interesting and take the entire bit sequence "111111111110101111001101" 222 | 223 | How does this work? 224 | 225 | Now it's time to explain the `Bitwise AND` or `&` 226 | 227 | ``` 228 | a & b 229 | ``` 230 | 231 | The Bitwise AND operator (`&`) returns a one in each position for the which the corresponding bits are both operands and ones. 232 | 233 | So if `0xFF` is the same as `11111111`... 234 | 235 | then when we call `111111111110101111001101` & `11111111` 236 | 237 | We get `11001101` 238 | 239 | In other words, we get the last 8 bits. 240 | 241 | Mind blown? That's okay. Play around with it a little bit and [read through the docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators). 242 | 243 | # Next Steps 244 | 245 | Open javascript/index.html to view tests 246 | 247 | Note: Knowing just a little bit about `.match()`, `.eval()` and `.test()` in relation to basic regex may save you some time - Learn about [regex in JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) 248 | 249 | #### hexToDecimalConverter 250 | 251 | Start by getting the `javascript/hexToDecimal-test.js` passing 252 | 253 | #### rgbToHex 254 | 255 | Then get the rgbToHex tests passing 256 | 257 | #### Bonus round 258 | 259 | Knowing what you now know, can you create any of the following? 260 | 261 | - In CodePen: A random rgb color generator that changes the background of a div every few seconds 262 | 263 | - In CodePen: A script that changes the color of a div by converting the current time of day to rgb. 264 | 265 | - Work backwards from this line of code in jQuery to figure out what the bitwise operators here are doing? [Code](https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/external/sizzle/dist/sizzle.js#L150) and [full cdn](https://code.jquery.com/jquery-3.1.1.js) 266 | 267 | - A decimal to hex function and test suite that _handles negative numbers correctly_ 268 | 269 | - A converter that handles [HSL and HSV](https://en.wikipedia.org/wiki/HSL_and_HSV) 270 | 271 | # Resources 272 | 273 | ## Good Resources 274 | 275 | - [What is a bit](https://en.wikipedia.org/wiki/Bit) 276 | - [MDN JavaScrpt Bitwise Operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) 277 | - http://stackoverflow.com/questions/141525/what-are-bitwise-shift-bit-shift-operators-and-how-do-they-work 278 | 279 | ## Meh Resources 280 | - http://rainyjune.net/node/341 281 | - [javascript-bit-manipulation](http://www.i-programmer.info/programming/javascript/2550-javascript-bit-manipulation.html) 282 | - [Color, Hexadecimal Numbers & Bitwise Binary Operators](http://www.webwasp.co.uk/tutorials/220/color.php) 283 | - [using-logical-bitshift-for-rgb-values](http://stackoverflow.com/questions/5751689/using-logical-bitshift-for-rgb-values) 284 | -------------------------------------------------------------------------------- /bitwise_binary_operators/javascript/README.md: -------------------------------------------------------------------------------- 1 | # Bitwise Binary Operators Problem 2 | 3 | ## Running the Tests 4 | 5 | You can run the tests in this folder in the browser. 6 | 7 | To run in the browser, open the `index.html` page and you should see your tests. 8 | -------------------------------------------------------------------------------- /bitwise_binary_operators/javascript/hexToDecimal-test.js: -------------------------------------------------------------------------------- 1 | describe('hexToDecimal', function(){ 2 | it('should convert a single decimal number', function() { 3 | var subject = '1'; 4 | expect(hexToDecimal(subject)).to.eq(1); 5 | }); 6 | 7 | xit('should convert one letter', function() { 8 | var subject = 'c'; 9 | expect(hexToDecimal(subject)).to.eq(12); 10 | }); 11 | 12 | xit('should convert 10', function() { 13 | var subject = '10'; 14 | expect(hexToDecimal(subject)).to.eq(16); 15 | }); 16 | 17 | xit('should convert multiple letters', function() { 18 | var subject = 'af'; 19 | expect(hexToDecimal(subject)).to.eq(175); 20 | }); 21 | 22 | xit('should convert large numbers', function() { 23 | var subject = '100'; 24 | expect(hexToDecimal(subject)).to.eq(256); 25 | }); 26 | 27 | xit('should convert large numbers and letters', function() { 28 | var subject = '19ace'; 29 | expect(hexToDecimal(subject)).to.eq(105166); 30 | }); 31 | 32 | xit('should return null for an invalid hex code', function(){ 33 | var subject = 'bananarama'; 34 | expect(hexToDecimal(subject)).to.eq(null); 35 | }); 36 | 37 | xit('should convert white', function() { 38 | var subject = 'ffffff'; 39 | expect(hexToDecimal(subject)).to.eq(16777215); 40 | }); 41 | 42 | xit('should convert papayawhip', function() { 43 | var subject = 'ffefd5'; 44 | expect(hexToDecimal(subject)).to.eq(16773077); 45 | }); 46 | }); -------------------------------------------------------------------------------- /bitwise_binary_operators/javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bitwise Binary Operators Test Suite 6 | 7 | 8 | 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /bitwise_binary_operators/javascript/rgbToHex-test.js: -------------------------------------------------------------------------------- 1 | describe('colorToHex', function(){ 2 | it('should convert white', function() { 3 | var white = 'rgb(255, 255, 255)'; 4 | expect(colorToHex(white)).to.eq('#ffffff') 5 | }); 6 | 7 | xit('should convert papayawhip (and be able to handle missing rgb)', function() { 8 | var papayawhip = '(255, 239, 213)'; 9 | expect(colorToHex(papayawhip)).to.eq('#ffefd5') 10 | }); 11 | 12 | xit('should convert steelblue (and be able to handle missing spaces)', function() { 13 | var steelblue = '(70,130,180)'; 14 | expect(colorToHex(steelblue)).to.eq('#4682b4') 15 | }); 16 | 17 | xit('should convert magenta', function() { 18 | var magenta = 'rgb(255, 0, 255)'; 19 | expect(colorToHex(magenta)).to.eq('#ff00ff') 20 | }); 21 | 22 | xit('should convert yellow', function() { 23 | var yellow = 'rgb(255, 255, 0)'; 24 | expect(colorToHex(yellow)).to.eq('#ffff00') 25 | }); 26 | 27 | xit('should convert green', function() { 28 | var green = 'rgb(0, 255, 0)'; 29 | expect(colorToHex(green)).to.eq('#00ff00') 30 | }); 31 | 32 | // It starts getting tricky here! 33 | 34 | xit('should convert black', function() { 35 | var black = 'rgb(0, 0, 0)'; 36 | expect(colorToHex(black)).to.eq('#000000') 37 | }); 38 | 39 | xit('should return the hex value if accidently given a hex', function() { 40 | expect(colorToHex('#ffffff')).to.eq('#ffffff'); 41 | }); 42 | }) -------------------------------------------------------------------------------- /bloom_filters/README.markdown: -------------------------------------------------------------------------------- 1 | ## Bloom Filters 2 | 3 | ### Background 4 | 5 | A [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) is a special-purpose 6 | data structure used to store _sets_ of data and determine if a given element is 7 | present in the set. 8 | 9 | There are other data structures we could use for this purpose ([Hash Maps](/hash_tables) or Sets come to mind), 10 | but Bloom Filters are particularly useful due to their space consumption properties. 11 | A Bloom Filter containing a given number of elements will consume much less space 12 | than a HashMap or Set containing the same elements. 13 | 14 | This is possible because Bloom Filters are a _probabilistic_ data structure -- they 15 | can tell you that an item is _probably in_ the set or that it is _definitely not in_ the set, 16 | but not that it is _definitely in_ the set. In other words, a Bloom Filter can 17 | sometimes give false positives, but never false negatives. 18 | 19 | This may sound silly at first, but in many applications, this type of behavior is actually 20 | perfectly acceptable, and if you can get by with "probable inclusion" in an application, 21 | you can take advantage of the great space savings a bloom filter provides. 22 | 23 | ### Construction 24 | 25 | To see how a Bloom Filter derives these properties, let's look at how one is constructed. 26 | 27 | A Bloom Filter is built on 2 main principles: 28 | 29 | 1. Bit Vector to represent the set of words 30 | 2. Collection of 2 or more hash functions for computing attribute presence 31 | 32 | Instead of using a more sophisticated storage mechanism like an array, a Bloom Filter 33 | uses a simple "bit vector" -- simply a binary integer which we will treat as a 34 | collection of 0/1 bits rather than as a single composite number. 35 | 36 | However to keep things a bit simpler, we can also represent the vector 37 | as a simple array of 8 0's. 38 | 39 | Let's imagine a simple 8-element array with no items in the set: 40 | 41 | ``` 42 | set = [0,0,0,0,0,0,0,0] 43 | ``` 44 | 45 | To add an element to the set, we'll use our hashing functions to generate 46 | `n` different hashes for the target string. For simplicity, we'll use MD5, 47 | SHA1, and SHA2: 48 | 49 | ``` 50 | require "digest" 51 | element = "pizza" 52 | d1 = Digest::MD5.hexdigest(element) 53 | d2 = Digest::SHA1.hexdigest(element) 54 | d3 = Digest::SHA2.hexdigest(element) 55 | ``` 56 | 57 | Each of these digests will give us a hexadecimal number representing a hash 58 | value of our input. 59 | 60 | Next we need to convert these hexadecimal digests into numbers, and modulo 61 | those numbers by the length of the bit array. 62 | 63 | ``` 64 | index1 = d1.hex % set.length 65 | => 0 66 | index2 = d2.hex % set.length 67 | => 7 68 | index3 = d3.hex % set.length 69 | => 6 70 | ``` 71 | 72 | Now, as a final insertion step, we need to toggle "on" all of the bits 73 | at these positions: 74 | 75 | ``` 76 | set[index1] = 1 77 | set[index2] = 1 78 | set[index3] = 1 79 | ``` 80 | 81 | At this point our "set" will look like: 82 | 83 | ``` 84 | [1, 0, 0, 0, 0, 0, 1, 1] 85 | ``` 86 | 87 | Now, to determine if an element is in the set, we would reverse the process, 88 | computing the hashes and vector indices, and determining whether these indices 89 | are present in the vector (set to "1"). 90 | 91 | ### Challenges 92 | 93 | 1. Implement a `BloomFilter` class which we can instantiate, `#add` elements to, and check for `#presence?` of an element 94 | 2. Implement the `BloomFilter` using a bit vector instead of an array 95 | 3. Customize your filter to accept a variable size argument for the size of the bit vector. 96 | 4. Implement logic for detecting false positives in the filter. How does the number of false 97 | positives change as we change the size of the storage vector. 98 | 99 | ### More Reading: 100 | 101 | * https://www.jasondavies.com/bloomfilter/ 102 | * http://billmill.org/bloomfilter-tutorial/ 103 | -------------------------------------------------------------------------------- /dungeon_generation/README.md: -------------------------------------------------------------------------------- 1 | ### Dungeon Generation 2 | 3 | Try to write an algorithm that generates a random layout of an 4 | imaginary "dungeon" for a game. 5 | 6 | * http://rubyquiz.com/quiz80.html 7 | * https://github.com/adonaac/blog/issues/7 8 | -------------------------------------------------------------------------------- /graphs/README.md: -------------------------------------------------------------------------------- 1 | ## Graphs and Graph Algorithms 2 | 3 | The term "graph" refers to a large family of data structures that appear 4 | frequently in computer science. At a deeper level a graph is a mathematical 5 | concept that has to do with modeling pieces of data and connections between 6 | them. 7 | 8 | Since we often deal with systems that can be modeled in this way (groups of 9 | nodes with various connections between them), graphs can be a useful data 10 | structure with a variety of applications. 11 | 12 | ### Terminology 13 | 14 | There are a lot of general terms that get thrown around when discussing graphs, 15 | so let's run through some of them. 16 | 17 | * __Vertices:__ These are the "points" in a graph. We'll often want to treat them as a 18 | composite data type that wraps some other piece of data, similar to nodes in a binary 19 | tree or linked list. 20 | 21 | * __Edges:__ Edges are the connections between vertices. Mathematically we might say they 22 | are a "set" unto themselves (in addition to the "set" of vertices which contain the graph's 23 | data), but in programming we would often represent them as object references. 24 | 25 | * __Adjacency:__ 2 vertices are adjacent if there is an edge connecting them. 26 | 27 | * __Graph:__ A data structure consisting of a set of Vertices and a set of Edges. Useful for 28 | modeling data where there are connections between different pieces of data. 29 | 30 | * __Loop:__ An edge connecting a vertex to itself 31 | 32 | * __Degree:__ The Degree of a vertex is the number of edges connecting to it. A self loop 33 | counts twice. 34 | 35 | ### Represeting Graphs 36 | 37 | There are a lot of ways to represent a graphs, but they largely fall into 2 categories -- textual/mathematical 38 | and graphical. 39 | 40 | When representing a graph graphically, it's common to represent vertices as circles containing identifiers 41 | and edges as lines between them. An unconnected vertex would be floating on the side with no lines to it. 42 | 43 | Here's one example: 44 | 45 | ![Graph](http://web.cecs.pdx.edu/~sheard/course/Cs163/Graphics/graph7.png) 46 | 47 | When representing a graph textually, it's common to represent it as a set of vertices: 48 | 49 | ``` 50 | {1,2,3,4,5,6,7,8} 51 | ``` 52 | 53 | And a set of Edges representing connections between pairs of vertices: 54 | 55 | ``` 56 | {(1,7), (2,6), (3,1), (3,5), (4,6), (5,4), (5,2), (6,8), (7,2), (7,8)} 57 | ``` 58 | 59 | Mathematically, a graph is often represented as 2 _sets_ -- one of edges and one of vertices. 60 | 61 | ### Graph Types 62 | 63 | Within the broader family of "graphs", we'll often categorize individual graphs based on 64 | various properties. Let's run through a few of these: 65 | 66 | * __Undirected:__ A graph where edges point in both directions at once. An undirected graph `A <-> B` 67 | would allow you to traverse A to B _or_ B to A 68 | 69 | * __Directed:__ A graph where edges point in only one direction. A directed graph `A -> B` would 70 | allow you to traverse from A to B but not from B to A. 71 | 72 | * __Cyclic:__ A graph where a path exists from a vertex back to itself. This could be done via traversing 73 | multiple other vertices, or via a self-referential edge which points from the vertex back to itself. 74 | The opposite of a Cyclic graph would be an __Acyclic Graph__ -- no cycles exist. 75 | 76 | * __Weighted:__ A graph where edges are labeled with an additional piece of data representing a "weight". 77 | This is often used to represent a graph where some edges are more costly to traverse than others 78 | (pathfinding with variable terrain, for example). 79 | 80 | * __Regular:__ A graph where all vertices have the same degree -- i.e. every vertex has the same number 81 | of connections to other vertices 82 | 83 | * __Connected:__ A graph where all vertices can be reached from any other vertex -- i.e. every vertex is 84 | connected via some sequence of edges. (Opposite would be a __Disconnected Graph__) 85 | 86 | * __Strongly Connected:__ A graph where every vertex is reachable from every other vertex. 87 | 88 | ### Categorizing a Graph 89 | 90 | An example graph can often be categorized across several of the types mentioned above. 91 | 92 | For example a graph could be Cyclic and Directed, or Undirected and Weighted. Many of 93 | the graphs graphs we interact with in computer science fall into a few common categories. 94 | Let's look at some of them: 95 | 96 | * Undirected Weighted Graph (e.g. terrain, map) 97 | * Directed Acyclic Graph (e.g. git history, spreadsheets, resource dependency tracking) 98 | * Directed Acyclic Regular Graph with Degree 1 99 | * Directed Acyclic Regular Graph with Degree 2 100 | 101 | ### Programming Challenges 102 | 103 | In the context of Graph manipulation, there are a number of common programming 104 | challenges we can look at. A few topics include: 105 | 106 | * Detecting Cycles -- Can we envision an algorithm for traversing a graph and determining if any 107 | "cycles" exist? 108 | * Detecting Connectivity -- Given a set of edges and set of vertices, are all vertices connected? 109 | * Minimum "Cuts" to Disconnect -- Given a connected graph, what is the minimum set of cuts we could 110 | make to disconnect it. 111 | * Shortest/Least Expensive Path -- Find the shortest path between 2 vertices in the graph. Often using 112 | a search algorithm like Breadth-First-Search. To account for variably expensive moves, add weights 113 | and use an algorithm like Dijkstra's Algorithm to find the least expensive path. 114 | 115 | ### More Reading 116 | 117 | * [Princeton Intro to Graph Theory](https://www.cs.princeton.edu/courses/archive/fall06/cos341/handouts/graph1.pdf) 118 | * [Project Euler problem 107](https://projecteuler.net/problem=107) 119 | * [Project Euler problem 79](https://projecteuler.net/problem=79) 120 | * [Euler 79 Discussion](http://alexmic.net/password-derivation-project-euler/) 121 | * [Cycle Detection](http://www.geeksforgeeks.org/detect-cycle-in-a-graph/) 122 | * [Shortest Path](http://www.geeksforgeeks.org/shortest-path-for-directed-acyclic-graphs/) 123 | -------------------------------------------------------------------------------- /hash_array_mapped_tries/README.markdown: -------------------------------------------------------------------------------- 1 | ## Hash Array Mapped Trie 2 | 3 | A Hash Array Mapped Trie (HAMT) is a data structure for implementing 4 | a traditional Hash-Map on top of a Trie rather than a traditional 5 | Array-based Hash-Table. The structure is more complex than 6 | a simple Hash Table, but provides a few key benefits, including: 7 | 8 | * Ability to grow the map indefinitely without re-sizing or chaining 9 | * Ability to share repeated structure between multiple maps to allow 10 | for cheap copying 11 | 12 | This last property makes it especially interesting from the perspective 13 | of immutability, which is why Rich Hickey used it as the [foundation](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentHashMap.java) 14 | for many of the immutable collection data structures in Clojure. 15 | 16 | The data structure was invented by Phil Bagwell, and you 17 | can find the original paper on it [here](http://lampwww.epfl.ch/papers/idealhashtrees.pdf). 18 | 19 | ### HAMT Structure 20 | 21 | To implement this data structure, we'll rely on a few key 22 | tools: 23 | 24 | 1. A Hashing Algorithm for uniquely differentiating pieces of data. 25 | Many languages already provide this -- in Ruby you can access an 26 | object's hashcode by calling `#hash` on it, or you can use a hashing function 27 | like the `SHA1` implementation including in the `digest` library. 28 | 2. A trie with very high branching factor -- this lets us store lots 29 | of data in a very shallow (and speedy) structure. 30 | 3. Bitwise operations to "consume" the data's hash code in small chunks, 31 | turning a hash code into a "path" to the data's location in the trie. 32 | 33 | So what does all this look like in practice? Let's look at 34 | an example creating a HAMT of order 32. 35 | 36 | Within the trie, each level can store 3 things: 37 | 38 | 1. A key 39 | 2. An associated value 40 | 3. Connections to up to 32 nested child trees 41 | 42 | When we want to insert a key-value pair, we'll use the key's 43 | hash code to choose a path through the trie until we find 44 | an empty position to insert it. 45 | 46 | For retrieval, we simply do the same thing in reverse -- hash 47 | the key, find the pathway through the trie represented by 48 | this hashcode, and check tree nodes until we either find the 49 | desired key or "bottom out" at the end of the tree. 50 | 51 | ### Insertion Algorithm 52 | 53 | Let's walk through the insertion process in more detail. 54 | 55 | To insert a piece of data, we need to find an appropriate path 56 | in the trie in which to place it. As we'll see, this path 57 | is ultimately determined by the key's hash value. 58 | 59 | As we walk down the trie, we'll be looking for 3 possible cases: 60 | 61 | 1. The current trie node is empty, so we can insert our new key 62 | and value here 63 | 2. The current trie node is not empty, but its key is equal 64 | to the one we are trying to insert, so we can overwrite its value 65 | 3. The current trie node is not empty, and its key value is not 66 | equal to ours, so we need to go deeper in the trie. 67 | 68 | #### Insertion Case 1 69 | 70 | Consider inserting a new K/V pair into an empty trie. 71 | We'll insert the key "pizza" with the value "yum". 72 | Our trie is empty so far, so the root tree node has no 73 | key and value, so we can insert our pair there. 74 | 75 | Pretty easy so far. 76 | 77 | #### Insertion Case 2 78 | 79 | Let's get the second easy case out of the way -- overwriting 80 | that K/V pair. We can insert the key "pizza" again, 81 | this time with the value of "real yum". 82 | 83 | We find that the root node is not empty, but its key is equal to 84 | the one we're trying to insert, so we simply change the value. 85 | 86 | Also pretty easy. 87 | 88 | #### Insertion Case 3 89 | 90 | Here is where things start to get more interesting. 91 | 92 | Let's insert the key "calzone" with the value "aw yiss". 93 | 94 | We first check the current (root) node -- it does have a 95 | key and value, and the key is *not* the one we're trying 96 | to insert. We need to go deeper into the trie to find 97 | a place for our new pair. 98 | 99 | To insert a key, we first need to generate its 100 | hash value. Again, in Ruby, we can use one of the hashing 101 | functions included in the Digest library. This gives us a (large) numeric 102 | value representing a unique digest of that piece of data. 103 | 104 | For example: 105 | 106 | ```ruby 107 | [1] pry(main)> require "digest" 108 | => true 109 | [2] pry(main)> Digest::SHA1.hexdigest("calzone").to_i(16) 110 | => 334703588949583183218034173573122019749278332384 111 | ``` 112 | 113 | To walk the trie and an appropriate location for this element, 114 | we'll "consume" this hash-code in 5-bit chunks. 115 | 116 | Why 5 bits at a time? 117 | 118 | This is determined by the branching factor of the tree 119 | -- with an order-32 trie, we have 32 possible children from each 120 | node in the tree. A 5-bit hash-code chunk allows us to concisely 121 | represent all 32 possible child branches using a single bitmap. 122 | (`2 ** 5 == 32`) 123 | 124 | To get the numeric value of the first 5 bits of our hashcode, 125 | we can bitwise `AND` it with a 5-bit number containing all "on" bits: 126 | 127 | ``` 128 | [13] pry(main)> 31.to_s(2) 129 | => "11111" 130 | [14] pry(main)> Digest::SHA1.hexdigest("calzone").to_i(16) & 31 131 | => 0 132 | ``` 133 | 134 | This tells us that the "right-most" 5 bits of the number 135 | 334703588949583183218034173573122019749278332384 ("calzone"'s hash code) 136 | are `00000`, or 0. 137 | 138 | This tells us the position in the current node's children array 139 | to insert this element. 140 | 141 | Thus we can move to the `0`th subtree under our current one 142 | and retry our insertion algorithm. In our case, "calzone" 143 | is only the second element to be inserted in the trie, so 144 | the `0`th child of the "pizza" node will be empty, and we 145 | can insert our data there. 146 | 147 | #### Consuming the hash code 148 | 149 | We mentioned that we would "consume" the key's hash code in 5-bit 150 | chunks. This helps us fully exploit the wide branching factor of the 151 | trie to insert a lot of elements in a fairly shallow data structure. 152 | 153 | If we simply re-use the same 5 (rightmost) bits that we used in the 154 | previous example, we effectively turn our trie into a collection 155 | of 32 linked lists, since all elements that share an initial 5-bit 156 | value will stack up on one another in a chain. 157 | 158 | We would prefer to get more of a "zig-zag" effect, and we can achieve 159 | this by making sure we use a different 5-bit chunk at each layer in 160 | the trie. 161 | 162 | To do this, we'll use another bit-wise operator, the **right shift**. 163 | 164 | A bitwise shift simply takes the bits that make a number and slides 165 | them in one direction in another. 166 | 167 | In the case of a left shift, we move the existing bits to the left, 168 | usually padding them with 0's on the righthand side. 169 | 170 | For example: 171 | 172 | ```ruby 173 | [20] pry(main)> 15.to_s(2) 174 | => "1111" 175 | [21] pry(main)> (15 << 4).to_s(2) 176 | => "11110000" 177 | ``` 178 | 179 | In our case, we just want to consume the next 5 bits of our 180 | hash code value, so we can use a right shift of 5 bits. 181 | 182 | Consider our "calzone" example from before: 183 | 184 | ```ruby 185 | [24] pry(main)> (Digest::SHA1.hexdigest("calzone").to_i(16) >> 5) & 31 186 | => 15 187 | ``` 188 | 189 | We now get a completely different subtrie index, helping us avoid 190 | the "stacking" behavior we would get if we just re-used the existing one. 191 | As we walk down the trie, we want to use this technique to shift off 5 bits at each 192 | layer. 193 | 194 | ### Retrieval Algorithm 195 | 196 | The retrieval process is effectively the same. We'll simply retrieve the located 197 | value rather than inserting one. Consider the same 3 cases: 198 | 199 | 1. The current tree node is empty -- this means we have "bottomed out", 200 | so our key must not exist in the trie 201 | 2. The current tree node contains the key you're searching for, so 202 | retrieve its value. 203 | 3. The current tree node is not empty, but doesn't contain the key 204 | we're looking for. Use another 5-bit slice of the hash code to identify 205 | the next step to take into the trie. 206 | 207 | ## HAMT Performance 208 | 209 | The strength of the HAMT is its wide branching factor. The 32-bit factor 210 | is common because it can be manipulated efficiently on 32-bit processors, 211 | but you could in theory use an even larger factor if needed. 212 | 213 | This branching factor allows us to store a large amount of keys and values 214 | in a relatively shallow tree which will still be very quick to traverse. 215 | 216 | For example in just 6 layers, we could store `33,554,432` (`32 ** 5`, assuming 217 | the root only stores 1 pair) keys and values. 218 | 219 | This means that in reality, the performance of our Tree will be logarithmic, 220 | as opposed to the than the Constant-time performance offered by many 221 | traditional Hash Map implementations. However the log base is `32`, 222 | which grows at such a small rate that its difference to constant time is 223 | fairly negligible. 224 | 225 | ## Recommended List of Operations 226 | 227 | Try implementing the following basic operations on your HAMT: 228 | 229 | * `set(key,val)` 230 | * `get(key)` 231 | * `keys` 232 | * `vals` 233 | * `get_in(key_path)` 234 | 235 | ## Other Considerations -- Structural Sharing 236 | 237 | We mentioned the ability of our tries to potentially share duplicated 238 | structure with other tries. This is a common approach to creating 239 | immutable or "persistent" hash maps and is used in several functional languages like 240 | Clojure, Scala, and Frege. 241 | 242 | The goal for this technique is to preserve every intermediate state of the Map 243 | (i.e. they "persist"). Thus each operation on the map should generate a new 244 | map value rather than modifying an existing one in place. 245 | 246 | This would be problematic if we had to completely copy every node in the trie 247 | each time we changed anything. But because of the trie's 248 | nested structure, we have a better option. 249 | 250 | Whenever we need to change the trie, we duplicate the node in question 251 | as well as all the nodes within its path to the root. 252 | 253 | Thus we get a new root node (this represents the "new" Map produced by our 254 | operation), and a new path to the internal node that was actually changed. 255 | 256 | The nodes that we copy can continue referring to the other existing 257 | nodes so that those don't have to be copied. In practice this allows 258 | us to produce a "copy" of the entire trie by actually copying only 259 | a handful of nodes. 260 | -------------------------------------------------------------------------------- /hash_tables/README.md: -------------------------------------------------------------------------------- 1 | ## Hash Tables (aka Hash Maps) 2 | 3 | A Hash Map is one of the most fundamental and commonly-used 4 | collection data structures. An implementation appears in most 5 | modern programming languages, and its versatility and performance 6 | characteristics make it something of a "swiss army knife" of 7 | data structures. 8 | 9 | So why is a Hash Map (this, by the way, is where Ruby gets the name for its `Hash` class) 10 | so powerful? As we will see, a Hash Map has 2 essential properties: 11 | 12 | 1. Ability to associate between arbitrary keys and arbitrary values 13 | 2. Ability to insert and retrieve values in constant time 14 | 15 | ### Associativity 16 | 17 | So what do we mean by "associate"? We say a data structure 18 | is associative when it allows us to define a relationship 19 | between 2 values and retrieve one in response to the other. 20 | 21 | We've actually already worked frequently with one fundamental 22 | associative data structure: the __Array__. 23 | 24 | Arrays define associative relationships between numeric indices and values contained in 25 | the array -- if we want to look something up in an array, 26 | we need to either know its numeric index so that we can go 27 | directly to that position in the array, __or__ we have to 28 | iterate through every element in the array and look at 29 | each one to see if it's the element we want. 30 | 31 | This is great if our data is ordered and if we are able to 32 | consistently retrieve it by going directly to its numeric 33 | index, but that isn't always the case. 34 | 35 | I often want to associate between _arbitrary_ keys 36 | and values (strings, objects, other arrays, etc)? For example, I want 37 | to associate the string "pizza" with the value "awesome", 38 | and I don't want to have to define an explicit ordering 39 | in the process. Additionally, I want to be able to add a whole lot of keys and 40 | values into the map and maintain a speedy lookup time. 41 | 42 | This is where a Hash Table comes in. 43 | 44 | ### Hash Table Structure 45 | 46 | To implement this data structure, we'll rely on a few key 47 | tools: 48 | 49 | 1. A Hashing Algorithm for uniquely differentiating pieces of data. 50 | Many languages already provide this -- in this example we'll look at 51 | using the SHA-1 implementation included with Ruby's Digest library. 52 | 2. An internal array which we'll ultimately use to store data 53 | 3. An additional abstraction for handling "collisions" between 54 | hashcodes within the data structure. 55 | 56 | To some extent, a Hash Table is a bit of Data Structure "sleight of hand" 57 | -- it allows us to translate arbitrary data (i.e. hash keys) into 58 | numeric array indices, and thus take advantage of the speedy 59 | index lookups we get out of the box with an array. 60 | 61 | ### Hash Function Basics 62 | 63 | This ability to translate arbitrary data to numeric array 64 | indices is the fundamental operation of a hash table, and 65 | the Hashing function is what makes it possible. 66 | 67 | Hash functions are a special type of algorithm designed 68 | to turn arbitrarily long data into consistent-length 69 | hash "digests" that represent that data. 70 | 71 | Importantly, a good hash function will do several things: 72 | 73 | 1. Be fast (this is important to help us maintain our hash table performance) 74 | 2. Be 1-way (given the input you can generate the digest, but with the digest 75 | there is no way to guess the input) 76 | 3. Be collision-resistant (it should be highly unlikely to find 2 inputs that 77 | generate the same digest value) 78 | 4. Be consistent -- hashing the same value should always yield the same digest 79 | 80 | Let's look at a ruby example using SHA-1: 81 | 82 | ```ruby 83 | [13] pry(main)> require "digest" 84 | => true 85 | [14] pry(main)> Digest::SHA1.hexdigest("pizza") 86 | => "1f6ccd2be75f1cc94a22a773eea8f8aeb5c68217" 87 | ``` 88 | 89 | Here I used SHA1 to hash the string "pizza" into a 40-character hexadecimal 90 | digest number. One thing to note is that a SHA1 hash will always be 40 hexadecimal 91 | digits (160 bits) -- this is true whether I hash the string "pizza" or the 92 | entire contents of _War and Peace_. 93 | 94 | Additionally keep in mind that the digest produced is actually a _number_. Ruby 95 | happens to represent it for us in this example as a string of hexadecimal 96 | characters, but we can convert it to its actual numeric value like so: 97 | 98 | ``` 99 | [14] pry(main)> Digest::SHA1.hexdigest("pizza") 100 | => "1f6ccd2be75f1cc94a22a773eea8f8aeb5c68217" 101 | [15] pry(main)> Digest::SHA1.hexdigest("pizza").to_i(16) 102 | => 975987071262755080377722350727279193143145743181 103 | ``` 104 | 105 | We'll exploit this property of the hash digests in the next step. 106 | 107 | ### Hash Table Algorithm 108 | 109 | So how does it work? In short, when creating a new Hash Table, we'll 110 | allocate an internal array to actually store our data. Later on 111 | we may introduce additional abstractions around Nodes, Elements, or Chains, 112 | but an array will be the fundamental storage and access mechanism. 113 | 114 | When asked to insert a Key/Value pair into the array, we need to 115 | map it to a numeric index within our internal array. Here's where 116 | we will leverage the hash function -- it produces a numeric 117 | representation of the data in question (the digest). Our hash 118 | table will use this numeric digest, modulo the length of our 119 | array, to determine which spot the element should be inserted into. 120 | 121 | Let's look at a rough example in ruby: 122 | 123 | ```ruby 124 | require "digest" 125 | 126 | table = Array.new(10) # 10-element hash table to start 127 | key = "pizza" 128 | value = "awesome" 129 | 130 | digest = Digest::SHA1.hexdigest(key).to_i(16) # 179405067335283640084579532467505022408577155607 131 | position = digest % table.length # 7 132 | 133 | table[position] = value 134 | ``` 135 | 136 | The hashing function produces a numeric digest of our data, 137 | and we use the array indices to map this to a numeric 138 | position in the element list. 139 | 140 | When we want to retrieve an element, we will follow the same 141 | process, but simply read from that array index rather 142 | than writing to it. 143 | 144 | Congrats! You made a naive but somwewhat functional hash table. 145 | 146 | ### Handling Collisions 147 | 148 | The example above gives us a good overview of the basic 149 | concept of a hash table, but it leaves out an important 150 | ingredient: resolving hash index collisions. 151 | 152 | Most Hashing Functions are designed to be *collision resistant* -- 153 | that is, it should be relatively impossible to find 2 inputs that 154 | hash to the same output. 155 | 156 | However consider what would happen if we wanted to insert 157 | the key "aardwolf" pointing at the value "strange critter". 158 | 159 | "aardwolf" hashes to 582992241920298993175351113381634332712414316697, 160 | which comes out to 7, modulo 10 -- the same as the "pizza" key we previously 161 | inserted. With our current implementation, we don't have a way to distinguish 162 | these keys. 163 | 164 | The problem is that we aren't actually utilizing 165 | the entire space of possible hash values (1461501637330902918203684832716283019655932542975 166 | possibilities in the case of SHA1). Rather, the number of slots we have available is limited by the size 167 | of the internal array we are using for data storage. 168 | 169 | So the enormous 170 | count of possibile values produced by our hash function gets reduced 171 | down to a fairly small amount once we start mod-ing it by the length 172 | of our table. 173 | 174 | This illustrates 2 interesting points about a Hash Table: 175 | 176 | 1. Determining the initial size of the table is a trade-off 177 | between efficient use of space and likelihood of collisions. 178 | Using a very large table will likely end up in lots of unused spaces, 179 | but we'll have a lower chance that 2 inputs hash to the same position. 180 | 2. Even with a large hash table collisions are still inevitable, so 181 | a viable implementation needs to be able to handle these cases. 182 | 183 | ### Chaining 184 | 185 | There are many ways to handle the collision problem, but they 186 | generally fall into 2 camps: 187 | 188 | 1. Use a secondary data structure to allow each hash "bucket" 189 | to contain multiple pairs of keys and values. Then when we retrieve 190 | the bucket index for a given key, we can filter through the secondary 191 | structure to find the specific K/V we're looking for. 192 | 2. Dynamically resize the table as it fills to avoid collisions by 193 | creating additional space 194 | 195 | In reality, many production implementations use some combination 196 | of these approaches. For now we're going to discuss the first 197 | approach, which is sometimes referred to as "Chaining" the hash 198 | table. 199 | 200 | The idea is relatively straightforward: Instead of simply dumping 201 | values into the table indicies indicated by a key's hash value, 202 | we will use each bucket to store a Linked List of table "nodes", 203 | each containing a key, a value, and potential link to the next 204 | node in the chain. 205 | 206 | Thus when we insert a K/V pair, we have 2 possible cases: 207 | 208 | 1. This is the first pair to be inserted into that bucket, 209 | so it becomes the "head" of the chain in that bucket. 210 | 2. There is already a chain in that bucket, so we need to 211 | append this new pair to the tail of the existing chain. 212 | 213 | Similarly, when retrieving values, we have 3 cases: 214 | 215 | 1. There is no value in the appropriate bucket (so our 216 | key is not their) 217 | 2. There is a value in the appropriate bucket, and its 218 | head node contains the key we are looking for. Thus we 219 | can read the value from that node. 220 | 3. There is a value in the appropriate bucket, but the head 221 | node does not contain the key we are looking for. Thus we need 222 | to keep looking through nodes in this chain until we find 223 | the key we're looking for. Reaching the tail without finding it means our 224 | key is not there. 225 | 226 | #### Chaining Drawbacks 227 | 228 | Let's briefly discuss the pros and cons of this approach. 229 | 230 | Pros: 231 | 232 | * Flexible 233 | * Can still start our table with relatively small number 234 | of buckets 235 | 236 | Cons: 237 | 238 | * Additional complexity in insertion / lookup process 239 | * Performance degrades as chain lengths grow 240 | 241 | The key with the chaining approach is to make sure none of your 242 | buckets get too big. We could in theory have a chained hash 243 | table with 1 bucket, but obviously it would just become a 244 | linked list with linear lookup time. 245 | 246 | However if we can have a balance of a fairly large table 247 | size with short chains of a handful of links each, our overall 248 | lookup time will remain constant. This is why, as we mentioned, 249 | more sophisticated implementations will generally use some combination 250 | of chaining and dynamic resizing. 251 | 252 | Another possible optimization includes using a more sophisticated 253 | data structure such as a BST or Red-Black Tree for chaining within the buckets. 254 | This allows the lookup time within each chain to be even faster. 255 | 256 | This approach has lots of interesting applications, and starts 257 | to blur the line between a traditional Hash Table and another 258 | related data structure, the [Hash-Array Mapped Trie](https://github.com/turingschool/data_structures_and_algorithms/blob/master/hash_array_mapped_tries/README.markdown). 259 | -------------------------------------------------------------------------------- /heaps/README.md: -------------------------------------------------------------------------------- 1 | ## Heap 2 | 3 | A heap falls with in the family of tree-based data structures. 4 | 5 | A heap can be useful for efficient sorting (heapsort), priority queues (like a stack) and locating a minimum or maximum in constant time 6 | 7 | More on Wikipedia: 8 | 9 | https://en.wikipedia.org/wiki/Heap_(data_structure) 10 | 11 | https://en.wikipedia.org/wiki/Binary_heap 12 | 13 | ### Overview 14 | 15 | A binary heap is a binary tree with two constraints: 16 | 17 | **Shape property** 18 | 19 | * A heap is a complete binary tree 20 | * A complete binary tree is defined as having all levels of the tree fully filled except possibly the last one 21 | * If the last level of the complete binary tree is not filled. The nodes are filled from left to right 22 | 23 | **Heap property** 24 | 25 | * All nodes are either greater than or equal to or less than or equal to each of its children, according to a comparison predicate defined for the heap 26 | * Common comparison predicates include max-heaps and min-heaps 27 | * Max-heaps use the comparison predicate ">=" and min-heaps use "<=" when constructing the heap 28 | 29 | **Max-heap** 30 | 31 | ```ruby 32 | top of heap - > 100 33 | / \ 34 | 20 40 35 | / \ / \ 36 | 10 15 32 8 37 | ``` 38 | 39 | **Min-heap** 40 | 41 | ```ruby 42 | top of heap - > 10 43 | / \ 44 | 20 40 45 | / \ / \ 46 | 30 25 45 80 47 | ``` 48 | 49 | Due to the unordered nature of heaps rebalancing will occur when inserting or deleting nodes. 50 | 51 | For example if 1000 was inserted into the previous max-heap: 52 | 53 | ```ruby 54 | top of heap - > 100 55 | / \ 56 | 20 40 57 | / \ / \ 58 | 10 15 32 8 59 | 60 | 61 | 100 62 | / \ 63 | 20 40 64 | / \ / \ 65 | 10 15 32 8 66 | / 67 | 1000 68 | 69 | 100 70 | / \ 71 | 20 40 72 | / \ / \ 73 | 1000 15 32 8 74 | / 75 | 10 76 | 100 77 | / \ 78 | 1000 40 79 | / \ / \ 80 | 20 15 32 8 81 | / 82 | 10 83 | 84 | 1000 85 | / \ 86 | 100 40 87 | / \ / \ 88 | 20 15 32 8 89 | / 90 | 10 91 | ``` 92 | **Heap sort** 93 | 94 | Heap sort is an efficient algorithm to sort an array. It is typically implemented on a max-heap by removing the node at the top of the heap and rebalancing. 95 | 96 | ### Implementation Tips 97 | 98 | * Binary heaps are typically implemented with an array 99 | * Ex: [1000, 100, 40, 20, 15, 32, 8, 10] 100 | * They are adjusted by shifting the contents of the array around 101 | * The heap property does not lend itself to creation by insertion (like a binary search tree) 102 | * Creation by insertion is suboptimal versus rearranging an already 'complete' binary tree 103 | * A well implemented heap should sort as it implements the heap and shape property 104 | -------------------------------------------------------------------------------- /huffman_coding/javascript/compression.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////// 2 | //// ♪┏(°.°)┛ LEAF ┗(°.°)┓♪ //// 3 | /////////////////////////////////////// 4 | function Leaf(character, count) { 5 | this.character = character; 6 | this.count = count; 7 | } 8 | 9 | Leaf.prototype.encoderObject = function(parentBits) { 10 | return {[this.character]: parentBits} 11 | }; 12 | 13 | Leaf.prototype.unsetParents = function() { 14 | return this; 15 | }; 16 | 17 | ///////////////////////////// 18 | //// (>’.’)> NODE <(‘.'<) /// 19 | ///////////////////////////// 20 | function Node(left, right) { 21 | this.left = left; 22 | this.right = right; 23 | 24 | Object.defineProperties(this, { 25 | count: {"get": function() { return this.left.count + this.right.count; }} 26 | }); 27 | } 28 | 29 | Node.prototype.encoderObject = function(parentBits = "") { 30 | var leftEncoderObject = this.left.encoderObject(parentBits + "0"); 31 | var rightEncoderObject = this.right.encoderObject(parentBits + "1"); 32 | return _.extend(leftEncoderObject, rightEncoderObject); 33 | }; 34 | 35 | Node.prototype.unsetParents = function() { 36 | delete this.left.unsetParents().parent; 37 | delete this.right.unsetParents().parent; 38 | return this; 39 | }; 40 | 41 | /////////////////////////////////// 42 | //// (⌐■_■) ENCODER (⌐■_■) //// 43 | ////////////////////////////////// 44 | function Encoder(message) { 45 | 46 | buildTree(this, message); 47 | 48 | var encoder = this; 49 | this.compressedBitstring = message.split('').reduce(function(bitstring, character){ 50 | return bitstring + encoder.characterToCode(character); 51 | }, ""); 52 | 53 | function buildTree(encoder, message) { 54 | var characterCounts = _.countBy(message.split('')); 55 | 56 | var nodeQueue = _.map(characterCounts, function(count, character) { 57 | return new Leaf(character, count); 58 | }); 59 | 60 | encoder.leaves = nodeQueue.slice(0); 61 | 62 | while(nodeQueue.length > 1) { 63 | nodeQueue = _.sortBy(nodeQueue, 'count'); 64 | newNode = new Node(nodeQueue.shift(), nodeQueue.shift()); 65 | newNode.left.parent = newNode.right.parent = newNode; 66 | nodeQueue.push(newNode); 67 | } 68 | 69 | encoder.root = nodeQueue[0]; 70 | } 71 | 72 | } 73 | 74 | Encoder.prototype.characterToCode = function(character) { 75 | return this.root.encoderObject()[character]; 76 | }; 77 | 78 | Encoder.prototype.decode = function(compressedBitstring) { 79 | 80 | // TODO: All the awesome 81 | 82 | }; 83 | 84 | /////////////////////////////////// 85 | //// ><((((‘> DECODER <`))))>< //// 86 | /////////////////////////////////// 87 | function Decoder(compressedBitstring, rootNode) { 88 | this.bitstring = compressedBitstring; 89 | this.root = rootNode; 90 | } 91 | 92 | Decoder.prototype.message = function(){ 93 | // TODO: It 94 | // YOU CAN DO IT!! 95 | }; 96 | -------------------------------------------------------------------------------- /huffman_coding/javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Win One for the Zipper Tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /huffman_coding/javascript/test.js: -------------------------------------------------------------------------------- 1 | var message = "That's no moon, it's a space station!"; 2 | 3 | var encoder = new Encoder(message); 4 | 5 | describe('compression', function() { 6 | 7 | context('leaf', function() { 8 | it('has a character and a count', function() { 9 | var leaf = new Leaf("J", 4); 10 | assert.ok(leaf.character, 'has a character'); 11 | assert.ok(leaf.count, 'has a count'); 12 | }); 13 | 14 | it('returns itself as an object', function() { 15 | var leaf = new Leaf("J", 4); 16 | assert.deepEqual(leaf.encoderObject("010"), {J: "010"}, 'has correct object returned'); 17 | }); 18 | 19 | }); 20 | 21 | context('node', function() { 22 | 23 | it('has a left node and a right node', function() { 24 | var node = new Node(new Leaf(), new Leaf()); 25 | assert.ok(node.left, 'has a left node'); 26 | assert.ok(node.right, 'has a right node'); 27 | }); 28 | 29 | it('has a count equal to the sum of the counts of its leaves', function() { 30 | var node = new Node(new Leaf("!", 1), new Leaf("@", 2)); 31 | assert.equal(node.count, 3); 32 | }); 33 | 34 | it('returns itself as an object', function() { 35 | var leftLeaf = new Leaf("J", 1); 36 | var rightLeaf = new Leaf("K", 1); 37 | var node = new Node(leftLeaf, rightLeaf); 38 | assert.deepEqual(node.encoderObject("0"), {J: "00", K: "01"}, 'has correct object returned'); 39 | }); 40 | 41 | }); 42 | 43 | context('rootNode', function() { 44 | it('has a count equal to the message length', function() { 45 | assert.equal(encoder.root.count, message.length); 46 | }); 47 | 48 | it('has children that know about their parents', function() { 49 | assert.equal(encoder.root, encoder.root.left.parent); 50 | assert.equal(encoder.root, encoder.root.right.parent); 51 | }); 52 | 53 | it('can unset parents', function() { 54 | var tempEncoder = new Encoder('Cowabunga'); 55 | tempEncoder.root.unsetParents(); 56 | assert.isUndefined(tempEncoder.root.left.parent); 57 | assert.isUndefined(tempEncoder.root.right.parent); 58 | }); 59 | 60 | }); 61 | 62 | context('encoding', function() { 63 | it('has an array of leaves', function() { 64 | var numCharacters = _.uniq(message.split("")).length; 65 | assert.equal(numCharacters, encoder.leaves.length); 66 | encoder.leaves.forEach(function(leaf) { 67 | assert.instanceOf(leaf, Leaf); 68 | }); 69 | 70 | }); 71 | 72 | it('can convert characters to binary codes', function() { 73 | assert.match(encoder.characterToCode("T"), /^[01]+$/); 74 | }); 75 | 76 | it('has shorter codes for characters with more frequency', function() { 77 | var codeLengths = ["t", "!", "n", " "].map(function(character) { 78 | return encoder.characterToCode(character); 79 | }); 80 | assert.deepEqual(codeLengths, codeLengths.sort()); 81 | }); 82 | 83 | it('can tell me the compressed bitstring', function() { 84 | var compressed = "100001000111110000110001110111001011010010010010111010011110011100001100011101111110001101001111101011011011000100011110000111010111010111"; 85 | assert.equal(encoder.compressedBitstring, compressed); 86 | }); 87 | 88 | }); 89 | 90 | context("decoding", function() { 91 | context("challenge 1", function() { 92 | it.skip('can decode a compressed message', function() { 93 | var decodedMessage = encoder.decode(encoder.compressedBitstring); 94 | assert.equal(decodedMessage, message); 95 | }); 96 | }); 97 | 98 | context("challenge 2", function() { 99 | it.skip('can decode from only a compressed bitstring and a tree', function() { 100 | var decoder = new Decoder(encoder.compressedBitstring, encoder.root); 101 | assert.equal(decoder.message(), message); 102 | }); 103 | }); 104 | 105 | context("batman challenge", function() { 106 | it('can decode from only a compressed bitstring and a tree where the nodes have lost their parents', function() { 107 | encoder.root.unsetParents(); 108 | var decoder = new Decoder(encoder.compressedBitstring, encoder.root); 109 | assert.equal(decoder.message(), message); 110 | }); 111 | }); 112 | 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /huffman_coding/readme.md: -------------------------------------------------------------------------------- 1 | Computers use one byte to store each character in a file. A byte is made up of 8 bits. There are 2^8 = 256 possible states of one byte. There are only 26 letters in the alphabet! Even when you include capital letters, numbers and special characters, you can only type 95 characters on a standard keyboard. We're wasting the rest of the space in those bytes. Maybe we should only use 7 bytes per character. 2 | 3 | The most common character in english is the letter 'E'. It appears over 100x more often than a 'Z'. And spaces occur twice as often as 'E's. Should all of these characters take up the same number of bits? Why are we wasting all this space!? 4 | 5 | # Huffman Coding 6 | 7 | Huffman coding is one of the oldest compression algorithms, and forms of it are still used in MP3 and JPEG files today. It uses frequency of different characters in a text file to recode the bits used for each character. 8 | 9 | ## The Tree 10 | 11 | ![The Tree](https://i.stack.imgur.com/9T1Am.png) 12 | 13 | ### Encoding 14 | 15 | A Huffman coding tree determines how you will code each character. To determine the code for a character, work your way from the top (root) to the "leaf" that contains your character. Each time you go left, add a `0`. Each time you go right, add a `1`. Once you get to the leaf, you have your code. 16 | 17 | In the above image, each `c` in your text gets replaced by `1011`, and each `a` is simply `0`. In ASCII, the 'bitstring' for `'abcd'` would be `01100001011000100110001101100100`. 4 characters = 4 bytes = 32 bits. After we encode it with our Huffman tree, our bitstring is `01111011100`. 11 bits = 1 3/8ths bytes. Only 34% of the original length. 18 | 19 | ### Building the tree 20 | 21 | All the code to build the tree from a given message is done for you and tested. If you're curious how it works, the basics are in this [6 minute youtube video](https://www.youtube.com/watch?v=ZdooBTdW5bM). 22 | 23 | ### Decoding 24 | 25 | So how do we get from `01111011100` to `'abcd'`? 26 | 27 | It's your job to write a decoder function, or rather functions. 28 | 29 | First, decode as part of the `Encoder` class itself. You'll have access to a list of `leaves`, the `encoderObject` function, and other things that may be helpful. 30 | 31 | Then create a `Decoder` class. It will be initialized by a compressed bitstring, and a huffman coding tree in the form of the root node. You no longer have access to the `encoderObject` or the `leaves` array. Try to solve this one without recreating those things. 32 | 33 | The last challenge is the Batman challenge. You're still working with the Decoder class, but now the nodes of your tree have lost their parents. Each node only has a `left` and a `right` 34 | -------------------------------------------------------------------------------- /linked_lists/README.markdown: -------------------------------------------------------------------------------- 1 | ## Linked Lists 2 | 3 | Linked Lists are one of the most fundamental Computer Science data structures. 4 | 5 | ### Singly-Linked List 6 | 7 | In a singly-linked list you have a *head*, the start of the list, and *nodes* which hold the data. Each *node* holds a single element of data and a link to the *next* node in the list. 8 | 9 | Using sweet ASCII art, it might look like this: 10 | 11 | ``` 12 | HEAD ---> ["hello" | -]--> ["world" | -]--> ["!" | ] 13 | ``` 14 | 15 | The three nodes here hold the data `"hello"`, `"world"`, and `"!"`. The first two node have links which point to other nodes. The last node, holding the data `"!"`, has no reference in the link spot. This signifies that it is the end of the list. 16 | 17 | #### Functionality 18 | 19 | A fully functional singly linked list can: 20 | 21 | * Insert elements 22 | * Pop an element from the end 23 | * Push an element onto the beginning 24 | * Remove the (first occurance | all occurances) of an element by data content 25 | * Remove an element by position 26 | * Add an element at an arbitrary position 27 | * Add an element after a known node 28 | * Find whether a data element is or is not in the list 29 | * Find the distance between two nodes 30 | 31 | #### Implementation 32 | 33 | Implement a singly-linked list using: 34 | 35 | * A) Iteration/looping 36 | * B) Recursion 37 | 38 | ### Doubly-Linked List 39 | 40 | In a doubly-linked list, each node has a link to both the previous and next nodes in the list. 41 | 42 | #### Functionality 43 | 44 | A doubly-linked list implements all the functionality of a singly-linked list and also: 45 | 46 | * Insert a node immediately before a known node 47 | * Find the shortest distance between two nodes 48 | 49 | ### Ring List 50 | 51 | In a circular or ring list, the "last" node links back to the first. How does this structure affect searching? -------------------------------------------------------------------------------- /linked_lists/javascript/ListNode.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turingschool/data_structures_and_algorithms/f9e9b47900e84e55d93be51600e51bb9297868aa/linked_lists/javascript/ListNode.js -------------------------------------------------------------------------------- /linked_lists/javascript/ListNode_test.js: -------------------------------------------------------------------------------- 1 | describe('ListNode', function() { 2 | it('should have a data property', function(){ 3 | var node = new ListNode(); 4 | expect(node).to.have.property('data'); 5 | }); 6 | 7 | // it('should have data', function() { 8 | // var node = new ListNode('info'); 9 | // expect(node.data).to.eq('info'); 10 | // var pizzaNode = new ListNode('pizza'); 11 | // expect(pizzaNode.data).to.eq('pizza'); 12 | // }); 13 | 14 | // it('should have a default empty nextNode', function() { 15 | // var node = new ListNode('pizza'); 16 | // expect(node.nextNode).to.eq(null); 17 | // }); 18 | 19 | // it('should allow setting a nextNode', function() { 20 | // var n1 = new ListNode('pizza'); 21 | // var n2 = new ListNode('cats'); 22 | // n1.nextNode = n2; 23 | // expect(n1.nextNode.data).to.eq('cats'); 24 | // expect(n1.nextNode instanceof ListNode).to.be.true; 25 | // }); 26 | 27 | // it('should allow a next node argument on creation', function(){ 28 | // var node = new ListNode('pizza', new ListNode('cats')); 29 | // expect(node.nextNode.data).to.eq('cats'); 30 | // expect(node.nextNode instanceof ListNode).to.be.true; 31 | // }); 32 | }); 33 | -------------------------------------------------------------------------------- /linked_lists/javascript/README.md: -------------------------------------------------------------------------------- 1 | # Linked List Problem 2 | 3 | Open the `test.html` page to see your tests run in the brower. 4 | 5 | There are two sets of tests to get passing! 6 | 7 | Start with the tests in `ListNode_test.js` 8 | 9 | Once those are passing, more on to the tests in `LinkedList_test.js` -------------------------------------------------------------------------------- /linked_lists/javascript/linkedList.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turingschool/data_structures_and_algorithms/f9e9b47900e84e55d93be51600e51bb9297868aa/linked_lists/javascript/linkedList.js -------------------------------------------------------------------------------- /linked_lists/javascript/linkedList_test.js: -------------------------------------------------------------------------------- 1 | describe('LinkedList', function() { 2 | var list; 3 | 4 | beforeEach(function(){ 5 | list = new List(); 6 | }); 7 | 8 | it.skip('should start with zero elements', function() { 9 | expect(list._length).to.eq(0); 10 | }); 11 | 12 | it.skip('should set its default head to null', function(){ 13 | expect(list.head).to.eq(null); 14 | }); 15 | 16 | describe('.push', function(){ 17 | context('with a single element', function(){ 18 | it.skip('should allow push of a single element to a list', function(){ 19 | list.push('pizza'); 20 | expect(list.head.data).to.eq('pizza'); 21 | }); 22 | 23 | it.skip('should increment the _length of the list', function(){ 24 | list.push('pizza'); 25 | expect(list._length).to.eq(1); 26 | }); 27 | }); 28 | 29 | context('with multiple elements', function(){ 30 | it.skip('should increment the length count', function(){ 31 | list.push('pizza'); 32 | list.push('stromboli'); 33 | list.push('mushroom'); 34 | expect(list._length).to.eq(3); 35 | }); 36 | 37 | it.skip('should assign the head to the first element pushed', function(){ 38 | expect(list.head).to.eq(null); 39 | list.push('pizza'); 40 | expect(list.head.data).to.eq('pizza'); 41 | list.push('stromboli'); 42 | expect(list.head.data).to.eq('pizza'); 43 | }); 44 | 45 | it.skip('should attach the second element to the first element', function(){ 46 | list.push('pizza'); 47 | list.push('stromboli'); 48 | expect(list.head.nextNode.data).to.eq('stromboli'); 49 | }); 50 | 51 | it.skip('should attach nextNodes in sequential order', function(){ 52 | list.push('pizza'); 53 | list.push('stromboli'); 54 | list.push('mushroom'); 55 | list.push('peanutbutter'); 56 | expect(list.head.data).to.eq('pizza'); 57 | expect(list.head.nextNode.data).to.eq('stromboli'); 58 | expect(list.head.nextNode.nextNode.data).to.eq('mushroom'); 59 | expect(list.head.nextNode.nextNode.nextNode.data).to.eq('peanutbutter'); 60 | }); 61 | }); 62 | }); 63 | 64 | describe('.pop', function(){ 65 | context('with no elements', function(){ 66 | it.skip('should return null', function(){ 67 | expect(list.pop()).to.eq(null); 68 | }); 69 | 70 | it.skip('should not decrement the _length', function(){ 71 | expect(list._length).to.eq(0); 72 | }); 73 | }); 74 | 75 | context('with one element', function(){ 76 | it.skip('should change the _length', function(){ 77 | list.push('hello'); 78 | var result = list.pop(); 79 | expect(list._length).to.eq(0); 80 | }); 81 | 82 | it.skip('should set the list head to null', function(){ 83 | list.push('hello'); 84 | var result = list.pop(); 85 | expect(list.head).to.eq(null); 86 | }); 87 | 88 | it.skip('should return the last element', function(){ 89 | list.push('hello'); 90 | var result = list.pop(); 91 | expect(result.data).to.eq('hello'); 92 | }); 93 | }); 94 | 95 | context('with multiple elements', function(){ 96 | it.skip('should return the last element from the list', function(){ 97 | list.push("hello"); 98 | list.push("new"); 99 | list.push("world"); 100 | list.push("today"); 101 | 102 | var output = list.pop(); 103 | expect(output.data).to.eq('today'); 104 | }); 105 | 106 | it.skip('should remove the last element from the list', function(){ 107 | list.push("hello"); 108 | list.push("world"); 109 | list.push("today"); 110 | 111 | var output = list.pop(); 112 | expect(output.data).to.eq('today'); 113 | expect(list._length).to.eq(2); 114 | 115 | var output2 = list.pop(); 116 | expect(output2.data).to.eq('world'); 117 | expect(output2.nextNode).to.eq(null); 118 | expect(list._length).to.eq(1); 119 | 120 | var output3 = list.pop(); 121 | expect(output3.data).to.eq('hello'); 122 | expect(output3.nextNode).to.eq(null); 123 | expect(list._length).to.eq(0); 124 | }); 125 | }); 126 | }); 127 | 128 | describe('.delete', function(){ 129 | context('with one node', function(){ 130 | it.skip('deletes a solo node', function(){ 131 | list.push('hello'); 132 | list.delete('hello'); 133 | expect(list._length).to.eq(0); 134 | expect(list.head).to.eq(null); 135 | }); 136 | 137 | it.skip('does not perform a delete when a node does not match', function(){ 138 | list.push('hello'); 139 | list.delete('goodbye'); 140 | expect(list._length).to.eq(1); 141 | expect(list.head.data).to.eq('hello'); 142 | }); 143 | }); 144 | 145 | context('with multiple nodes', function(){ 146 | beforeEach(function(){ 147 | list.push('hello'); 148 | list.push('darkness'); 149 | list.push('my'); 150 | list.push('old'); 151 | list.push('friend'); 152 | }); 153 | 154 | it.skip('changes the list _.length', function(){ 155 | expect(list.head.nextNode.data).to.eq('darkness'); 156 | expect(list._length).to.eq(5); 157 | list.delete('friend'); 158 | expect(list._length).to.eq(4); 159 | list.delete('my'); 160 | expect(list._length).to.eq(3); 161 | list.delete('happy'); 162 | expect(list._length).to.eq(3); 163 | }); 164 | 165 | it.skip('resets the nextNode property on the node before the deleted node', function(){ 166 | expect(list.head.nextNode.data).to.eq('darkness'); 167 | list.delete('darkness'); 168 | expect(list.head.nextNode.data).to.eq('my'); 169 | }); 170 | 171 | it.skip('resets the list.head if deleting the first node', function(){ 172 | expect(list.head.data).to.eq('hello'); 173 | list.delete('hello'); 174 | expect(list.head.data).to.eq('darkness'); 175 | }); 176 | }); 177 | }); 178 | 179 | describe('.toArray', function(){ 180 | context('when there are no elements', function(){ 181 | it.skip('converts to an array', function(){ 182 | expect(list.toArray()).to.deep.equal([]); 183 | }); 184 | }); 185 | 186 | context('when there are several elements', function(){ 187 | beforeEach(function(){ 188 | list.push('The'); 189 | list.push('rain'); 190 | list.push('in'); 191 | list.push('Spain'); 192 | }); 193 | 194 | it.skip('can convert to an array', function(){ 195 | expect(list.toArray()).to.deep.equal(['The', 'rain', 'in', 'Spain']); 196 | }); 197 | }); 198 | }); 199 | 200 | describe('.lastNode', function(){ 201 | context('with several nodes', function(){ 202 | beforeEach(function(){ 203 | list.push('The'); 204 | list.push('rain'); 205 | list.push('in'); 206 | list.push('Spain'); 207 | }); 208 | 209 | it.skip('finds the last node', function(){ 210 | expect(list.lastNode().data).to.eq('Spain'); 211 | }); 212 | }); 213 | 214 | context('with one node', function(){ 215 | beforeEach(function(){ 216 | list.push('Ahoy!'); 217 | }); 218 | 219 | it.skip('finds the only node', function(){ 220 | expect(list.lastNode().data).to.eq('Ahoy!'); 221 | }); 222 | }); 223 | 224 | context('with no nodes', function(){ 225 | it.skip('returns null', function(){ 226 | expect(list.lastNode()).to.eq(null); 227 | }); 228 | }); 229 | }); 230 | 231 | describe('.include', function(){ 232 | beforeEach(function(){ 233 | list.push('The'); 234 | list.push('rain'); 235 | list.push('in'); 236 | list.push('Spain'); 237 | }); 238 | 239 | it.skip('should return true if node is in list', function(){ 240 | expect(list.include("rain")).to.eq(true); 241 | }); 242 | 243 | it.skip('should return false if node is not in list', function(){ 244 | expect(list.include("nope")).to.eq(false); 245 | }); 246 | }); 247 | 248 | describe('.find', function(){ 249 | beforeEach(function(){ 250 | list.push('oh'); 251 | list.push('hello'); 252 | list.push('world'); 253 | }); 254 | 255 | it.skip('should return true the node if node in list', function(){ 256 | var result = list.find("hello"); 257 | expect(result.data).to.eq('hello'); 258 | expect(result.nextNode.data).to.eq('world'); 259 | }); 260 | 261 | it.skip('should return null if node is missing', function(){ 262 | var result = list.find("nope"); 263 | expect(result).to.eq(null); 264 | }); 265 | }); 266 | 267 | describe('.index', function(){ 268 | beforeEach(function(){ 269 | list.push('oh'); 270 | list.push('hello'); 271 | list.push('world'); 272 | }); 273 | 274 | it.skip('should return expected indexes', function(){ 275 | expect(list.index('oh')).to.eq(0); 276 | expect(list.index('world')).to.eq(2); 277 | expect(list.index('nope')).to.eq(null); 278 | }); 279 | }); 280 | 281 | describe('.insert', function(){ 282 | beforeEach(function(){ 283 | list.push('dark'); 284 | list.push('stormy'); 285 | }); 286 | 287 | it.skip('should insert nodes', function(){ 288 | expect(list._length).to.eq(2); 289 | list.insert(1, 'and'); 290 | list.insert(3, 'night'); 291 | expect(list._length).to.eq(4); 292 | expect(list.toArray()).to.deep.equal(['dark', 'and', 'stormy', 'night']); 293 | }); 294 | }); 295 | 296 | describe('.insertAfter', function(){ 297 | beforeEach(function(){ 298 | list.push('dark'); 299 | list.push('stormy'); 300 | }); 301 | 302 | it.skip('should insert nodes after other nodes', function(){ 303 | expect(list._length).to.eq(2); 304 | list.insertAgfter('dark', 'and'); 305 | list.insertAfter('stormy', 'night'); 306 | expect(list._length).to.eq(4); 307 | expect(list.toArray()).to.deep.equal(['dark', 'and', 'stormy', 'night']); 308 | }); 309 | }); 310 | 311 | describe('.distance', function(){ 312 | beforeEach(function(){ 313 | list.push("hello") 314 | list.push("pizza") 315 | list.push("world") 316 | list.push("today") 317 | list.push("tomorrow") 318 | }); 319 | 320 | it.skip('should calculate distance between nodes', function(){ 321 | expect(list.distance("hello", "today")).to.eq(3); 322 | expect(list.distance("pizza", "today")).to.eq(2); 323 | expect(list.distance("hello", "world")).to.eq(2); 324 | expect(list.distance("hello", "tomorrow")).to.eq(4); 325 | expect(list.distance("world", "today")).to.eq(1); 326 | }); 327 | }); 328 | }); 329 | -------------------------------------------------------------------------------- /linked_lists/javascript/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Linked List Test Suite 6 | 7 | 8 | 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /linked_lists/ruby/linked_list_test.rb: -------------------------------------------------------------------------------- 1 | gem 'minitest' 2 | require 'minitest/autorun' 3 | require 'minitest/pride' 4 | 5 | class LinkedListTest < Minitest::Test 6 | attr_reader :list 7 | 8 | def setup 9 | @list = LinkedList.new 10 | end 11 | 12 | def test_nodes_have_data 13 | skip 14 | node = Node.new("pizza") 15 | assert_equal "pizza", node.data 16 | end 17 | 18 | def test_nodes_have_next_node 19 | skip 20 | n1 = Node.new("pizza") 21 | n2 = Node.new("cats") 22 | n1.next_node = n2 23 | assert_equal "cats", n1.next_node.data 24 | assert_equal Node, n1.next_node.class 25 | end 26 | 27 | def test_it_accepts_next_node_on_init 28 | skip 29 | n1 = Node.new("pizza", Node.new("cats")) 30 | assert_equal "cats", n1.next_node.data 31 | assert_equal Node, n1.next_node.class 32 | end 33 | 34 | def test_it_starts_with_zero_elements 35 | skip 36 | assert_equal 0, list.count 37 | end 38 | 39 | def test_a_new_list_starts_with_nil_head 40 | skip 41 | assert_equal nil, LinkedList.new.head 42 | end 43 | 44 | def test_it_pushes_a_single_element_onto_a_list 45 | skip 46 | list.push("pizza") 47 | assert_equal "pizza", list.head.data 48 | assert_equal 1, list.count 49 | end 50 | 51 | def test_it_pushes_two_elements 52 | skip 53 | list.push("pizza") 54 | assert_equal "pizza", list.head.data 55 | list.push("stromboli") 56 | assert_equal "stromboli", list.head.next_node.data 57 | end 58 | 59 | def test_it_pushes_three_elements_onto_a_list 60 | skip 61 | list.push("hello") 62 | assert_equal "hello", list.head.data 63 | list.push("world") 64 | assert_equal "world", list.head.next_node.data 65 | list.push("today") 66 | assert_equal "world", list.head.next_node.data 67 | assert_equal "today", list.head.next_node.next_node.data 68 | assert_equal 3, list.count 69 | end 70 | 71 | def test_it_adds_four_elements 72 | skip 73 | list.push("hello") 74 | list.push("world") 75 | list.push("today") 76 | list.push("pizza") 77 | assert_equal "today", list.head.next_node.next_node.data 78 | assert_equal "pizza", list.head.next_node.next_node.next_node.data 79 | assert_equal 4, list.count 80 | end 81 | 82 | def test_it_pops_the_last_element_from_the_list 83 | skip 84 | list.push("hello") 85 | list.push("world") 86 | list.push("today") 87 | output = list.pop 88 | assert_equal "today", output 89 | assert_equal 2, list.count 90 | end 91 | 92 | def test_a_popped_element_is_removed 93 | skip 94 | list.push("hello") 95 | output = list.pop 96 | assert_equal "hello", output 97 | assert_equal 0, list.count 98 | end 99 | 100 | def test_it_pops_nil_when_there_are_no_elements 101 | skip 102 | assert_nil list.pop 103 | end 104 | 105 | def test_it_deletes_a_solo_node 106 | skip 107 | list.push("hello") 108 | list.delete("hello") 109 | assert_equal 0, list.count 110 | end 111 | 112 | def test_it_does_not_delete_when_the_data_does_not_match 113 | skip 114 | list.push("hello") 115 | list.push("world") 116 | list.delete("today") 117 | assert_equal 2, list.count 118 | end 119 | 120 | def test_it_deletes_a_last_node 121 | skip 122 | list.push("hello") 123 | list.push("world") 124 | list.push("today") 125 | list.delete("today") 126 | assert_equal 2, list.count 127 | end 128 | 129 | def test_it_deletes_a_middle_node 130 | skip 131 | list.push("hello") 132 | list.push("world") 133 | list.push("today") 134 | list.delete("world") 135 | assert_equal 2, list.count 136 | assert_equal "today", list.pop 137 | assert_equal "hello", list.pop 138 | end 139 | 140 | def test_it_deletes_the_head_when_there_are_more_nodes 141 | skip 142 | list.push("hello") 143 | list.push("world") 144 | list.push("today") 145 | list.delete("hello") 146 | assert_equal 2, list.count 147 | assert_equal "today", list.pop 148 | assert_equal "world", list.pop 149 | end 150 | 151 | def test_it_converts_to_an_array_when_there_are_no_elements 152 | skip 153 | assert_equal [], list.to_a 154 | end 155 | 156 | def test_it_converts_to_an_array_with_several_elements 157 | skip 158 | list.push("hello") 159 | list.push("world") 160 | list.push("today") 161 | assert_equal ["hello", "world", "today"], list.to_a 162 | end 163 | 164 | def test_it_finds_the_last_node 165 | skip 166 | list.push("hello") 167 | list.push("world") 168 | node = list.last_node 169 | assert_equal "world", node.data 170 | end 171 | 172 | def test_a_node_links_to_its_next_element 173 | skip 174 | list.push("hello") 175 | list.push("world") 176 | assert_equal "world", list.last_node.data 177 | assert_equal "world", list.head_node.next_node.data 178 | end 179 | 180 | def test_next_node_for_the_last_node_is_nil 181 | skip 182 | list.push("world") 183 | assert_nil list.last_node.next_node 184 | end 185 | 186 | def test_find_if_an_element_is_included_in_the_list 187 | skip 188 | list.push("hello") 189 | list.push("world") 190 | assert_equal true, list.include?("hello") 191 | assert_equal false, list.include?("bogus") 192 | end 193 | 194 | def test_find_a_given_node 195 | skip 196 | list.push("hello") 197 | list.push("world") 198 | list.push("today") 199 | 200 | assert_equal "world", list.find("world").data 201 | assert_equal "today", list.find("world").next_node.data 202 | end 203 | 204 | def test_inserts_node_at_arbitrary_position 205 | skip 206 | list.push("hello") 207 | list.push("world") 208 | list.push("today") 209 | 210 | list.insert(1, "pizza") 211 | 212 | assert_equal 1, list.index("pizza") 213 | assert_equal ["hello", "pizza", "world", "today"], list.to_a 214 | end 215 | 216 | def test_inserted_node_is_next_node_for_previous_node 217 | skip 218 | list.push("hello") 219 | list.push("world") 220 | list.push("today") 221 | 222 | list.insert(1, "pizza") 223 | 224 | assert_equal "world", list.find("pizza").next_node.data 225 | assert_equal "pizza", list.find("hello").next_node.data 226 | end 227 | 228 | def test_insert_after_adds_a_node_after_a_given_node 229 | skip 230 | list.push("hello") 231 | list.push("world") 232 | list.push("today") 233 | 234 | list.insert_after("hello", "pizza") 235 | 236 | assert_equal "world", list.find("pizza").next_node.data 237 | assert_equal "pizza", list.find("hello").next_node.data 238 | end 239 | 240 | def test_distance_returns_distance_between_two_nodes 241 | skip 242 | list.push("hello") 243 | list.push("pizza") 244 | list.push("world") 245 | list.push("today") 246 | list.push("tomorrow") 247 | assert_equal 3, list.distance("hello", "today") 248 | assert_equal 2, list.distance("pizza", "today") 249 | assert_equal 2, list.distance("hello", "world") 250 | assert_equal 4, list.distance("hello", "tomorrow") 251 | assert_equal 1, list.distance("world", "today") 252 | end 253 | end 254 | -------------------------------------------------------------------------------- /luhn_algorithm/README.markdown: -------------------------------------------------------------------------------- 1 | ## Luhn Algorithm 2 | 3 | The Luhn algorithm is a check-summing algorithm best known for checking the validity of credit card numbers. 4 | 5 | You can checkout the full description on Wikipedia: http://en.wikipedia.org/wiki/Luhn_algorithm 6 | 7 | ### Description 8 | 9 | (adapted from Wikipedia) 10 | 11 | The formula verifies a number against its included check digit, which is usually appended to a partial account number to generate the full account number. This full account number must pass the following test: 12 | 13 | * from the rightmost digit, which is the check digit, moving left, double the value of every second digit 14 | * if product of this doubling operation is greater than 9 (e.g., 7 * 2 = 14), then sum the digits of the products (e.g., 10: 1 + 0 = 1, 14: 1 + 4 = 5). 15 | * take the sum of all the digits 16 | * if and only if the total modulo 10 is equal to 0 then the number is valid 17 | 18 | ### Example 19 | 20 | #### Calculating the Check Digit 21 | 22 | Take an account identifier `7992739871`. To make it an account number, we need to calculate and append a check digit. Calling the digit `x`, the full account number will look like `7992739871x`. 23 | 24 | We use the algorithm to calculate the correct checksum digit: 25 | 26 | * `Account identifier: 7 9 9 2 7 3 9 8 7 1 x` 27 | * `2x every other digit: 7 18 9 4 7 6 9 16 7 2 x` 28 | * `Summed digits over 10: 7 9 9 4 7 6 9 7 7 2 x` 29 | * `Results summed: 7 9 9 4 7 6 9 7 7 2` = 67 30 | 31 | With the result of `67`, we take the ones digit (`7`) and subtract it from `10`: `10 - 7 = 3`. Thus `3` is the check digit. 32 | 33 | The full account number with check digit is `79927398713`. 34 | 35 | #### Validating an Account Number 36 | 37 | We can use the same process to validate an account number. Using `79927398713` as our sample input: 38 | 39 | * `Account identifier: 7 9 9 2 7 3 9 8 7 1 3` 40 | * `2x every other digit: 7 18 9 4 7 6 9 16 7 2 3` 41 | * `Summed digits over 10: 7 9 9 4 7 6 9 7 7 2 3` 42 | * `Results summed: 7 9 9 4 7 6 9 7 7 2 3` = 70 43 | 44 | Since the summed results modulo 10 is zero, the account number is valid according to the algorithm. 45 | 46 | 47 | -------------------------------------------------------------------------------- /luhn_algorithm/luhn_test.rb: -------------------------------------------------------------------------------- 1 | gem 'minitest' 2 | require 'minitest/autorun' 3 | require_relative 'luhn_validator' 4 | 5 | class LuhnTest < Minitest::Test 6 | # This test suite is not meant to be exhaustive, 7 | # just a start. Write your own tests! 8 | 9 | attr_reader :validator 10 | 11 | def setup 12 | @validator = LuhnValidator.new 13 | end 14 | 15 | def test_it_validates_the_example_account_number 16 | assert validator.validate('79927398713') 17 | end 18 | 19 | def test_it_finds_the_check_digit_for_an_identifier 20 | assert_equal '3', validator.check_digit_for('7992739871') 21 | end 22 | end -------------------------------------------------------------------------------- /md5/README.markdown: -------------------------------------------------------------------------------- 1 | ## MD5 Hashing 2 | 3 | One-way hash functions like MD5 have many uses from security to data validity. 4 | 5 | MD5 is often used to hash passwords, though it is a poor choice for that application since the algorithm is designed to be fast. Therefore, if the hashed password is exposed, an attacker can iterate through many possible solutions quickly. 6 | 7 | Instead, MD5 is best suited to verifying the integrity of data. For instance, if you're transferring a large file between two machines, you could run MD5 on the source machine and on the receiving machine. If the two hashes match exactly, then you know the file transferred without corruption. 8 | 9 | ### The Algorithm 10 | 11 | Pseudocode: 12 | 13 | ```plain 14 | //Note: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating 15 | var int[64] s, K 16 | 17 | //s specifies the per-round shift amounts 18 | s[ 0..15] := { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22} 19 | s[16..31] := { 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20} 20 | s[32..47] := { 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23} 21 | s[48..63] := { 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21} 22 | 23 | //Use binary integer part of the sines of integers (Radians) as constants: 24 | for i from 0 to 63 25 | K[i] := floor(abs(sin(i + 1)) × (2 pow 32)) 26 | end for 27 | //(Or just use the following table): 28 | K[ 0.. 3] := { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee } 29 | K[ 4.. 7] := { 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 } 30 | K[ 8..11] := { 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be } 31 | K[12..15] := { 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 } 32 | K[16..19] := { 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa } 33 | K[20..23] := { 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 } 34 | K[24..27] := { 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed } 35 | K[28..31] := { 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a } 36 | K[32..35] := { 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c } 37 | K[36..39] := { 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 } 38 | K[40..43] := { 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 } 39 | K[44..47] := { 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 } 40 | K[48..51] := { 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 } 41 | K[52..55] := { 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 } 42 | K[56..59] := { 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 } 43 | K[60..63] := { 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 } 44 | 45 | //Initialize variables: 46 | var int a0 := 0x67452301 //A 47 | var int b0 := 0xefcdab89 //B 48 | var int c0 := 0x98badcfe //C 49 | var int d0 := 0x10325476 //D 50 | 51 | //Pre-processing: adding a single 1 bit 52 | append "1" bit to message 53 | /* Notice: the input bytes are considered as bits strings, 54 | where the first bit is the most significant bit of the byte.[41] 55 | 56 | 57 | //Pre-processing: padding with zeros 58 | append "0" bit until message length in bit ≡ 448 (mod 512) 59 | append length mod (2 pow 64) to message 60 | 61 | 62 | //Process the message in successive 512-bit chunks: 63 | for each 512-bit chunk of message 64 | break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 65 | //Initialize hash value for this chunk: 66 | var int A := a0 67 | var int B := b0 68 | var int C := c0 69 | var int D := d0 70 | //Main loop: 71 | for i from 0 to 63 72 | if 0 ≤ i ≤ 15 then 73 | F := (B and C) or ((not B) and D) 74 | g := i 75 | else if 16 ≤ i ≤ 31 76 | F := (D and B) or ((not D) and C) 77 | g := (5×i + 1) mod 16 78 | else if 32 ≤ i ≤ 47 79 | F := B xor C xor D 80 | g := (3×i + 5) mod 16 81 | else if 48 ≤ i ≤ 63 82 | F := C xor (B or (not D)) 83 | g := (7×i) mod 16 84 | dTemp := D 85 | D := C 86 | C := B 87 | B := B + leftrotate((A + F + K[i] + M[g]), s[i]) 88 | A := dTemp 89 | end for 90 | //Add this chunk's hash to result so far: 91 | a0 := a0 + A 92 | b0 := b0 + B 93 | c0 := c0 + C 94 | d0 := d0 + D 95 | end for 96 | 97 | var char digest[16] := a0 append b0 append c0 append d0 //(Output is in little-endian) 98 | 99 | //leftrotate function definition 100 | leftrotate (x, c) 101 | return (x << c) binary or (x >> (32-c)); 102 | ``` 103 | 104 | ### References 105 | 106 | * [IETF RFC 1321](http://tools.ietf.org/html/rfc1321) 107 | * [Wikipedia](http://en.wikipedia.org/wiki/MD5#Algorithm) 108 | -------------------------------------------------------------------------------- /md5/md5.rb: -------------------------------------------------------------------------------- 1 | class MD5 2 | def self.hexdigest(input) 3 | 4 | end 5 | end -------------------------------------------------------------------------------- /md5/md5_test.rb: -------------------------------------------------------------------------------- 1 | gem 'minitest' 2 | require 'minitest/autorun' 3 | require_relative 'md5' 4 | require 'digest/md5' 5 | 6 | class MD5Test < Minitest::Test 7 | def test_it_hashes_a_string 8 | expected = Digest::MD5.hexdigest("Hello World\n") 9 | result = MD5.hexdigest("Hello World\n") 10 | assert_equal expected, result 11 | end 12 | end -------------------------------------------------------------------------------- /pathfinding/Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | guard :minitest do 5 | watch(%r{^test/(.*)\/?(.*)\.rb$}) 6 | watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" } 7 | end 8 | -------------------------------------------------------------------------------- /pathfinding/lib/landscape.rb: -------------------------------------------------------------------------------- 1 | class Landscape 2 | attr_reader :matrix 3 | 4 | def initialize(matrix) 5 | @matrix = matrix 6 | end 7 | 8 | def self.load(file_path) 9 | matrix = File.open(file_path) do |file| 10 | file.each_line.map do |line| 11 | line.chomp!.split(//) 12 | end 13 | end 14 | self.new(matrix) 15 | end 16 | 17 | def to_s 18 | matrix.map { |l| l.join("") }.join("\n") 19 | end 20 | 21 | def start 22 | element_coords("S") 23 | end 24 | 25 | def finish 26 | element_coords("F") 27 | end 28 | 29 | def element_coords(element) 30 | row = matrix.find { |f| f.include?(element) } 31 | y = matrix.index(row) 32 | x = row.index(element) 33 | [x,y] 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /pathfinding/lib/pathfinder.rb: -------------------------------------------------------------------------------- 1 | class Pathfinder 2 | def solve(landscape) 3 | [] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /pathfinding/test/fixtures/landscape1.txt: -------------------------------------------------------------------------------- 1 | ########### 2 | # S # 3 | # ##### # # 4 | # # # # 5 | ### # ### # 6 | # # # 7 | # # ### ### 8 | # # # # 9 | # ### # # # 10 | # # F # 11 | ########### 12 | -------------------------------------------------------------------------------- /pathfinding/test/fixtures/landscape2.txt: -------------------------------------------------------------------------------- 1 | ###################### 2 | # GGGGGGGGGGGG # 3 | # GGGGGGGGGGGG # 4 | # SGGGGGGGGGGGGF # 5 | # GGGGGGGGGGGG # 6 | # # 7 | ###################### 8 | -------------------------------------------------------------------------------- /pathfinding/test/landscape_test.rb: -------------------------------------------------------------------------------- 1 | gem 'minitest' 2 | require 'minitest/autorun' 3 | require 'minitest/pride' 4 | require 'minitest/spec' 5 | require_relative '../lib/landscape' 6 | require_relative '../lib/pathfinder' 7 | 8 | describe Landscape do 9 | it "generates a landscape from a fixture file" do 10 | matrix = [["#", "#", "#", "#", "#", "#", "#", "#", "#", "#", "#"], 11 | ["#", " ", "S", " ", " ", " ", " ", " ", " ", " ", "#"], 12 | ["#", " ", "#", "#", "#", "#", "#", " ", "#", " ", "#"], 13 | ["#", " ", " ", " ", "#", " ", " ", " ", "#", " ", "#"], 14 | ["#", "#", "#", " ", "#", " ", "#", "#", "#", " ", "#"], 15 | ["#", " ", " ", " ", " ", " ", "#", " ", " ", " ", "#"], 16 | ["#", " ", "#", " ", "#", "#", "#", " ", "#", "#", "#"], 17 | ["#", " ", "#", " ", " ", " ", "#", " ", " ", " ", "#"], 18 | ["#", " ", "#", "#", "#", " ", "#", " ", "#", " ", "#"], 19 | ["#", " ", " ", " ", " ", " ", "#", " ", "F", " ", "#"], 20 | ["#", "#", "#", "#", "#", "#", "#", "#", "#", "#", "#"]] 21 | loaded = Landscape.load(File.join(__dir__, "fixtures", "landscape1.txt")).matrix 22 | matrix.each_with_index do |line, index| 23 | assert_equal line, loaded[index], "line #{index} did not match" 24 | end 25 | end 26 | 27 | it "prints itself" do 28 | landscape = Landscape.load(File.join(__dir__, "fixtures", "landscape1.txt")) 29 | assert_equal File.read(File.join(__dir__, "fixtures", "landscape1.txt")).chomp, landscape.to_s 30 | end 31 | 32 | it "finds start and finish" do 33 | landscape = Landscape.load(File.join(__dir__, "fixtures", "landscape1.txt")) 34 | assert_equal [2,1], landscape.start 35 | assert_equal [8,9], landscape.finish 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /pathfinding/test/pathfinder_test.rb: -------------------------------------------------------------------------------- 1 | gem 'minitest' 2 | require 'minitest/autorun' 3 | require 'minitest/pride' 4 | require 'minitest/spec' 5 | require_relative '../lib/landscape' 6 | require_relative '../lib/pathfinder' 7 | 8 | describe Pathfinder do 9 | before do 10 | @landscape = Landscape.load(File.join(__dir__, "fixtures", "landscape1.txt")) 11 | @landscape2 = Landscape.load(File.join(__dir__, "fixtures", "landscape2.txt")) 12 | @pathfinder = Pathfinder.new 13 | end 14 | 15 | describe "#solve" do 16 | it "returns a path as an array of coordinates" do 17 | solution = [[3,1],[4,1],[5,1],[6,1],[7,1],[8,1],[9,1], 18 | [9,2],[9,3],[9,4],[9,5],[8,5],[7,5],[7,6], 19 | [7,7],[7,8],[7,9]] 20 | assert_equal solution, @pathfinder.solve(@landscape) 21 | end 22 | 23 | it "Solves with grass" do 24 | solution = [[3,4],[3,5],[4,5],[5,5],[6,5],[7,5],[8,5], 25 | [9,5],[10,5],[11,5],[12,5],[13,5],[14,5], 26 | [15,5],[16,5],[16,4]] 27 | assert_equal solution, @pathfinder.solve(@landscape2) 28 | end 29 | end 30 | end 31 | 32 | -------------------------------------------------------------------------------- /red_black_trees/README.markdown: -------------------------------------------------------------------------------- 1 | ### Red/Black Binary Trees 2 | 3 | * Subtype of Binary Tree 4 | * Trying to solve the problem of worst-case / guaranteed performance 5 | 6 | #### Worst-case Binary Trees 7 | 8 | * Inserting ordered data: 9 | 10 | ``` 11 | 1 12 | \ 13 | 2 14 | \ 15 | 3 16 | \ 17 | 4 18 | ``` 19 | 20 | * Inserting reverse-ordered Data: 21 | 22 | ``` 23 | 4 24 | / 25 | 3 26 | / 27 | 2 28 | / 29 | 1 30 | ``` 31 | 32 | * Inserting ordered data alternating between first / last 33 | 34 | ``` 35 | 1 36 | \ 37 | 6 38 | / 39 | 2 40 | \ 41 | 5 42 | / 43 | 3 44 | \ 45 | 4 46 | ``` 47 | 48 | In each of these cases, we can see that the order of the data 49 | causes us to lose the crucial branching factor which makes 50 | binary trees efficient. 51 | 52 | In fact, we end up with a "directed graph of order 1"... i.e. a 53 | Linked List. 54 | 55 | #### Solving the problem 56 | 57 | One solution to the problem is to periodically stop and re-balance 58 | the tree. This is partly why balancing is such an interesting 59 | and important operation for trees. 60 | 61 | However doing a lengthy re-balance can sometimes be very costly. 62 | It would be ideal if we could ensure our tree remained balanced 63 | in the first place by doing small, incremental re-balancing 64 | as we go. 65 | 66 | This is exactly the problem a [Red-Black Tree](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) solves. 67 | 68 | #### RB Tree Coloring Conventions 69 | 70 | So why is a Red-Black tree named as such? 71 | 72 | In order for the tree to self balance it needs a general understanding of its nodes. Color coding each as red or black (pink/green if you want to fight the power!) serves this purpose. 73 | 74 | #### RB Tree Fundamentals 75 | 76 | **Properties** 77 | 78 | 1. A node is either red or black. 79 | 2. The root is black. This rule is sometimes omitted. Since the root can always be changed from red to black, but not necessarily vice versa, this rule has little effect on analysis. 80 | 3. All leaves (NIL) are black. 81 | 4. If a node is red, then both its children are black. 82 | 5. Every path from a given node to any of its descendant NIL nodes contains the same number of black nodes. The uniform number of black nodes in the paths from root to leaves is called the black-height of the red–black tree. 83 | 84 | Source: https://en.wikipedia.org/wiki/Red–black_tree 85 | 86 | **Property overview** 87 | 88 | 1. This is simplistic and can be represented by a bit (0 / 1) with in each node 89 | 2. Simpler to implement if you just assume "Root must be black!!" 90 | 3. In a normal Binary Search Tree leaves are typically nodes with values associated with them. RB trees also have leaves but they have a NIL value associated with them. 91 | 4. This property combined with property 5 will determine the shape of the tree 92 | 5. Unfortunately the definition above is pretty good (even though it is confusing). An example will hopefully give some context: 93 | 94 | ``` 95 | x = NIL(B) 96 | 97 | 5(B) 98 | / \ 99 | 4(R) 6(R) 100 | / \ / \ 101 | x x x x 102 | ``` 103 | In the valid tree above the root would have a black height of 1. The black height is the number of black nodes between the given node (root) and a descendant leaf. So if the left most path is taken the root would encounter 1 black node (which would be the leaf itself). Property 5 asserts on that all of the paths from the said node (our case the root) must have the same number of black node encounters. So for all four paths the root can take to the leaves the black height must be one in order for it to be a valid RB tree 104 | 105 | The combination of properties 4 and 5 force the tree to be about balanced on both sides. 106 | 107 | #### RB Tree Insertion 108 | 109 | Insertion is one of the crucial operations for an RBT, as this is 110 | when we need to make the appropriate adjustments to keep the tree 111 | balanced. 112 | 113 | A rough algorithm for the insertion process is as follows 114 | 115 | In fact, we can partly divide the insertion process into 2 116 | steps -- standard insertion, followed by possible rotation 117 | / rebalancing. 118 | 119 | __Node Creation / Insertion__ 120 | 121 | 1. Create a new node `N` to hold the inserted piece of data 122 | 2. If the tree is empty, `N` will become the new root node, so 123 | make it black 124 | 3. Otherwise, insert `N` into the tree as with a normal BST and make it red 125 | 126 | __Tree Rotation / Rebalancing__ 127 | 128 | Now, we need to check if the tree has remained balanced. 129 | Possibly we will need to do some re-balancing, and there are, 130 | unfortunately, quite a few possible cases that need to be 131 | handled. 132 | 133 | For this step, we'll often start out looking at a given 134 | Node's "aunt"/"uncle". As in human geneology, the aunt node 135 | of a given node is the sibling of its parent. 136 | 137 | So in this example, we would say 6 is an uncle node of 3: 138 | 139 | ``` 140 | 5 141 | / \ 142 | 4 6 143 | / 144 | 3 145 | ``` 146 | 147 | __Aunt/Uncle Cases__ 148 | 149 | Now, we need to look at how to adjust the tree based on the various 150 | arrangements of our tree when we have inserted a new node. 151 | Again, we have to subdivide these into 2 sub-groups of cases, 152 | depending on the color of the aunt nodes. 153 | 154 | 1. Aunt/Uncle node is Red 155 | 2. Aunt/Uncle node is Black 156 | 157 | #### Aunt/Uncle Node is Red Subcase 158 | 159 | In this case just recoloring can be implemented (no rotations needed!): 160 | 161 | ``` 162 | 5(B) 163 | / \ 164 | 4(R) 6(R) 165 | / 166 | 3(R) 167 | ``` 168 | Property 4 will be violated sometimes a new node is inserted on the tree. 169 | If the Aunt (Uncle) is red we can implement recoloring. Parent, Aunt and 170 | Grandparent should each inverse their colors. 171 | 172 | ``` 173 | 5(R) 174 | / \ 175 | 4(B) 6(B) 176 | / 177 | 3(R) 178 | ``` 179 | Depending on whether this is a subtree or the root it could violate property 2! 180 | When it is the root feel free to recolor it to black. This will not violate 181 | property 5. 182 | 183 | 184 | #### Aunt/Uncle Node is Black Subcases 185 | 186 | In these cases, we need to 187 | consider the tree from a perspective of 3 "generations" at a time: 188 | the newly inserted node, `N`, the Parent / Uncle generation, and the 189 | Grandparent generation. 190 | 191 | For this generation configuration, we will see that we actually have 192 | 2 possible positions for our new node (also duplicated on the opposite side): 193 | 194 | **Case One (aka Left-Left - `N` is left child of left child)** 195 | 196 | ``` 197 | G(B) 198 | / \ 199 | P(R) NIL(B) 200 | / 201 | N(R) 202 | ``` 203 | NIL leaf and a node colored black are the same logic 204 | 205 | **Case Two (aka Left-Right - `N` is right child of left child)** 206 | 207 | ``` 208 | G(B) 209 | / \ 210 | P(R) NIL(B) 211 | \ 212 | N(R) 213 | ``` 214 | NIL leaf and a node colored black are the same logic 215 | 216 | 217 | ####Solutions 218 | 219 | **Solution One (aka Left-Left - `N` is left child of left child)** 220 | 221 | ``` 222 | Initial insertion 223 | 224 | G(B) 225 | / \ 226 | P(R) NIL(B) 227 | / 228 | N(R) 229 | 230 | Rotate G(B) right 231 | 232 | P(R) 233 | / \ 234 | N(R) G(B) 235 | \ 236 | NIL(B) 237 | 238 | Recolor 239 | 240 | P(B) 241 | / \ 242 | N(R) G(R) 243 | \ 244 | NIL(B) 245 | 246 | ``` 247 | 248 | **Solution Two (aka Left-Right - `N` is right child of left child)** 249 | 250 | ``` 251 | Initial insertion 252 | 253 | G(B) 254 | / \ 255 | P(R) NIL(B) 256 | \ 257 | N(R) 258 | 259 | Rotate P(R) left 260 | 261 | G(B) 262 | / \ 263 | N(R) NIL(B) 264 | / 265 | P(R) 266 | 267 | 268 | Rotate G(B) right 269 | 270 | N(R) 271 | / \ 272 | P(R) G(B) 273 | \ 274 | NIL(B) 275 | 276 | Recolor 277 | 278 | N(B) 279 | / \ 280 | P(R) G(R) 281 | \ 282 | NIL(B) 283 | 284 | ``` 285 | 286 | __Implementation Tips and Tricks__ 287 | 288 | 1. After case one or case two no properties should be violated 289 | 2. Recolor can recurse its way up the tree (to a rotate if needed) 290 | 3. BST functionality is useful to build before building RBT functionality 291 | 292 | 293 | Insertion cases writeup: http://www.geeksforgeeks.org/red-black-tree-set-2-insert/ 294 | 295 | Insertion cases video: https://www.youtube.com/watch?v=g9SaX0yeneU 296 | 297 | Better visual representation of rotations: http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-introduction-to-algorithms-sma-5503-fall-2005/video-lectures/lecture-10-red-black-trees-rotations-insertions-deletions/lec10.pdf 298 | -------------------------------------------------------------------------------- /resources/number_generator.rb: -------------------------------------------------------------------------------- 1 | filename = "sample_numbers.txt" 2 | file = File.open("./resources/" + filename, "w") 3 | quantity = 1000000 4 | quantity.times do |i| 5 | if i % 100 == 0 6 | puts "#{quantity - i} left" 7 | end 8 | file.write(rand(quantity/2).to_s + "\n") 9 | end -------------------------------------------------------------------------------- /sha_1/README.markdown: -------------------------------------------------------------------------------- 1 | # SHA-1 2 | 3 | ## Hash Function Intro 4 | 5 | ## SHA-1 Resources 6 | 7 | A great writeup of the SHA-1 process with fairly detailed 8 | examples can be found [here](http://m.metamorphosite.com/one-way-hash-encryption-sha1-data-software). 9 | 10 | ## SHA-1 Process 11 | 12 | ### 1. Prep the message 13 | 14 | Before we start on the core of the algorithm, we need 15 | to run through a few preparatory steps. The main body of the 16 | function will be operating on the underlying bits that make up 17 | our (originally textual) message. 18 | 19 | In order to smooth things out, we need to pre-process the message 20 | a bit in order to get it into a compatible format. A rough 21 | outline of the steps looks like this: 22 | 23 | 1. Convert the message into its bit-array (or byte-array) representation 24 | 2. "Pad" the message bits to be congruent with 448 modulo 512 25 | 3. Separate the message from the appended padding by a "1" bit 26 | 4. Append 64 bits representing the original length of the message 27 | in characters (or bytes) 28 | 29 | __Converting the message to bytes__ 30 | 31 | For the remainder of the algorithm, we'll be working with the 32 | underlying bits (or bytes) that make up the text of the message. 33 | To get started, we need to first convert the message into 34 | a sequence of bits. 35 | 36 | There are various ways to handle this, and they will likely vary 37 | depending on how "low level" your language is, and what facilities 38 | it exposes to you for doing low-level bit manipulation. 39 | 40 | For example in C, we might simply allocate a large block of memory 41 | equivalent to the bit-length we'll ultimately need to work with 42 | and start going to town. 43 | 44 | In Java or other JVM languages, we might work with a byte array -- 45 | a sequential data structure for working with a series of bytes, 46 | or 8-bit units. 47 | 48 | Yet another option which, while probably not the fastest, might 49 | make things easier to inspect and reason about, would be to 50 | use your language's built-in String facilities to model the problem 51 | around a series of literal "0" and "1" characters. 52 | 53 | In any case, you'll want to start out by converting your message 54 | character by character into a sequence of bytes. This is pretty 55 | easy for simple Latin text, since the ASCII character encoding is 56 | built around a byte-per-character model. 57 | 58 | Each character in ASCII maps to a predefined byte value, which 59 | we can ultimately encode in our string as a sequence of 8 bits. 60 | See [this table](http://www.asciitable.com/) for a list of the various 61 | encodings. 62 | 63 | In Ruby, for example, we can convert a character to its ASCII value 64 | like so: 65 | 66 | ```ruby 67 | >"pizza".each_byte.to_a 68 | => [112, 105, 122, 122, 97] 69 | ``` 70 | 71 | The `String#ord` method is useful for this as well: 72 | 73 | ```ruby 74 | >"p".ord 75 | => 112 76 | > "pizza".chars.map(&:ord) 77 | => [112, 105, 122, 122, 97] 78 | ``` 79 | 80 | Next we would want to convert these each to an 8-bit binary 81 | number, and combine them all together: 82 | 83 | ```ruby 84 | > "pizza".chars.map(&:ord).map { |i| i.to_s(2) } 85 | ["1110000", "1101001", "1111010", "1111010", "1100001"] 86 | ``` 87 | 88 | And then join them together: 89 | 90 | ```ruby 91 | > "pizza".chars.map(&:ord).map { |i| i.to_s(2) }.join 92 | "pizza".chars.map(&:ord).map { |i| i.to_s(2) }.join 93 | "11100001101001111101011110101100001" 94 | ``` 95 | 96 | Getting close, but if we look closely, we'll see that we 97 | ended up with a 35-bit binary string, as opposed to the expected 98 | 40 bits. 99 | 100 | One tricky issue when working with binary numbers is to make sure 101 | we aren't losing or unintentionally colliding our numbers. 102 | In this case, Ruby omits leading `0`'s when converting a number 103 | to a binary string. 104 | 105 | This is one area where working with something like a byte 106 | array would help us (since a literal byte will preserve the appropriate 107 | 8 bits), but if you want to go down the string route you'll need 108 | to "pad" your bits to preserve leading 0's. 109 | 110 | For now, we can imagine doing something like this: 111 | 112 | ```ruby 113 | >"pizza".chars.map(&:ord).map do |i| 114 | i.to_s(2) 115 | end.map do |i| 116 | "0" + i 117 | end.join.length 118 | => "0111000001101001011110100111101001100001" 119 | ``` 120 | 121 | __Padding the Message__ 122 | 123 | Now that we have some binary representation of our incoming message, 124 | we need to "pad" it. The reason for padding the message is that the 125 | SHA-1 algorithm is designed to work on blocks of 512 bits at a time. 126 | 127 | To facilitate this, we need to massage the message to give it a bit-length 128 | that fits neatly into our block size. 129 | 130 | * Modulo Congruence 131 | * Target 448 congruence 132 | * Leaving room for 64-bit message length 133 | 134 | __Appending Binary Message-Length__ 135 | 136 | Now that we've padded out the message, we're going to finish 137 | out the last block by adding a series of bits representing 138 | the length of the original message. 139 | 140 | The length we're interested in is the original length of the 141 | message in bytes (or characters, in ASCII), and we'll be 142 | expressing it as a 64-bit integer. Hence why it was so important 143 | to pad the message out to congruence with 448 mod 512 -- to leave 144 | room for these 64 message-length bits at the end. 145 | 146 | For this step, take the length of the message, turn it to binary, and 147 | left-pad it to make sure the overall length works out to 64 bits. 148 | 149 | (Alternatively, you can simply make sure your value is cast to a 150 | 64-bit int, for example Java's `long` type) 151 | 152 | #### random notes 153 | 154 | "Add appropriate number of 0 bytes to bring message into congruence with 448 155 | mod 512 (56 mod 64 bytes). Additionally, the first bit of this padding sequence 156 | should be set 1 to separate original message from padding." 157 | 158 | "takes incoming string message through various steps needed to 159 | convert it to a mod-512 byte array with appropriate padding and 160 | meessage length appended" 161 | 162 | ```clojure 163 | ;; extending block 164 | ;; iterate i = 16 - 79 165 | ;; each iteration, take diffs: 166 | ;; i - 3 167 | ;; i - 8 168 | ;; i - 14 169 | ;; i - 16 170 | ;; as we go, we will be adding new words to the end of our 171 | ;; sequence, so e.g. i=17 doesn't exist at the start, but it 172 | ;; will get added adter the first iteration 173 | 174 | 175 | ;; within each loop, take the 4 selected words, 176 | ;; and reduce them via XOR 177 | ;; so first, (XOR (i-3) (i-8)) (i.e. word 13 and word 8) 178 | ;; then (XOR _result (i-14)) 179 | ;; then (XOR _result (i-16)) 180 | ;; something like this: 181 | #(-> (aget block (- i 3)) 182 | (bit-xor (aget block (- i 8))) 183 | (bit-xor (aget block (- i 14))) 184 | (bit-xor (aget block (- i 16)))) 185 | 186 | ;; next, "carry through left bit rotation by a factor of 1" ?!?!? 187 | ;; i.e. remove first digit of left-most word and append it to the end 188 | ;; of the word -- probably some right-shifts and left shifts? 189 | 190 | ;; Finally, after this rotation 191 | ;; this word gets appended to the end of our existing 16 words, 192 | ;; becoming word 17 193 | ;; this sequence repeats until 80 words are generated 194 | ;; from the original 17 195 | ``` 196 | 197 | ;; string "ab" should give 198 | ;; 97,98 binary 199 | ;; + "1" 200 | ;; + diff to make congruent mod 512 201 | ;; + message length in characters padded to 64 bits 202 | ;; or: 203 | ;; "11000011" + "100010" + "1" + ("0" * 443) + (64-bit padded 2) 204 | 205 | 206 | "pads short message to 56 total bytes, leaving 8 bytes in 207 | same 512-bit block for message length. Additionally, leading 208 | padding bit should be 1 to distinguish padding from message" 209 | 210 | 211 | "pads message longer than 512 bits to be congruent with 448 212 | mod 512, leaving 8 bytes for message length in next block" 213 | 214 | "message longer that is greater than 448 mod 512 bits still has to 215 | pad to 448 congruence in next block since there aren't enough bits 216 | left in current block for 64-bit message-length encoding" 217 | 218 | "generates byte-array for encoding message length as 64-bit long" 219 | 220 | "preps message by converting to bytes, adding padding separator bit, 221 | adding congruence padding, and adding message length bits" 222 | 223 | "take a 64-byte chunk consisting of 16 4-byte words 224 | and 'extend' it to generate 80 words from the original 225 | 16" 226 | -------------------------------------------------------------------------------- /topics_wishlist.markdown: -------------------------------------------------------------------------------- 1 | Unsorted list of various technical interview questions/topics culled from the internet. 2 | 3 | ### General 4 | 5 | * Find the most frequent integer in an array 6 | * Find pairs in an integer array whose sum is equal to 10 (bonus: do it in linear time) 7 | * Given 2 integer arrays, determine of the 2nd array is a rotated version of the 1st array. Ex. Original Array A={1,2,3,5,6,7,8} Rotated Array B={5,6,7,8,1,2,3} 8 | * Write fibbonaci iteratively and recursively (bonus: use dynamic programming) 9 | * Find the only element in an array that only occurs once. 10 | * Find the common elements of 2 int arrays 11 | * Implement binary search of a sorted array of integers 12 | * Implement binary search in a rotated array (ex. {5,6,7,8,1,2,3}) 13 | * Use dynamic programming to find the first X prime numbers 14 | * Write a function that prints out the binary form of an int 15 | * Implement parseInt 16 | * Implement squareroot function 17 | * Implement an exponent function (bonus: now try in log(n) time) 18 | * Write a multiply function that multiples 2 integers without using * 19 | * HARD: Given a function rand5() that returns a random int between 0 and 5, implement rand7() 20 | * HARD: Given a 2D array of 1s and 0s, count the number of "islands of 1s" (e.g. groups of connecting 1s) 21 | * Count the number of set bits in a byte/int32 (7 different solutions) 22 | * Implement a function to return a ratio from a double (ie 0.25 -> 1/4). The function will also take a tolerance so if toleran ce is .01 then FindRatio(.24, .01) -> 1/4 23 | 24 | ### Strings 25 | 26 | * Find the first non-repeated character in a String 27 | * Reverse a String iteratively and recursively 28 | * Determine if 2 Strings are anagrams 29 | * Check if String is a palindrome 30 | * Check if a String is composed of all unique characters 31 | * Determine if a String is an int or a double 32 | * HARD: Find the shortest palindrome in a String 33 | * HARD: Print all permutations of a String 34 | * HARD: Given a single-line text String and a maximum width value, write the function 'String justify(String text, int maxWidth)' that formats the input text using full-justification, i.e., extra spaces on each line are equally distributed between the words; the first word on each line is flushed left and the last word on each line is flushed right 35 | * Reverse words in a string (words are separated by one or more spaces). Now do it in-place. By far the most popular string question! 36 | * Remove duplicate chars from a string ("AAA BBB" -> "A B") 37 | * Find the first non-repeating character in a string:("ABCA" -> B ) 38 | 39 | ### Trees 40 | 41 | * Implement a BST with insert and delete functions 42 | * Print a tree using BFS and DFS 43 | * Write a function that determines if a tree is a BST 44 | * Find the smallest element in a BST 45 | * Find the 2nd largest number in a BST 46 | * Given a binary tree which is a sum tree (child nodes add to parent), write an algorithm to determine whether the tree is a valid sum tree 47 | * Find the distance between 2 nodes in a BST and a normal binary tree 48 | * Print the coordinates of every node in a binary tree, where root is 0,0 49 | * Print a tree by levels 50 | * Given a binary tree which is a sum tree, write an algorithm to determine whether the tree is a valid sum tree 51 | * Given a tree, verify that it contains a subtree. 52 | * HARD: Find the max distance between 2 nodes in a BST. 53 | * HARD: Construct a BST given the pre-order and in-order traversal Strings 54 | * Insert 55 | * PrintInOrder 56 | * PrintPreOrder 57 | * PrintPostOrder 58 | * Implement a non-recursive PrintInOrder 59 | 60 | ### Linked Lists 61 | 62 | This is an extremely popular topic. I've had linked lists on every interview. 63 | You must be able to produce simple clean linked list implementations quickly. 64 | 65 | * Implement a linked list (with insert and delete functions) 66 | * singly-linked linked list 67 | * sorted linked list 68 | * circular linked list 69 | * Find the Nth element in a linked list 70 | * Remove the Nth element of a linked list 71 | * Check if a linked list has cycles 72 | * Given a circular linked list, find the node at the beginning of the loop. Example: A-->B-->C --> D-->E -->C, C is the node that begins the loop 73 | * Check whether a link list is a palindrome 74 | * Reverse a linked list iteratively and recursively 75 | * Split a linked list given a pivot value 76 | * Find the middle of a linked list. Now do it while only going through the list once. (same solution as finding cycles) 77 | 78 | ### Sorting 79 | 80 | * Implement bubble sort 81 | * Implement selection sort 82 | * Implement insertion sort 83 | * Implement merge sort 84 | * Implement quick sort 85 | 86 | ### Arrays 87 | 88 | * You are given an array with integers between 1 and 1,000,000. One integer is in the array twice. How can you determine which one? Can you think of a way to do it using little extra memory. 89 | * You are given an array with integers between 1 and 1,000,000. One integer is missing. How can you determine which one? Can you think of a way to do it while iterating through the array only once. Is overflow a problem in the solution? Why not? 90 | * Returns the largest sum of contiguous integers in the array -- Example: if the input is (-10, 2, 3, -2, 0, 5, -15), the largest sum is 8 91 | * Implement Shuffle given an array containing a deck of cards and the number of cards. Now make it O(n). 92 | * Return the sum two largest integers in an array 93 | * Sum n largest integers in an array of integers where every integer is between 0 and 9 94 | 95 | ### Heaps / Stacks / Queues 96 | 97 | * Difference between heap and stack? Write a function to figure out if stack grows up or down. 98 | * Implement a stack with push and pop functions 99 | * Implement a queue with queue and dequeue functions 100 | * Find the minimum element in a stack in O(1) time 101 | * Write a function that sorts a stack (bonus: sort the stack in place without extra memory) 102 | * Implement a binary min heap. Turn it into a binary max heap 103 | * HARD: Implement a queue using 2 stacks 104 | 105 | ### SQL 106 | 107 | TK 108 | -------------------------------------------------------------------------------- /tries/README.md: -------------------------------------------------------------------------------- 1 | ## Tries 2 | 3 | A Trie is a type of branching data structure often used for storing 4 | and querying words within a body of text. The name comes from the idea 5 | of "Re__trie__val" -- a Trie is well suited for rapid retrieval of 6 | a provided search string. Additionally, its organization as a tree 7 | structure allows it to efficiently find all "children" of a given substring, 8 | making it ideally suited for predictive text applications. 9 | 10 | ### Visualizing a Trie 11 | 12 | A Trie can be implemented as an N-ary tree, where the root node 13 | is empty and all of its children represent the first characters 14 | of the various strings contained in the trie. Consider this 15 | very simple example storing the strings "cat", "dog", and "do": 16 | 17 | ``` 18 | Root 19 | c/ \d 20 | node-() node-() 21 | |a |o 22 | node() node-(do) 23 | |t |g 24 | node-(cat) node-(dog) 25 | ``` 26 | 27 | To search this Trie for cat or dog, we start from the root and 28 | check each character in turn to see if it appears along a path 29 | between the root and the leaves. If we tried to search for "catty" 30 | we would run out of nodes along the "cat" path before we found all 31 | of our characters, and thus could determine that "catty" does not 32 | appear. 33 | 34 | Additionally, we can see that some interior "nodes" in the trie serve only as branching or connection 35 | points to valid nodes that exist further down in the structure (such as the "ca") 36 | node. 37 | 38 | Alternatively, some interior nodes also represent valid words in and of 39 | themselves (e.g. the "do" node). Your trie implementation will likely 40 | need some way to distinguish these types of interior nodes. 41 | 42 | ### Branching 43 | 44 | A great thing about Tries is the ability to represent branching of 45 | strings that share common prefixes. Let's add "car" and "dot" to our 46 | Trie above: 47 | 48 | ``` 49 | Root 50 | c/ \d 51 | node node 52 | a| |o 53 | node node————— 54 | t/ \ \ \t 55 | node-cat \r \g node-dot 56 | node-car \ 57 | node-dog 58 | ``` 59 | 60 | Now, when searching, we would encounter branches at the "c-a" or "d-o" 61 | points. Depending which string we are looking for, we could go left or 62 | right. 63 | 64 | ### Node/Word Storage 65 | 66 | In these diagrams we've been representing nodes as containing the word 67 | that they represent, but in a proper Trie structure, nodes don't 68 | actually contain data. Rather, the data a node represents can be 69 | inferred from the path that was used to reach it (so if we reach a node 70 | by walking d-o-g, we know already that the node represents "dog" without 71 | having to label it as such). 72 | 73 | For a node to be a terminating (leaf) node, it must represent the result 74 | of a valid path through the Trie (i.e. a valid word). For intermediate 75 | (interior) nodes that represent valid words (e.g. "d-o" on the way to 76 | "d-o-g"), they will need to be labeled or marked in some way. 77 | 78 | 79 | ### Challenge -- Tries for Text Prediction 80 | 81 | A common real-word use-case for tries is doing word prediction, 82 | such as the auto-completion on your smartphone. 83 | 84 | See if you can complete a trie data structure that will 85 | allow us to fulfill a basic version of this feature. 86 | We'd like to provide an interface along the lines of: 87 | 88 | ``` 89 | t = Trie.new 90 | input_words.each do |w| 91 | t.insert(w) 92 | end 93 | 94 | t.suggest("piz") 95 | => ["pizza", "pizzeria", "pizzicato"] 96 | ``` 97 | --------------------------------------------------------------------------------