├── .prettierrc ├── lessons ├── images │ ├── bst.png │ ├── heap.jpg │ └── radix.png ├── radix-sort.md ├── search.md ├── tries.md ├── intro.md ├── graphs.md ├── bloom-filters.md ├── tree-traversal.md ├── pathfinding.md ├── heap-sort.md └── maze-generation.md ├── gatsby-browser.js ├── gatsby-ssr.js ├── .gitignore ├── README.md ├── src ├── pages │ ├── 404.js │ ├── page-2.js │ ├── index.css │ └── index.js ├── components │ ├── TOCCard.css │ └── TOCCard.js ├── templates │ └── blogTemplate.js └── layouts │ ├── index.js │ └── index.css ├── LICENSE ├── gatsby-config.js ├── gatsby-node.js └── package.json /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /lessons/images/bst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkh44/four-semesters-of-cs-part-two/master/lessons/images/bst.png -------------------------------------------------------------------------------- /lessons/images/heap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkh44/four-semesters-of-cs-part-two/master/lessons/images/heap.jpg -------------------------------------------------------------------------------- /lessons/images/radix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkh44/four-semesters-of-cs-part-two/master/lessons/images/radix.png -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's Browser APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/browser-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it -------------------------------------------------------------------------------- /gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's SSR (Server Side Rendering) APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/ssr-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project dependencies 2 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 3 | node_modules 4 | .cache/ 5 | # Build directory 6 | public/ 7 | .DS_Store 8 | yarn-error.log 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Under Construction 2 | 3 | Under active development. Feel free to peruse but nothing is guaranteed to be correct, correctly cited, or anything of the sort. Catch this workshop at ForwardJS 2018 or in May on FrontendMasters.com 4 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const NotFoundPage = () => ( 4 |
5 |

NOT FOUND

6 |

You just hit a route that doesn't exist... the sadness.

7 |
8 | ) 9 | 10 | export default NotFoundPage 11 | -------------------------------------------------------------------------------- /src/pages/page-2.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'gatsby-link' 3 | 4 | const SecondPage = () => ( 5 |
6 |

Hi from the second page

7 |

Welcome to page 2

8 | Go back to the homepage 9 |
10 | ) 11 | 12 | export default SecondPage 13 | -------------------------------------------------------------------------------- /src/components/TOCCard.css: -------------------------------------------------------------------------------- 1 | .lesson { 2 | border: 1px solid #ccc; 3 | border-radius: 8px; 4 | width: 100%; 5 | margin: 0; 6 | overflow: hidden; 7 | background-color: white; 8 | } 9 | 10 | .lesson-title { 11 | font-size: 20px; 12 | padding: 15px 30px; 13 | } 14 | 15 | .lesson-content { 16 | padding: 0 15px 15px 15px; 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Brian Holt 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /src/components/TOCCard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "gatsby-link"; 3 | 4 | import "./TOCCard.css"; 5 | 6 | const LessonCard = ({ content, title }) => ( 7 |
8 |

{title}

9 |
10 |
    11 | {content.map(lesson => ( 12 |
  1. 13 | 14 | {lesson.node.frontmatter.title} 15 | 16 |
  2. 17 | ))} 18 |
19 |
20 |
21 | ); 22 | 23 | export default LessonCard; 24 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: `4 Semesters of Computer Science Part 2` 4 | }, 5 | pathPrefix: "/four-semesters-of-cs-part-two", 6 | plugins: [ 7 | { 8 | resolve: `gatsby-source-filesystem`, 9 | options: { 10 | path: `${__dirname}/lessons`, 11 | name: "markdown-pages" 12 | } 13 | }, 14 | `gatsby-plugin-react-helmet`, 15 | { 16 | resolve: `gatsby-transformer-remark`, 17 | options: { 18 | plugins: [ 19 | { 20 | resolve: `gatsby-remark-images`, 21 | maxWidth: 800, 22 | linkImagesToOriginal: true, 23 | sizeByPixelDensity: false 24 | } 25 | ] 26 | } 27 | } 28 | ] 29 | }; 30 | -------------------------------------------------------------------------------- /src/pages/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #eee; 3 | } 4 | 5 | .index { 6 | width: 97%; 7 | max-width: 750px; 8 | margin: 0 auto; 9 | margin-top: 20px; 10 | } 11 | 12 | .index .jumbotron { 13 | } 14 | 15 | .example-table { 16 | border-collapse: separate; 17 | } 18 | 19 | .example-table td { 20 | border: 1px solid black; 21 | width: 20px; 22 | height: 20px; 23 | } 24 | 25 | .example-table .current { 26 | background-color: #fcc; 27 | } 28 | 29 | .example-table .n { 30 | border-top-color: transparent; 31 | } 32 | 33 | .example-table .s { 34 | border-bottom-color: transparent; 35 | } 36 | 37 | .example-table .e { 38 | border-right-color: transparent; 39 | } 40 | 41 | .example-table .w { 42 | border-left-color: transparent; 43 | } 44 | -------------------------------------------------------------------------------- /src/templates/blogTemplate.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Template({ data }) { 4 | const { markdownRemark } = data; // data.markdownRemark holds our post data 5 | const { frontmatter, html } = markdownRemark; 6 | return ( 7 |
8 |
9 |

{frontmatter.title}

10 |

{frontmatter.date}

11 |
15 |
16 |
17 | ); 18 | } 19 | 20 | export const pageQuery = graphql` 21 | query LessonByPath($path: String!) { 22 | markdownRemark(frontmatter: { path: { eq: $path } }) { 23 | html 24 | frontmatter { 25 | path 26 | title 27 | } 28 | } 29 | } 30 | `; 31 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Card from "../components/TOCCard"; 3 | import Link from "gatsby-link"; 4 | 5 | import "./index.css"; 6 | 7 | const IndexPage = props => 8 | console.log(props) || ( 9 |
10 |
11 |

4 Semesters of CS in 5 Hours

12 |

Part II

13 |
14 | 15 | 19 |
20 | ); 21 | 22 | export const pageQuery = graphql` 23 | query HomepageTOC { 24 | allMarkdownRemark(sort: { order: ASC, fields: [frontmatter___order] }) { 25 | edges { 26 | node { 27 | id 28 | frontmatter { 29 | order 30 | path 31 | title 32 | } 33 | } 34 | } 35 | } 36 | } 37 | `; 38 | 39 | export default IndexPage; 40 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | exports.createPages = ({ boundActionCreators, graphql }) => { 4 | const { createPage } = boundActionCreators; 5 | 6 | const blogPostTemplate = path.resolve(`src/templates/blogTemplate.js`); 7 | 8 | return graphql(` 9 | { 10 | allMarkdownRemark( 11 | sort: { order: DESC, fields: [frontmatter___order] } 12 | limit: 1000 13 | ) { 14 | edges { 15 | node { 16 | excerpt(pruneLength: 250) 17 | html 18 | id 19 | frontmatter { 20 | order 21 | path 22 | title 23 | } 24 | } 25 | } 26 | } 27 | } 28 | `).then(result => { 29 | console.log(result); 30 | if (result.errors) { 31 | return Promise.reject(result.errors); 32 | } 33 | 34 | result.data.allMarkdownRemark.edges.forEach(({ node }) => { 35 | createPage({ 36 | path: node.frontmatter.path, 37 | component: blogPostTemplate 38 | }); 39 | }); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /src/layouts/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Link from "gatsby-link"; 4 | import Helmet from "react-helmet"; 5 | 6 | import "bootstrap/dist/css/bootstrap.css"; 7 | import "./index.css"; 8 | 9 | const Header = () => ( 10 |
11 | 12 | 4 Semesters of Computer Science in 5 Hours, Part II 13 | 14 |
15 | ); 16 | 17 | const TemplateWrapper = ({ children }) => ( 18 |
19 | 34 |
35 |
{children()}
36 |
37 | ); 38 | 39 | TemplateWrapper.propTypes = { 40 | children: PropTypes.func 41 | }; 42 | 43 | export default TemplateWrapper; 44 | -------------------------------------------------------------------------------- /src/layouts/index.css: -------------------------------------------------------------------------------- 1 | .gradient { 2 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#f29d9b+0,efdbb6+51,cbe8f0+100 */ 3 | background: #f29d9b; /* Old browsers */ 4 | background: -moz-linear-gradient( 5 | 45deg, 6 | #f29d9b 0%, 7 | #efdbb6 51%, 8 | #cbe8f0 100% 9 | ); /* FF3.6-15 */ 10 | background: -webkit-linear-gradient( 11 | 45deg, 12 | #f29d9b 0%, 13 | #efdbb6 51%, 14 | #cbe8f0 100% 15 | ); /* Chrome10-25,Safari5.1-6 */ 16 | background: linear-gradient( 17 | 45deg, 18 | #f29d9b 0%, 19 | #efdbb6 51%, 20 | #cbe8f0 100% 21 | ); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ 22 | filter: progid:DXImageTransform.Microsoft.gradient( 23 | startColorstr="#f29d9b", 24 | endColorstr="#cbe8f0", 25 | GradientType=1 26 | ); /* IE6-9 fallback on horizontal gradient */ 27 | } 28 | 29 | .navbar { 30 | border-bottom: 1px solid #ccc; 31 | position: sticky; 32 | width: 100%; 33 | top: 0; 34 | z-index: 10; 35 | } 36 | 37 | .navbar-brand { 38 | text-transform: uppercase; 39 | } 40 | 41 | .blog-post { 42 | margin: 15px; 43 | padding: 15px; 44 | background-color: #fff; 45 | border-radius: 8px; 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "four-semesters-of-cs-part-two", 3 | "description": "Gatsby default starter", 4 | "version": "1.0.0", 5 | "author": "Brian Holt ", 6 | "private": true, 7 | "dependencies": { 8 | "bootstrap": "^4.0.0-beta.3", 9 | "gatsby": "^1.9.145", 10 | "gatsby-link": "^1.6.32", 11 | "gatsby-plugin-react-helmet": "^1.0.8", 12 | "gatsby-remark-images": "^1.5.36", 13 | "gatsby-source-filesystem": "^1.5.11", 14 | "gatsby-transformer-remark": "^1.7.28", 15 | "gh-pages": "^1.1.0" 16 | }, 17 | "keywords": ["gatsby"], 18 | "license": "Apache 2.0", 19 | "main": "n/a", 20 | "scripts": { 21 | "build": "gatsby build --prefix-paths", 22 | "deploy": "npm -s run build && gh-pages -d ./public -b gh-pages", 23 | "develop": "gatsby develop", 24 | "format": 25 | "prettier --trailing-comma es5 --no-semi --single-quote --write \"src/**/*.js\"", 26 | "test": "echo \"Error: no test specified\" && exit 1" 27 | }, 28 | "devDependencies": { 29 | "eslint": "^4.14.0", 30 | "prettier": "^1.9.2" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/btholt/four-semesters-of-cs-part-two.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/btholt/four-semesters-of-cs-part-two/issues" 38 | }, 39 | "homepage": "https://github.com/btholt/four-semesters-of-cs-part-two#readme" 40 | } 41 | -------------------------------------------------------------------------------- /lessons/radix-sort.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Radix Sort" 3 | path: "/radix-sort" 4 | order: 9 5 | --- 6 | 7 | Radix sorting enters us into a new frontier of sorting that we haven't talked about: non-comparison based sorting. Up to this point _all_ of the sorts we've talked about in both courses have been comparison based sorts. That is to say, we decide the order of the numbers based on asking the question is this element bigger than that one over-and-over again until numbers are in order and the rest of the algorithm is just optimizing how often we ask that question. The big O of these comparison based algorithms cannot be any faster `n log n` so in order to get beyond that, we have to change what we're doing. We have to sort based on other criteria. 8 | 9 | Enter non-comparison based algorithms. There are a few variations but we're going to focus on radix sort as it's one of the more useful algorithms. The basic idea is we're to enqueue each number in different queues based on what the last digit in the digit of the number (the "ones" place.) Once we do that, we'll dequeue each queue in order back into the original array. If you think about it, it'll everything will be sorted _up to the the ones place_. So your array will look like `[10, 1, 52, 102, 33, 45, 6, 18, 9]` or something like that. Notice all the ones places are in ascending order; just nothing else is. After we've done this, we'll repeate the process again but with the tens place. With the former example, it'd look like `[1, 102, 6, 9, 10, 18, 33, 45, 52]`. Lastly we'll do the process on the hundreds place (which in this example is just going to be the 0 bucket and the 1 bucket) and end up with `[1, 6, 9, 10, 18, 33, 45, 52, 102]`. That's it! It's sorted. 10 | 11 | ![Radix Sort Diagram](./images/radix.png) 12 | Courtesy of [Wikimedia](https://commons.wikimedia.org/wiki/File:Dsa_radix_sort.png) 13 | 14 | That's the whole algorithm. Most variations of radix sort have micro optimizations that speed up the algorithm but the overall theme is what you've seen here. Often you'll see radix is done using a binary number system instead of a decimal one (like we've done) but this is best for simplicity. So there you go! Go give it a shot. 15 | 16 | * [Exercise][exercise] 17 | * [Completed][completed] 18 | 19 | [exercise]: https://codepen.io/btholt/pen/eVWyPd?editors=0010 20 | [completed]: https://codepen.io/btholt/pen/VQbMGJ?editors=0010 21 | -------------------------------------------------------------------------------- /lessons/search.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Searching for an Element in an Array" 3 | path: "/search" 4 | order: 7 5 | --- 6 | 7 | This is a bit of a glaring omission from part one but we're going to fix it now: searching! This very similar to sorting, as you'll quickly figure out. Search is the act of looking for a particular element in an array. 8 | 9 | There are essentially two common ways of doing search: linear search and binary search. The former is the simplest code and really only useful if the list you're searching on is not sorted in any way. You just go through from 0 to the length of the array and ask "is the is the element I'm looking for?" No frills here. Its complexity is O(n). 10 | 11 | Binary search is a bit more interesting. It only works if the array is already sorted. To explain it, let's take the example of how you find a name in a telephone book (if you even know what that is anymore!) A telephone book is a sorted list of names. You'll open the book more or less to the middle (or say you do, for argument's sake.) From there, if the name you're looking for is smaller/earlier in the alphabet, you'll go halfway to the smaller/earlier side of the book, and so-on-and-so-forth, keeping going halfway until eventually you land on the name you're looking for. Let's see how that works in practice. 12 | 13 | ``` 14 | [0, 5, 10, 12, 15, 19, 21, 22, 24, 30] 15 | 16 | search for 12 17 | 18 | start in the middle, is 19 === 12? no, smaller, go left 19 | 20 | look in the middle of the smaller half, 10 === 12? no, larger, go right 21 | 22 | look in the middle of the larger half (which is now just one number), is 12 === 12? yes, found element 23 | ``` 24 | 25 | This turns out to work quickly even on extremely large datasets. Its complexity is O(log n). Let's implement them! 26 | 27 | * [Exercise][exercise] 28 | * [Completed][completed] 29 | 30 | If you'd like to see how binary vs linear search performs, [check out this JSPef benchmark][perf]. I threw in Lodash's find function as well to show you something: you need to understand the tools you're using. While Lodash's find function works and works well, it's not made for the situation we're throwing at it: finding an element in a large sorted array. Because of this, it's not going to perform well in this situation. However if you're only operating occasionally on a small array, it doesn't matter if your algorithm is O(log n) or O(n), just use whatever find method that's easiest to use. Just make sure you're making the trade-offs consciously! 31 | 32 | [exercise]: https://codepen.io/btholt/pen/wydwQg?editors=0010 33 | [completed]: https://codepen.io/btholt/pen/ZrKzea?editors=0010 34 | [perf]: https://jsperf.com/linear-vs-binary-search-4cs 35 | -------------------------------------------------------------------------------- /lessons/tries.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tries" 3 | path: "/tries" 4 | order: 6 5 | --- 6 | 7 | First of all, let's make sure you're saying this word correctly. The word "trie" is a play on the fact that this particular data structure makes it easy to re**trie**ve data, so trie is said just like it is there, tree. However, because tries and trees are often discussed together, you'll here people call them "tries" (as in the plural try) or try to disambiguate them in other methods. Good luck. 8 | 9 | Okay, so what is a trie? It's a tree. LOL. It's a tree that's optimized for searching by prefix. The classic example of what you would use this for is autocomplete: you know when you type in "San" and it offers suggestions of what to finish with like "Francisco", "Diego", or "Jose". Tries are really useful for that. 10 | 11 | So let's examine what looks like. A trie starts with a root node that doesn't represent anything (often it's given the `value` of `''` (empty string.) It has a bunch of child nodes (as many are necessary) that represent one letter, the first letter of all the words added to the data structure. Each of those letter-nodes will have children nodes for all the _second_ letters of the words that are represented in the data structure. So on and so forth, there will be a chain of nodes that represent each letter in the data structure. 12 | 13 | Why is this useful? If a user types `bo` in the text input, you can go through your data structure, find the `o` node in that chain, and then all you have to do is a depth-first traversal of the children nodes to for a list of autocomplete suggestions. 14 | 15 | ``` 16 | a – [various children] 17 | / 18 | b – o – s – t – o – n 19 | \ 20 | i – s – e 21 | ``` 22 | 23 | So based on this, you'd get suggestions of "Boston" and "Boise". 24 | 25 | Since some words are contained within chains of others (for example, there are two separate cities, one called "Salina" and one called "Salinas" or "Sandy" and "Sandy Springs".) You'll often have a flag in there that signifies the node you're on is a complete word so you can just add it to the list and then keep going on the children. 26 | 27 | There are more complicated things you can do with tries as well that we won't explore here. You can have autocompletes for mid-word completions (if I type "francisco" it won't autocomplete "san francisco" at the moment.) You can add weights to certain edges/children so they're suggested first (so San Francisco comes before San Mateo.) But this exercise, assume all words weighted equally. 28 | 29 | You'll also represent a space in the tree as its own node so when you type `san` it autocompletes San Francisco instead of Santa Fe. In other words, no characters are given special treatment. That can be unintuitve. 30 | 31 | Go give it a shot! 32 | 33 | * [Exercise][exercise] 34 | * [Completed][completed] 35 | 36 | [completed]: https://codepen.io/btholt/pen/PQGVxR?editors=0010 37 | [exercise]: https://codepen.io/btholt/pen/RQobyV?editors=0010 38 | -------------------------------------------------------------------------------- /lessons/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction" 3 | path: "/intro" 4 | order: 0 5 | --- 6 | 7 | Welcome back to the course 4 Semesters of Computer Science in 5 Hours! This is part two in the series. While these courses are not required to take in order and certainly you can take them out of order, I'd suggest you give a once-over to the [first one in the series][first] as well. These concepts I would judge to be a bit more advanced than what was contained in the first and it'd be good to jog those through your memory. 8 | 9 | So why are we here? For me, I'm a college drop-out. As soon as I realized I could have an employer pay me to learn instead of paying a university to do it, I was out of there! The unfortunate part of that is there are some things that your job will likely never teach you. A job is (normally, yours may be different and that's cool if so) typically require you to write features and ship bug fixes; in other words, you're not typically given time to learn about NP-Complete problems or to diagram how a heap sort is going to work. The issue that arises given that is that when it does come time to have complex operations that make use of these loftier CS concepts, they're just not in your toolbox and that sucks. 10 | 11 | Furthermore, these concepts are often to the gate keepers to getting jobs in our field, for good or for bad. Developers are often made interviewers without any training to accurately assess if a candidate can perform a position or not and they're left to their own devices. Given this, developers resort to one of a few things: that thing they learned that they found impressive, something they learned in college they found difficult, or just Googling what everyone else is doing. None of these are ideal or even very useful. Nonetheless, these are our gate keepers in today's world and I want to help you pay the toll and enter into the job you want. 12 | 13 | This course is in JavaScript. This is because I'm writing it and I know JavaScript best primarily. It's secondarily because many front end devs want to learn more computer science but don't want to learn Lisp, C++, Python, or something else to do so. This aims to fill that gap. If you have a basic understanding on JS then you are set. If you don't I _highly_ suggest Kyle Simpson's [books][books] and [courses][courses]. 14 | 15 | Lastly, much of what I'm telling you is from my own education and largely thanks to the book [Cormen's Algorithms][cormen]. This book is pricy and really dry but if you understand everything in it, you'll go really far. At some point I'd suggest taking the month(s) to tackle it. 16 | 17 | Thank you for reading/watching the course! Please open pull requests for anything wrong you see, star the repo and hit me up on Twitter with your thoughts! 18 | 19 | [first]: https://btholt.github.io/four-semesters-of-cs/ 20 | [books]: https://smile.amazon.com/You-Dont-Know-Js-Book/dp/B01AY9P0P6 21 | [courses]: https://frontendmasters.com/courses/javascript-basics/ 22 | [cormen]: https://smile.amazon.com/Introduction-Algorithms-3rd-MIT-Press/dp/0262033844 23 | [twitter]: https://www.twitter.com/holtbt 24 | -------------------------------------------------------------------------------- /lessons/graphs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Graphs" 3 | path: "/graphs" 4 | order: 4 5 | --- 6 | 7 | Let's chat about a datastructure that is extremely useful, you probably interact with many on a daily basis, but you may not use them in your code on a day-to-day: graphs. Graphs are all about modeling relations between many items. For example, think of Facebook's Social Graph. I'm friends with you and you're friends with me. But you're also friends with six hundred other people which is about five hundred fifty too many. Those people in turn also have too friends. But many of my friends are your friends, so the connections aren't linear, they're … well, they're graph-like. 8 | 9 | In the Facebook example, each person would be a node. A node represents some entity, much like a row in an SQL database. Every so-called "friendship" would be called an edge. An edge represents some connection between two items. In this case, our Facebook friendship is bidirectional: if I'm friends with you then you're friends with me. Twitter would be an example of a unidirectional edge: just because I follow you doesn't mean you follow me. 10 | 11 | Graphs are **everywhere**. Your various social networks, your Internet of Things devices that have relationships with each other, your neural-networks machine-learning libraries, everywhere. As we continue to model more-and-more of the natural world in virtual space graphs become ever-more important since relationships between things and beings exist all around us. 12 | 13 | In your example, you'll be tracing a made-up social network. In this social network, you're going to be trying to find the most common job title amongst the people you follow. At the first level, it's easy, you just look at your immediate connections and loop over them and see what their jobs are. However, if we go further than that, we have to look at connections' connections! Hopefully this sounds vaguely familiar … it sort of sounds like trees. Or pathfinding. Aw 💩 we have to use those same algorithms again!! So let's visualize a basic graph 14 | 15 | ``` 16 | Bob — Sally 17 | / \ 18 | me Alice 19 | \ / 20 | Maria 21 | ``` 22 | 23 | In this case, let's say I'm looking for what the job titles are for the people within my second degree network: my connections and their connections, or no more than two edges away from me. If hop first to Bob, then I'll count Sally and Alice in his connections. If I hop to Maria, then I'll count Alice in her connections … for the second time. This is where graphs differ a bit: since there's no clear parent-child relationship you need to be aware there will be cycles and other more difficult patterns to deal with. In this case, I'll just keep track of users I've crawled before and not add them to my total the second time. 24 | 25 | So traversing algorithm fits best here? We're analysizing everything in a limited depth of a sub-tree and breadth-first is well equipped to do that. Instead of letting breadth-first traversal run to completion, we'll just limit how many times that outer loop runs, effectively limiting how many levels down it goes, or how many degrees of separation! 26 | 27 | So let's give the exercise a try, see how you do! 28 | 29 | * [Exercise][exercise] 30 | * [Completed][completed] 31 | 32 | [exercise]: https://codepen.io/btholt/pen/KZYdKW?editors=0010 33 | [completed]: https://codepen.io/btholt/pen/qpvdJJ?editors=0010 34 | -------------------------------------------------------------------------------- /lessons/bloom-filters.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Bloom Filters" 3 | path: "/bloom-filters" 4 | order: 1 5 | --- 6 | 7 | Bloom filters are an interesting data structure which are designed to tell you quickly and efficiently if an item is in a set. If you need a reminder of what a set is, see the previous course. In exchange for being really fast and memory efficient, bloom filters trade off the fact that it can't tell you definitely if an item is in the set; it can only tell you definitely that item is **not** in the set. Stated differently, bloom filters have a false positive rate but do not have false negatives. 8 | 9 | Why is that useful? Sometimes you don't care about false positives, you just want to make sure something is _not_ in the set. [Medium][medium] has a great article on what they use bloom filters for: they use them to filter out artciles they don't want to show you in their recommendations, whether those are items you've read before or things they've recommended too many times. What about that false positive rate? Well, they'll just filter out something they could have shown you and then show you something they definitely can show you. It's an acceptable trade off. 10 | 11 | Check out [Wikipedia][wiki] for some more examples of applications. 12 | 13 | So let's talk about how they work. 14 | 15 | Imagine you have an array with ten elements in it. Every element in the array is a `0` bit. This is an empty bloom filter. Now we want to add `"Brian"` to the array. I'm going to run `"Brian"` through three different hashing functions (see previous course for explanation on hashing functions.) Each hashing function should be _fast_ and definitely not cryptographically secure (which are by-design slow.) This means _don't_ use SHA or MD5. They should also have a uniform distribution as much as possible. 16 | 17 | Okay, so I run my string through three different hashing functions and they give me `2`, `5`, and `8` (I'm making up the numbers; we won't implement hashing functions so it doesn't really matter how they work.) I'll flip all those bits at those indexes so my new array is `[0, 0, 1, 0, 0, 1, 0, 0, 1, 0]`. 18 | 19 | After doing this, I'll check to see if `"Sarah"` is in the array. After running through the hashing function, they give `2`, `2`, and `4`. `2` is flipped but `4` is not, so I can definitively say that `"Sarah"` is not in the data set. 20 | 21 | So let's add one more item to the array, `"Simona"`. The indexes we get back `0`, `4`, and `5`. So now our array is `[1, 0, 1, 0, 1, 1, 0, 0, 1, 0]`. We flip both 0 and 4 indexes and 5 was already flipped so we do nothing to it. Now what happens if we check `"Sarah"` again? This time we'll get a false positive that `"Sarah"` is in the dataset. That's why the two answers you can get back from the question "Is X in the bloom filter" are no and maybe. 22 | 23 | That's it! 24 | 25 | So when you add more items to a bloom filter, you'll increase your false positive rate. You can mitigate this by having a larger array, but you'll be trading off on having a larger memory footprint. You can also have more or less hashing functions, trading off on how quickly memory will fill up versus false positive rates. 26 | 27 | So let's build one! 28 | 29 | * [Exercise][exercise] 30 | * [Completed][completed] 31 | 32 | [exercise]: https://codepen.io/btholt/pen/JMebQd?editors=0010 33 | [completed]: https://codepen.io/btholt/pen/LeXRwq?editors=0010 34 | [medium]: https://blog.medium.com/what-are-bloom-filters-1ec2a50c68ff 35 | [wiki]: https://en.wikipedia.org/wiki/Bloom_filter#Examples 36 | [by-example]: http://llimllib.github.io/bloomfilter-tutorial/ 37 | -------------------------------------------------------------------------------- /lessons/tree-traversal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tree Traversals" 3 | path: "/tree-traversals" 4 | order: 2 5 | --- 6 | 7 | Trees are an essential part of storing data, or at computer scientists like to refer them as, data structures. Among their benefits is that they're optimized to be searchable. Occasionally you need to serialize the entire tree into a flat data structure. Today we'll show you how to do that. 8 | 9 | ![BST](./images/bst.png) 10 | 11 | The picture tree is a valid binary search tree (BST.) If you need a review of what a BST is, checkout [the previous course][bst]. We're going to show you four different ways serialization of this BST: three variations of depth-first traversal and one that is breadth-first traversal. 12 | 13 | ## Depth-first Traversal 14 | 15 | Let's start with one variant depth-first traversals: pre-order traversal. The basic gist is that for each of the nodes, you process the node (in our case, save it to an array since we're serializing this tree,) then process the left subtree and then the right tree. Let's write out that works. 16 | 17 | Given the above tree: 18 | 19 | 1. Call our method (let's call it preorderTraverse) on the root node, 8. 20 | 1. Add 8 to our array. 21 | 1. Call preorderTraverse on the left child, 3. 22 | 1. Add 3 to our array. 23 | 1. Call preorderTraverse on the left child, 1. 24 | 1. Add 1 to our array. 25 | 1. Has no children, returns. 26 | 1. Going back up the tree, we'll call preorderTraverse on 6. 27 | 1. Add 6 to our array. 28 | 1. Call preorderTraverse on the left child, 4. 29 | 1. Add 4 to our array. 30 | 1. No children, returns. 31 | 1. Going back up the tree, we'll call preorderTraverse on 7. 32 | 1. Add 7 to the array. 33 | 1. So on and so forth. 34 | 35 | We end up with the array of `[8, 3, 1, 6, 4, 7, 10, 14, 13]`. This is called preorder traversal. 36 | 37 | The other variants are quite similar; the only thing we do is change the order. When I say "process the node," I mean you do whatever operation you're going to do: add it to an array, copy the node, or whatever that may be. 38 | 39 | In preorder traversal, you process the node, then recursively call the method on the left subtree and then the right subtree. 40 | 41 | In inorder traversal, you first recursively call the method on the left tree, then process the node, and then call the method on the right tree. 42 | 43 | Postorder traversal, as you have guessed, you recursively call the method on the left subtree, then the left subtree, then you process the node. The results of these are as follows: 44 | 45 | ``` 46 | // preorder 47 | [8, 3, 1, 6, 4, 7, 10, 14, 13] 48 | 49 | // inorder 50 | [1, 3, 5, 6, 7, 8, 10, 13, 14] 51 | 52 | // postorder 53 | [1, 4, 7, 6, 3, 13, 14, 10, 8] 54 | ``` 55 | 56 | As you can see, it depends on what you're doing on which of these you use. For a sorted list out of a BST, you'd want to use inorder. If you're making a deep copy of a tree, preorder traversal is super useful since you'd copy a node, and then add its left child and then its right tree. Postorder would be useful if you're deleting a tree since you'd process the left tree, then the right, and only after the children had been deleted would you delete the node you're working on. 57 | 58 | So let's go give this a shot! 59 | 60 | * [Exercise][cp-depth-first] 61 | * [Completed Pen][cp-depth-first-answer] 62 | 63 | ## Breadth-first Traversal 64 | 65 | Now that you've done depth-first, let's tackle breadth-first. Breadth-first isn't recursive processing of subtrees like depth-first. Instead we want to process one layer at a time. Using the tree above, we want the resulting order to `[8, 3, 10, 1, 6, 14, 4, 7, 13]`. In other words, we start at the root, and slowly make our way "down" the tree. 66 | 67 | The way we accomplish this is using our old friend, the queue. If you want to review what queues are, check out [the previous course's section on them here][queue]. The short of it is that queues are first-in first-out. 68 | 69 | What we're going to do is process the node, then add the left child to the queue and then add the right child to the queue. After that, we'll just dequeue an item off of the queue and call our function recursively on that node. You keep going until there's no items left in the queue. 70 | 71 | Let's do the exercise! This can be solved recursively or iteratively, with the iterative result being the preferred of the two. 72 | 73 | * [Exercise][cp-breadth-first] 74 | * [Completed Pen][cp-breadth-first-answer] 75 | 76 | Breadth-first traversals are useful for many things and we'll be using the algorithm when we do path-finding, but the gist of when you use them is that you know the answer for what you're looking for is "closer" to the root node as opposed to far away when you would use depth-first. Again, it's all trade-offs. 77 | 78 | [bst]: https://btholt.github.io/four-semesters-of-cs/ 79 | [queue]: https://btholt.github.io/four-semesters-of-cs/ 80 | [cp-depth-first-answer]: https://codepen.io/btholt/pen/rprwwm?editors=0010 81 | [cp-depth-first]: https://codepen.io/btholt/pen/jYpwQV?editors=0010 82 | [cp-breadth-first-answer]: https://codepen.io/btholt/pen/WdgRrB?editors=0010 83 | [cp-breadth-first]: https://codepen.io/btholt/pen/wpEgdb?editors=0010 84 | -------------------------------------------------------------------------------- /lessons/pathfinding.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Pathfinding" 3 | path: "/pathfinding" 4 | order: 3 5 | --- 6 | 7 | Let's take what we've learned and go a step further. Imagine you have a 6 by 6 grid. You have point A at [1,1] and point B at [2,6]. ([0, 0] is top left.) 8 | 9 | ``` 10 | • • • • • • 11 | • A • • • • 12 | • • • • • • 13 | • • • • • • 14 | • • • • • • 15 | • • B • • • 16 | ``` 17 | 18 | How would you write code that finds the _shortest_ path between A and B (no diagonals.) You could probably write some variant of looking to see if yA (the y of the A coordinate) is less than yB (y of the B coordinate). If it is, move one square from yA to yB. Rinse and repeat for the x axis. This works and given the current constraint will find you one of the shortest paths. It'd look something like: 19 | 20 | ``` 21 | • • • • • • 22 | • A • • • • 23 | • 1 • • • • 24 | • 2 • • • • 25 | • 3 • • • • 26 | • 4 5 • • • 27 | ``` 28 | 29 | Now let's add a wall to the mix. 30 | 31 | ``` 32 | • • • • • • 33 | • A • • • • 34 | • • • • • • 35 | • X X X X X 36 | • • • • • • 37 | • • B • • • 38 | ``` 39 | 40 | Suddenly our algorithm falls apart. You could probably devise some strategy to mitigate this but we can keep making more complicated wall structures to make it harder. So let's try something different: Dijkstra's algorithm. 41 | 42 | The basic gist of Dijkstra's algorithm is that we'll start at both the beginning and the end node and begin "spiraling" outwards, marking each node with how far away it is from from its original Node. We'll alternate spiraling one level with one node, and then one level with the other. After one iteration of the point A (doesn't matter if you start with the A or B) it'd look like: 43 | 44 | ``` 45 | • 1 • • • • 46 | 1 A 1 • • • 47 | • 1 • • • • 48 | • X X X X X 49 | • • • • • • 50 | • • B • • • 51 | ``` 52 | 53 | Then we'd do the same to point B which would like this: 54 | 55 | ``` 56 | • 1 • • • • 57 | 1 A 1 • • • 58 | • 1 • • • • 59 | • X X X X X 60 | • • 1 • • • 61 | • 1 B 1 • • 62 | ``` 63 | 64 | We'll do this until we intersect the two spirals. As soon as the spiral intersect we know we've found the shortest possible path. Let's keep going with point A: 65 | 66 | ``` 67 | 2 1 2 • • • 68 | 1 A 1 • • • 69 | 2 1 2 • • • 70 | • X X X X X 71 | • • 1 • • • 72 | • 1 B 1 • • 73 | ``` 74 | 75 | Notice this algorithm accounts for obstacles: if there's an obstacle, you just skip that node and keep going. 76 | 77 | Now point B: 78 | 79 | ``` 80 | 2 1 2 • • • 81 | 1 A 1 2 • • 82 | 2 1 2 • • • 83 | • X X X X X 84 | • 2 1 2 • • 85 | 2 1 B 1 2 • 86 | ``` 87 | 88 | You get the point, we alternate. So let's do the next steps for both point A and B. 89 | 90 | ``` 91 | 2 1 2 3 • • 92 | 1 A 1 2 3 • 93 | 2 1 2 3 • • 94 | 3 X X X X X 95 | 3 2 1 2 3 • 96 | 2 1 B 1 2 3 97 | ``` 98 | 99 | You can see they've intersected but our algorithm hasn't made that connection yet. But on the next iteration, as the spiraling is happening, those that point will see that the node it's going to has been marked by another origin point. Because of that, we know that we've found one of the shortest paths (there could be other paths that connect of the same length.) As such we've solved our problem: the shortest path is of length six. If you were keeping track of the nodes, you could give the coordinates of the path. 100 | 101 | So, given this as the basic gist, let's speak about the more technical details. The way to accomplish is _very_ similar to what we just did with tree traversals. In fact it's literally the same algorithm applied a different way. Let's take point A and reimagine it as a tree. 102 | 103 | ``` 104 | A [1,1] 105 | / / \ \ 106 | [1,0] [0,1] [1,2] [2,1] 107 | /| |\ /| |\ /| |\ /| |\ 108 | [lots more children] 109 | ``` 110 | 111 | Instead of each node having at most two children like a binary search tree, each node has at most four children. Okay, now that we're thinking of this matrix as a tree, what we want to do is investigate first the nodes surrounding it, or in tree terms: its immediate children. We literally just learned how to do that with trees. It's breadth-first traversal! Let's apply the same secret algorithmic sauce to our problem here. We're going to take the root node (A) and add all of its children (neighbors, really) to a queue, and then begin processing each node as we pop them off the queue. Remember we need to alternate processing point A and point B, so we'll just alternate which ones we're pushing on the queue. Finally we'll have to keep track of visited nodes and distances, we'll just keep a duplicate matrix and mark it with what length it was and which origin point marked it. That's it! 112 | 113 | Technically what we're is actually breadth-first search, but the mechanics of Dijkstra's algorithm is the same and so the two are often conflated with each other. [See here][so-bfs-vs-d] for a more thorough explanation. 114 | 115 | If you want to see how this works using a visualizer, [check this out][visual]. 116 | 117 | Awesome, let's give it a shot in JavaScript. 118 | 119 | * [Exercise][exercise] 120 | * [Completed][completed] 121 | 122 | [visual]: https://qiao.github.io/PathFinding.js/visual/ 123 | [so-bfs-vs-d]: https://stackoverflow.com/questions/25449781/what-is-difference-between-bfs-and-dijkstras-algorithms-when-looking-for-shorte 124 | [completed]: https://codepen.io/btholt/pen/LeqeoQ?editors=0010 125 | [exercise]: https://codepen.io/btholt/pen/BJMxVM?editors=0010 126 | -------------------------------------------------------------------------------- /lessons/heap-sort.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Heap Sort" 3 | path: "/heap-sort" 4 | order: 8 5 | --- 6 | 7 | Back to sorting! Part 1 covers a lot of sorting algorithms and we're going to cover two more here. The first one we're going to deal with heap sort, a very clever algorithm based on trees. 8 | 9 | Let's first address what a heap is. A heap is an array that represents a tree data structure and has to be sorted in a particular way to represent that tree. Priority queues are often represented as heaps and often those two terms are used interchangeably even if the the priority queue is implemented a different way. 10 | 11 | We're going to be talking about binary heaps today but know there are others. A binary heap translates to a binary tree, similar to the binary search trees that part one covers. Let's list out what makes a BST (binary search tree) and a binary heap different 12 | 13 | * A binary heap is an array; a BST is made up of node objects (typically.) 14 | * In a BST, there is a strict order where a left child is always smaller than the parent and the right node is always greater. This is not true of a binary heap. The only guarantee of binary heap is the parent is greater than the children, there are no guarantees between sibling nodes. 15 | * Similar to the above point, if you do an in-order traversal of a BST, you'll get a sorted list. This does not work in a binary heap. 16 | * A binary heap is a "complete binary tree." This means it's as compact as possible. All the children of each node are as full as they can be and left children are filled out first. This is not true of a BST: they can be sparse. 17 | 18 | Binary heaps come in two falvors: max heaps and min heaps. We'll be dealing with max heaps (which help you find the greatest number in the heap) but you can imagine that if you flip all the comparisons you'd have a min heap (which helps you find the smallest number.) 19 | 20 | So this is why a priority queue is often a binary heap: it's very easy to tell the largest number in a binary heap. None of the other is guaranteed, but once you dequeue (or take the next element off) it's easy to find the next item in the queue. In fact, that's how heapsort works: you construct an internal priority queue and then remove an item at a time and stick it at the end and then find the next largest item in the priority queue; rinse and repeat. 21 | 22 | The way to represent a binary tree as an array is that for any index of an array _n_, its left child is stored at 2n + 1; and its right child is at 2n + 2. The root node will always be at 0. That means the root node's left child is at 1 and right child is at 2. 1's left child is at 3 and right is at 4. 23 | 24 | ![Binary Heap Visual Diagram](./images/heap.jpg) 25 | Courtesy of [Wikimedia]() 26 | 27 | Once you construct a heap, removing an item from it is done in constant time since you need to find the next largest node to move to the root. This process is typically called _heapify_. In order to construct a max heap, you run heapify starting at the middle of the array and work backwards to the root. You don't need to do it from the end because heapify will inherently look at those nodes. 28 | 29 | So the process of heapsort is 30 | 31 | * Make the array a max heap 32 | * Loop over the array, dequeuing the root node (which will give you the largest item) and swapping that with last item in the array 33 | * After dequeuing each item, run heapify again (same function we used to create the heap) once to find the next root node 34 | * Next loop you'll dequeue the root node and swap it with the second-to-last item in the array and run heapify again. 35 | * Once you've run out of items to dequeue, you have a sorted array! Let's see what that looks like. 36 | 37 | ``` 38 | // initial array 39 | [5, 3, 2, 10, 1, 9, 8, 6, 4, 7] 40 | 41 | // heapify the array 42 | start at index 5, value 9 43 | its left child is at index 11 and right 12, out of bounds 44 | nothing to do, next iteration 45 | 46 | i-- index 4, value 1 47 | left child is index 9 value 7, right child is index 10, out of bounds 48 | 7 is larger than 1, swap left child and parent 49 | [5, 3, 2, 10, 7, 9, 8, 6, 4, 1] 50 | call heapify on index 9, does nothing 51 | 52 | i-- index 3, value 10 53 | left child is index 7 value 6, right child is index 8 value 4 54 | neither is larger than 10 55 | nothing to do, next iteration 56 | 57 | i-- index 2, value 2 58 | left child is index 5 value 9, right child is index 6 value 8 59 | 9 is the largest number, swap with parent 60 | [5, 3, 9, 10, 7, 2, 8, 6, 4, 1] 61 | call heapify on index 5, does nothing 62 | 63 | i-- index 1, value 3 64 | left child is index 3 value 10, right child is index 4 value 7 65 | 10 is in the largest number, swap with parent 66 | [5, 10, 9, 3, 7, 2, 8, 6, 4, 1] 67 | call heapify on index 3 68 | left child is index 7 value 6, right child is index 8 value 4 69 | 6 is larger, swap with parent 70 | [5, 10, 9, 6, 7, 2, 8, 3, 4, 1] 71 | call heapify on index 7, does nothing 72 | 73 | i-- index 0, value 5 74 | left child is index 1 value 10, right child is index 2 value 9 75 | 10 is in the largest number, swap with parent 76 | [10, 5, 9, 6, 7, 2, 8, 3, 4, 1] 77 | call heapify on index 1 78 | left child is index 3 value 6, right child is index 4 value 7 79 | 7 is larger, swap with parent 80 | [10, 7, 9, 6, 5, 2, 8, 3, 4, 1] 81 | call heapify on index 4 82 | left child is index 9 value 1, right child is index 10, out of bounds 83 | parent is larger, does nothing 84 | ``` 85 | 86 | Now our array is officially a heap. Now we can begin dequeuing items and sorting our array. 87 | 88 | ``` 89 | Swap 10 and 1 90 | [1, 7, 9, 6, 5, 2, 8, 3, 4, 10] 91 | call heapify on index 0 92 | left child is index 1 value 7, right child is index 2 value 9 93 | 9 is the larger, swap with parent 94 | [9, 7, 1, 6, 5, 2, 8, 3, 4, 10] 95 | call heapify on index 2 96 | left child is index 5 value 2, right child is index 6 value 8 97 | 8 is larger, swap with parent 98 | [9, 7, 8, 6, 5, 2, 1, 3, 4, 10] 99 | call heapifiy on index 6, does nothing since children are out of bounds 100 | 101 | Swap 9 and 4 102 | [4, 7, 8, 6, 5, 2, 1, 3, 9, 10] 103 | call heapify on index 0 104 | 105 | Continue swapping the first element (the root) and last element of the heap 106 | and then call heapify on element 0 107 | After all these iterations, the array will be sorted 108 | ``` 109 | 110 | Hopefully this demonstrates how the it works! Go give it a shot to implement. 111 | 112 | * [Exercise][exercise] 113 | * [Completed][completed] 114 | 115 | [exercise]: https://codepen.io/btholt/pen/PQmKPa?editors=0010 116 | [completed]: https://codepen.io/btholt/pen/eVWRNP?editors=0010 117 | -------------------------------------------------------------------------------- /lessons/maze-generation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Generating a Maze" 3 | path: "/maze-generation" 4 | order: 5 5 | --- 6 | 7 | We're going back to working on mazes but with slightly different twist. With pathfinding we were trying to find the shortest path between point A and point B. In this lesson we're going to be trying to generate a random and curvy maze. I find this to be a pretty fun exercise. 8 | 9 | As you may imagine, there are many ways to accomplish this task, each with varying sets tradeoffs you need to make. Some produce very straight paths, some take less time to process, and others are easier to implement. Check out [here][algos] for an overview of many of the most common ones. I'd recommend checking it out because he has nice JavaScript demos of each algorithm. 10 | 11 | In particular we will be focusing on the [recursive backtracking algorithm][buckblog]. The idea of this algorithm is similar to what we've done before. We're going to treat the maze as a graph. As opposed to before, each node will keep track of each of it's borders: north, east, south, and west. For this exercise, assume [0, 0] is the most southwestern corner of the grid. We will start with every node in the grid having walls on all four sides, and this algorithm will slowly take down walls. 12 | 13 | We will start at an origin point (provided to you) and then we will call a function that will give us back a randomized list of directions to try. As opposed to processing a binary search tree where you always processed the left tree first, in this one you will call a function (in our example it's be called `randomizeDirections()`) that will give you back an array of `['n', 'e', 's', 'w']` in a random order. From there you will process your nodes in that order. If the next node to go to is unvisited, you will "knock down" the walls between the two nodes, remembering that if I go north on a node, I need to tear down the north wall of that node **and** the the south wall of the other node. If a node has been visited, you simply return and continue on processing the previous node. 14 | 15 | Again, this is similar to something else we've already learned: depth-first traversal. The previous times we've used breadth-first but in this case depth-first will be more useful. There are other methods of maze generation that are more similar to breadth-first. 16 | 17 | Let's do a simple example. 18 | 19 | 20 | 21 | 25 | 26 | 30 | 31 | 35 |
22 | 23 | 24 |
27 | 28 | 29 |
32 | 33 | 34 |
36 | 37 | In this example, we'll start our maze at [0,0] (colored pink.) First thing we'll do is mark this node as "visited" so we don't process it again later. Then we'll call `randomizeDirections()`. Let's assume it hands back `['w', 'n', 's', 'e']`. We'll try to go to [-1, 0] but since that's out of bounds, we'll go to the next direction, north. Since [1, 0] has not been processed, we'll tear down the north wall of [0, 0] and the south wall of [1, 0]. 38 | 39 | 40 | 41 | 45 | 46 | 50 | 51 | 55 |
42 | 43 | 44 |
47 | 48 | 49 |
52 | 53 | 54 |
56 | 57 | Notice we've moved on to processing [1, 0] now; we did not finish processing [0, 0], just like what happened with depth-first traversal of binary search trees. Once we've completed the rest of the [1, 0] and its subsequent calls (which probably be the rest of the grid) _then_ it will process. Okay, so let's say `randomizeDirections()` hands back `['s', 'w', 'e', 'n']`. We try to go south but since [0, 0] is already marked as visited we won't process it again. West is out of bounds, we'll end up going east. We'll tear down the walls between the two, making it look like: 58 | 59 | 60 | 61 | 65 | 66 | 70 | 71 | 75 |
62 | 63 | 64 |
67 | 68 | 69 |
72 | 73 | 74 |
76 | 77 | Okay, let's fast forward a bit. Next `randomizeDirectionCall()` gives us `['e', 's', 'n', 'w']` so we go right. 78 | 79 | 80 | 81 | 85 | 86 | 90 | 91 | 95 |
82 | 83 | 84 |
87 | 88 | 89 |
92 | 93 | 94 |
96 | 97 | Then `['s', 'n', 'e', 'w']`. 98 | 99 | 100 | 101 | 105 | 106 | 110 | 111 | 115 |
102 | 103 | 104 |
107 | 108 | 109 |
112 | 113 | 114 |
116 | 117 | Then `['w', 'e', 'n', 's']`. 118 | 119 | 120 | 121 | 125 | 126 | 130 | 131 | 135 |
122 | 123 | 124 |
127 | 128 | 129 |
132 | 133 | 134 |
136 | 137 | Okay, now that we're here, we've run out of places to go, so we'll return and go up our recursion to previous we were. 138 | 139 | 140 | 141 | 145 | 146 | 150 | 151 | 155 |
142 | 143 | 144 |
147 | 148 | 149 |
152 | 153 | 154 |
156 | 157 | This too has no where to go. So we'll go back up the stack. 158 | 159 | 160 | 161 | 165 | 166 | 170 | 171 | 175 |
162 | 163 | 164 |
167 | 168 | 169 |
172 | 173 | 174 |
176 | 177 | Last time we were here, `randomizeDirection()` gave us `['s', 'n', 'e', 'w']`. We already went south, so we'll go the next one, north. You can probably see how this ends. So let's just leave you with the finished product here: 178 | 179 | 180 | 181 | 185 | 186 | 190 | 191 | 195 |
182 | 183 | 184 |
187 | 188 | 189 |
192 | 193 | 194 |
196 | 197 | There you have it! This algorithm will make sure that every node in the array is connected to one contiguous maze. It won't necessarily give you a "finish" but that's not terribly hard to do either: you'd just track the furthest point away and mark that as the end. 198 | 199 | Let's have you go give it a shot. Make sure to read the comments in the CodePen! I've provided a lot of functionality for you so you can focus on the maze generation. 200 | 201 | * [Exercise][exercise] 202 | * [Completed][completed] 203 | 204 | [completed]: https://codepen.io/btholt/pen/rJxOyK?editors=0010 205 | [exercise]: https://codepen.io/btholt/pen/YeWjNO?editors=0010 206 | [algos]: http://weblog.jamisbuck.org/2011/2/7/maze-generation-algorithm-recap 207 | [buckblog]: http://weblog.jamisbuck.org/2010/12/27/maze-generation-recursive-backtracking 208 | --------------------------------------------------------------------------------