freeCodeCamp/challenges/08-coding-interview-questio.../coding-interview-data-struc...

2704 lines
187 KiB
JSON

{
"name": "Coding Interview Data Structure Questions",
"order": 9,
"time": "50 hours",
"helpRoom": "Help",
"challenges": [
{
"id": "587d8253367417b2b2512c6a",
"title": "Typed Arrays",
"description": [
"Arrays are JavaScript objects that can hold a lot of different elements.",
"<code>var complexArr = [1, 5, \"2\", \"Word\", {\"name\": \"James\"}];</code>",
"Basically what happens in the background is that your browser will automatically give the right amount of memory space for that array. It will also change as needed if you add or remove data.",
"However, in the world of high performance and different element types, sometimes you need to be more specific on how much memory is given to an array.",
"<dfn>Typed arrays</dfn> are the answer to this problem. You are now able to say how much memory you want to give an array. Below is a basic overview of the different types of arrays available and the size in bytes for each element in that array.",
"<table class='table table-striped'><tr><th>Type</th><th>Each element size in bytes</th></tr><tr><td><code>Int8Array</code></td><td>1</td></tr><tr><td><code>Uint8Array</code></td><td>1</td></tr><tr><td><code>Uint8ClampedArray</code></td><td>1</td></tr><tr><td><code>Int16Array</code></td><td>2</td></tr><tr><td><code>Uint16Array</code></td><td>2</td></tr><tr><td><code>Int32Array</code></td><td>4</td></tr><tr><td><code>Uint32Array</code></td><td>4</td></tr><tr><td><code>Float32Array</code></td><td>4</td></tr><tr><td><code>Float64Array</code></td><td>8</td></tr></table>",
"There are two ways in creating these kind of arrays. One way is to create it directly. Below is how to create a 3 length <code>Int16Array</code>.",
"<blockquote>var i8 = new Int16Array(3);<br>console.log(i8);<br>// Returns [0, 0, 0]</blockquote>",
"You can also create a <dfn>buffer</dfn> to assign how much data (in bytes) you want the array to take up.",
"<strong>Note</strong><br>To create typed arrays using buffers, you need to assign the number of bytes to be a multiple of the bytes listed above.",
"<blockquote>// Create same Int16Array array differently<br>var byteSize = 6; // Needs to be multiple of 2<br>var buffer = new ArrayBuffer(byteSize);<br>var i8View = new Int16Array(buffer);<br>buffer.byteLength; // Returns 6<br>i8View.byteLength; // Returns 6<br>console.log(i8View); // Returns [0, 0, 0]</blockquote>",
"<dfn>Buffers</dfn> are general purpose objects that just carry data. You cannot access them normally. To access them, you need to first create a <dfn>view</dfn>.",
"<blockquote>i8View[0] = 42;<br>console.log(i8View); // Returns [42, 0, 0]</blockquote>",
"<strong>Note</strong><br>Typed arrays do not have some of the methods traditional arrays have such as <code>.pop()</code> or <code>.push()</code>. Typed arrays also fail <code>Array.isArray()</code> that checks if something is an array. Although simpler, this can be an advantage for less-sophisticated JavaScript engines to implement them.",
"<hr>",
"First create a <code>buffer</code> that is 64-bytes. Then create a <code>Int32Array</code> typed array with a view of it called <code>int32View</code>."
],
"challengeSeed": [
"var buffer;",
"var i32View;"
],
"tests": [
"assert(buffer.byteLength === 64, 'message: Your <code>buffer</code> should be 64 bytes large.');",
"assert(i32View.byteLength === 64, 'message: Your <code>i32View</code> view of your buffer should be 64 bytes large.');",
"assert(i32View.length === 16, 'message: Your <code>i32View</code> view of your buffer should be 16 elements long.');"
],
"type": "waypoint",
"solutions": [
"var buffer = new ArrayBuffer(64);\nvar i32View = new Int32Array(buffer);"
],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8250367417b2b2512c5e",
"title": "Learn how a Stack Works",
"description": [
"You are probably familiar with stack of books on your table. You might have used the undo feature in your computer. You are also used to hitting the back button on your phone to go back to the previous view in your app.",
"You know what they all have in common? They all store the data in a way so that you can traverse backwards.",
"The topmost book in the stack was the one that was put there at the last. If you remove that book from your stack's top, you would expose the book that was put there before the last book and so on.",
"If you think about it, in all the above examples, you are getting Last-In-First-Out type of service. We will try to mimic this with our code - create a similar data storage scheme with JS arrays and functions that we always get back first what we put there last.",
"Oh, and did I mention such data storage scheme is called Stack! In particular, we would have to implement the push function that pushes JS object at the top of the stack; and pop function, that removes the JS object that's at the top of the stack at the current moment.",
"Instructions",
"Here we have a stack of homework assignments represented as an array: BIO12 is at the base, and PSY44 is at the top of the stack.",
"Remove the element on top of the stack, and Add CS50."
],
"challengeSeed": [
"var homeworkStack = ['BIO12','HIS80','MAT122','PSY44'];",
"// Only change code below this line"
],
"tests": [
"assert(homeworkStack.length === 4, 'message: <code>homeworkStack</code> should only contain 4 elements');",
"assert(homeworkStack[3] === 'CS50', 'message: The last element in <code>homeworkStack</code> should be CS50');",
"assert(homeworkStack.indexOf('PSY44') === -1, 'message: <code>homeworkStack</code> should not contain PSY44');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d8250367417b2b2512c5f",
"title": "Create a Stack Class",
"description": [
"In the last section, we talked about what a stack is and how we can use an array to represent a stack. In this section, we will be creating our own stack class.",
"Although you can use arrays to create stacks, sometimes it is best to limit the amount of control we have with our stacks.",
"Apart from the <code>push</code> and <code>pop</code> method, stacks have other useful methods. Let's add a <code>peek</code>, <code>isEmpty</code>, and <code>clear</code> method to our stack class.",
"Instructions",
"Write a <code>push</code> method that pushes an element to the top of the stack, a <code>pop</code> method that removes the element on the top of the stack, a <code>peek</code> method that looks at the first element in the stack, an <code>isEmpty</code> method that checks if the stack is empty, and a <code>clear</code> method that removes all elements from the stack.",
"Normally stacks don't have this, but we've added a <code>print</code> helper method that console logs the collection."
],
"challengeSeed": [
"function Stack() { ",
" collection = [];",
" this.print = function() {",
" console.log(collection);",
" };",
" // Only change code below this line",
"",
" // Only change code above this line",
"}"
],
"tests": [
"assert((function(){var test = new Stack(); return (typeof test.push === 'function')}()), 'message: Your <code>Stack</code> class should have a <code>push</code> method.');",
"assert((function(){var test = new Stack(); return (typeof test.pop === 'function')}()), 'message: Your <code>Stack</code> class should have a <code>pop</code> method.');",
"assert((function(){var test = new Stack(); return (typeof test.peek === 'function')}()), 'message: Your <code>Stack</code> class should have a <code>peek</code> method.');",
"assert((function(){var test = new Stack(); return (typeof test.isEmpty === 'function')}()), 'message: Your <code>Stack</code> class should have a <code>isEmpty</code> method.');",
"assert((function(){var test = new Stack(); return (typeof test.clear === 'function')}()), 'message: Your <code>Stack</code> class should have a <code>clear</code> method.');",
"assert((function(){var test = new Stack(); test.push('CS50'); return (test.peek() === 'CS50')}()), 'message: The <code>peek</code> method should return the top element of the stack');",
"assert((function(){var test = new Stack(); test.push('CS50'); return (test.pop() === 'CS50');}()), 'message: The <code>pop</code> method should remove and return the top element of the stack');",
"assert((function(){var test = new Stack(); return test.isEmpty()}()), 'message: The <code>isEmpty</code> method should return true if a stack does not contain any elements');",
"assert((function(){var test = new Stack(); test.push('CS50'); test.clear(); return (test.isEmpty())}()), 'message: The <code>clear</code> method should remove all element from the stack');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d8250367417b2b2512c60",
"title": "Create a Queue Class",
"description": [
"Like stacks, queues are a collection of elements. But unlike stacks, queues follow the FIFO (First-In First-Out) principle. Elements added to a queue are pushed to the tail, or the end, of the queue, and only the element at the front of the queue is allowed to be removed.",
"We could use an array to represent a queue, but just like stacks, we want to limit the amount of control we have over our queues.",
"The two main methods of a queue class is the enqueue and the dequeue method. The enqueue method pushes an element to the tail of the queue, and the dequeue method removes and returns the element at the front of the queue. Other useful methods are the front, size, and isEmpty methods.",
"Instructions",
"Write an enqueue method that pushes an element to the tail of the queue, a dequeue method that removes and returns the front element, a front method that lets us see the front element, a size method that shows the length, and an isEmpty method to check if the queue is empty."
],
"challengeSeed": [
"function Queue () { ",
" collection = [];",
" this.print = function() {",
" console.log(collection);",
" };",
" // Only change code below this line",
"",
" // Only change code above this line",
"}"
],
"tests": [
"assert((function(){var test = new Queue(); return (typeof test.enqueue === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>enqueue</code> method.');",
"assert((function(){var test = new Queue(); return (typeof test.dequeue === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>dequeue</code> method.');",
"assert((function(){var test = new Queue(); return (typeof test.front === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>front</code> method.');",
"assert((function(){var test = new Queue(); return (typeof test.size === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>size</code> method.');",
"assert((function(){var test = new Queue(); return (typeof test.isEmpty === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>isEmpty</code> method.');",
"assert((function(){var test = new Queue(); test.enqueue('Smith'); return (test.dequeue() === 'Smith')}()), 'message: The <code>dequeue</code> method should remove and return the front element of the queue');",
"assert((function(){var test = new Queue(); test.enqueue('Smith'); test.enqueue('John'); return (test.front() === 'Smith')}()), 'message: The <code>front</code> method should return value of the front element of the queue');",
"assert((function(){var test = new Queue(); test.enqueue('Smith'); return (test.size() === 1)}()), 'message: The <code>size</code> method should return the length of the queue');",
"assert((function(){var test = new Queue(); test.enqueue('Smith'); return !(test.isEmpty())}()), 'message: The <code>isEmpty</code> method should return <code>false</code> if there are elements in the queue');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d8255367417b2b2512c74",
"title": "Create a Priority Queue Class",
"description": [
"In this challenge you will be creating a Priority Queue. A Priority Queue is a special type of Queue with one primary difference, that you can set the priority in the queue as either high represented by 0 or low represented by an integer greater than 0 during the enqueue process. Item priority will override the placement order: that is, an item enqueue with a higher priority than any preceding items will be moved to the front of the queue. If items have equal priority, then placement order will decide their dequeue order. For instance:",
"collection = [['taco',1]]",
"PriorityQueue.enqueue(['kitten', 1]);",
"console.log([...PriorityQueue])",
"// would be [['taco', 1], ['kitten', 1]]",
"// not [['kitten', 1], ['taco', 1]]",
"For this challenge, you will create an object called PriorityQueue. You will need to add an enqueue method for adding items with a priority, a dequeue method for popping off the front item, a size method to return the number of items in the queue, and finally an isEmpty method that will return true or false if the queue is empty."
],
"challengeSeed": [
"function PriorityQueue () {",
" this.collection = [];",
" this.printCollection = function() {",
" (console.log(this.collection));",
" };",
" // Only change code below this line",
"",
" // Only change code above this line",
"}"
],
"tests": [
"assert((function(){var test = new PriorityQueue(); return (typeof test.enqueue === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>enqueue</code> method.');",
"assert((function(){var test = new PriorityQueue(); return (typeof test.dequeue === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>dequeue</code> method.');",
"assert((function(){var test = new PriorityQueue(); return (typeof test.front === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>front</code> method.');",
"assert((function(){var test = new PriorityQueue(); return (typeof test.size === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>size</code> method.');",
"assert((function(){var test = new PriorityQueue(); return (typeof test.isEmpty === 'function')}()), 'message: Your <code>Queue</code> class should have a <code>isEmpty</code> method.');",
"assert((function(){var test = new PriorityQueue(); test.enqueue(['David Brown', 2]);test.enqueue(['Jon Snow', 1]); return (test.size()===2)}()), 'message: Your code did not correctly add the incoming items.');",
"assert((function(){var test = new PriorityQueue(); test.enqueue(['David Brown', 2]);test.enqueue(['Jon Snow', 1]);test.dequeue(); return (test.size()===1)}()), 'message: Your code did not correctly dequeue 1 item.');",
"assert((function(){var test = new PriorityQueue(); test.enqueue(['David Brown', 2]);test.enqueue(['Jon Snow', 1]);test.dequeue();test.dequeue(); return (test.isEmpty()===true)}()), 'message: Your code is not correctly reporting that the queue is empty.');",
"assert((function(){var test = new PriorityQueue(); test.enqueue(['David Brown', 1]);test.enqueue(['Jon Snow', 1]); return (test.dequeue() === 'David Brown' && test.dequeue() === 'Jon Snow')}()), 'message: Your code did not correctly prioritize the incoming items. If 2 items have the same priority the older item remains infront of new items');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8255367417b2b2512c75",
"title": "How to Create a Circular Queue",
"description": [
"In this challenge you will be creating a Circular Queue. A circular queue is basically a queue that writes to the end of a collection then begins over writing itself at the beginning of the collection. This is type of data structure has some useful applications in certain situations. For example, a circular buffer can be used in streaming media. Once the queue is full, new media data simply begins to overwrite old data.",
"A good way to illustrate this concept is with an array:",
"<blockquote>[1, 2, 3, 4, 5]<br> ^Read @ 0<br> ^Write @ 0</blockquote>",
"Here the read and write are both at position <code>0</code>. Now the queue gets 3 new records <code>a</code>, <code>b</code>, and <code>c</code>. Our queue now looks like:",
"<blockquote>[a, b, c, 4, 5]<br> ^Read @ 0<br> ^Write @ 3</blockquote>",
"As the read head reads, it can remove values or keep them:",
"<blockquote>[null, null, null, 4, 5]<br> ^Read @ 3<br> ^Write @ 3</blockquote>",
"Once the write reaches the end of the array it loops back to the beginning:",
"<blockquote>[f, null, null, d, e]<br> ^Read @ 3<br> ^Write @ 1</blockquote>",
"So as you can see the buffer consumes less memory, this can be useful for processing large data file conversions and prevent a memory leak from slowing down a conversion process.",
"Instructions:",
"In this challenge you will implement a 5 element circular buffer. Your component will maintain a read pointer and a write pointer. New items can be added singly or as an array of items (Hint, use <code>Array.isArray()</code>). New items will be added to the collection starting at position <code>0</code>, advancing the write pointer. As the queue is dequeued it will delete the data and advance the read pointer. We will also create methods to return the entire queue and the front of the queue."
],
"challengeSeed": [
"function CircularQueue () {",
" var collection = [];",
" this.printCollection = function(){",
" return collection;",
" };",
" // Only change code below this line",
"",
" // Only change code above this line",
" }"
],
"tests": [
"assert((function(){var test = new CircularQueuee(); test.enqueue('a'); test.enqueue('b'); test.enqueue('c'); test.enqueue('d'); test.enqueue('e'); test.enqueue('f'); return (test.printCollection === ['f', 'b', 'c', 'd', 'e'])}()), 'message: Your <code>Queue</code> class should have a <code>enqueue</code> method.');",
"assert((function(){var test = new CircularQueuee(); test.enqueue('a'); test.enqueue('b'); test.enqueue('c'); test.enqueue('d'); test.enqueue('e'); test.enqueue('f'); test.dequeue; return (test.printCollection === ['', 'b', 'c', 'd', 'e'])}()), 'message: Your <code>dequeue</code> method did not remove the first item.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "8d1323c8c441eddfaeb5bdef",
"title": "Create a Set Class",
"description": [
"In the next few exercises we are going to create a function to emulate a data structure called a \"Set\". A Set is like an array, but it cannot contain duplicate values. The typical use for a Set is to simply check for the presence of an item. This can be implemented with an object, for instance:",
"<code>var set = new Object();</code>",
"<code>set.foo = true;</code>",
"<code>// See if foo exists in our set:</code>",
"<code>console.log(set.foo) // true</code>",
"In the next few exercises, we will build a full featured Set from scratch.",
"In this exercise you will add a function to add a value to our set collection:",
"<code>this.add = function(){</code>",
"<code> //some code to add value to the set</code>",
"<code>}</code>"
],
"challengeSeed": [
"function Set() {",
" // the var collection will hold our set",
" var collection = [];",
" // this method will check for the presence of an element and return true or false",
" this.has = function(element) {",
" return (collection.indexOf(element) !== -1);",
" };",
" // this method will return all the values in the set",
" this.values = function() {",
" return collection;",
" };",
" // change code below this line",
" // change code above this line",
"}"
],
"tests": [
"assert((function(){var test = new Set(); return (typeof test.add === 'function')}()), 'message: Your <code>Set</code> class should have a <code>add</code> method.');",
"assert((function(){var test = new Set(); test.add('a'); test.add('b'); test.add('a'); var vals = test.values(); console.log(vals); return (vals[0] === 'a' && vals[1] === 'b' && vals.length === 2)}()), 'message: Your <code>Set</code> should not add duplicate values.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8253367417b2b2512c6b",
"title": "Remove from a Set",
"description": [
"In this exercises we are going to create a delete function for our set. The function should be named <code>this.remove</code>. This function should accept a value and remove that value from the set."
],
"challengeSeed": [
"function Set() {",
" // the var collection will hold the set",
" var collection = [];",
" // this method will check for the presence of an element and return true or false",
" this.has = function(element) {",
" return (collection.indexOf(element) !== -1);",
" };",
" // this method will return all the values in the set",
" this.values = function() {",
" return collection;",
" };",
" // this method will add an element to the set",
" this.add = function(element) {",
" if(!this.has(element)){",
" collection.push(element);",
" return true;",
" }",
" return false;",
" };",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function(){var test = new Set(); return (typeof test.remove === 'function')}()), 'message: Your <code>Set</code> class should have a <code>remove</code> method.');",
"assert((function(){var test = new Set(); test.add(\"a\");test.add(\"b\");test.remove(\"a\"); var vals = test.values(); return (vals[0] === 'b' && vals.length === 1)}()), 'message: Your code should remove the item from');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "8d1923c8c441eddfaeb5bdef",
"title": "Size of the Set",
"description": [
"In this exercise we are going to create a size function for our Set. This function should be named this.size and it should return the size of the collection."
],
"challengeSeed": [
"function Set() {",
" // the var collection will hold the set",
" var collection = [];",
" // this method will check for the presence of an element and return true or false",
" this.has = function(element) {",
" return (collection.indexOf(element) !== -1);",
" };",
" // this method will return all the values in the set",
" this.values = function() {",
" return collection;",
" };",
" // this method will add an element to the set",
" this.add = function(element) {",
" if(!this.has(element)){",
" collection.push(element);",
" return true;",
" }",
" return false;",
" };",
" // this method will remove an element from a set",
" this.remove = function(element) {",
" if(this.has(element)){",
" index = collection.indexOf(element);",
" collection.splice(index,1);",
" return true;",
" }",
" return false;",
" };",
" // change code below this line",
" // change code above this line",
"}"
],
"tests": [
"assert((function(){var test = new Set(); return (typeof test.size === 'function')}()), 'message: Your <code>Set</code> class should have a <code>size</code> method.');",
"assert((function(){var test = new Set(); test.add(\"a\");test.add(\"b\");test.remove(\"a\");return (test.size() === 1)}()), 'message: The <code>size</code> method should return the number of elements in the collection.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8253367417b2b2512c6c",
"title": "Perform a Union on Two Sets",
"description": [
"In this exercise we are going to perform a union on two sets of data. We will create a method on our Set data structure called <code>union</code>. This method should take another set as an argument and update the set you called the method on to be the union of the two sets, excluding any duplicate values."
],
"challengeSeed": [
"function Set() {",
" // the var collection will hold the set",
" var collection = [];",
" // this method will check for the presence of an element and return true or false",
" this.has = function(element) {",
" return (collection.indexOf(element) !== -1);",
" };",
" // this method will return all the values in the set",
" this.values = function() {",
" return collection;",
" };",
" // this method will add an element to the set",
" this.add = function(element) {",
" if(!this.has(element)){",
" collection.push(element);",
" return true;",
" }",
" return false;",
" };",
" // this method will remove an element from a set",
" this.remove = function(element) {",
" if(this.has(element)){",
" index = collection.indexOf(element);",
" collection.splice(index,1);",
" return true;",
" }",
" return false;",
" };",
" // this method will return the size of the set",
" this.size = function() {",
" return collection.length;",
" };",
" // change code below this line",
"",
" // change code above this line",
"};"
],
"tests": [
"assert((function(){var test = new Set(); return (typeof test.union === 'function')})(), 'message: Your <code>Set</code> class should have a <code>union</code> method.');",
"assert((function(){var setA = new Set(); var setB = new Set(); setA.add(\"a\"); setA.add(\"b\"); setA.add(\"c\"); setB.add(\"c\"); setB.add(\"d\"); var unionSetAB = setA.union(setB); var final = unionSetAB.values(); return (final.indexOf('a') !== -1 && final.indexOf('b') !== -1 && final.indexOf('c') !== -1 && final.indexOf('d') !== -1 && final.length === 4)})(), 'message: The proper collection was returned');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8253367417b2b2512c6d",
"title": "Perform an Intersection on Two Sets of Data",
"description": [
"In this exercise we are going to perform an intersection on 2 sets of data. An intersection of sets represents all values that are common to two or more sets. This method should be called this.intersection. This method should return a new set containing the intersection of the sets the method was called against."
],
"challengeSeed": [
"function Set() {",
" // the var collection will hold the set",
" var collection = [];",
" // this method will check for the presence of an element and return true or false",
" this.has = function(element) {",
" return (collection.indexOf(element) !== -1);",
" };",
" // this method will return all the values in the set",
" this.values = function() {",
" return collection;",
" };",
" // this method will add an element to the set",
" this.add = function(element) {",
" if(!this.has(element)){",
" collection.push(element);",
" return true;",
" }",
" return false;",
" };",
" // this method will remove an element from a set",
" this.remove = function(element) {",
" if(this.has(element)){",
" index = collection.indexOf(element);",
" collection.splice(index,1);",
" return true;",
" }",
" return false;",
" };",
" // this method will return the size of the collection",
" this.size = function() {",
" return collection.length;",
" };",
" // this method will return the union of two sets",
" this.union = function(otherSet) {",
" var unionSet = new Set();",
" var firstSet = this.values();",
" var secondSet = otherSet.values();",
" firstSet.forEach(function(e){",
" unionSet.add(e);",
" });",
" secondSet.forEach(function(e){",
" unionSet.add(e);",
" });",
" return unionSet;",
" };",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function(){var test = new Set(); return (typeof test.intersection === 'function')}, 'message: Your <code>Set</code> class should have a <code>intersection</code> method.');",
"assert(function(){ var setA = new Set(); var setB = new Set(); setA.add(\"a\"); setA.add(\"b\"); setA.add(\"c\"); setB.add(\"c\"); setB.add(\"d\"); var intersectionSetAB = setA.intersection(setB); return (intersectionSetAB.values() === [ 'c' ])}), 'message: The proper collection was returned');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8254367417b2b2512c6e",
"title": "Perform a Difference on Two Sets of Data",
"description": [
"In this exercise we are going to perform a difference on 2 sets of data. A difference of sets should compare two sets and return the items present in the first set that are absent in the second. This method should be called this.difference and should return a new set containing only the items that result from taking the difference of two sets."
],
"challengeSeed": [
"function Set() {",
" // the var collection will hold the set",
" var collection = [];",
" // this method will check for the presence of an element and return true or false",
" this.has = function(element) {",
" return (collection.indexOf(element) !== -1);",
" };",
" // this method will return all the values in the set",
" this.values = function() {",
" return collection;",
" };",
" // this method will add an element to the set",
" this.add = function(element) {",
" if(!this.has(element)){",
" collection.push(element);",
" return true;",
" }",
" return false;",
" };",
" // this method will remove an element from a set",
" this.remove = function(element) {",
" if(this.has(element)){",
" index = collection.indexOf(element);",
" collection.splice(index,1);",
" return true;",
" }",
" return false;",
" };",
" // this method will return the size of the collection",
" this.size = function() {",
" return collection.length;",
" };",
" // this method will return the union of two sets",
" this.union = function(otherSet) {",
" var unionSet = new Set();",
" var firstSet = this.values();",
" var secondSet = otherSet.values();",
" firstSet.forEach(function(e){",
" unionSet.add(e);",
" });",
" secondSet.forEach(function(e){",
" unionSet.add(e);",
" });",
" return unionSet;",
" };",
" // this method will return the intersection of two sets as a new set",
" this.intersection = function(otherSet) {",
" var intersectionSet = new Set();",
" var firstSet = this.values();",
" firstSet.forEach(function(e){",
" if(otherSet.has(e)){",
" intersectionSet.add(e);",
" }",
" });",
" return intersectionSet;",
" };",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function(){var test = new Set(); return (typeof test.difference === 'function')}, 'message: Your <code>Set</code> class should have a <code>difference</code> method.');",
"assert(function(){ var setA = new Set(); var setB = new Set(); setA.add(\"a\"); setA.add(\"b\"); setA.add(\"c\"); setB.add(\"c\"); setB.add(\"d\"); var differenceSetAB = setA.difference(setB);return (differenceSetAB.values() === [ 'a', 'b' ])}), 'message: The proper collection was returned');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8254367417b2b2512c6f",
"title": "Perform a Subset Check on Two Sets of Data",
"description": [
"In this exercise we are going to perform a subset test on 2 sets of data. This will compare the first set, against the second and if the first set is fully contained within the Second then it will return true. This method should be called this.subset. At this point, we have added a lot of useful functionality to our Set data structure. This will complete the challenges for the set data structure."
],
"challengeSeed": [
"function Set() {",
" // the var collection will hold the set",
" var collection = [];",
" // this method will check for the presence of an element and return true or false",
" this.has = function(element) {",
" return (collection.indexOf(element) !== -1);",
" };",
" // this method will return all the values in the set",
" this.values = function() {",
" return collection;",
" };",
" // this method will add an element to the set",
" this.add = function(element) {",
" if(!this.has(element)){",
" collection.push(element);",
" return true;",
" }",
" return false;",
" };",
" // this method will remove an element from a set",
" this.remove = function(element) {",
" if(this.has(element)){",
" index = collection.indexOf(element);",
" collection.splice(index,1);",
" return true;",
" }",
" return false;",
" };",
" // this method will return the size of the collection",
" this.size = function() {",
" return collection.length;",
" };",
" // this method will return the union of two sets",
" this.union = function(otherSet) {",
" var unionSet = new Set();",
" var firstSet = this.values();",
" var secondSet = otherSet.values();",
" firstSet.forEach(function(e){",
" unionSet.add(e);",
" });",
" secondSet.forEach(function(e){",
" unionSet.add(e);",
" });",
" return unionSet;",
" };",
" // this method will return the intersection of two sets as a new set",
" this.intersection = function(otherSet) {",
" var intersectionSet = new Set();",
" var firstSet = this.values();",
" firstSet.forEach(function(e){",
" if(otherSet.has(e)){",
" intersectionSet.add(e);",
" }",
" });",
" return intersectionSet;",
" };",
" // this method will return the difference of two sets as a new set",
" this.difference = function(otherSet) {",
" var differenceSet = new Set();",
" var firstSet = this.values();",
" firstSet.forEach(function(e){",
" if(!otherSet.has(e)){",
" differenceSet.add(e);",
" }",
" });",
" return differenceSet;",
" };",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function(){var test = new Set(); return (typeof test.subset === 'function')}, 'message: Your <code>Set</code> class should have a <code>union</code> method.');",
"assert(function(){ var setA = new Set(); var setB = new Set(); setA.add(\"a\"); setB.add(\"b\"); setB.add(\"c\"); setB.add(\"a\"); setB.add(\"d\"); var subSetSetAB = setA.subset(setB);return (subSetSetAB === true)}), 'message: The first Set() was contained in the second Set');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8254367417b2b2512c70",
"title": "Creating and Adding to Sets in ES6",
"description": [
"Now we have worked through ES5, we are going to perform similar actions in ES6. This will be considerably easier. Set is now now a provided data structure like Object, Array, etc. so many of the operations we wrote by hand are now included for us. Let's take a look:",
"To create a new empty set:",
"var set = new Set();",
"You can create a set with a value:",
"var set = new Set(1);",
"You can create a set with an array:",
"var set = new Set([1,2,3]);",
"Once you have created a set, you can add the values you wish using set.add:",
"var set = new Set([1,2,3]);",
"set.add([4,5,6]);",
"For this exercise, we will return a set with the following values: 1, 2, 3, 'Taco', 'Cat', 'Awesome'",
"Test suite (using the assert method)",
"assert((function(){var test = checkSet(); var testArr = [...test]; testArr === [ 1, 2, 3, 'Taco', 'Cat', 'Awesome'])}, 'message: Your Set was returned correctly!');"
],
"challengeSeed": [
"function checkSet(){",
" var set = new Set([1,2,3,3,2,1,2,3,1]);",
" // change code below this line",
" // change code above this line",
" console.log(set)",
"};",
"checkSet();"
],
"tests": [
"assert((function(){var test = checkSet(); var testArr = [...test]; testArr === [ 1, 2, 3, 'Taco', 'Cat', 'Awesome'])}, 'message: Your Set was returned correctly!');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8254367417b2b2512c71",
"title": "Removing items from a set in ES6",
"description": [
"Now we will see how we can remove items from an ES6 Set.",
"Let's create a Set:",
"var set = new Set([1,2,3]);",
"Once you have created a set object, you can remove values you no longer want with delete. For example:",
"var set = new Set([1,2,3]);",
"set.delete(1);",
"console.log([...set]) // should return [ 2, 3 ]",
"In this exercise we will return a Set with the passed parameter."
],
"challengeSeed": [
"function checkSet(itemToRemove){",
" var set = new Set([1,2,3,3,2,1,2,3,1]);",
" // change code below this line",
" // change code above this line",
" console.log(set)",
"}"
],
"tests": [
"assert((function(){var test = checkSet(); var testArr = [...test]; testArr === [ 2, 3 ])}, 'message: Your Set was returned correctly!');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8255367417b2b2512c72",
"title": "Using .has and .size on an ES6 Set",
"description": [
"Now we will look at the .has and .size methods available on the Set object.",
"var set = new Set([1,2,3]);",
"var hasTwo = set.has(2); // will return true or false",
"var howBig = set.size; // will return an integer representing the size of the Array",
"console.log(\"Has a 2: \" + hasTwo); // should be true",
"console.log(\"Length of Set: \" + howBig); // should show 3",
"In this exercise we will pass in an array of a values and a value to check for. We will return a new array with (true or false) for the presence of the value and the size of the Set."
],
"challengeSeed": [
"function checkSet(arrToBeSet, checkValue){",
" // change code below this line",
"",
" // change code above this line",
" console.log(set)",
"}"
],
"tests": [
"assert((function(){var test = checkSet([1,2,3], 2); test === [ true, 3 ])}, 'message: Your Set was returned correctly!');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8255367417b2b2512c73",
"title": "Using Spread and Notes for ES5 Set() Integration",
"description": [
"Next up is one of the coolest things in ES6!",
"The noble spread operator! '...' can take iterable objects in ES6 and turn them into arrays",
"Let's create a Set, and check out the spread function.",
"var set = new Set([1,2,3]);",
"var setToArr = [...set]",
"console.log(setToArr) // returns [ 1, 2, 3 ]",
"Awesome, right!?",
"In this exercise we will pass in a set object. We will return an array with the values of the Set.",
"The ES5 Set() function we created, can be used to perform union, intersection, difference and subset functions using the new ES6 Sets with very little modification."
],
"challengeSeed": [
"function checkSet(set){",
" // change code below this line",
"",
" // change code above this line",
"};"
],
"tests": [
"assert((function(){var test = checkSet(new Set([1,2,3,4,5,6,7])); test === [ 1, 2, 3, 4, 5, 6, 7 ])}, 'message: Your Set was returned correctly!');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "8d5823c8c441eddfaeb5bdef",
"title": "Create a Map Data Structure",
"description": [
"The next few challenges will cover maps and hash tables. Maps are data structurs that store key-value pairs. In JavaScript, these are available to us as objects. Maps provide rapid lookup of stored items based on key values and are very common and useful data structures.",
"Instructions: Let's get some practice creating our own map. Because JavaScript objects provide a much more efficient map structure than anything we could write here, this is intended primarily as a learning exercise. However, JavaScript objects only provide us with certain operations. What if we wanted to define custom operations?",
"Use the <code>Map</code> object provided here as a wrapper around a JavaScript <code>object</code>. Create the following methods and operations on the Map object:",
"<code>add</code> accepts a <code>key, value</code> pair to add to the map",
"<code>remove</code> accepts a key and removes the associated <code>key, value</code> pair",
"<code>get</code> accepts a <code>key</code> and returns the stored <code>value</code>",
"<code>has</code> returns a <code>boolean</code> for the presence or absence of an item",
"<code>values</code> returns an array of all the values in the map",
"<code>size</code> returns the number of items in the map",
"<code>clear</code> empties the map"
],
"challengeSeed": [
"var Map = function() {",
" this.collection = {};",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof Map !== 'undefined') { test = new Map() }; return (typeof test == 'object')})(), 'message: The Map data structure exists.');",
"assert((function() { var test = false; if (typeof Map !== 'undefined') { test = new Map() }; return (typeof test.add == 'function' && typeof test.remove == 'function' && typeof test.get == 'function' && typeof test.has == 'function' && typeof test.values == 'function' && typeof test.clear == 'function' && typeof test.size == 'function')})(), 'message: The Map object has the following methods: add, remove, get, has, values, clear, and size.');",
"assert((function() { var test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add(5,6); test.add(2,3); test.add(2,5); return (test.size() == 3)})(), 'message: The add method adds items to the map.');",
"assert((function() { var test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add('test','value'); return (test.has('test') && !test.has('false'))})(), 'message: The has method returns true for added items and false for absent items.');",
"assert((function() { var test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add('abc','def'); return (test.get('abc') == 'def')})(), 'message: The get method accepts keys as input and returns the associated values.');",
"assert((function() { var test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add('a','b'); test.add('c','d'); test.add('e','f'); var vals = test.values(); return (vals.indexOf('b') != -1 && vals.indexOf('d') != -1 && vals.indexOf('f') != -1)})(), 'message: The values method returns all the values stored in the map as strings in an array.');",
"assert((function() { var test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add('b','b'); test.add('c','d'); test.remove('asdfas'); var init = test.size(); test.clear(); return (init == 2 && test.size() == 0)})(), 'message: The clear method empties the map and the size method returns the number of items present in the map.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d825b367417b2b2512c8d",
"title": "Create an ES6 JavaScript Map",
"description": [
"The new version of JavaScript provides us with a built-in Map object which provides much of the functionality we wrote by hand in the last challenge. This Map object, although similar to regular JavaScript objects, provides some useful functionality that normal objects lack. For example, an ES6 Map tracks the insertion order of items that are added to it. Here is a more complete overview of its methods:",
"<code>.has(key)</code> returns true or false based on the presence of a key",
"<code>.get(key)</code> returns the value associated with a key",
"<code>.set(key, value)</code> sets a new key, value pair",
"<code>.delete(key)</code> removes a key, value pair",
"<code>.clear()</code> removes all key, value pairs",
"<code>.entries()</code> returns an array of all the keys in insertion order",
"<code>.values()</code> returns an array of all the values in insertion order",
"Instructions: Define a JavaScript Map object and assign to it a variable called myMap. Add the key, value pair <code>freeCodeCamp</code>, <code>Awesome!</code> to it."
],
"challengeSeed": [
"// change code below this line"
],
"tests": [
"assert(typeof myMap === 'object', 'message: The myMap object exists.');",
"assert(myMap.get('freeCodeCamp') === 'Awesome!', 'message: myMap contains the key value pair <code>freeCodeCamp</code>, <code>Awesome!</code>.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d825b367417b2b2512c8e",
"title": "Create a Hash Table",
"description": [
"In this challenge we will learn about hash tables. A Hash table is used to implement associative arrays, or mappings of key-value pairs, like the objects and Maps we have just been studying. A JavaScript object could be implemented as a hash table, for instance (its actual implementation will depend on the environment it's running in). The way a hash table works is that it takes a key input and hashes this key in a deterministic way to some numerical value. This numerical value is then used as the actual key the associated value is stored by. Then, if you try to access the same key again, the hashing function will process the key, return the same numerical result, which will then be used to look up the associated value. This provides very efficient O(n) lookup time on average.",
"Hash tables can be implemented as arrays with hash functions producing array indices within a specified range. In this method, the choice of the array size is important, as is the hashing function. For instance, what if the hashing function produces the same value for two different keys? This is called a collision. One way to handle collisions is to just store both key-value pairs at that index. Then, upon lookup of either, you would have to iterate through the bucket of items to find the key you are looking for. A good hashing function will minimize collisions to maintain efficient search time.",
"Here, we won't be concerned with the details of hashing or hash table implementation, we will just try to get a general sense of how they work.",
"Instructions: Let's create the basic functionality of a hash table. We've created a naive hashing function for you to use. You can pass a string value to the function hash and it will return a hashed value you can use as a key for storage. Store items based on this hashed value in the this.collection object. Create these three methods: add, remove, and lookup. The first should accept a key value pair to add to the hash table. The second should remove a key-value pair when passed a key. The third should accept a key and return the associated value or null if the key is not present.",
"Be sure to write your code to account for collisions!"
],
"head": [
" var called = 0;",
" var hash = (string) => {",
" called++;",
" var hash = 0;",
" for (var i = 0; i < string.length; i++) { hash += string.charCodeAt(i); };",
" return hash;",
" };"
],
"challengeSeed": [
"var called = 0;",
"var hash = (string) => {",
" called++;",
" var hash = 0;",
" for (var i = 0; i < string.length; i++) { hash += string.charCodeAt(i); };",
" return hash;",
"};",
"var HashTable = function() {",
" this.collection = {};",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; return (typeof test === 'object')})(), 'message: The HashTable data structure exists.');",
"assert((function() { var test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; return ((typeof test.add) === 'function')})(), 'message: The HashTable has an add method.');",
"assert((function() { var test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; return ((typeof test.remove) === 'function')})(), 'message: The HashTable has an remove method.');",
"assert((function() { var test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; return ((typeof test.lookup) === 'function')})(), 'message: The HashTable has an lookup method.');",
"assert((function() { var test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; test.add('key', 'value'); return (test.lookup('key') === 'value')})(), 'message: The add method adds key value pairs and the lookup method returns the values associated with a given key.');",
"assert((function() { var test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; test.add('key', 'value'); test.remove('key'); return (test.lookup('key') === null)})(), 'message: The remove method accepts a key as input and removes the associated key value pair.');",
"assert((function() { var test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; called = 0; test.add('key1','value1'); test.add('key2','value2'); test.add('key3','value3'); return (called === 3)})(), 'message: Items are added using the hash function.');",
"assert((function() { var test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; called = 0; test.add('key1','value1'); test.add('1key','value2'); test.add('ke1y','value3'); return (test.lookup('key1') === 'value1' && test.lookup('1key') == 'value2' && test.lookup('ke1y') == 'value3')})(), 'message: The hash table handles collisions.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8251367417b2b2512c61",
"title": "Work with Nodes in a Linked List",
"description": [
"Another common data structure is the linked list. In a linked list, elements are stored in a node. The node contains two key pieces of information: the element itself, and a reference to the next node.",
"Imagine that you are in a conga line. You have your hands on the next person in the line, and the person behind you has their hands on you. You can see the person straight ahead of you, but they are blocking the view of the other people ahead in line. A node is just like a person in a conga line, they know who they are and they can only see the next person in line, but they are not aware of the other people ahead or behind them.",
"Instructions",
"In the code, we've create two nodes, Kitten and Puppy, and we've manually connected the Kitten node to the Puppy node.",
"Create a Cat and Dog node and manually add them to the line."
],
"challengeSeed": [
"var Node = function(element){",
" this.element = element; ",
" this.next = null; ",
"};",
"var Kitten = new Node(\"Kitten\");",
"var Puppy = new Node(\"Puppy\");",
"",
"Kitten.next = Puppy;",
"// only add code below this line",
"",
"// test your code",
"console.log(Kitten.next)"
],
"tests": [
"assert(Puppy.next.element === \"Cat\", 'message: Your <code>Puppy</code> node should have a reference to a <code>Cat</code> node.');",
"assert(Cat.next.element === \"Dog\", 'message: Your <code>Cat</code> node should have a reference to a <code>Dog</code> node.');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d8251367417b2b2512c62",
"title": "Create a Linked List Class",
"description": [
"Let's create a linked list class. Every linked list has a head and length. The head is the first node added to the linked list. The length is the size of the linked list. When an element is added to the linked list, the length should increment by one.",
"The first method of a linked list is the add method. When an element is added to a linked list, a new node is created. If is it the first node created, it is assigned to the head of the linked list. If it is not the first node, the previous node should reference the new created node.",
"Instructions",
"Write an add method that assigns head to the first node push to the linked list, and after that, every node should be referenced by the previous node.",
"We've added a head and size helper method.",
"Note",
"Length should increase by one every time an element is pushed to the linked list."
],
"challengeSeed": [
"function LinkedList() { ",
" var length = 0; ",
" var head = null; ",
"",
" var Node = function(element){",
" this.element = element; ",
" this.next = null; ",
" }; ",
"",
" this.head = function(){",
" return head;",
" };",
"",
" this.size = function(){",
" return length;",
" };",
"",
" this.add = function(element){",
" // Only change code below this line",
"",
" // Only change code above this line",
" };",
"};"
],
"tests": [
"assert((function(){var test = new LinkedList(); return (typeof test.add === 'function')}()), 'message: Your <code>LinkedList</code> class should have a <code>add</code> method.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); return test.head().element === 'cat'}()), 'message: Your <code>LinkedList</code> class should assign <code>head</code> to the first node added.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); return test.head().next.element === 'dog'}()), 'message: The previous <code>node</code> in your <code>LinkedList</code> class should have reference to the newest node created.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); return test.size() === 2}()), 'message: The <code>size</code> of your <code>LinkedList</code> class should equal the amount of nodes in the linked list.');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d8251367417b2b2512c63",
"title": "Remove Elements from a Linked List",
"description": [
"The next important method of a linked list is the remove method. The remove method takes an element and searches the linked list to find and remove the node containing that element. When a node is removed, the previous node that had reference to removed node, now has reference to the node that the removed node once referenced.",
"This might sound really confusing, but let's return to the conga line example. You're in conga line, and the person straight ahead of you leaves the line. The person leaving the line, no longer has his hands on any one in line and you no longer have hands on the person that left. You step forward and put your hands on next person you see.",
"If the element being removed is the head element, the head is reassigned to the second node of the linked list.",
"Instructions",
"Write a remove method that takes an element and removes it from the linked list.",
"Note",
"Length should decrease by one every time an element is removed from the linked list."
],
"challengeSeed": [
"function LinkedList() { ",
" var length = 0; ",
" var head = null; ",
"",
" var Node = function(element){ ",
" this.element = element; ",
" this.next = null; ",
" }; ",
"",
" this.length = function(){",
" return length;",
" };",
"",
" this.head = function(){",
" return head;",
" };",
"",
" this.add = function(element){",
" var node = new Node(element);",
" if(head === null){",
" head = node;",
" } else {",
" currentNode = head;",
"",
" while(currentNode.next){",
" currentNode = currentNode.next;",
" }",
"",
" currentNode.next = node;",
" }",
"",
" length++;",
" }; ",
"",
" this.remove = function(element){",
" // Only change code below this line",
"",
" // Only change code above this line",
" };",
"};"
],
"tests": [
"assert((function(){var test = new LinkedList(); return (typeof test.remove === 'function')}()), 'message: Your <code>LinkedList</code> class should have a <code>remove</code> method.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.remove('cat'); return test.head().element === 'dog'}()), 'message: Your <code>Remove</code> method should reassign <code>head</code> to the second node when the first node is removed.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.remove('cat'); return test.length() === 1}()), 'message: Your <code>Remove</code> method should decrease the <code>length</code> of the linked list by one for every node removed.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog');test.add('kitten'); test.remove('dog'); return test.head().next.element === 'kitten'}()), 'message: Your <code>Remove</code> method should reassign the reference of the previous node of the removed node to the removed node's <code>next</code> reference ');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d8251367417b2b2512c64",
"title": "Search within a Linked List",
"description": [
"Let's add a few more useful methods to our linked list class. Like the stack and queue classes, we should add an isEmpty method to check if the linked list is empty.",
"We also want to find elements in our linked list. Let's create a indexOf method that takes an element and returns the indexs of it in the linked list. The method should return -1 if the element is not found in the linked list. We also need an elementAt method that takes an index and returns the element at the given index. The method should return undefined if no element is found.",
"Instructions",
"Write isEmpty method that checks if the linked list is empty, a size method that returns the length of the linked list, a indexOf method that returns the index of a given element, and an elementAt that returns an element at a given index."
],
"challengeSeed": [
"function LinkedList() { ",
" var length = 0; ",
" var head = null; ",
"",
" var Node = function(element){ // {1} ",
" this.element = element; ",
" this.next = null; ",
" }; ",
"",
" this.size = function() {",
" return length;",
" };",
"",
" this.head = function(){",
" return head;",
" };",
"",
" this.add = function(element){",
" var node = new Node(element);",
" if(head === null){",
" head = node;",
" } else {",
" currentNode = head;",
"",
" while(currentNode.next){",
" currentNode = currentNode.next;",
" }",
"",
" currentNode.next = node;",
" }",
"",
" length++;",
" }; ",
"",
" this.remove = function(element){",
" var currentNode = head;",
" var previousNode;",
" if(currentNode.element === element){",
" head = currentNode.next;",
" } else {",
" while(currentNode.element !== element) {",
" previousNode = currentNode;",
" currentNode = currentNode.next;",
" }",
"",
" previousNode.next = currentNode.next;",
" }",
"",
" length --;",
" };",
"",
" // Only change code below this line",
"",
" // Only change code above this line",
"};"
],
"tests": [
"assert((function(){var test = new LinkedList(); return (typeof test.indexOf === 'function')}()), 'message: Your <code>LinkedList</code> class should have a <code>indexOf</code> method.');",
"assert((function(){var test = new LinkedList(); return (typeof test.elementAt === 'function')}()), 'message: Your <code>LinkedList</code> class should have a <code>elementAt</code> method.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.add('kitten'); return test.size() === 3}()), 'message: Your <code>size</code> method should return the length of the linked list');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.add('kitten'); return test.indexOf('kitten') === 2}()), 'message: Your <code>indexOf</code> method should return the index of the given element.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.add('kitten'); return test.elementAt(1) === 'dog'}()), 'message: Your <code>elementAt</code> method should return at element at a given index.');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d8251367417b2b2512c65",
"title": "Remove Elements from a Linked List by Index",
"description": [
"Now we need to create a remove method that removes the element at a given index. The method should be called removeAt(index). To remove an element at a certain index we need to keep count of each node as we move along the linked list. Starting at the head of the linked list, our currentIndex should be 0. The currentIndex should increment by one for each node we pass. Just like the remove(element) method, we need to reconnect the nodes. The node that has reference to the removed node should now have reference to the next node.",
"Instructions",
"Write a removeAt(index) method that removes and returns a node at a given index. The method should return null if the given index is a negative or is more than or equal to the length of the linked list.",
"Note",
"Remember to keep count of the currentIndex."
],
"challengeSeed": [
"function LinkedList() { ",
" var length = 0; ",
" var head = null; ",
"",
" var Node = function(element){ // {1} ",
" this.element = element; ",
" this.next = null; ",
" }; ",
"",
" this.size = function(){",
" return length;",
" };",
"",
" this.head = function(){",
" return head;",
" };",
"",
" this.add = function(element){",
" var node = new Node(element);",
" if(head === null){",
" head = node;",
" } else {",
" currentNode = head;",
"",
" while(currentNode.next){",
" currentNode = currentNode.next;",
" }",
"",
" currentNode.next = node;",
" }",
"",
" length++;",
" }; ",
"",
" this.remove = function(element){",
" var currentNode = head;",
" var previousNode;",
" if(currentNode.element === element){",
" head = currentNode.next;",
" } else {",
" while(currentNode.element !== element) {",
" previousNode = currentNode;",
" currentNode = currentNode.next;",
" }",
"",
" previousNode.next = currentNode.next;",
" }",
"",
" length --;",
" };",
"",
" // Only change code below this line",
"",
" // Only change code above this line",
"};"
],
"tests": [
"assert((function(){var test = new LinkedList(); return (typeof test.removeAt === 'function')}()), 'message: Your <code>LinkedList</code> class should have a <code>removeAt</code> method.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.add('kitten'); test.removeAt(1); return test.size() === 2}()), 'message: Your <code>removeAt</code> method should reduce the <code>length</code> of the linked list');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.add('kitten'); return test.removeAt(1) === 'dog'}()), 'message: Your <code>removeAt</code> method should also return the element of the removed node.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.add('kitten'); return (test.removeAt(-1) === null)}()), 'message: Your <code>removeAt</code> method should also return <code>null</code> if the given index is less than <code>0</code>');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.add('kitten'); return (test.removeAt(3) === null)}()), 'message: Your <code>removeAt</code> method should also return <code>null</code> if the given index is equal or more than the <code>length</code> of the linked list.');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d8252367417b2b2512c67",
"title": "Add Elements at a Specific Index in a Linked List",
"description": [
"Let's create a addAt(index,element) method that adds an element at a given index.",
"Just like how we remove elements at a given index, we need to keep track of the currentIndex as we traverse the linked list. When the currentIndex matches the given index, we would need to reassign the previous node's next property to reference the new added node. And the new node should reference the next node in the currentIndex.",
"Returning to the conga line example, a new person wants to join the line, but he wants to join in the middle. You are in the middle of the line, so you take your hands off of the person ahead of you. The new person walks over and puts his hands on the person you once had hands on, and you now have your hands on the new person.",
"Instructions",
"Create an addAt(index,element) method that adds an element at a given index. Return false if an element was unable to be added.",
"Note",
"Remember to check if the given index is a negative or is longer than the length of the linked list."
],
"challengeSeed": [
"function LinkedList() { ",
" var length = 0; ",
" var head = null; ",
"",
" var Node = function(element){",
" this.element = element; ",
" this.next = null; ",
" }; ",
"",
" this.size = function(){",
" return length;",
" };",
"",
" this.head = function(){",
" return head;",
" };",
"",
" this.add = function(element){",
" var node = new Node(element);",
" if(head === null){",
" head = node;",
" } else {",
" currentNode = head;",
"",
" while(currentNode.next){",
" currentNode = currentNode.next;",
" }",
"",
" currentNode.next = node;",
" }",
"",
" length++;",
" }; ",
"",
" // Only change code below this line",
"",
" // Only change code above this line",
"",
"};"
],
"tests": [
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.addAt(0,'cat'); return test.head().element === 'cat'}()), 'message: Your <code>addAt</code> method should reassign <code>head</code> to the new node when the given index is 0.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.addAt(0,'cat'); return test.size() === 3}()), 'message: Your <code>addAt</code> method should increase the length of the linked list by one for each new node added to the linked list.');",
"assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); return (test.addAt(4,'cat') === false); }()), 'message: Your <code>addAt</code> method should return <code>false</code> if a node was unable to be added.');"
],
"solutions": [],
"hints": [],
"type": "waypoint",
"challengeType": 1,
"translations": {}
},
{
"id": "587d825a367417b2b2512c87",
"title": "Create a Doubly Linked List",
"description": [
"All of the linked lists we've created so far are singly linked lists. Here, we'll create",
"a doubly linked list. As the name implies, nodes in a doubly linked list have references to the next and previous node in the list. This allows us to traverse the list in both directions but it also requires more memory to be used because every node must contain an additional reference to the previous node in the list.",
"Instructions: We've provided a Node object and started our DoublyLinkedList. Let's",
"add two methods to our doubly linked list called add and remove. The add method should accept integers or strings and add them to the list and remove should accept an integer or string and remove all copies of this integer or string that exist in the list. Be careful to handle any possible edge cases when writing these methods, such as deletions for the first or last element. Also, removing any item on an empty list should return null."
],
"challengeSeed": [
"var Node = function(data, prev) {",
" this.data = data;",
" this.prev = prev;",
" this.next = null;",
"};",
"var DoublyLinkedList = function() {",
" this.head = null;",
" this.tail = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tail": [
"DoublyLinkedList.prototype = {",
" print() {",
" if (this.head == null) {",
" return null;",
" } else {",
" var result = new Array();",
" var node = this.head;",
" while (node.next != null) {",
" result.push(node.data);",
" node = node.next;",
" };",
" result.push(node.data);",
" return result;",
" };",
" },",
" printReverse() {",
" if (this.tail == null) {",
" return null;",
" } else {",
" var result = new Array();",
" var node = this.tail;",
" while (node.prev != null) {",
" result.push(node.data);",
" node = node.prev;",
" };",
" result.push(node.data);",
" return result;",
" };",
" } ",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; return (typeof test == 'object')})(), 'message: The DoublyLinkedList data structure exists.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; if (test.add == undefined) { return false; }; return (typeof test.add == 'function')})(), 'message: The DoublyLinkedList has a method called add.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; if (test.remove == undefined) { return false; }; return (typeof test.remove == 'function')})(), 'message: The DoublyLinkedList has a method called remove.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; return (test.remove(100) == null); })(), 'message: Removing an item from an empty list returns null.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; test.add(5); test.add(6); test.add(723); return (test.print().join('') == '56723'); })(), 'message: The add method adds items to the list.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; test.add(50); test.add(68); test.add(73); return (test.printReverse().join('') == '736850'); })(), 'message: Each node keeps track of the previous node.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; test.add(25); test.add(35); test.add(60); test.remove(25); return ( test.print().join('') == '3560' ) })(), 'message: The first item can be removed from the list.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; test.add(25); test.add(35); test.add(60); test.remove(60); return ( test.print().join('') == '2535' ) })(), 'message: The last item can be removed from the list.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d825a367417b2b2512c88",
"title": "Reverse a Doubly Linked List",
"description": [
"Let's create one more method for our doubly linked list called reverse which reverses the list in place. Once the method is executed the head should point to the previous tail and the tail should point to the previous head. Now, if we traverse the list from head to tail we should meet the nodes in a reverse order compared to the original list. Trying to reverse an empty list should return null."
],
"challengeSeed": [
"var Node = function(data, prev) {",
" this.data = data;",
" this.prev = prev;",
" this.next = null;",
"};",
"var DoublyLinkedList = function() {",
" this.head = null;",
" this.tail = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; return (typeof test == 'object')})(), 'message: The DoublyLinkedList data structure exists.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; if (test.add == undefined) { return false; }; return (typeof test.add == 'function')})(), 'message: The DoublyLinkedList has a method called add.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; if (test.reverse == undefined) { return false; }; return (typeof test.reverse == 'function')})(), 'message: The DoublyLinkedList has a method called reverse.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; return (test.reverse() == null); })(), 'message: Reversing an empty list returns null.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; test.add(58); test.add(61); test.add(32); test.reverse(); return (test.print().join('') == '326158'); })(), 'message: The reverse method reverses the list.');",
"assert((function() { var test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; test.add(11); test.add(22); test.add(33); test.reverse(); return (test.printReverse().join('') == '112233'); })(), 'message: The next and previous references are correctly maintained when a list is reversed.');"
],
"tail": [
"DoublyLinkedList.prototype = {",
" add(data) {",
" if (this.head == null) {",
" this.head = new Node(data, null);",
" this.tail = this.head;",
" } else {",
" var node = this.head;",
" var prev = null;",
" while (node.next != null) {",
" prev = node;",
" node = node.next;",
" };",
" var newNode = new Node(data, node);",
" node.next = newNode;",
" this.tail = newNode;",
" };",
" },",
" print() {",
" if (this.head == null) {",
" return null;",
" } else {",
" var result = new Array();",
" var node = this.head;",
" while (node.next != null) {",
" result.push(node.data);",
" node = node.next;",
" };",
" result.push(node.data);",
" return result;",
" };",
" },",
" printReverse() {",
" if (this.tail == null) {",
" return null;",
" } else {",
" var result = new Array();",
" var node = this.tail;",
" while (node.prev != null) {",
" result.push(node.data);",
" node = node.prev;",
" };",
" result.push(node.data);",
" return result;",
" };",
" }",
"};"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8256367417b2b2512c7a",
"title": "Find the Minimum and Maximum Value in a Binary Search Tree",
"description": [
"This series of challenges will introduce the tree data structure. Trees are an important and versatile data structure in computer science. Of course, their name comes from the fact that when visualized they look much like the trees we are familiar with in the natural world. A tree data structure begins with one node, typically referred to as the root, and from here branches out to additional nodes, each of which may have more child nodes, and so on and so forth. The data structure is usually visualized with the root node at the top; you can think of it as a natural tree flipped upside down.",
"First, let's describe some common terminology we will encounter with trees. The root node is the top of the tree. Data points in the tree are called nodes. Nodes with branches leading to other nodes are referred to as the parent of the node the branch leads to (the child). Other more complicated familial terms apply as you might expect. A subtree refers to all the descendants of a particular node, branches may be referred to as edges, and leaf nodes are nodes at the end of the tree that have no children. Finally, note that trees are inherently recursive data structures. That is, any children of a node are parents of their own subtree, and so on. The recursive nature of trees is important to understand when designing algorithms for common tree operations.",
"To begin, we will discuss a particular type of a tree, the binary tree. In fact, we will actually discuss a particular binary tree, a binary search tree. Let's describe what this means. While the tree data structure can have any number of branches at a single node, a binary tree can only have two branches for every node. Furthermore, a binary search tree is ordered with respective to the child subtrees, such that each left subtree is less than or equal to the parent node, and each right subtree is greater than or equal to the parent node. Binary search trees are very common and useful data structures because they provide logarithmic time in the average case for several common operations such as lookup, insertion, and deletion.",
"Instructions: We'll start simple. We've defined the skeleton of a binary search tree structure here in addition to a function to create nodes for our tree. Observe that each node may have a left and right value. These will be assigned child subtrees if they exist. In our binary search tree, define two methods,",
"findMin and findMax. These methods should return the minimum and maximum value held in the binary search tree. If you get stuck, reflect on the invariant that must be true for binary search trees: each left subtree is less than or equal to its parent and each right subtree is greater than or equal to",
"its parent. Let's also say that our tree can only store integer values. If the tree is empty, either method should return null."
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"function BinarySearchTree() {",
" this.root = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.findMin == 'function')})(), 'message: The binary search tree has a method called findMin.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.findMax == 'function')})(), 'message: The binary search tree has a method called findMax.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.findMin !== 'function') { return false; }; test.add(4); test.add(1); test.add(7); test.add(87); test.add(34); test.add(45); test.add(73); test.add(8); return test.findMin() == 1; })(), 'message: The findMin method returns the minimum value in the binary search tree.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.findMax !== 'function') { return false; }; test.add(4); test.add(1); test.add(7); test.add(87); test.add(34); test.add(45); test.add(73); test.add(8); return test.findMax() == 87; })(), 'message: The findMax method returns the maximum value in the binary search tree.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.findMin !== 'function') { return false; }; if (typeof test.findMax !== 'function') { return false; }; return (test.findMin() == null && test.findMax() == null) })(), 'message: The findMin and findMax methods return null for an empty tree.');"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" }",
"};"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8257367417b2b2512c7b",
"title": "Add a New Element to a Binary Search Tree",
"description": [
"Now that we have an idea of the basics lets write a more complex method.",
"In this challenge, we will create a method to add new values to our binary search tree. The method should be called add and it should accept an integer value to add to the tree. Take care to maintain the invariant of a binary search tree: the value in each left child should be less than or equal to the parent value, and the value in each right child should be greater than or equal to the parent value. Here, let's make it so our tree cannot hold duplicate values. If we try to add a value that already exists, the method should return null. Otherwise, if the addition is successful, undefined should be returned.",
"Hint: trees are naturally recursive data structures!"
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"function BinarySearchTree() {",
" this.root = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tail": [
"BinarySearchTree.prototype = {",
" isBinarySearchTree() {",
" if (this.root == null) {",
" return null;",
" } else {",
" var check = true;",
" function checkTree(node) {",
" if (node.left != null) {",
" var left = node.left;",
" if (left.value > node.value) {",
" check = false;",
" } else {",
" checkTree(left);",
" }",
" }",
" if (node.right != null) {",
" var right = node.right;",
" if (right.value < node.value) {",
" check = false;",
" } else {",
" checkTree(right);",
" };",
" };",
" };",
" checkTree(this.root);",
" return check;",
" };",
" }",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.add == 'function')})(), 'message: The binary search tree has a method called add.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.add !== 'function') { return false; }; test.add(4); test.add(1); test.add(7); test.add(87); test.add(34); test.add(45); test.add(73); test.add(8); return (test.isBinarySearchTree()); })(), 'message: The add method adds elements according to the binary search tree rules.')",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.add !== 'function') { return false; }; test.add(4); return test.add(4) == null; })(), 'message: Adding an element that already exists returns null');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8257367417b2b2512c7c",
"title": "Check if an Element is Present in a Binary Search Tree",
"description": [
"Now that we have a general sense of what a binary search tree is let's talk about it in a little more detail. Binary search trees provide logarithmic time for the common operations of lookup, insertion, and deletion in the average case, and linear time in the worst case. Why is this? Each of those basic operations requires us to find an item in the tree (or in the case of insertion to find where it should go) and because of the tree structure at each parent node we are branching left or right and effectively excluding half the size of the remaining tree. This makes the search proportional to the logarithm of the number of nodes in the tree, which creates logarithmic time for these operations in the average case.",
"Ok, but what about the worst case? Well, consider constructing a tree from the following values, adding them left to right: 10, 12, 17, 25. Following our rules for a binary search tree, we will add 12 to the right of 10, 17 to the right of this, and 25 to the right of this. Now our tree resembles a linked list and traversing it to find 25 would require us to traverse all the items in linear fashion. Hence, linear time in the worst case. The problem here is that the tree is unbalanced. We'll look a little more into what this means in the following challenges.",
"Instructions: In this challenge, we will create a utility for our tree. Write a method isPresent which takes an integer value as input and returns a boolean value for the presence or absence of that value in the binary search tree."
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"function BinarySearchTree() { ",
" this.root = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" }",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.isPresent == 'function')})(), 'message: The binary search tree has a method called isPresent.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.isPresent !== 'function') { return false; }; test.add(4); test.add(7); test.add(411); test.add(452); return ( test.isPresent(452) && test.isPresent(411) && test.isPresent(7) && !test.isPresent(100) ); })(), 'message: The isPresent method correctly checks for the presence or absence of elements added to the tree.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.isPresent !== 'function') { return false; }; return test.isPresent(5) == false; })(), 'message: isPresent handles cases where the tree is empty.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8257367417b2b2512c7d",
"title": "Find the Minimum and Maximum Height of a Binary Search Tree",
"description": [
"In the last challenge we described a scenario in which a tree could become unbalanced. To understand the concept of balance, let's take a look at another tree property: height. Height in a tree represents the distance from the root node to any given leaf node. Different paths in a highly branched tree structure may have different heights, but for a given tree there will be a minimum and maximum height. If the tree is balanced, these values will differ at most by one. This means that in a balanced tree, all the leaf nodes exist within the same level, or if they are not within the same level they are at most one level apart.",
"The property of balance is important for trees because it is what determines the efficiency of tree operations. As we explained in the last challenge, we face worst case time complexity for heavily unbalanced trees. Self-balancing trees are commonly used to account for this issue in trees with dynamic data sets. Common examples of these include AVL trees, red-black trees, and B-trees. These trees all contain additional internal logic which re-balance the tree when insertions or deletions create a state of imbalance.",
"Note: A similar property to height is depth, which refers to how far a given node is from the root node.",
"Instructions: Write two methods for our binary tree: findMinHeight and findMaxHeight. These methods should return an integer value for the minimum and maximum height within a given binary tree, respectively. If the node is empty let's assign it a height of -1 (that's the base case). Finally, add a third method isBalanced which returns a true or false depending on whether the tree is balanced or not. You can use the first two methods you just wrote to determine this."
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"function BinarySearchTree() {",
" this.root = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" }",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.findMinHeight == 'function')})(), 'message: The binary search tree has a method called findMinHeight.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.findMaxHeight == 'function')})(), 'message: The binary search tree has a method called findMaxHeight.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.isBalanced == 'function')})(), 'message: The binary search tree has a method called isBalanced.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.findMinHeight !== 'function') { return false; }; test.add(4); test.add(1); test.add(7); test.add(87); test.add(34); test.add(45); test.add(73); test.add(8); return (test.findMinHeight() == 1); })(), 'message: The findMinHeight method returns the minimum height of the tree.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.findMaxHeight !== 'function') { return false; }; test.add(4); test.add(1); test.add(7); test.add(87); test.add(34); test.add(45); test.add(73); test.add(8); return (test.findMaxHeight() == 5); })(), 'message: The findMaxHeight method returns the maximum height of the tree.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.findMaxHeight !== 'function') { return false; }; return (test.findMaxHeight() == -1); })(), 'message: An empty tree returns a height of -1.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.isBalanced !== 'function') { return false; }; test.add(4); test.add(1); test.add(7); test.add(87); test.add(34); test.add(45); test.add(73); test.add(8); return test.isBalanced(); })(), 'message: The isBalanced method returns true if the tree is a balanced binary search tree.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8257367417b2b2512c7e",
"title": "Use Depth First Search in a Binary Search Tree",
"description": [
"We know how to search a binary search tree for a specific value. But what if we just want to explore the entire tree? Or what if we don't have an ordered tree and we need to just search for a value? Here we will introduce some tree traversal methods which can be used to explore tree data structures. First up is depth-first search. In depth-first search, a given subtree is explored as deeply as possible before the search continues on to another subtree. There are three ways this can be done:",
"In-order: Begin the search at the left-most node and end at the right-most node.",
"Pre-order: Explore all the roots before the leaves.",
"Post-order: Explore all the leaves before the roots.",
"As you may guess, you may choose different search methods depending on what type of data your tree is storing and what you are looking for. For a binary search tree, an inorder traversal returns the nodes in sorted order.",
"Instructions: Here we will create these three search methods on our binary search tree. Depth-first search is an inherently recursive operation which continues to explore further subtrees so long as child nodes are present. Once you understand this basic concept, you can simply rearrange the order in which you explore the nodes and subtrees to produce any of the three searches above. For example, in post-order search we would want to recurse all the way to a leaf node before we begin to return any of the nodes themselves, whereas in pre-order search we would want to return the nodes first, and then continue recursing down the tree.",
"Define inorder, preorder, and postorder methods on our tree. Each of these methods should return an array of items which represent the tree traversal. Be sure to return the integer values at each node in the array, not the nodes themselves. Finally, return null if the tree is empty."
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"function BinarySearchTree() {",
" this.root = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" }",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.inorder == 'function')})(), 'message: The binary search tree has a method called inorder.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.preorder == 'function')})(), 'message: The binary search tree has a method called preorder.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.postorder == 'function')})(), 'message: The binary search tree has a method called postorder.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.inorder !== 'function') { return false; }; test.add(7); test.add(1); test.add(9); test.add(0); test.add(3); test.add(8); test.add(10); test.add(2); test.add(5); test.add(4); test.add(6); return (test.inorder().join('') == '012345678910'); })(), 'message: The inorder method returns an array of the node values that result from an inorder traversal.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.preorder !== 'function') { return false; }; test.add(7); test.add(1); test.add(9); test.add(0); test.add(3); test.add(8); test.add(10); test.add(2); test.add(5); test.add(4); test.add(6); return (test.preorder().join('') == '710325469810'); })(), 'message: The preorder method returns an array of the node values that result from a preorder traversal.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.postorder !== 'function') { return false; }; test.add(7); test.add(1); test.add(9); test.add(0); test.add(3); test.add(8); test.add(10); test.add(2); test.add(5); test.add(4); test.add(6); return (test.postorder().join('') == '024653181097'); })(), 'message: The postorder method returns an array of the node values that result from a postorder traversal.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.inorder !== 'function') { return false; }; return (test.inorder() == null); })(), 'message: The inorder method returns null for an empty tree.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.preorder !== 'function') { return false; }; return (test.preorder() == null); })(), 'message: The preorder method returns null for an empty tree.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.postorder !== 'function') { return false; }; return (test.postorder() == null); })(), 'message: The postorder method returns null for an empty tree.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8258367417b2b2512c7f",
"title": "Use Breadth First Search in a Binary Search Tree",
"description": [
"Here we will introduce another tree traversal method: breadth-first search. In contrast to the depth-first search methods from the last challenge, breadth-first search explores all the nodes in a given level within a tree before continuing on to the next level. Typically, queues are utilized as helper data structures in the design of breadth-first search algorithms.",
"In this method, we start by adding the root node to a queue. Then we begin a loop where we dequeue the first item in the queue, add it to a new array, and then inspect both its child subtrees. If it's children are not null, they are each enqueued. This process continues until the queue is empty.",
"Instructions: Let's create a breadth-first search method in our tree called levelOrder. This method should return an array containing the values of all the tree nodes, explored in a breadth-first manner. Be sure to return the values in the array, not the nodes themselves. A level should be",
"traversed from left to right. Next, let's write a similar method called reverseLevelOrder which performs the same search but in the reverse direction (right to left) at each level."
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"function BinarySearchTree() {",
" this.root = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" }",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.levelOrder == 'function')})(), 'message: The binary search tree has a method called levelOrder.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.reverseLevelOrder == 'function')})(), 'message: The binary search tree has a method called reverseLevelOrder.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.levelOrder !== 'function') { return false; }; test.add(7); test.add(1); test.add(9); test.add(0); test.add(3); test.add(8); test.add(10); test.add(2); test.add(5); test.add(4); test.add(6); return (test.levelOrder().join('') == '719038102546'); })(), 'message: The levelOrder method returns an array of the tree node values explored in level order.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.reverseLevelOrder !== 'function') { return false; }; test.add(7); test.add(1); test.add(9); test.add(0); test.add(3); test.add(8); test.add(10); test.add(2); test.add(5); test.add(4); test.add(6); return (test.reverseLevelOrder().join('') == '791108305264'); })(), 'message: The reverseLevelOrder method returns an array of the tree node values explored in reverse level order.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.levelOrder !== 'function') { return false; }; return (test.levelOrder() == null); })(), 'message: The levelOrder method returns null for an empty tree.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.reverseLevelOrder !== 'function') { return false; }; return (test.reverseLevelOrder() == null); })(), 'message: The reverseLevelOrder method returns null for an empty tree.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8258367417b2b2512c80",
"title": "Delete a Leaf Node in a Binary Search Tree",
"description": [
"This is the first of three challenges where we will implement a more difficult operation in binary search trees: deletion. Deletion is difficult because removing nodes breaks links in the tree. These links must be carefully reestablished to ensure the binary tree structure is maintained. For some deletions, this means the tree must be rearranged. In general, you will encounter one of three cases when trying to delete a node:",
"Leaf Node: The target to delete has zero children.",
"One Child: The target to delete only has one child.",
"Two Children: The target to delete has two child nodes.",
"Removing a leaf node is easy, we simply remove it. Deleting a node with one child is also relatively easy, we simply remove it and link its parent to child of the node we deleted. Removing a node with two children is more difficult, however, because this creates two child nodes that need to be reconnected to the parent tree. We'll see how to deal with this case in the third challenge. Additionally, you need to be mindful of some edge cases when handling deletion. What if the tree is empty? What if the node to delete is the root node? What if there are only two elements in the tree? For now, let's handle the first case where we delete a leaf node.",
"Instructions: Create a method on our binary tree called remove. We'll build the logic for our deletion operation in here. First, you'll want to create a function within remove that finds the node we are trying to delete in the current tree. If the node is not present in the tree, remove should return null. Now, if the target node is a leaf node with no children, then the parent reference to it should be set to null. This effectively deletes the node from the tree. To do this, you will have to keep track of",
"the parent of the node we are trying to delete as well. It will also be useful to create a way to track the number of children the target node has, as this will determine which case our deletion falls under.",
"We will handle the second and third cases in the next challenges. Good luck!"
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"",
"function BinarySearchTree() {",
" this.root = null;",
" // case 1: target has no children, change code below this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function')})(), 'message: The binary search tree has a method called remove.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; return (test.remove(100) == null); })(), 'message: Trying to remove an element that does not exist returns null.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(500); test.remove(500); return (test.inorder() == null); })(), 'message: If the root node has no children, deleting it sets the root to null.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(5); test.add(3); test.add(7); test.add(6); test.add(10); test.add(12); test.remove(3); test.remove(12); test.remove(10); return (test.inorder().join('') == '567'); })(), 'message: The remove method removes leaf nodes from the tree');"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" },",
" inorder: function() {",
" if (this.root == null) {",
" return null;",
" } else {",
" var result = new Array();",
" function traverseInOrder(node) {",
" if (node.left != null) {",
" traverseInOrder(node.left);",
" };",
" result.push(node.value);",
" if (node.right != null) {",
" traverseInOrder(node.right);",
" };",
" }",
" traverseInOrder(this.root);",
" return result;",
" };",
" }, ",
" isBinarySearchTree() {",
" if (this.root == null) {",
" return null;",
" } else {",
" var check = true;",
" function checkTree(node) {",
" if (node.left != null) {",
" var left = node.left;",
" if (left.value > node.value) {",
" check = false;",
" } else {",
" checkTree(left);",
" }",
" }",
" if (node.right != null) {",
" var right = node.right;",
" if (right.value < node.value) {",
" check = false;",
" } else {",
" checkTree(right);",
" };",
" };",
" };",
" checkTree(this.root);",
" return check;",
" }",
" }",
"};"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8258367417b2b2512c81",
"title": "Delete a Node with One Child in a Binary Search Tree",
"description": [
"Now that we can delete leaf nodes let's move on to the second case: deleting a node with one child. For this case, say we have a tree with the following nodes 1 — 2 — 3 where 1 is the root. To delete 2, we simply need to make the right reference in 1 point to 3. More generally,",
"to delete a node with only one child, we make that node's parent reference the next node in the tree.",
"Instructions: We've provided some code in our remove method that accomplishes the tasks from the last challenge. We find the target to delete and its parent and define the number of children",
"the target node has. Let's add the next case here for target nodes with only one child. Here, we'll have to determine if the single child is a left or right branch in the tree and then set the correct reference in the parent to point to this node. In addition, let's account for the case where the target is the root node (this means the parent node will be null). Feel free to replace all the starter code with your own as long as it passes the tests."
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"",
"function BinarySearchTree() {",
" this.root = null;",
" this.remove = function(value) {",
" if (this.root == null) {",
" return null;",
" };",
" var target;",
" var parent = null;",
" // find the target value and its parent",
" (function findValue(node = this.root) {",
" if (value == node.value) {",
" target = node;",
" } else if (value < node.value && node.left != null) {",
" parent = node;",
" return findValue(node.left);",
" } else if (value < node.value && node.left == null) {",
" return null;",
" } else if (value > node.value && node.right != null) {",
" parent = node;",
" return findValue(node.right);",
" } else {",
" return null;",
" };",
" }).bind(this)();",
" if (target == null) {",
" return null;",
" }",
" // count the children of the target to delete",
" var children = (target.left != null ? 1 : 0) + (target.right != null ? 1 : 0);",
" // case 1: target has no children",
" if (children == 0) {",
" if (target == this.root) {",
" this.root = null;",
" }",
" else {",
" if (parent.left == target) {",
" parent.left = null;",
" } else {",
" parent.right = null;",
" };",
" };",
" }",
" // case 2: target has one child, change code below this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function')})(), 'message: The binary search tree has a method called remove.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; return (test.remove(100) == null); })(), 'message: Trying to remove an element that doesn\\'t exist returns null.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(500); test.remove(500); return (test.inorder() == null); })(), 'message: If the root node has no children, deleting it sets the root to null.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(5); test.add(3); test.add(7); test.add(6); test.add(10); test.add(12); test.remove(3); test.remove(12); test.remove(10); return (test.inorder().join('') == '567'); })(), 'message: The remove method removes leaf nodes from the tree');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(-1); test.add(3); test.add(7); test.add(16); test.remove(16); test.remove(7); test.remove(3); return (test.inorder().join('') == '-1'); })(), 'message: The remove method removes nodes with one child.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(15); test.add(27); test.remove(15); return (test.inorder().join('') == '27'); })(), 'message: Removing the root in a tree with two nodes sets the second to be the root.');"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" },",
" inorder: function() {",
" if (this.root == null) {",
" return null;",
" } else {",
" var result = new Array();",
" function traverseInOrder(node) {",
" if (node.left != null) {",
" traverseInOrder(node.left);",
" };",
" result.push(node.value);",
" if (node.right != null) {",
" traverseInOrder(node.right);",
" };",
" }",
" traverseInOrder(this.root);",
" return result;",
" };",
" }, ",
" isBinarySearchTree() {",
" if (this.root == null) {",
" return null;",
" } else {",
" var check = true;",
" function checkTree(node) {",
" if (node.left != null) {",
" var left = node.left;",
" if (left.value > node.value) {",
" check = false;",
" } else {",
" checkTree(left);",
" }",
" }",
" if (node.right != null) {",
" var right = node.right;",
" if (right.value < node.value) {",
" check = false;",
" } else {",
" checkTree(right);",
" };",
" };",
" };",
" checkTree(this.root);",
" return check;",
" }",
" }",
"};"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8258367417b2b2512c82",
"title": "Delete a Node with Two Children in a Binary Search Tree",
"description": [
"Removing nodes that have two children is the hardest case to implement. Removing a node like this produces two subtrees that are no longer connected to the original tree structure. How can we reconnect them? One method is to find the smallest value in the right subtree of the target node and replace the target node with this value. Selecting the replacement in this way ensures that it is greater than every node in the left subtree it becomes the new parent of but also less than every node in the right subtree it becomes the new parent of.",
"Once this replacement is made the replacement node must be removed from the right subtree. Even this operation is tricky because the replacement may be a leaf or it may itself be the parent of a right subtree. If it is a leaf we must remove its parent's reference to it. Otherwise, it must be the right child of the target. In this case, we must replace the target value with the replacement value and make the target reference the replacement's right child.",
"Instructions: Let's finish our remove method by handling the third case. We've provided some code again for the first two cases. Add some code now to handle target nodes with two children. Any edge cases to be aware of? What if the tree has only three nodes? Once you are finished this will complete our deletion operation for binary search trees. Nice job, this is a pretty hard problem!"
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"",
"function BinarySearchTree() {",
" this.root = null;",
" this.remove = function(value) {",
" if (this.root == null) {",
" return null;",
" };",
" var target;",
" var parent = null;",
" // find the target value and its parent",
" (function findValue(node = this.root) {",
" if (value == node.value) {",
" target = node;",
" } else if (value < node.value && node.left != null) {",
" parent = node;",
" return findValue(node.left);",
" } else if (value < node.value && node.left == null) {",
" return null;",
" } else if (value > node.value && node.right != null) {",
" parent = node;",
" return findValue(node.right);",
" } else {",
" return null;",
" };",
" }).bind(this)();",
" if (target == null) {",
" return null;",
" }",
" // count the children of the target to delete",
" var children = (target.left != null ? 1 : 0) + (target.right != null ? 1 : 0);",
" // case 1: target has no children",
" if (children == 0) {",
" if (target == this.root) {",
" this.root = null;",
" }",
" else {",
" if (parent.left == target) {",
" parent.left = null;",
" } else {",
" parent.right = null;",
" };",
" };",
" }",
" // case 2: target has one child",
" else if (children == 1) {",
" var newChild = (target.left != null) ? target.left : target.right;",
" if (parent == null) {",
" target.value = newChild.value;",
" target.left = null;",
" target.right = null;",
" } else if (newChild.value < parent.value) {",
" parent.left = newChild;",
" } else {",
" parent.right = newChild;",
" }",
" target = null;",
" }",
" // case 3: target has two children, change code below this line",
" };",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function')})(), 'message: The binary search tree has a method called remove.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function') ? (test.remove(100) == null) : false})(), 'message: Trying to remove an element that doesn\\'t exist returns null.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; test.add(500); test.remove(500); return (typeof test.remove == 'function') ? (test.inorder() == null) : false})(), 'message: If the root node has no children, deleting it sets the root to null.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; test.add(5); test.add(3); test.add(7); test.add(6); test.add(10); test.add(12); test.remove(3); test.remove(12); test.remove(10); return (typeof test.remove == 'function') ? (test.inorder().join('') == '567') : false})(), 'message: The remove method removes leaf nodes from the tree');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(-1); test.add(3); test.add(7); test.add(16); test.remove(16); test.remove(7); test.remove(3); return (test.inorder().join('') == '-1'); })(), 'message: The remove method removes nodes with one child.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(15); test.add(27); test.remove(15); return (test.inorder().join('') == '27'); })(), 'message: Removing the root in a tree with two nodes sets the second to be the root.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(1); test.add(4); test.add(3); test.add(7); test.add(9); test.add(11); test.add(14); test.add(15); test.add(19); test.add(50); test.remove(9); if (!test.isBinarySearchTree()) { return false; }; test.remove(11); if (!test.isBinarySearchTree()) { return false; }; test.remove(14); if (!test.isBinarySearchTree()) { return false; }; test.remove(19); if (!test.isBinarySearchTree()) { return false; }; test.remove(3); if (!test.isBinarySearchTree()) { return false; }; test.remove(50); if (!test.isBinarySearchTree()) { return false; }; test.remove(15); if (!test.isBinarySearchTree()) { return false; }; return (test.inorder().join('') == '147'); })(), 'message: The remove method removes nodes with two children while maintaining the binary search tree structure.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(100); test.add(50); test.add(300); test.remove(100); return (test.inorder().join('') == 50300); })(), 'message: The root can be removed on a tree of three nodes.');"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" },",
" inorder: function() {",
" if (this.root == null) {",
" return null;",
" } else {",
" var result = new Array();",
" function traverseInOrder(node) {",
" if (node.left != null) {",
" traverseInOrder(node.left);",
" };",
" result.push(node.value);",
" if (node.right != null) {",
" traverseInOrder(node.right);",
" };",
" }",
" traverseInOrder(this.root);",
" return result;",
" };",
" }, ",
" isBinarySearchTree() {",
" if (this.root == null) {",
" return null;",
" } else {",
" var check = true;",
" function checkTree(node) {",
" if (node.left != null) {",
" var left = node.left;",
" if (left.value > node.value) {",
" check = false;",
" } else {",
" checkTree(left);",
" }",
" }",
" if (node.right != null) {",
" var right = node.right;",
" if (right.value < node.value) {",
" check = false;",
" } else {",
" checkTree(right);",
" };",
" };",
" };",
" checkTree(this.root);",
" return check;",
" }",
" }",
"};"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8259367417b2b2512c83",
"title": "Invert a Binary Tree",
"description": [
"Here will we create a function to invert a binary tree. Given a binary tree, we want to produce a new tree that is equivalently the mirror image of this tree. Running an inorder traversal on an inverted tree will explore the nodes in reverse order when compared to the inorder traversal of the original tree. Write a method to do this called invert on our binary tree. Calling this method should invert the current tree structure. Ideally, we would like to do this in-place in linear time. That is, we only visit each node once and we modify the existing tree structure as we go, without using any additional memory. Good luck!"
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"function Node(value) {",
" this.value = value;",
" this.left = null;",
" this.right = null;",
"};",
"function BinarySearchTree() {",
" this.root = null;",
" // change code below this line",
" // change code above this line",
"};"
],
"tail": [
"BinarySearchTree.prototype = {",
" add: function(value) {",
" var node = this.root;",
" if (node == null) {",
" this.root = new Node(value);",
" return;",
" } else {",
" function searchTree(node) {",
" if (value < node.value) {",
" if (node.left == null) {",
" node.left = new Node(value);",
" return;",
" } else if (node.left != null) {",
" return searchTree(node.left)",
" };",
" } else if (value > node.value) {",
" if (node.right == null) {",
" node.right = new Node(value);",
" return;",
" } else if (node.right != null) {",
" return searchTree(node.right);",
" };",
" } else {",
" return null;",
" };",
" };",
" return searchTree(node);",
" };",
" },",
" inorder: function() {",
" if (this.root == null) {",
" return null;",
" } else {",
" var result = new Array();",
" function traverseInOrder(node) {",
" if (node.left != null) {",
" traverseInOrder(node.left);",
" };",
" result.push(node.value);",
" if (node.right != null) {",
" traverseInOrder(node.right);",
" };",
" }",
" traverseInOrder(this.root);",
" return result;",
" };",
" }",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'message: The BinarySearchTree data structure exists.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.invert == 'function')})(), 'message: The binary search tree has a method called invert.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.invert !== 'function') { return false; }; test.add(4); test.add(1); test.add(7); test.add(87); test.add(34); test.add(45); test.add(73); test.add(8); test.invert(); return test.inorder().join('') == '877345348741'; })(), 'message: The invert method correctly inverts the tree structure.');",
"assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.invert !== 'function') { return false; }; return (test.invert() == null); })(), 'message: Inverting an empty tree returns null.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8259367417b2b2512c84",
"title": "Create a Trie Search Tree",
"description": [
"Here we will move on from binary search trees and take a look at another type of tree structure called a trie. A trie is an ordered search tree commonly used to hold strings, or more generically associative arrays or dynamic datasets in which the keys are strings. They are very good at storing sets of data when many keys will have overlapping prefixes, for example, all",
"the words in a dictionary.",
"Unlike a binary tree, nodes are not associated with actual values. Instead, the path to a node represents a specific key. For instance, if we wanted to store the string code in a trie, we would have four nodes, one for each letter: c — o — d — e. Following that path through all these nodes will then create code as a string — that path is the key we stored. Then, if we wanted to add the string coding, it would share the first three nodes of code before branching away after the d. In this way, large datasets can be stored very compactly. In addition, search can be very quick because it is effectively limited to the length of the string you are storing. Furthermore, unlike binary trees a node can store any number of child nodes.",
"As you might have guessed from the above example, some metadata is commonly stored at nodes that hold the end of a key so that on later traversals that key can still be retrieved. For instance, if we added codes in our example above we would need some way to know that the e in code represents the end of a key that was previously entered. Otherwise, this information would effectively be lost when we add codes.",
"Instructions: Let's create a trie to store words. It will accept words through an add method and store these in a trie data structure. It will also allow us to query if a given string is a word with an isWord method, and retrieve all the words entered into the trie with a print method. isWord should return a",
"boolean value and print should return an array of all these words as string values.",
"In order for us to verify that this data structure is implemented correctly, we've provided a Node structure for each node in the tree. Each node will be an object with a keys property which is a JavaScript Map object. This will hold the individual letters that are valid keys of each node. We've also",
"created an end property on the nodes that can be set to true if the node represents the termination of a word."
],
"challengeSeed": [
"var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));",
"var Node = function() {",
" this.keys = new Map();",
" this.end = false;",
" this.setEnd = function() {",
" this.end = true;",
" };",
" this.isEnd = function() {",
" return this.end;",
" };",
"};",
"var Trie = function() {",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function testTrie() { var test = false; if (typeof Trie !== 'undefined') { test = new Trie() } else { return false; }; return (typeof test.add == 'function') }()), 'message: The Trie has an add method.');",
"assert((function testTrie() { var test = false; if (typeof Trie !== 'undefined') { test = new Trie() } else { return false; }; return (typeof test.print == 'function') }()), 'message: The Trie has a print method.');",
"assert((function testTrie() { var test = false; if (typeof Trie !== 'undefined') { test = new Trie() } else { return false; }; return (typeof test.isWord == 'function') }()), 'message: The Trie has an isWord method.');",
"assert((function testTrie() { var test = false; if (typeof Trie !== 'undefined') { test = new Trie() } else { return false; }; test.add('jump'); test.add('jumps'); test.add('jumped'); test.add('house'); test.add('mouse'); var added = test.print(); return (added.indexOf('jump') != -1 && added.indexOf('jumps') != -1 && added.indexOf('jumped') != -1 && added.indexOf('house') != -1 && added.indexOf('mouse') != -1 && added.length == 5); }()), 'message: The print method returns all items added to the trie as strings in an array.');",
"assert((function testTrie() { var test = false; if (typeof Trie !== 'undefined') { test = new Trie() } else { return false; }; test.add('hop'); test.add('hops'); test.add('hopped'); test.add('hoppy'); test.add('hope'); return (test.isWord('hop') && !test.isWord('ho') && test.isWord('hopped') && !test.isWord('hopp') && test.isWord('hoppy') && !test.isWord('hoping')); }()), 'message: The isWord method returns true only for words added to the trie and false for all other words.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d825a367417b2b2512c8a",
"title": "Insert an Element into a Max Heap",
"description": [
"Now we will move on to another tree data structure, the binary heap. A binary heap is a partially ordered binary tree which satisfies the heap property. The heap property specifies a relationship between parent and child nodes. You may have a max heap, in which all parent nodes are greater than or equal to their child nodes, or a min heap, in which the reverse is true. Binary heaps are also complete binary trees. This means that all levels of the tree are fully filled and if the last level is partially filled it is filled from left to right.",
"While binary heaps may be implemented as tree structures with nodes that contain left and right references, the partial ordering according to the heap property allows us to represent the heap with an array. The parent-children relationship is what we're interested in and with simple arithmetic we can compute the children of any parent and the parent of any child node.",
"For instance, consider this array representation of a binary max heap:",
"<code>[ 6, 22, 30, 37, 63, 48, 42, 76 ]</code>",
"The root node is the first element, 6. Its children are 22 and 30. If we look at the relationship between the array indices of these values, for index i the children are 2 * i + 1 and 2 * i + 2. Similarly, the element at index 0 is the parent of these two children at indices 1 and 2. More generally, we can find the parent of a node at any index with the following: i - 1 / 2. These patterns will hold true as the binary tree grows to any size. Finally, we can make a slight adjustment to make this arithmetic even easier by skipping the first element in the array. Doing this creates the following relationship for any element at a given index i:",
"Example Array representation: <code>[ null, 22, 30, 37, 63, 48, 42, 76 ]</code>",
"An element's left child: i * 2",
"An element's right child: i * 2 + 1",
"An element's parent: i / 2",
"Once you wrap your head around the math, using an array representation is very useful because node locations can be quickly determined with this arithmetic and memory usage is diminished because you don't need to maintain references to child nodes.",
"Instructions: Here we will create a max heap. Start by just creating an insert method which adds elements to our heap. During insertion, it is important to always maintain the heap property. For a max heap this means the root element should always have the greatest value in the tree and all parent nodes should be greater than their children. For an array implementation of a heap, this is typically accomplished in three steps:",
"Add the new element to the end of the array.",
"If the element is larger than its parents, switch them.",
"Continue switching until the new element is either smaller than its parent or you reach the root of the tree.",
"Finally, add a print method which returns an array of all the items that have been added to the heap."
],
"challengeSeed": [
"var MaxHeap = function() {",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() }; return (typeof test == 'object')})(), 'message: The MaxHeap data structure exists.');",
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() } else { return false; }; return (typeof test.insert == 'function')})(), 'message: MaxHeap has a method called insert.');",
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() } else { return false; }; return (typeof test.print == 'function')})(), 'message: MaxHeap has a method called print.');",
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() } else { return false; }; test.insert(50); test.insert(100); test.insert(700); test.insert(32); test.insert(51); let result = test.print(); return ((result.length == 5) ? result[0] == 700 : result[1] == 700) })(), 'message: The insert method adds elements according to the max heap property.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d825b367417b2b2512c8b",
"title": "Remove an Element from a Max Heap",
"description": [
"Now that we can add elements to our heap let's see how we can remove elements. Removing and inserting elements both require similar logic. In a max heap you will usually want to remove the greatest value, so this involves simply extracting it from the root of our tree. This will break the heap property of our tree, so we must reestablish it in some way. Typically, for a max heap this is done in the following way:",
"Move the last element in the heap into the root position.",
"If either child of the root is greater than it, swap the root with the child of greater value.",
"Continue swapping until the parent is greater than both children, or you reach the last level in the tree.",
"Instructions: Add a method to our max heap called remove. This method should return the greatest value that has been added to our max heap and remove it from the heap. It should also reorder the heap so the heap property is maintained. After removing an element, the next greatest element remaining in the heap should become the root. Add your insert method again here as well."
],
"challengeSeed": [
"var MaxHeap = function() {",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() }; return (typeof test == 'object')})(), 'The MaxHeap data structure exists.');",
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() } else { return false; }; return (typeof test.print == 'function')})(), 'MaxHeap has a method called print.');",
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() } else { return false; }; return (typeof test.insert == 'function')})(), 'MaxHeap has a method called insert.');",
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() } else { return false; }; return (typeof test.remove == 'function')})(), 'MaxHeap has a method called remove.');",
"assert((function() { var test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() } else { return false; }; test.insert(30); test.insert(300); test.insert(500); test.insert(10); let result = []; result.push(test.remove()); result.push(test.remove()); result.push(test.remove()); result.push(test.remove()); return (result.join('') == '5003003010') })(), 'The remove method removes the greatest element from the max heap while maintaining the max heap property.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d825b367417b2b2512c8c",
"title": "Implement Heap Sort with a Min Heap",
"description": [
"Now that we can add and remove elements let's see some of the applications heaps can be used for. Heaps are commonly used to implement priority queues because they always store an item of greatest or least value in first position. In addition, they are used to implement a sorting algorithm called heap sort. We'll see how to do this here. Heap sort uses a min heap, the reverse of a max heap. A min heap always stores the element of least value in the root position.",
"Heap sort works by taking an unsorted array, adding each item in the array into a min heap, and then extracting every item out of the min heap into a new array. The min heap structure ensures that the new array will contain the original items in least to greatest order. This is one of the most efficient sorting algorithms with average and worst case performance of O(nlog(n)).",
"Instructions: Let's implement heap sort with a min heap. Feel free to adapt your max heap code here. Create an object MinHeap with insert, remove, and sort methods. The sort method should return an array of all the elements in the min heap sorted from smallest to largest."
],
"challengeSeed": [
"// check if array is sorted",
"function isSorted(arr) {",
" var check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);",
" return check(0);",
"};",
"// generate a randomly filled array",
"var array = new Array();",
"(function createArray(size = 5) {",
" array.push(+(Math.random() * 100).toFixed(0));",
" return (size > 1) ? createArray(size - 1) : undefined;",
"})(25);",
"var MinHeap = function() {",
" // change code below this line",
" // change code above this line",
"};"
],
"tests": [
"assert((function() { var test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() }; return (typeof test == 'object')})(), 'message: The MinHeap data structure exists.');",
"assert((function() { var test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() } else { return false; }; return (typeof test.insert == 'function')})(), 'message: MinHeap has a method called insert.');",
"assert((function() { var test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() } else { return false; }; return (typeof test.remove == 'function')})(), 'message: MinHeap has a method called remove.');",
"assert((function() { var test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() } else { return false; }; return (typeof test.sort == 'function')})(), 'message: MinHeap has a method called sort.');",
"assert((function() { var test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() } else { return false; }; test.insert(3); test.insert(12); test.insert(5); test.insert(10); test.insert(1); test.insert(27); test.insert(42); test.insert(57); test.insert(5); var result = test.sort(); return (isSorted(result)); })(), 'message: The sort method returns an array containing all items added to the min heap in sorted order.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8255367417b2b2512c76",
"title": "Introduction to Graphs",
"description": [
[
"//i.stack.imgur.com/XfuFe.gif",
"Picture of an X-Y coordinate graph seen in typical math classes",
"This challenge starts a series of challenges that will teach graph data structures and some algorithms involving graphs. You may be familiar with graphs with an x- and y-axis found in your math classes. However, here graphs mean a different thing.",
""
],
[
"//upload.wikimedia.org/wikipedia/commons/5/5b/6n-graf.svg",
"Figure of undirected network with 6 nodes",
"The data structure of <dfn>graphs</dfn> are collections of things and the relationships or connections among them. Graphs are also known as networks.<br><br>When talking about graphs, the precise term for \"objects\" are <dfn>nodes</dfn> or <dfn>vertices</dfn>. Similarly, the precise term for \"connections\" is <dfn>edges</dfn>.",
""
],
[
"//upload.wikimedia.org/wikipedia/commons/b/b6/Moreno_Sociogram_2nd_Grade.png",
"Figure of psychologist Moreno's social network of a 2nd grade class",
"One example of graphs is a social network where the <dfn>nodes</dfn> are you and other people, and the edges are whether two people are friends with each other.",
""
],
[
"//i.stack.imgur.com/5xkVt.png",
"Figures of directed graphs with arrows on edges while undirected graphs do not have directions on edges",
"There are two major types of graphs: <dfn>directed</dfn> and <dfn>undirected</dfn>. Undirected graphs are graphs without any direction on the edges between nodes. Directed graphs, in contrast, are graphs with a direction in its edges.<br><br>An example of an undirected graph could be a social network. The nodes are people and the edges are friendship. An example of a directed network could be the internet and web page links. Here, the nodes are web pages and the directed edges are links to other pages, which might not necessarily point the other way.",
""
]
],
"challengeSeed": [],
"tests": [],
"type": "waypoint",
"solutions": [],
"challengeType": 7,
"translations": {}
},
{
"id": "587d8256367417b2b2512c77",
"title": "Adjacency List",
"description": [
"Graphs can be represented in different ways. Here we describe one way, which is called an <dfn>adjacency list</dfn>.",
"An adjacency list is essentially a bulleted list where the left side is the node and the right side lists all the other nodes it's connected to. Below is a representation of an adjacency list.",
"<blockquote>Node1: Node2, Node3<br>Node2: Node1<br>Node3: Node1</blockquote>",
"Above is an undirected graph because <code>Node1</code> is connected to <code>Node2</code> and <code>Node3</code>, and that information is consistent with the connections <code>Node2</code> and <code>Node3</code> show. An adjacency list for a directed graph would mean each row of the list shows direction. If the above was directed, then <code>Node2: Node1</code> would mean there the directed edge is pointing from <code>Node2</code> towards <code>Node1</code>.",
"We can represent the undirected graph above as an adjacency list by putting it within a JavaScript object.",
"<blockquote>var undirectedG = {<br> Node1: [\"Node2\", \"Node3\"],<br> Node2: [\"Node1\"],<br> Node3: [\"Node3\"]<br>};</blockquote>",
"This can also be more simply represented as an array where the nodes just have numbers rather than string labels.",
"<blockquote>var undirectedGArr = [<br> [1, 2], # Node1<br> [0], # Node2<br> [2] # Node3<br>];</blockquote>",
"<hr>",
"Create a social network as an undirected graph with 4 nodes/people named <code>James</code>, <code>Jill</code>, <code>Jenny</code>, and <code>Jeff</code>. There are edges/relationships between James and Jeff, Jill and Jenny, and Jeff and Jenny."
],
"challengeSeed": [
"var undirectedAdjList = {",
"};"
],
"tests": [
"assert(Object.keys(undirectedAdjList).length === 4, 'message: <code>undirectedAdjList</code> should only contain four nodes.');",
"assert(undirectedAdjList.James.includes(\"Jeff\") && undirectedAdjList.Jeff.includes(\"James\"), 'message: There should be an edge between <code>Jeff</code> and <code>James</code>.');",
"assert(undirectedAdjList.Jill.includes(\"Jenny\") && undirectedAdjList.Jill.includes(\"Jenny\"), 'message: There should be an edge between <code>Jill</code> and <code>Jenny</code>.');",
"assert(undirectedAdjList.Jeff.includes(\"Jenny\") && undirectedAdjList.Jenny.includes(\"Jeff\"), 'message: There should be an edge between <code>Jeff</code> and <code>Jenny</code>.');"
],
"type": "waypoint",
"solutions": [
"var undirectedAdjList = {\n\"James\": [\"Jeff\"],\"Jill\": [\"Jenny\"],\"Jenny\": [\"Jill\", \"Jeff\"],\n\"Jeff\": [\"James\", \"Jenny\"]\n};"
],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8256367417b2b2512c78",
"title": "Adjacency Matrix",
"description": [
"Another way to represent a graph is to put it in an <dfn>adjacency matrix</dfn>.",
"An <dfn>adjacency matrix</dfn> is a two-dimensional (2D) array where each nested array has the same number of elements as the outer array. In other words, it is a matrix or grid of numbers, where the numbers represent the edges. Zeros mean there is no edge or relationship.",
"<blockquote> 1 2 3<br> ------<br>1 | 0 1 1<br>2 | 1 0 0<br>3 | 1 0 0</blockquote>",
"Above is a very simple, undirected graph where you have three nodes, where the first node is connected to the second and third node. <strong>Note</strong>: The numbers to the top and left of the matrix are just labels for the nodes.",
"Below is a JavaScript implementation of the same thing.",
"<blockquote>var adjMat = [<br> [0, 1, 1],<br> [1, 0, 0],<br> [1, 0, 0]<br>];</blockquote>",
"Unlike an adjacency list, each \"row\" of the matrix has to have the same number of elements as nodes in the graph. Here we have a three by three matrix, which means we have three nodes in our graph.",
"A directed graph would look similar. Below is a graph where the first node has an edge pointing toward the second node, and then the second node has an edge pointing to the third node.",
"<blockquote>var adjMatDirected = [<br> [0, 1, 0],<br> [0, 0, 1],<br> [0, 0, 0]<br>];</blockquote>",
"Graphs can also have <dfn>weights</dfn> on their edges. So far, we have <dfn>unweighted</dfn> edges where just the presence and lack of edge is binary (<code>0</code> or <code>1</code>). You can have different weights depending on your application.",
"<hr>",
"Create an adjacency matrix of an undirected graph with five nodes. This matrix should be in a multi-dimensional array. These five nodes have relationships between the first and fourth node, the first and third node, the third and fifth node, and the fourth and fifth node. All edge weights are one."
],
"challengeSeed": [
"var adjMatUndirected = [",
"];"
],
"tests": [
"assert((adjMatUndirected.length === 5) && adjMatUndirected.map(function(x) { return x.length === 5 }).reduce(function(a, b) { return a && b }) , 'message: <code>undirectedAdjList</code> should only contain five nodes.');",
"assert((adjMatUndirected[0][3] === 1) && (adjMatUndirected[3][0] === 1), 'message: There should be an edge between the first and fourth node.');",
"assert((adjMatUndirected[0][2] === 1) && (adjMatUndirected[2][0] === 1), 'message: There should be an edge between the first and third node.');",
"assert((adjMatUndirected[2][4] === 1) && (adjMatUndirected[4][2] === 1), 'message: There should be an edge between the third and fifth node.');",
"assert((adjMatUndirected[3][4] === 1) && (adjMatUndirected[4][3] === 1), 'message: There should be an edge between the fourth and fifth node.');"
],
"type": "waypoint",
"solutions": [
"var adjMatUndirected = [[0, 0, 1, 1, 0],[0, 0, 0, 0, 0],[1, 0, 0, 0, 1],[1, 0, 0, 0, 1],[0, 0, 1, 1, 0]];"
],
"challengeType": 1,
"translations": {}
},
{
"id": "587d8256367417b2b2512c79",
"title": "Incidence Matrix",
"description": [
"Yet another way to represent a graph is to put it in an <dfn>incidence matrix.</dfn>",
"An <dfn>incidence matrix</dfn> is a two-dimensional (2D) array. Generally speaking, an incidence matrix relates two different classes of objects between its two dimensions. This kind of matrix is similar to an adjacency matrix. However, the rows and columns mean something else here.",
"In graphs, we have edges and nodes. These will be our \"two different classes of objects\". This matrix will have the rows be the nodes and columns be the edges. This means that we can have an uneven number of rows and columns.",
"Each column will represent a unique edge. Also, each edge connects two nodes. To show that there is an edge between two nodes, you will put a 1 in the two rows of a particular column. Below is a 3 node graph with one edge between node 1 and node 3.",
"<blockquote> 1<br> ---<br>1 | 1<br>2 | 0<br>3 | 1</blockquote>",
"Here is an example of an <code>incidence matrix</code> with 4 edges and 4 nodes. Remember, the columns are the edges and rows are the nodes themselves.",
"<blockquote> 1 2 3 4<br> --------<br>1 | 0 1 1 1<br>2 | 1 1 0 0<br>3 | 1 0 0 1<br>4 | 0 0 1 0</blockquote>",
"Below is a JavaScript implementation of the same thing.",
"<blockquote>var incMat = [<br> [0, 1, 1, 1],<br> [1, 1, 0, 0],<br> [1, 0, 0, 1],<br> [0, 0, 1, 0]<br>];</blockquote>",
"To make a directed graph, use <code>-1</code> for an edge leaving a particular node and <code>1</code> for an edge entering a node.",
"<blockquote>var incMatDirected = [<br> [ 0, -1, 1, -1],<br> [-1, 1, 0, 0],<br> [ 1, 0, 0, 1],<br> [ 0, 0, -1, 0]<br>];</blockquote>",
"Graphs can also have <dfn>weights</dfn> on their edges. So far, we have <dfn>unweighted</dfn> edges where just the presence and lack of edge is binary (<code>0</code> or <code>1</code>). You can have different weights depending on your application. A different weight is represented as numbers greater than 1.",
"<hr>",
"Create an incidence matrix of an undirected graph with five nodes and four edges. This matrix should be in a multi-dimensional array.",
"These five nodes have relationships following relationships. The first edge is between the first and second node. The second edge is between the second and third node. The third edge is between the third and fifth node. And four edge is between the fourth and second node. All edge weights are one and the edge order matters."
],
"challengeSeed": [
"var incMatUndirected = [",
" ",
"];"
],
"tests": [
"assert((incMatUndirected.length === 5) && incMatUndirected.map(function(x) { return x.length === 4 }).reduce(function(a, b) { return a && b }) , 'message: <code>incMatUndirected</code> should only contain five nodes.');",
"assert((incMatUndirected[0][0] === 1) && (incMatUndirected[1][0] === 1), 'message: There should be a first edge between the first and second node.');",
"assert((incMatUndirected[1][1] === 1) && (incMatUndirected[2][1] === 1), 'message: There should be a second edge between the second and third node.');",
"assert((incMatUndirected[2][2] === 1) && (incMatUndirected[4][2] === 1), 'message: There should be a third edge between the third and fifth node.');",
"assert((incMatUndirected[1][3] === 1) && (incMatUndirected[3][3] === 1), 'message: There should be a fourth edge between the second and fourth node.');"
],
"type": "waypoint",
"solutions": [
"var incMatUndirected = [[1, 0, 0, 0],[1, 1, 0, 1],[0, 1, 1, 0],[0, 0, 0, 1],[0, 0, 1, 0]];"
],
"challengeType": 1,
"translations": {}
},
{
"id": "587d825c367417b2b2512c90",
"title": "Breadth-First Search",
"description": [
"So far, we've learned different ways of creating representations of graphs. What now? One natural question to have is what are the distances between any two nodes in the graph? Enter <dfn>graph traversal algorithms</dfn>.",
"<dfn>Traversal algorithms</dfn> are algorithms to traverse or visit nodes in a graph. One type of traversal algorithm is the breadth-first search algorithm.",
"This algorithm starts at one node, first visits all its neighbors that are one edge away, then goes on to visiting each of their neighbors.",
"Visually, this is what the algorithm is doing.",
"<img class='img-responsive' src='https://camo.githubusercontent.com/2f57e6239884a1a03402912f13c49555dec76d06/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f342f34362f416e696d617465645f4246532e676966'>",
"To implement this algorithm, you'll need to input a graph structure and a node you want to start at.",
"First, you'll want to be aware of the distances from the start node. This you'll want to start all your distances initially some large number, like <code>Infinity</code>. This gives a reference for the case where a node may not be reachable from your start node.",
"Next, you'll want to go from the start node to its neighbors. These neighbors are one edge away and at this point you should add one unit of distance to the distances you're keeping track of.",
"Last, an important data structure that will help implement the breadth-first search algorithm is the queue. This is an array where you can add elements to one end and remove elements from the other end. This is also known as a <dfn>FIFO</dfn> or <dfn>First-In-First-Out</dfn> data structure.",
"<hr>",
"Write a function <code>bfs()</code> that takes an adjacency matrix graph (a two-dimensional array) and a node label root as parameters. The node label will just be the integer value of the node between <code>0</code> and <code>n - 1</code>, where <code>n</code> is the total number of nodes in the graph.",
"Your function will output a JavaScript object key-value pairs with the node and its distance from the root. If the node could not be reached, it should have a distance of <code>Infinity</code>."
],
"challengeSeed": [
"function bfs(graph, root) {",
" // Distance object returned",
" var nodesLen = {};",
" ",
" return nodesLen;",
"}",
"",
"var exBFSGraph = [",
" [0, 1, 0, 0],",
" [1, 0, 1, 0],",
" [0, 1, 0, 1],",
" [0, 0, 1, 0]",
"];",
"console.log(bfs(exBFSGraph, 3));"
],
"tests": [
"assert((function() { var graph = [[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0]]; var results = bfs(graph, 1); return isEquivalent(results, {0: 1, 1: 0, 2: 1, 3: 2})})(), 'message: The input graph <code>[[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0]]</code> with a start node of <code>1</code> should return <code>{0: 1, 1: 0, 2: 1, 3: 2}</code>');",
"assert((function() { var graph = [[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]]; var results = bfs(graph, 1); return isEquivalent(results, {0: 1, 1: 0, 2: 1, 3: Infinity})})(), 'message: The input graph <code>[[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]]</code> with a start node of <code>1</code> should return <code>{0: 1, 1: 0, 2: 1, 3: Infinity}</code>');",
"assert((function() { var graph = [[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0]]; var results = bfs(graph, 0); return isEquivalent(results, {0: 0, 1: 1, 2: 2, 3: 3})})(), 'message: The input graph <code>[[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0]]</code> with a start node of <code>0</code> should return <code>{0: 0, 1: 1, 2: 2, 3: 3}</code>');",
"assert((function() { var graph = [[0, 1], [1, 0]]; var results = bfs(graph, 0); return isEquivalent(results, {0: 0, 1: 1})})(), 'message: The input graph <code>[[0, 1], [1, 0]]</code> with a start node of <code>0</code> should return <code>{0: 0, 1: 1}</code>');"
],
"tail": [
"// Source: http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html",
"function isEquivalent(a, b) {",
" // Create arrays of property names",
" var aProps = Object.getOwnPropertyNames(a);",
" var bProps = Object.getOwnPropertyNames(b);",
" // If number of properties is different,",
" // objects are not equivalent",
" if (aProps.length != bProps.length) {",
" return false;",
" }",
" for (var i = 0; i < aProps.length; i++) {",
" var propName = aProps[i];",
" // If values of same property are not equal,",
" // objects are not equivalent",
" if (a[propName] !== b[propName]) {",
" return false;",
" }",
" }",
" // If we made it this far, objects",
" // are considered equivalent",
" return true;",
"}"
],
"type": "waypoint",
"solutions": [
"function bfs(graph, root) {\n// Distance object returned\nvar nodesLen = {};\n// Set all distances to infinity\nfor (var i = 0; i < graph.length; i++) {\nnodesLen[i] = Infinity;\n}\nnodesLen[root] = 0; // ...except root node\nvar queue = [root]; // Keep track of nodes to visit\nvar current; // Current node traversing\n// Keep on going until no more nodes to traverse\nwhile (queue.length !== 0) {\ncurrent = queue.shift();\n// Get adjacent nodes from current node\nvar curConnected = graph[current]; // Get layer of edges from current\nvar neighborIdx = []; // List of nodes with edges\nvar idx = curConnected.indexOf(1); // Get first edge connection\nwhile (idx !== -1) {\nneighborIdx.push(idx); // Add to list of neighbors\nidx = curConnected.indexOf(1, idx + 1); // Keep on searching\n}\n// Loop through neighbors and get lengths\nfor (var j = 0; j < neighborIdx.length; j++) {\n// Increment distance for nodes traversed\nif (nodesLen[neighborIdx[j]] === Infinity) {\nnodesLen[neighborIdx[j]] = nodesLen[current] + 1;\nqueue.push(neighborIdx[j]); // Add new neighbors to queue\n}\n}\n}\nreturn nodesLen;}"
],
"challengeType": 1,
"translations": {}
},
{
"id": "587d825d367417b2b2512c96",
"title": "Depth-First Search",
"description": [
"Similar to breadth-first search, here we will learn about another graph traversal algorithm called depth-first search.",
"Whereas the breadth-first search searches incremental edge lengths away from the source node, depth-first search first goes down a path of edges as far as it can.",
"Once it reaches one end of a path, the search will backtrack to the last node with an un-visited edge path and continue searching.",
"Visually, this is what the algorithm is doing where the top node is the starting point of the search.",
"<img src='https://camo.githubusercontent.com/aaad9e39961daf34d967c616edeb50abf3bf1235/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f372f37662f44657074682d46697273742d5365617263682e676966'>",
"A simple output of this algorithm is a list of nodes which are reachable from a given node. So when implementing this algorithm, you'll need to keep track of the nodes you visit.",
"Instructions",
"Write a function dfs() that takes an undirected, adjacency matrix graph and a node label root as parameters. The node label will just be the numeric value of the node between 0 and n - 1, where n is the total number of nodes in the graph.",
"Your function should output an array of all nodes reachable from root."
],
"challengeSeed": [
"function dfs(graph, root) {",
" ",
"};",
"var exDFSGraph = [",
" [0, 1, 0, 0],",
" [1, 0, 1, 0],",
" [0, 1, 0, 1],",
" [0, 0, 1, 0]",
"];",
"console.log(dfs(exDFSGraph, 3));"
],
"tests": [
"assert.deepEqual((function() { var graph = [[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0]]; return dfs(graph, 1);})(), [0, 1, 2, 3], 'message: The input graph <code>[[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0]]</code> with a start node of <code>1</code> should return <code>[0, 1, 2, 3]</code>.');",
"assert.deepEqual((function() { var graph = [[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]]; return dfs(graph, 3);})(), [3], 'message: The input graph <code>[[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]]</code> with a start node of <code>1</code> should return <code>[3]</code>.');",
"assert.deepEqual((function() { var graph = [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]; return dfs(graph, 3);})(), [2, 3], 'message: The input graph <code>[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]</code> with a start node of <code>3</code> should return <code>[2, 3]</code>.');",
"assert.deepEqual((function() { var graph = [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]; return dfs(graph, 0);})(), [0, 1], 'message: The input graph <code>[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]</code> with a start node of <code>0</code> should return <code>[0, 1]</code>.');"
],
"type": "waypoint",
"solutions": [],
"challengeType": 1,
"translations": {}
}
]
}