From ffe8c743d2da8aaef4eb9dc2e24fe530d797a38c Mon Sep 17 00:00:00 2001 From: Sean Smith Date: Wed, 25 Jan 2017 00:04:16 -0800 Subject: [PATCH] Improvements to adv. algos and data structures --- .../coding-interview-algorithm-questions.json | 197 +- ...ng-interview-data-structure-questions.json | 5123 +++++++---------- 2 files changed, 2143 insertions(+), 3177 deletions(-) diff --git a/challenges/08-coding-interview-questions-and-take-home-assignments/coding-interview-algorithm-questions.json b/challenges/08-coding-interview-questions-and-take-home-assignments/coding-interview-algorithm-questions.json index 01d7732f6f4..52d8660e9b3 100644 --- a/challenges/08-coding-interview-questions-and-take-home-assignments/coding-interview-algorithm-questions.json +++ b/challenges/08-coding-interview-questions-and-take-home-assignments/coding-interview-algorithm-questions.json @@ -98,7 +98,7 @@ "id": "a3f503de51cf954ede28891d", "title": "Symmetric Difference", "description": [ - "Create a function that takes two or more arrays and returns an array of the iterative symmetric difference ( or ) of the provided arrays. In other words, start with the first array and keep on taking symmetric differences of each the following arrays after it.", + "Create a function that takes two or more arrays and returns an array of the symmetric difference ( or ) of the provided arrays.", "Given two sets (for example set A = {1, 2, 3} and set B = {2, 3, 4}), the mathematical term \"symmetric difference\" of two sets is the set of elements which are in either of the two sets, but not in both (A △ B = C = {1, 4}). For every additional symmetric difference you take (say on a set D = {2, 3}), you should get the set with elements which are in either of the two the sets but not both (C △ D = {1, 4} △ {2, 3} = {1, 2, 3, 4}). The resulting array must contain only unique values (no duplicates).", "Remember to use Read-Search-Ask if you get stuck. Try to pair program. Write your own code." ], @@ -378,7 +378,6 @@ } } }, - { "id": "a3f503de51cfab748ff001aa", "title": "Pairwise", @@ -448,6 +447,198 @@ ] } } + }, + { + "id": "8d5123c8c441eddfaeb5bdef", + "title": "Implement Bubble Sort", + "description": [ + "This is the first of several challenges on sorting algorithms. Given an array of unsorted items, we want to be able to return a sorted array. We will see several different methods to do this and learn some tradeoffs between these different approaches. While most modern languages have built-in sorting methods for operations like this, it is still important to understand some of the common basic approaches and learn how they can be implemented.", + "Here we will see bubble sort. The bubble sort method starts at the beginning of an unsorted array and 'bubbles up' unsorted values towards the end, iterating through the array until it is completely sorted. It does this by comparing adjacent items and swapping them if they are out of order. The method continues looping through the array until no swaps occur at which point the array is sorted.", + "This method requires multiple iterations through the array and for average and worst cases has quadratic time complexity. While simple, it is usually impractical in most situations.", + "Instructions: Write a function bubbleSort which takes an array of integers as input and returns an array of these integers in sorted order. We've provided a helper method to generate a random array of integer values." + ], + "challengeSeed": [ + "// helper function to generate a randomly filled array", + "var array = [];", + "(function createArray(size) {", + " array.push(+(Math.random() * 100).toFixed(0));", + " return (size > 1) ? createArray(size - 1) : undefined;", + "})(12);", + "", + "function bubbleSort(array) {", + " // change code below this line", + "", + " // change code above this line", + " return array;", + "}" + ], + "tail": [ + "function isSorted(arr) {", + " var check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", + " return check(0);", + "};" + ], + "tests": [ + "assert(typeof bubbleSort == 'function', 'message: bubbleSort is a function.');", + "assert(isSorted(bubbleSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'message: bubbleSort returns a sorted array.');" + ], + "type": "waypoint", + "solutions": [], + "challengeType": 1, + "translations": {} + }, + { + "id": "587d8259367417b2b2512c85", + "title": "Implement Selection Sort", + "description": [ + "Here we will implement selection sort. Selection sort works by selecting the minimum value in a list and swapping it with the first value in the list. It then starts at the second position, selects the smallest value in the remaining list, and swaps it with the second element. It continues iterating through the list and swapping elements until it reaches the end of the list. Now the list is sorted. Selection sort has quadratic time complexity in all cases.", + "Instructions: Write a function selectionSort which takes an array of integers as input and returns an array of these integers in sorted order." + ], + "challengeSeed": [ + "// helper function to generate a randomly filled array", + "var array = [];", + "(function createArray(size) {", + " array.push(+(Math.random() * 100).toFixed(0));", + " return (size > 1) ? createArray(size - 1) : undefined;", + "})(12);", + "", + "function selectionSort(array) {", + " // change code below this line", + "", + " // change code above this line", + " return array;", + "}" + ], + "tail": [ + "function isSorted(arr) {", + " var check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", + " return check(0);", + "};" + ], + "tests": [ + "assert(typeof selectionSort == 'function', 'message: selectionSort is a function.');", + "assert(isSorted(selectionSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'message: selectionSort returns a sorted array.');" + ], + "type": "waypoint", + "solutions": [], + "challengeType": 1, + "translations": {} + }, + { + "id": "587d8259367417b2b2512c86", + "title": "Implement Insertion Sort", + "description": [ + "The next sorting method we'll look at is insertion sort. This method works by building up a sorted array at the beginning of the list. It begins the sorted array with the first element. Then it inspects the next element and swaps it backwards into the sorted array until it is in sorted position. It continues iterating through the list and swapping new items backwards into the sorted portion until it reaches the end. This algorithm has quadratic time complexity in the average and worst cases.", + "Instructions: Write a function insertionSort which takes an array of integers as input and returns an array of these integers in sorted order." + ], + "challengeSeed": [ + "// helper function to generate a randomly filled array", + "var array = [];", + "(function createArray(size) {", + " array.push(+(Math.random() * 100).toFixed(0));", + " return (size > 1) ? createArray(size - 1) : undefined;", + "})(12);", + "", + "function insertionSort(array) {", + " // change code below this line", + "", + " // change code above this line", + " return array;", + "}" + ], + "tail": [ + "function isSorted(arr) {", + " var check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", + " return check(0);", + "};" + ], + "tests": [ + "assert(typeof insertionSort == 'function', 'message: insertionSort is a function.');", + "assert(isSorted(insertionSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'message: insertionSort returns a sorted array.');" + ], + "type": "waypoint", + "solutions": [], + "challengeType": 1, + "translations": {} + }, + { + "id": "587d825a367417b2b2512c89", + "title": "Implement Quick Sort", + "description": [ + "Here we will move on to an intermediate sorting algorithm: quick sort. Quick sort is an efficient, recursive divide-and-conquer approach to sorting an array. In this method, a pivot value is chosen in the original array. The array is then partitioned into two subarrays of values less than and greater than the pivot value. We then combine the result of recursively calling the quick sort algorithm on both sub-arrays. This continues until the base case of an empty or single-item array is reached, which we return. The unwinding of the recursive calls return us the sorted array.", + "Quick sort is a very efficient sorting method, providing O(nlog(n)) performance on average. It is also relatively easy to implement. These attributes make it a popular and useful sorting method.", + "Instructions: Write a function quickSort which takes an array of integers as input and returns an array of these integers in sorted order. While the choice of the pivot value is important, any pivot will do for our purposes here. For simplicity, the first or last element could be used." + ], + "challengeSeed": [ + "// helper function generate a randomly filled array", + "var array = [];", + "(function createArray(size) {", + " array.push(+(Math.random() * 100).toFixed(0));", + " return (size > 1) ? createArray(size - 1) : undefined;", + "})(12);", + "", + "function quickSort(array) {", + " // change code below this line", + "", + " // change code above this line", + " return array;", + "}" + ], + "tests": [ + "assert(typeof quickSort == 'function', 'message: quickSort is a function.');", + "assert(isSorted(quickSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'message: quickSort returns a sorted array.');" + ], + "tail": [ + "function isSorted(arr) {", + " var check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", + " return check(0);", + "};" + ], + "type": "waypoint", + "solutions": [], + "challengeType": 1, + "translations": {} + }, + { + "id": "587d825c367417b2b2512c8f", + "title": "Implement Merge Sort", + "description": [ + "Another intermediate sorting algorithm that is very common is merge sort. Like quick sort, merge sort also uses a divide-and-conquer, recursive methodology to sort an array. It takes advantage of the fact that it is relatively easy to sort two arrays as long as each is sorted in the first place. But we'll start with only one array as input, so how do we get to two sorted arrays from that? Well, we can recursively divide the original input in two until we reach the base case of an array with one item. A single-item array is naturally sorted, so then we can start combining. This combination will unwind the recursive calls that split the original array, eventually producing a final sorted array of all the elements. The steps of merge sort, then, are:", + "1) Recursively split the input array in half until a sub-array with only one element is produced.", + "2) Merge each sorted sub-array together to produce the final sorted array.", + "Merge sort is an efficient sorting method, with time complexity of O(nlog(n)). This algorithm is popular because it is performant and relatively easy to implement.", + "As an aside, this will be the last sorting algorithm we cover here. However, later in the section on tree data structures we will describe heap sort, another efficient sorting method that requires a binary heap in its implementation.", + "Instructions: Write a function mergeSort which takes an array of integers as input and returns an array of these integers in sorted order. A good way to implement this is to write one function, for instance merge, which is responsible for merging two sorted arrays, and another function, for instance mergeSort, which is responsible for the recursion that produces single-item arrays to feed into merge. Good luck!" + ], + "challengeSeed": [ + "// helper function generate a randomly filled array", + "var array = [];", + "(function createArray(size) {", + " array.push(+(Math.random() * 100).toFixed(0));", + " return (size > 1) ? createArray(size - 1) : undefined;", + "})(25);", + "", + "function mergeSort(array) {", + " // change code below this line", + "", + " // change code above this line", + " return array;", + "}" + ], + "tests": [ + "assert(typeof mergeSort == 'function', 'message: mergeSort is a function.');", + "assert(isSorted(mergeSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'message: mergeSort returns a sorted array.');" + ], + "tail": [ + "function isSorted(arr) {", + " var check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", + " return check(0);", + "};" + ], + "type": "waypoint", + "solutions": [], + "challengeType": 1, + "translations": {} } ] -} +} \ No newline at end of file diff --git a/challenges/08-coding-interview-questions-and-take-home-assignments/coding-interview-data-structure-questions.json b/challenges/08-coding-interview-questions-and-take-home-assignments/coding-interview-data-structure-questions.json index 593ed766ee5..2e48491eac6 100644 --- a/challenges/08-coding-interview-questions-and-take-home-assignments/coding-interview-data-structure-questions.json +++ b/challenges/08-coding-interview-questions-and-take-home-assignments/coding-interview-data-structure-questions.json @@ -4,6 +4,66 @@ "time": "50 hours", "helpRoom": "Help", "challenges": [ + { + "id": "587d8253367417b2b2512c6a", + "title": "Typed Arrays", + "description": [ + "Arrays are JavaScript objects that can hold a lot of different elements.", + "var complexArr = [1, 5, \"2\", \"Word\", {\"name\": \"James\"}];", + "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.", + "Typed arrays 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.", + "Type Each element size in bytes", + "Int8Array 1", + "Uint8Array 1", + "Uint8ClampedArray 1", + "Int16Array 2", + "Uint16Array 2", + "Int32Array 4", + "Uint32Array 4", + "Float32Array 4", + "Float64Array 8", + "There are two ways in creating these kind of arrays. One way is to create it directly. Below is how to create a 7 length Int16Array.", + "var i8 = new Int16Array(3);", + "console.log(i8);", + "// Returns [0, 0, 0]", + "You can also create a buffer to assign how much data (in bytes) you want the array to take up.", + "Note", + "To create typed arrays using buffers, you need to assign the number of bytes to be a multiple of the bytes listed above.", + "// Create same Int16Array array differently", + "var byteSize = 6; // Needs to be multiple of 2", + "var buffer = new ArrayBuffer(byteSize);", + "var i8View = new Int16Array(buffer);", + "buffer.byteLength; // Returns 6", + "i8View.byteLength; // Returns 6", + "console.log(i8View); // Returns [0, 0, 0]", + "Buffers are general purpose objects that just carry data. You cannot access them normally. To access them, you need to first create a view.", + "i8View[0] = 42;", + "console.log(i8View); // Returns [42, 0, 0]", + "Note", + "Typed arrays do not have some of the methods traditional arrays have such as .pop() or .push(). Typed arrays also fail Array.isArray() that checks if something is an array. Although simplier, this can be an advantage for less-sophisticated JavaScript engines to implement them.", + "Instructions", + "First create a buffer that is 64-bytes. Then create a Int32Array typed array with a view of it called int32View.", + "Here are some helpful links:", + "Typed arrays", + "The code for the table used above is:", + "
TypeEach element size in bytes
Int8Array1
Uint8Array1
Uint8ClampedArray1
Int16Array2
Uint16Array2
Int32Array4
Uint32Array4
Float32Array4
Float64Array8
", + "The class='table table-striped' is specific to FreeCodeCamp's CSS style." + ], + "challengeSeed": [ + "var buffer;", + "var i32View;" + ], + "tests": [ + "assert(buffer.byteLength === 64, 'message: Your buffer should be 64 bytes large.');", + "assert(i32View.byteLength === 64, 'message: Your i32View view of your buffer should be 64 bytes large.');", + "assert(i32View.length === 16, 'message: Your i32View view of your buffer should be 16 elements long.');" + ], + "type": "waypoint", + "solutions": [], + "challengeType": 1, + "translations": {} + }, { "id": "587d8250367417b2b2512c5e", "title": "Learn how a Stack Works", @@ -18,7 +78,7 @@ "Remove the element on top of the stack, and Add CS50." ], "challengeSeed": [ - "var homeworkStack = ['BIO12','HIS80','MAT122','PSY44']", + "var homeworkStack = ['BIO12','HIS80','MAT122','PSY44'];", "// Only change code below this line" ], "tests": [ @@ -63,29 +123,21 @@ "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 push and pop method, stacks has other useful methods. Let's add a peek, isEmpty, and clear method to our stack class.", + "Apart from the push and pop method, stacks have other useful methods. Let's add a peek, isEmpty, and clear method to our stack class.", "Instructions", - "Write a push method that pushes an element to the top of the stack, a pop method that removes the element on the top of the stack, a peek method that looks at the first element in the stack, an isEmpty method that checks if the stack is empty, and a clear method that removes all elements from the stack.", - "Normally stacks don't have this, but we've added a print() helper method that console logs the collection." + "Write a push method that pushes an element to the top of the stack, a pop method that removes the element on the top of the stack, a peek method that looks at the first element in the stack, an isEmpty method that checks if the stack is empty, and a clear method that removes all elements from the stack.", + "Normally stacks don't have this, but we've added a print helper method that console logs the collection." ], "challengeSeed": [ "function Stack() { ", " collection = [];", " this.print = function() {", - " console.log(collection)", - " }", + " console.log(collection);", + " };", " // Only change code below this line", "", " // Only change code above this line", - "}", - "", - "// Test your stack class", - "var homework = new Stack();", - "homework.push('CS50');", - "console.log(homework.peek())", - "console.log(homework.isEmpty())", - "homework.clear();", - "console.log(homework.isEmpty())" + "}" ], "tests": [ "assert((function(){var test = new Stack(); return (typeof test.push === 'function')}()), 'message: Your Stack class should have a push method.');", @@ -142,33 +194,24 @@ "challengeSeed": [ "function Queue () { ", " collection = [];", - " this.print() = function(){", + " this.print = function() {", " console.log(collection);", - " }", + " };", " // Only change code below this line", "", " // Only change code above this line", - "}", - "", - "// Test your queue class", - "var DMV = new Queue();", - "DMV.enqueue('David Brown');", - "DMV.enqueue('Jon Snow');", - "DMV.size();", - "DMV.dequeue();", - "DMV.front();", - "DMV.isEmpty();" + "}" ], "tests": [ "assert((function(){var test = new Queue(); return (typeof test.enqueue === 'function')}()), 'message: Your Queue class should have a enqueue method.');", - "assert((function(){var test = new Queue(); return (typeof test.denqueue === 'function')}()), 'message: Your Queue class should have a denqueue method.');", + "assert((function(){var test = new Queue(); return (typeof test.dequeue === 'function')}()), 'message: Your Queue class should have a dequeue method.');", "assert((function(){var test = new Queue(); return (typeof test.front === 'function')}()), 'message: Your Queue class should have a front method.');", "assert((function(){var test = new Queue(); return (typeof test.size === 'function')}()), 'message: Your Queue class should have a size method.');", "assert((function(){var test = new Queue(); return (typeof test.isEmpty === 'function')}()), 'message: Your Queue class should have a isEmpty method.');", "assert((function(){var test = new Queue(); test.enqueue('Smith'); return (test.dequeue() === 'Smith')}()), 'message: The dequeue 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 front 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 size method should return the length of the queue');", - "assert((function(){var test = new Queue(); test.enqueue('Smith'); return !(test.isEmpty())}()), 'message: The isEmpty method should return false if there are elements in the queue');" + "assert((function(){var test = new Queue(); test.enqueue('Smith'); return !(test.isEmpty())}()), 'message: The isEmpty method should return false if there are elements in the queue');" ], "solutions": [], "hints": [], @@ -201,6 +244,721 @@ } } }, + { + "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 Queue class should have a enqueue method.');", + "assert((function(){var test = new PriorityQueue(); return (typeof test.dequeue === 'function')}()), 'message: Your Queue class should have a dequeue method.');", + "assert((function(){var test = new PriorityQueue(); return (typeof test.front === 'function')}()), 'message: Your Queue class should have a front method.');", + "assert((function(){var test = new PriorityQueue(); return (typeof test.size === 'function')}()), 'message: Your Queue class should have a size method.');", + "assert((function(){var test = new PriorityQueue(); return (typeof test.isEmpty === 'function')}()), 'message: Your Queue class should have a isEmpty 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:", + "
[1, 2, 3, 4, 5]
^Read @ 0
^Write @ 0
", + "Here the read and write are both at position 0. Now the queue gets 3 new records a, b, and c. Our queue now looks like:", + "
[a, b, c, 4, 5]
^Read @ 0
^Write @ 3
", + "As the read head reads, it can remove values or keep them:", + "
[null, null, null, 4, 5]
^Read @ 3
^Write @ 3
", + "Once the write reaches the end of the array it loops back to the beginning:", + "
[f, null, null, d, e]
^Read @ 3
^Write @ 1
", + "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 Array.isArray()). New items will be added to the collection starting at position 0, 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 Queue class should have a enqueue 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 dequeue 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:", + "var set = new Object();", + "set.foo = true;", + "// See if foo exists in our set:", + "console.log(set.foo) // true", + "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:", + "this.add = function(){", + " //some code to add value to the set", + "}" + ], + "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 Set class should have a add 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 Set 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 this.remove. 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 Set class should have a remove 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 Set class should have a size method.');", + "assert((function(){var test = new Set(); test.add(\"a\");test.add(\"b\");test.remove(\"a\");return (test.size() === 1)}()), 'message: The size 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 union. 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 Set class should have a union 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 Set class should have a intersection 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 Set class should have a difference 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 Set class should have a union 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 Map object provided here as a wrapper around a JavaScript object. Create the following methods and operations on the Map object:", + "add accepts a key, value pair to add to the map", + "remove accepts a key and removes the associated key, value pair", + "get accepts a key and returns the stored value", + "has returns a boolean for the presence or absence of an item", + "values returns an array of all the values in the map", + "size returns the number of items in the map", + "clear 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:", + ".has(key) returns true or false based on the presence of a key", + ".get(key) returns the value associated with a key", + ".set(key, value) sets a new key, value pair", + ".delete(key) removes a key, value pair", + ".clear() removes all key, value pairs", + ".entries() returns an array of all the keys in insertion order", + ".values() 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 Free Code Camp, Awesome! to it." + ], + "challengeSeed": [ + "// change code below this line" + ], + "tests": [ + "assert(typeof myMap === 'object', 'message: The myMap object exists.');", + "assert(myMap.get('Free Code Camp') === 'Awesome!', 'message: myMap contains the key value pair Free Code Camp, Awesome!.');" + ], + "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", @@ -294,13 +1052,8 @@ " // Only change code below this line", "", " // Only change code above this line", - " }; ", - "} ", - "", - "// Test your code", - "var conga = new LinkedList();", - "conga.add('Kitten');", - "console.log(conga.head().element);" + " };", + "};" ], "tests": [ "assert((function(){var test = new LinkedList(); return (typeof test.add === 'function')}()), 'message: Your LinkedList class should have a add method.');", @@ -390,16 +1143,8 @@ " // Only change code below this line", "", " // Only change code above this line", - " }", - "} ", - "", - "// Test your code", - "var conga = new LinkedList();", - "conga.add('Kitten');", - "conga.add('Puppy');", - "console.log(conga.length());", - "conga.remove('Kitten');", - "console.log(conga.head());" + " };", + "};" ], "tests": [ "assert((function(){var test = new LinkedList(); return (typeof test.remove === 'function')}()), 'message: Your LinkedList class should have a remove method.');", @@ -502,16 +1247,7 @@ " // Only change code below this line", "", " // Only change code above this line", - "}", - "", - "// Test your code", - "var conga = new LinkedList();", - "conga.add('Kitten');", - "conga.add('Puppy');", - "console.log(conga.indexOf('Puppy'))", - "console.log(conga.elementAt('0'))", - "conga.remove('Kitten');", - "console.log(conga.head());" + "};" ], "tests": [ "assert((function(){var test = new LinkedList(); return (typeof test.indexOf === 'function')}()), 'message: Your LinkedList class should have a indexOf method.');", @@ -616,18 +1352,7 @@ " // Only change code below this line", "", " // Only change code above this line", - "} ", - "", - "// Test your code", - "var conga = new LinkedList();", - "conga.add('Kitten');", - "conga.add('Puppy');", - "conga.add('Dog');", - "conga.add('Cat');", - "conga.add('Fish');", - "console.log(conga.size());", - "console.log(conga.removeAt(3));", - "console.log(conga.size());" + "};" ], "tests": [ "assert((function(){var test = new LinkedList(); return (typeof test.removeAt === 'function')}()), 'message: Your LinkedList class should have a removeAt method.');", @@ -667,103 +1392,6 @@ } } }, - { - "id": "587d8252367417b2b2512c66", - "title": "Learn how Binary Search Trees Work", - "description": [ - "Lets look at one of the most popular Data Structures : Trees. Like the literal meaning, a tree can have many leaves and many brances. Here, lets see an example of a simple tree, a Binary Search Tree. This tree is special, as at any level it can have two child's, a left child and a right child. Although, it has another unique speciality : The left child's value must be less than the parent, and the parents value must be less than the right child. Sound's confusing at first? Well, this is a rule enforced for all Binary Search Trees (BST)'s around the world for one simple reason, fast searching. Yes, hence the name Search in the Tree!", - "You might have guessed it by now : Suppose your job is to find the value 7 from a tree containing 1 to 10 values. Now whenever you encounter a value, instantly you can guess which child will contain 7. That is, say you face a node having value of 5, instantly you know that 7 must lie somewhere under the right child of this node. Simple yet elegant!", - "Today we are going to add a number in a Tree. While adding though you need to be careful about the golden rule : left child < parent < right child. So turn up your thinking hats and solve this challenge, such that every time an element is added its in the right position! And btw, lookout for the null values as usual! Btw, we have added a simple helper function for you to visualize the tree.", - "Also, we are going to display how trivial it is to search for an element in a Binary Search Tree. Given a value test whether the number isPresent in the tree!" - ], - "challengeSeed": [ - "// helper function", - "function Node(val) {", - " this.value = val;", - " this.left = null;", - " this.right = null;", - "}", - "", - "function BinarySearchTree() {", - " this.root = null;", - "", - " /**", - " * Add an element in a BST", - " */", - " this.add = function(val) {", - " var root = this.root;", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - " }", - "", - " /**", - " * Return true if value present in tree, false otherwise", - " */", - " this.isPresent = function(query) {", - " var root = this.root;", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - " }", - "}", - "", - "var bst = new BinarySearchTree();", - "bst.add(2);", - "bst.add(1);", - "bst.add(3);", - "displayTree(bst);", - "bst.isPresent(3);", - "bst.isPresent(5);", - "", - "", - "// helper function", - "function displayTree(tree) {", - " console.log(JSON.stringify(tree, null, 2))", - "}" - ], - "tests": [ - "assert((function(){var test = new BinarySearchTree(); return (typeof test.add === 'function')}()), 'message: Your BinarySearchTree class should have a add method.');", - "assert((function(){var test = new BinarySearchTree(); test.add(2); test.add(1); test.add(3); return test.root.value === 2}()), 'message: Tree should have root element created from the first input');", - "assert((function(){var test = new BinarySearchTree(); test.add(2); test.add(1); test.add(3); return test.root.left.value === 1}()), 'message: Tree should have left child of root element as the least value input');", - "assert((function(){var test = new BinarySearchTree(); test.add(2); test.add(1); test.add(3); return test.root.right.value === 3}()), 'message: Tree should have right child of root element as the greatest value input');", - "assert((function(){var test = new BinarySearchTree(); test.add(1); test.add(2); test.add(3); return (test.root.value === 1 && test.root.right.value ==2 && test.root.right.right.value == 3)}()), 'message: When added in ascending order, all elements must be in right nodes');", - "assert((function(){var test = new BinarySearchTree(); test.add(3); test.add(2); test.add(1); return (test.root.value === 3 && test.root.left.value ==2 && test.root.left.left.value == 1)}()), 'message: When added in descending order, all elements must be in left nodes');", - "assert((function(){var test = new BinarySearchTree(); test.add(3); test.add(2); test.add(1); var t = test.isPresent(2); var f = test.isPresent(5); return (t == true && f == false)}()), 'message: If value present in tree, return isPresent true else false');" - ], - "solutions": [], - "hints": [], - "type": "waypoint", - "challengeType": 1, - "translations": { - "de": { - "description": [], - "title": "" - }, - "fr": { - "description": [], - "title": "" - }, - "pt-br": { - "description": [], - "title": "" - }, - "ru": { - "description": [], - "title": "" - }, - "es": { - "description": [], - "title": "" - }, - "cn": { - "description": [], - "title": "" - } - } - }, { "id": "587d8252367417b2b2512c67", "title": "Add Elements at a Specific Index in a Linked List", @@ -815,14 +1443,7 @@ "", " // Only change code above this line", "", - "} ", - "", - "// Test your code", - "var conga = new LinkedList();", - "conga.add('Kitten');", - "console.log(conga.addAt(1,'Puppy'));", - "console.log(conga.addAt(2,'Cat'));", - "console.log(conga.head());" + "};" ], "tests": [ "assert((function(){var test = new LinkedList(); test.add('cat'); test.add('dog'); test.addAt(0,'cat'); return test.head().element === 'cat'}()), 'message: Your addAt method should reassign head to the new node when the given index is 0.');", @@ -861,1210 +1482,1220 @@ } }, { - "id": "587d8252367417b2b2512c68", - "title": "Traverse a Binary Search Tree In-order, Pre-order and Post-order", + "id": "587d825a367417b2b2512c87", + "title": "Create a Doubly Linked List", "description": [ - "When talking about trees, lets see how we can walk along a tree. There are three ways we can do so:", - "Inorder traversal : Traverse the nodes according to their value (ascending to descending)", - "Preorder traversal : Traverse the nodes according as they are added", - "Postorder traversal : Traverse the nodes in the reverse way as they were added. Usefull to delete a tree", - "These traversals can be easily implemented using a slight recursion technique. All you need to know when to recurse and when to print.", - "Here for our convenience we will not print a node, in turn we will push that node in an array and pass that array to the function for later use.", - "Remember to return that array as well!", - "Bonus exercise : Return the size of the tree." + "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": [ - "function Node(val) {", - " this.value = val;", - " this.left = null;", - " this.right = null;", - "}", - "", - "function BinarySearchTree() {", - " this.root = null;", - " this.current = null;", - "", - " this.add = function(val) {", - " var root = this.root;", - " if(!root) {", - " this.root = new Node(val);", - " return;", - " }", - "", - " var currentNode = root;", - " var newNode = new Node(val);", - "", - " while(currentNode) {", - " if(val < currentNode.value) {", - " if(!currentNode.left) {", - " currentNode.left = newNode;", - " break;", - " }", - " else {", - " currentNode = currentNode.left;", - " }", - " } else {", - " if(!currentNode.right) {", - " currentNode.right = newNode;", - " break;", - " } else {", - " currentNode = currentNode.right;", - " }", - " }", - " }", - " }", - "", - " this.isPresent = function(query) {", - " var root = this.root;", - " var currentNode = root;", - " while(currentNode) {", - " if(currentNode.value == query) {", - " return true;", - " } else if(query < currentNode.value) {", - " currentNode = currentNode.left;", - " } else {", - " currentNode = currentNode.right;", - " }", - " }", - " return false;", - " // Only change your code above this line", - " }", - "", - " /**", - " * Inorder traversal. For BST, the ouput should be a sorted array", - " * Hint : For each node, the strategy is: recur left, print the node data, recur right", - " */", - " this.inorder = function(node, arr) {", - " var root = this.root;", - " // Only change your code below this line", - "", - " // Only change your code above this line", - " }", - "", - " /**", - " * Preorder traversal.", - " * Hint : For each node, the strategy is: print the node data, recur left, recur right", - " */", - " this.preorder = function(node, arr) {", - " var root = this.root;", - " // Only change your code above this line", - "", - " // Only change your code above this line", - " }", - "", - " /**", - " * Postorder traversal.", - " * Hint : For each node, the strategy is: recur left, recur right, print the node data", - " */", - " this.postorder = function(node, arr) {", - " var root = this.root;", - " // Only change your code above this line", - "", - " // Only change your code above this line", - " }", - "", - " /**", - " * Size of tree.", - " */", - " this.size = function() {", - " // Only change your code above this line", - "", - " // Only change your code above this line", - " }", - "", - "}", - "", - "// helper function", - "function displayTree(tree) {", - " console.log(JSON.stringify(tree, null, 2))", - "}", - "", - "var bst = new BinarySearchTree();", - "bst.add(2);", - "bst.add(1);", - "bst.add(3);", - "displayTree(bst);", - "", - "var a = bst.inorder();", - "console.log(a);", - "console.log(bst.size());", - "", - "var b = bst.postorder();", - "console.log(b);", - "", - "var c = bst.preorder();", - "console.log(c);" + "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 = new BinarySearchTree(); return (typeof test.inorder === 'function' && typeof test.preorder === 'function' && typeof test.postorder === 'function' && typeof test.size === 'function')}()), 'message: Your BinarySearchTree class should have inorder, preorder, postorder and size methods.');", - "assert((function(){var test = new BinarySearchTree(); test.add(1); test.add(2); test.add(3); return (test.root.value === 1 && test.root.right.value ==2 && test.root.right.right.value == 3)}()), 'message: When added in ascending order, all elements must be in right nodes');", - "assert((function(){var test = new BinarySearchTree(); test.add(3); test.add(2); test.add(1); return (test.root.value === 3 && test.root.left.value ==2 && test.root.left.left.value == 1)}()), 'message: When added in descending order, all elements must be in left nodes');", - "assert((function(){var test = new BinarySearchTree(); test.add(2); test.add(1); test.add(3); var i = test.inorder(); return (i[0] == 1 && i[1] == 2 && i[2] == 3)}()), 'message: Inorder traversal must return sorted array');", - "assert((function(){var test = new BinarySearchTree(); test.add(2); test.add(1); test.add(3); var i = test.preorder(); return (i[0] == 2 && i[1] == 1 && i[2] == 3)}()), 'message: Preorder traversal must return BFS array');", - "assert((function(){var test = new BinarySearchTree(); test.add(2); test.add(1); test.add(3); var i = test.postorder(); return (i[0] == 1 && i[1] == 3 && i[2] == 2)}()), 'message: Postrder traversal must return reversed array');", - "assert((function(){var test = new BinarySearchTree(); test.add(2); test.add(1); test.add(3); var l = test.size(); return (l === 3)}()), 'message: Postrder traversal must return reversed array');" + "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.');" ], - "solutions": [], - "hints": [], "type": "waypoint", + "solutions": [], "challengeType": 1, - "translations": { - "de": { - "description": [], - "title": "" - }, - "fr": { - "description": [], - "title": "" - }, - "pt-br": { - "description": [], - "title": "" - }, - "ru": { - "description": [], - "title": "" - }, - "es": { - "description": [], - "title": "" - }, - "cn": { - "description": [], - "title": "" - } - } + "translations": {} }, { - "id": "587d8252367417b2b2512c69", - "title": "Delete Leaf Nodes from a Binary Search Tree", + "id": "587d825a367417b2b2512c88", + "title": "Reverse a Doubly Linked List", "description": [ - "This is a long and complicated task, but very useful to know. Basically while deleting tree nodes we encounter the following three cases:", - "Root to be deleted is a :", - "Leaf Node", - "A Node with just one child", - "Node with two children", - "Each case requires special handling. Also, it depends whether the node to delete is actually a root itself or not, because then you have to consider the child nodes as well.", - "Let's discuss the cases in detail :", - "Case 1 : Leaf Node", - "No brainer here, just obliterate the leaf node. No special things to do here, invalidate the pointer from its parent to itself!", - "Case 2 : Node with just one child", - "First, figure out which child it is, i.e either a left child or a right child. Then \"skip\" the pointer from its parent to its child, It's simple as that!", - "Case 3 : Node with two children", - "Now here is when the things start going crazy! Let us see this with an example image : https://en.wikipedia.org/wiki/File:Binary_search_tree.svg", - "With a root of 8 and a left child of 3, what would happen if the 3 was removed? There are two possibilities: 1 (3′s left child, called the in-order predecessor) could take the place of 3 or 4 (the left-most child of the right subtree, called the in-order successor) can take the place of 3.", - "Either of these two options is appropriate. To find the in-order predecessor, the value that comes before the value being removed, examine the left subtree of the node to remove and select the right-most descendant; to find the in-order successor, the value that comes immediately after the value being removed, reverse the process and examine the right subtree for the left-most descendant. Each of these requires another traversal of the tree to complete the operation.", - "Sounds tough? See this tutorial : https://webdocs.cs.ualberta.ca/~holte/T26/del-from-bst.html, or better yet, play with this superb animation : https://www.cs.usfca.edu/~galles/visualization/BST.html", - "Ready? Lets begin coding!" + "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": [ - "function Node(val) {", - " this.value = val;", + "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;", - " this.current = null;", - "", - " this.add = function(val) {", - " var root = this.root;", - " if(!root) {", - " this.root = new Node(val);", - " return;", - " }", - "", - " var currentNode = root;", - " var newNode = new Node(val);", - "", - " while(currentNode) {", - " if(val < currentNode.value) {", - " if(!currentNode.left) {", - " currentNode.left = newNode;", - " break;", - " }", - " else {", - " currentNode = currentNode.left;", - " }", - " } else {", - " if(!currentNode.right) {", - " currentNode.right = newNode;", - " break;", + " // 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 {", - " currentNode = currentNode.right;", + " 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;", + "};", "", - " this.isPresent = function(query) {", - " var root = this.root;", - " var currentNode = root;", - " while(currentNode) {", - " if(currentNode.value == query) {", - " return true;", - " } else if(query < currentNode.value) {", - " currentNode = currentNode.left;", + "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 {", - " currentNode = currentNode.right;", - " }", + " return null;", + " };", + " }).bind(this)();", + " if (target == null) {", + " return null;", " }", - " return false;", - " }", - "", - " this.remove = function (val) {", - " var found = false,", - " parent = null,", - " current = this.root,", - " childCount,", - " replacement,", - " replacementParent", - "", - " // find the node and set `found` = true, with current pointing to the found node", - " // and `parent` its parent", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - "", - " // If node was found, then we proceed deletion", - " if(found) {", - " // figure out how many children (i.e either 0 or 1 or 2)", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - "", - " // case : if value is at the root", - " if(current == this.root) {", - " console.log('root case')", - " switch(childCount) {", - " // case : no children, so we will just proceed to erase the root", - " case 0:", - " console.log('case 0')", - " // Only change your code below this line", - "", - " // Only change your code above this line", - " break;", - " // case : one child, figure out which child and then use that as root", - " case 1:", - " console.log('case 1')", - " // Only change your code below this line", - "", - " // Only change your code above this line", - " break;", - " // case : two children", - " case 2:", - " console.log('case 2')", - " // let us select new root to be old root's left child", - " replacement = this.root.left;", - " // now let's find the right most leaf node to be the new root", - " // find the right most leaf node of `replacement` and save the its parent", - " // within `replacementParent`", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - "", - " // If it is not the first left node", - " if (replacementParent !== null) {", - " // remove the new root from its previous position", - " // i.e, replace the right child of replacement parent with left child of the", - " // node to be replaced", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - "", - " // Give the new root all of old root's children", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - " } else {", - " // assign the children to the root's right child", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - " }", - " // finally assign new root", - " this.root = replacement;", - " break;", - " }", + " // 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;", " }", - " // case if value is not root", " else {", - " console.log('child case')", - " switch(childCount) {", - " // case 0 : no children, just detach it from parent", - " case 0:", - " console.log('case 0')", - " // if current's value is less than its parents, null out the left pointer,", - " // else null out the right pointer", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - " break;", - " // case 1: one child, just reassign to parent", - " case 1:", - " console.log('case 1')", - " // if current's value is less than its parents, reset the left pointer,", - " // else reset the right pointer", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - " break;", - " // case 2: two child", - " case 2:", - " console.log('case 2')", - " // reset pointers for new traversal", - " replacement = current.left;", - " // find the right most node", - " // find the right most leaf node of `replacement` and save the its parent", - " // within `replacementParent`", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - " if (replacementParent) {", - " // remove the new node from its previous position", - " // i.e, replace the right child of replacement parent with left child of the", - " // node to be replaced", - " // Only change your code below this line", - "", - " // Only change your code above this line", - "", - " // assign all current's children to the replacement", - " // Only change your code below this line", - "", - "", - " // Only change your code above this line", - " } else {", - " // assign the children to the current's right child", - " // Only change your code below this line", - "", - " // Only change your code above this line", - " }", - " // place the replacement in the right spot", - " // if current value is less than parent, add replacement as parent left child,", - " // else right", - " // Only change your code below this line", - "", - " // Only change your above below this line", - " break;", + " 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;", " }", " }", - "}", - "", - "// helper function", - "function displayTree(tree) {", - " console.log(JSON.stringify(tree, null, 2))", - "}", - "", - "var bst = new BinarySearchTree();", - "bst.add(3);", - "bst.add(1);", - "bst.add(4);", - "bst.add(2);", - "bst.add(0);", - "bst.add(5);", - "bst.add(6);", - "displayTree(bst);", - "", - "bst.remove(1);", - "displayTree(bst);" + "};" ], - "tests": [ - "assert((function(){var test = new BinarySearchTree(); return (typeof test.remove === 'function' && typeof test.add === 'function')}()), 'message: Your BinarySearchTree class should have add and remove methods.');", - "assert((function(){var test = new BinarySearchTree(); test.add(3); test.add(1); test.add(4); test.add(2); test.add(0); test.add(5); test.add(6); test.remove(6); return (test.isPresent(6) !== true)}()), 'message: Leaf node should be removed properly');", - "assert((function(){var test = new BinarySearchTree(); test.add(3); test.add(1); test.add(4); test.add(2); test.add(0); test.add(5); test.add(6); test.remove(3); return (test.isPresent(3) !== true && test.root.value == 2)}()), 'message: Root node should be removed properly and replaced by suitable candidate');", - "assert((function(){var test = new BinarySearchTree(); test.add(3); test.add(1); test.add(4); test.add(2); test.add(0); test.add(5); test.add(6); test.remove(5); return (test.isPresent(5) !== true)}()), 'message: Node with one child should be removed properly');", - "assert((function(){var test = new BinarySearchTree(); test.add(3); test.add(1); test.add(4); test.add(2); test.add(0); test.add(5); test.add(6); test.remove(1); return (test.isPresent(1) !== true && test.root.left.value == 0)}()), 'message: Node with two child should be removed properly and replaced by suitable candidate');" - ], - "solutions": [], - "hints": [], "type": "waypoint", + "solutions": [], "challengeType": 1, - "translations": { - "de": { - "description": [], - "title": "" - }, - "fr": { - "description": [], - "title": "" - }, - "pt-br": { - "description": [], - "title": "" - }, - "ru": { - "description": [], - "title": "" - }, - "es": { - "description": [], - "title": "" - }, - "cn": { - "description": [], - "title": "" - } - } - }, - { - "id": "587d8253367417b2b2512c6a", - "title": "Typed Arrays", - "description": [ - "Arrays are JavaScript objects that can hold a lot of different elements.", - "var complexArr = [1, 5, \"2\", \"Word\", {\"name\": \"James\"}];", - "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.", - "Typed arrays 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.", - "Type Each element size in bytes", - "Int8Array 1", - "Uint8Array 1", - "Uint8ClampedArray 1", - "Int16Array 2", - "Uint16Array 2", - "Int32Array 4", - "Uint32Array 4", - "Float32Array 4", - "Float64Array 8", - "There are two ways in creating these kind of arrays. One way is to create it directly. Below is how to create a 7 length Int16Array.", - "var i8 = new Int16Array(3);", - "console.log(i8);", - "// Returns [0, 0, 0]", - "You can also create a buffer to assign how much data (in bytes) you want the array to take up.", - "Note", - "To create typed arrays using buffers, you need to assign the number of bytes to be a multiple of the bytes listed above.", - "// Create same Int16Array array differently", - "var byteSize = 6; // Needs to be multiple of 2", - "var buffer = new ArrayBuffer(byteSize);", - "var i8View = new Int16Array(buffer);", - "buffer.byteLength; // Returns 6", - "i8View.byteLength; // Returns 6", - "console.log(i8View); // Returns [0, 0, 0]", - "Buffers are general purpose objects that just carry data. You cannot access them normally. To access them, you need to first create a view.", - "i8View[0] = 42;", - "console.log(i8View); // Returns [42, 0, 0]", - "Note", - "Typed arrays do not have some of the methods traditional arrays have such as .pop() or .push(). Typed arrays also fail Array.isArray() that checks if something is an array. Although simplier, this can be an advantage for less-sophisticated JavaScript engines to implement them.", - "Instructions", - "First create a buffer that is 64-bytes. Then create a Int32Array typed array with a view of it called int32View.", - "Here are some helpful links:", - "Typed arrays", - "The code for the table used above is:", - "
TypeEach element size in bytes
Int8Array1
Uint8Array1
Uint8ClampedArray1
Int16Array2
Uint16Array2
Int32Array4
Uint32Array4
Float32Array4
Float64Array8
", - "The class='table table-striped' is specific to FreeCodeCamp's CSS style." - ], - "challengeSeed": [ - "var buffer;", - "var i32View;" - ], - "tests": [ - "assert(buffer.byteLength === 64, 'message: Your `buffer` should be 64 bytes large.');", - "assert(i32View.byteLength === 64, 'message: Your `i32View` view of your buffer should be 64 bytes large.');", - "assert(i32View.length === 16, 'message: Your `i32View` view of your buffer should be 16 elements long.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, "translations": {} }, { - "id": "8d1323c8c441eddfaeb5bdef", - "title": "Creating a Set Class", + "id": "587d8258367417b2b2512c82", + "title": "Delete a Node with Two Children in a Binary Search Tree", "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:", - "var set = new Object();", - "set.foo = true;", - "// See if foo exists in our set:", - "console.log(set.foo) // true", - "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:", - "this.add = function(){", - " //some code to add value to the set", - "}" + "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": [ - "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", + "var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));", + "function Node(value) {", + " this.value = value;", + " this.left = null;", + " this.right = null;", "};", - "var setA = new Set();", - "setA.add(\"a\");", - "setA.add(\"b\");", - "setA.add(\"b\");", - "console.log(setA.values() // should be [ 'a', 'b'];" - ], - "tests": [ - "assert((function(){var test = new Set(); return (typeof test.add === 'function')}()), 'message: Your Set class should have a add method.');", - "assert(function(){var test = new Set; test.add(\"a\");test.add(\"b\";test.add(\"a\");return test.values() === [ 'a', 'b' ])}(), 'message: Your code needs to prevent duplicates.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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 this.remove" - ], - "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;", + "", + "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;", " }", - " return false;", - " };", - " // change code below this line", - " // change code above this line", - "};", - "var setA = new Set();", - "setA.add(\"a\");", - "setA.add(\"b\");", - "setA.remove(\"b\");", - "console.log(setA.values())//should be [ 'a' ];" - ], - "tests": [ - "assert((function(){var test = new Set(); return (typeof test.remove === 'function')}, 'message: Your Set class should have a remove method.');", - "assert(function(){var test = new Set; test.add(\"a\");test.add(\"b\");test.remove(\"a\");return (test.values() === [ 'b' ])}), 'message: Your code should remove the item from');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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", - "}", - "var setA = new Set();", - "setA.add(\"a\");", - "setA.add(\"b\");", - "console.log(setA.size()) // should be 2;" - ], - "tests": [ - "assert((function(){var test = new Set(); return (typeof test.size === 'function')}, 'message: Your Set class should have a size method.');", - "assert(function(){var test = new Set; test.add(\"a\");test.add(\"b\");test.remove(\"a\");return (test.size() === 2)}), 'message: return the number of elements in the collection.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d8253367417b2b2512c6c", - "title": "Perform a union on 2 Sets of data", - "description": [ - "In this exercise we are going to perform a union on 2 sets of data. We will create a method on our Set data structure called union. 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", - "};", - " 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);", - " console.log(unionSetAB.values())//should be [ 'a', 'b', 'c', 'd' ];" - ], - "tests": [ - "assert((function(){var test = new Set(); return (typeof test.union === 'function')}, 'message: Your Set class should have a union 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);return (unionSetAB.values() === [ 'a', 'b', 'c', 'd' ])}), 'message: The proper collection was returned');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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", - "};", - " 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);", - " console.log(intersectionSetAB.values())//should be [ 'c' ];" - ], - "tests": [ - "assert((function(){var test = new Set(); return (typeof test.intersection === 'function')}, 'message: Your Set class should have a intersection 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": 0, - "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);", + " // 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;", " }", - " });", - " return intersectionSet;", + " 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 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);", - " console.log(differenceSetAB.values()) // should be [ 'a', 'b' ];" + "var Trie = function() {", + " // change code below this line", + " // change code above this line", + "};" ], "tests": [ - "assert((function(){var test = new Set(); return (typeof test.difference === 'function')}, 'message: Your Set class should have a difference 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');" + "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": 0, + "challengeType": 1, "translations": {} }, { - "id": "587d8254367417b2b2512c6f", - "title": "Perform a subset check on two Sets of data", + "id": "587d825a367417b2b2512c8a", + "title": "Insert an Element into a Max Heap", "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." + "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:", + "[ 6, 22, 30, 37, 63, 48, 42, 76 ]", + "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: [ null, 22, 30, 37, 63, 48, 42, 76 ]", + "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": [ - "assert((function(){var test = new Set(); return (typeof test.subset === 'function')}, 'message: Your Set class should have a union 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');" + "var MaxHeap = function() {", + " // change code below this line", + " // change code above this line", + "};" ], "tests": [ - "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", + "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);", "};", - " 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);", - " console.log(subSetSetAB) // should be true;" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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:", - "let set = new Set();", - "You can create a set with a value:", - "let set = new Set(1);", - "You can create a set with an array:", - "let set = new Set([1,2,3]);", - "Once you have created a set, you can add the values you wish using set.add:", - "let 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(){let test = checkSet(); let testArr = [...test]; testArr === [ 1, 2, 3, 'Taco', 'Cat', 'Awesome'])}, 'message: Your Set was returned correctly!');" - ], - "challengeSeed": [ - "function checkSet(){", - " let 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();" + "// 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(){let test = checkSet(); let testArr = [...test]; testArr === [ 1, 2, 3, 'Taco', 'Cat', 'Awesome'])}, 'message: Your Set was returned correctly!');" + "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": 0, - "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:", - "let 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:", - "let 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){", - " let 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(1);" - ], - "tests": [ - "assert((function(){let test = checkSet(); let testArr = [...test]; testArr === [ 2, 3 ])}, 'message: Your Set was returned correctly!');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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.", - "let set = new Set([1,2,3]);", - "let hasTwo = set.has(2); // will return true or false", - "let 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)", - "}", - "let checkSetOutput = checkSet([1,3,5],5);", - "console.log(checkSetOutput)" - ], - "tests": [ - "assert((function(){let test = checkSet([1,2,3], 2); test === [ true, 3 ])}, 'message: Your Set was returned correctly!');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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.", - "let set = new Set([1,2,3]);", - "let 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", - "};", - "let checkSetOutput = checkSet(new Set([1, 2, 3, 4, 5, 6, 27]));", - "console.log(checkSetOutput);" - ], - "tests": [ - "assert((function(){let 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": 0, - "translations": {} - }, - { - "id": "587d8255367417b2b2512c74", - "title": "Methods for queuing with priority using the Priority Queue", - "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 () {", - " let collection = [];", - " this.printCollection = function(){", - " (console.log(collection));", - " };", - " // Only change code below this line", - " // Only change code above this line", - " }", - "// Test your queue class", - "var DMV = new PriorityQueue();", - "DMV.enqueue(['David Brown', 2]);", - "DMV.enqueue(['Jon Snow', 1]);", - "DMV.dequeue();", - "DMV.front();", - "DMV.isEmpty();", - "DMV.size();", - "DMV.dequeue();", - "DMV.isEmpty();" - ], - "tests": [ - "assert((function(){var test = new PriorityQueue(); return (typeof test.enqueue === 'function')}()), 'message: Your Queue class should have a enqueue method.');", - "assert((function(){var test = new PriorityQueue(); return (typeof test.denqueue === 'function')}()), 'message: Your Queue class should have a denqueue method.');", - "assert((function(){var test = new PriorityQueue(); return (typeof test.front === 'function')}()), 'message: Your Queue class should have a front method.');", - "assert((function(){var test = new PriorityQueue(); return (typeof test.size === 'function')}()), 'message: Your Queue class should have a size method.');", - "assert((function(){var test = new PriorityQueue(); return (typeof test.isEmpty === 'function')}()), 'message: Your Queue class should have a isEmpty method.');", - "assert((function(){var test = new PriorityQueue(); DMV.enqueue(['David Brown', 2]);DMV.enqueue(['Jon Snow', 1]); return ([...test]===[['Jon Snow', 1],['David Brown', 2]])}()),'message: Your code did not correctly prioritize the incoming items.')", - "assert((function(){var test = new PriorityQueue(); DMV.enqueue(['David Brown', 2]);DMV.enqueue(['Jon Snow', 1]); return (test.size()===2)}()),'message: Your code did not correctly add the incoming items.')", - "assert((function(){var test = new PriorityQueue(); DMV.enqueue(['David Brown', 2]);DMV.enqueue(['Jon Snow', 1]);DMV.dequeue(); return (test.size()===1)}()),'message: Your code did not correctly dequeue 1 item.')", - "assert((function(){var test = new PriorityQueue(); DMV.enqueue(['David Brown', 2]);DMV.enqueue(['Jon Snow', 1]);DMV.dequeue();DMV.dequeue(); return (DMV.isEmpty()===true)}()),'message: Your code is not correctly reporting that the queue is empty.')", - "assert((function(){var test = new PriorityQueue(); DMV.enqueue(['David Brown', 1]);DMV.enqueue(['Jon Snow', 1]); return ([...test]===[['David Brown', 2], ['Jon Snow', 1]])}()),'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": 0, - "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:", - "[1, 2, 3, 4, 5]", - "^Read @ 0", - "^Write @ 0", - "Here the read and write are both at position 0. Now the queue gets 3 new records a, b, and c. Our queue now looks like:", - "[a, b, c, 4, 5]", - "^Read @ 0", - " ^write @ 3", - "As the read head reads, it can remove values or keep them:", - "[, , , 4, 5]", - " ^Read @ 3", - " ^write @ 3", - "Once the write reaches the end of the array it loops back to the beginning:", - "[f, , , d, e]", - " ^Read @ 3", - " ^write @ 1", - "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 Array.isArray()). New items will be added to the collection starting at position 0, 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 () {", - " let collection = [,,,,];//remember length of 5", - " this.printCollection = function(){", - " (console.log(collection));", - " return collection;", - " };", - " // Only change code below this line", - " // Only change code above this line", - " }", - "// Test your queue class", - "var CircQue = new CircularQueue();", - "CircQue.enqueue([1,2,3,4,5,6]);", - "CircQue.printCollection();//collection should look like [6,2,3,4,5]", - "CircQue.dequeue()", - "CircQue.printCollection();//collection should look like [,2,3,4,5]", - "CircQue.dequeue()", - "CircQue.printCollection();//collection should look like [,,3,4,5]", - "CircQue.dequeue()", - "CircQue.dequeue()", - "CircQue.front()//should return 5", - "CircQue.dequeue()", - "CircQue.front()//should return Null", - "CircQue.dequeue()//should not advance past WriteHead" - ], - "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 Queue class should have a enqueue 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 dequeue method did not remove the first item.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, + "challengeType": 1, "translations": {} }, { @@ -2091,7 +2722,7 @@ ], "type": "waypoint", "solutions": [], - "challengeType": 0, + "challengeType": 1, "translations": {} }, { @@ -2131,7 +2762,7 @@ ], "type": "waypoint", "solutions": [], - "challengeType": 0, + "challengeType": 1, "translations": {} }, { @@ -2176,7 +2807,7 @@ ], "type": "waypoint", "solutions": [], - "challengeType": 0, + "challengeType": 1, "translations": {} }, { @@ -2231,1504 +2862,7 @@ ], "type": "waypoint", "solutions": [], - "challengeType": 0, - "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": [ - "const 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() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.findMin == 'function')})(), 'The binary search tree has a method called findMin.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.findMax == 'function')})(), 'The binary search tree has a method called findMax.');", - "assert((function() { let 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; })(), 'The findMin method returns the minimum value in the binary search tree.');", - "assert((function() { let 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; })(), 'The findMax method returns the maximum value in the binary search tree.');", - "assert((function() { let 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) })(), 'The findMin and findMax methods return null for an empty tree.');" - ], - "tail": [ - "BinarySearchTree.prototype = {", - " add: function(value) {", - " let 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": 0, - "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": [ - "const 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 {", - " let check = true;", - " function checkTree(node) {", - " if (node.left != null) {", - " let left = node.left;", - " if (left.value > node.value) {", - " check = false;", - " } else {", - " checkTree(left);", - " }", - " }", - " if (node.right != null) {", - " let right = node.right;", - " if (right.value < node.value) {", - " check = false;", - " } else {", - " checkTree(right);", - " };", - " };", - " };", - " checkTree(this.root);", - " return check;", - " };", - " }", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.add == 'function')})(), 'The binary search tree has a method called add.');", - "assert((function() { let 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()); })(), 'The add method adds elements according to the binary search tree rules.')", - "assert((function() { let 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; })(), 'Adding an element that already exists returns null');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const 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) {", - " let 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() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.isPresent == 'function')})(), 'The binary search tree has a method called isPresent.');", - "assert((function() { let 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) ); })(), 'The isPresent method correctly checks for the presence of absence of elements added to the tree.')", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.isPresent !== 'function') { return false; }; return test.isPresent(5) == false; })(), 'isPresent handles cases where the tree is empty.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const 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) {", - " let 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() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.findMinHeight == 'function')})(), 'The binary search tree has a method called findMinHeight.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.findMaxHeight == 'function')})(), 'The binary search tree has a method called findMaxHeight.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.isBalanced == 'function')})(), 'The binary search tree has a method called isBalanced.');", - "assert((function() { let 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); })(), 'The findMinHeight method returns the minimum height of the tree.');", - "assert((function() { let 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); })(), 'The findMaxHeight method returns the maximum height of the tree.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.findMaxHeight !== 'function') { return false; }; return (test.findMaxHeight() == -1); })(), 'An empty tree returns a height of -1.');", - "assert((function() { let 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(); })(), 'The isBalanced method returns true if the tree is a balanced binary search tree.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const 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) {", - " let 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() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.inorder == 'function')})(), 'The binary search tree has a method called inorder.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.preorder == 'function')})(), 'The binary search tree has a method called preorder.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.postorder == 'function')})(), 'The binary search tree has a method called postorder.');", - "assert((function() { let 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'); })(), 'The inorder method returns an array of the node values that result from an inorder traversal.');", - "assert((function() { let 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'); })(), 'The preorder method returns an array of the node values that result from a preorder traversal.');", - "assert((function() { let 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'); })(), 'The postorder method returns an array of the node values that result from a postorder traversal.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.inorder !== 'function') { return false; }; return (test.inorder() == null); })(), 'The inorder method returns null for an empty tree.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.preorder !== 'function') { return false; }; return (test.preorder() == null); })(), 'The preorder method returns null for an empty tree.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.postorder !== 'function') { return false; }; return (test.postorder() == null); })(), 'The postorder method returns null for an empty tree.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const 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) {", - " let 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() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.levelOrder == 'function')})(), 'The binary search tree has a method called levelOrder.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.reverseLevelOrder == 'function')})(), 'The binary search tree has a method called reverseLevelOrder.');", - "assert((function() { let 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'); })(), 'The levelOrder method returns an array of the tree node values explored in level order.');", - "assert((function() { let 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'); })(), 'The reverseLevelOrder method returns an array of the tree node values explored in reverse level order.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.levelOrder !== 'function') { return false; }; return (test.levelOrder() == null); })(), 'The levelOrder method returns null for an empty tree.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.reverseLevelOrder !== 'function') { return false; }; return (test.reverseLevelOrder() == null); })(), 'The reverseLevelOrder method returns null for an empty tree.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));", - "function Node(value) {", - " this.value = value;", - " this.left = null;", - " this.right = null;", - "};", - "// SEED CODE --------------------------------", - "function BinarySearchTree() {", - " this.root = null;", - " // case 1: target has no children, change code below this line", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function')})(), 'The binary search tree has a method called remove.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; return (test.remove(100) == null); })(), 'Trying to remove an element that doesn\\'t exist returns null.');", - "assert((function() { let 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); })(), 'If the root node has no children, deleting it sets the root to null.');", - "assert((function() { let 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'); })(), 'The remove method removes leaf nodes from the tree');" - ], - "tail": [ - "BinarySearchTree.prototype = {", - " add: function(value) {", - " let 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 {", - " let check = true;", - " function checkTree(node) {", - " if (node.left != null) {", - " let left = node.left;", - " if (left.value > node.value) {", - " check = false;", - " } else {", - " checkTree(left);", - " }", - " }", - " if (node.right != null) {", - " let right = node.right;", - " if (right.value < node.value) {", - " check = false;", - " } else {", - " checkTree(right);", - " };", - " };", - " };", - " checkTree(this.root);", - " return check;", - " }", - " }", - "};" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));", - "function Node(value) {", - " this.value = value;", - " this.left = null;", - " this.right = null;", - "};", - "// SEED CODE --------------------------------", - "function BinarySearchTree() {", - " this.root = null;", - " this.remove = function(value) {", - " if (this.root == null) {", - " return null;", - " };", - " let target;", - " let 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", - " let 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() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function')})(), 'The binary search tree has a method called remove.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; return (test.remove(100) == null); })(), 'Trying to remove an element that doesn\\'t exist returns null.');", - "assert((function() { let 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); })(), 'If the root node has no children, deleting it sets the root to null.');", - "assert((function() { let 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'); })(), 'The remove method removes leaf nodes from the tree');", - "assert((function() { let 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'); })(), 'The remove method removes nodes with one child.');", - "assert((function() { let 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'); })(), 'Removing the root in a tree with two nodes sets the second to be the root.');" - ], - "tail": [ - "BinarySearchTree.prototype = {", - " add: function(value) {", - " let 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 {", - " let check = true;", - " function checkTree(node) {", - " if (node.left != null) {", - " let left = node.left;", - " if (left.value > node.value) {", - " check = false;", - " } else {", - " checkTree(left);", - " }", - " }", - " if (node.right != null) {", - " let right = node.right;", - " if (right.value < node.value) {", - " check = false;", - " } else {", - " checkTree(right);", - " };", - " };", - " };", - " checkTree(this.root);", - " return check;", - " }", - " }", - "};" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));", - "function Node(value) {", - " this.value = value;", - " this.left = null;", - " this.right = null;", - "};", - "// SEED CODE --------------------------------", - "function BinarySearchTree() {", - " this.root = null;", - " this.remove = function(value) {", - " if (this.root == null) {", - " return null;", - " };", - " let target;", - " let 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", - " let 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) {", - " let 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() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function')})(), 'The binary search tree has a method called remove.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function') ? (test.remove(100) == null) : false})(), 'Trying to remove an element that doesn\\'t exist returns null.');", - "assert((function() { let 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})(), 'If the root node has no children, deleting it sets the root to null.');", - "assert((function() { let 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})(), 'The remove method removes leaf nodes from the tree');", - "assert((function() { let 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'); })(), 'The remove method removes nodes with one child.');", - "assert((function() { let 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'); })(), 'Removing the root in a tree with two nodes sets the second to be the root.');", - "assert((function() { let 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'); })(), 'The remove method removes nodes with two children while maintaining the binary search tree structure.');", - "assert((function() { let 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); })(), 'The root can be removed on a tree of three nodes.');" - ], - "tail": [ - "BinarySearchTree.prototype = {", - " add: function(value) {", - " let 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 {", - " let check = true;", - " function checkTree(node) {", - " if (node.left != null) {", - " let left = node.left;", - " if (left.value > node.value) {", - " check = false;", - " } else {", - " checkTree(left);", - " }", - " }", - " if (node.right != null) {", - " let right = node.right;", - " if (right.value < node.value) {", - " check = false;", - " } else {", - " checkTree(right);", - " };", - " };", - " };", - " checkTree(this.root);", - " return check;", - " }", - " }", - "};" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const 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) {", - " let 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() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The BinarySearchTree data structure exists.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.invert == 'function')})(), 'The binary search tree has a method called invert.');", - "assert((function() { let 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'; })(), 'The invert method correctly inverts the tree structure.');", - "assert((function() { let test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.invert !== 'function') { return false; }; return (test.invert() == null); })(), 'Inverting an empty tree returns null.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "const displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));", - "let Node = function() {", - " this.keys = new Map();", - " this.end = false;", - " this.setEnd = function() {", - " this.end = true;", - " };", - " this.isEnd = function() {", - " return this.end;", - " };", - "};", - "let Trie = function() {", - " // change code below this line", - " // change code above this line", - "};" - ], - "tests": [ - "assert((function testTrie() { let test = false; if (typeof Trie !== 'undefined') { test = new Trie() } else { return false; }; return (typeof test.add == 'function') }()), 'The Trie has an add method.');", - "assert((function testTrie() { let test = false; if (typeof Trie !== 'undefined') { test = new Trie() } else { return false; }; return (typeof test.print == 'function') }()), 'The Trie has a print method.');", - "assert((function testTrie() { let test = false; if (typeof Trie !== 'undefined') { test = new Trie() } else { return false; }; return (typeof test.isWord == 'function') }()), 'The Trie has an isWord method.');", - "assert((function testTrie() { let 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'); let 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); }()), 'The print method returns all items added to the trie as strings in an array.');", - "assert((function testTrie() { let 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')); }()), 'The isWord method returns true only for words added to the trie and false for all other words.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "8d5123c8c441eddfaeb5bdef", - "title": "Implement Bubble Sort", - "description": [ - "This is the first of several challenges on sorting algorithms. Given an array of unsorted items, we want to be able to return a sorted array. We will see several different methods to do this and learn some tradeoffs between these different approaches. While most modern languages have built-in sorting methods for operations like this, it is still important to understand some of the common basic approaches and learn how they can be implemented.", - "Here we will see bubble sort. The bubble sort method starts at the beginning of an unsorted array and 'bubbles up' unsorted values towards the end, iterating through the array until it is completely sorted. It does this by comparing adjacent items and swapping them if they are out of order. The method continues looping through the array until no swaps occur at which point the array is sorted.", - "This method requires multiple iterations through the array and for average and worst cases has quadratic time complexity. While simple, it is usually impractical in most situations.", - "Instructions: Write a function bubbleSort which takes an array of integers as input and returns an array of these integers in sorted order. We've provided a helper method to generate a random array of integer values." - ], - "challengeSeed": [ - "// generate a randomly filled array", - "let array = new Array();", - "(function createArray(size = 5) {", - " array.push(+(Math.random() * 100).toFixed(0));", - " return (size > 1) ? createArray(size - 1) : undefined;", - "})(12);", - "function bubbleSort(array) {", - " // change code below this line", - " // change code above this line", - " return array;", - "};" - ], - "tail": [ - "function isSorted(arr) {", - " let check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", - " return check(0);", - "};" - ], - "tests": [ - "console.assert(typeof bubbleSort == 'function', 'bubbleSort is a function');", - "console.assert(isSorted(bubbleSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'bubbleSort returns a sorted array');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d8259367417b2b2512c85", - "title": "Implement Selection Sort", - "description": [ - "Here we will implement selection sort. Selection sort works by selecting the minimum value in a list and swapping it with the first value in the list. It then starts at the second position, selects the smallest value in the remaining list, and swaps it with the second element. It continues iterating through the list and swapping elements until it reaches the end of the list. Now the list is sorted. Selection sort has quadratic time complexity in all cases.", - "Instructions: Write a function selectionSort which takes an array of integers as input and returns an array of these integers in sorted order." - ], - "challengeSeed": [ - "// generate a randomly filled array", - "let array = new Array();", - "(function createArray(size = 5) {", - " array.push(+(Math.random() * 100).toFixed(0));", - " return (size > 1) ? createArray(size - 1) : undefined;", - "})(12);", - "function selectionSort(array) {", - " // change code below this line", - " // change code above this line", - " return array;", - "};" - ], - "tail": [ - "function isSorted(arr) {", - " let check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", - " return check(0);", - "};" - ], - "tests": [ - "console.assert(typeof selectionSort == 'function', 'selectionSort is a function');", - "console.assert(isSorted(selectionSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'selectionSort returns a sorted array');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d8259367417b2b2512c86", - "title": "Implement Insertion Sort", - "description": [ - "The next sorting method we'll look at is insertion sort. This method works by building up a sorted array at the beginning of the list. It begins the sorted array with the first element. Then it inspects the next element and swaps it backwards into the sorted array until it is in sorted position. It continues iterating through the list and swapping new items backwards into the sorted portion until it reaches the end. This algorithm has quadratic time complexity in the average and worst cases.", - "Instructions: Write a function insertionSort which takes an array of integers as input and returns an array of these integers in sorted order." - ], - "challengeSeed": [ - "// generate a randomly filled array", - "let array = new Array();", - "(function createArray(size = 5) {", - " array.push(+(Math.random() * 100).toFixed(0));", - " return (size > 1) ? createArray(size - 1) : undefined;", - "})(12);", - "function insertionSort(array) {", - " // change code below this line", - " // change code above this line", - " return array;", - "};" - ], - "tail": [ - "function isSorted(arr) {", - " let check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", - " return check(0);", - "};" - ], - "tests": [ - "console.assert(typeof insertionSort == 'function', 'insertionSort is a function');", - "console.assert(isSorted(insertionSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'insertionSort returns a sorted array');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "let Node = function(data, prev) {", - " this.data = data;", - " this.prev = prev;", - " this.next = null;", - "};", - "let 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 {", - " let result = new Array();", - " let 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 {", - " let result = new Array();", - " let node = this.tail;", - " while (node.prev != null) {", - " result.push(node.data);", - " node = node.prev;", - " };", - " result.push(node.data);", - " return result;", - " };", - " } ", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; return (typeof test == 'object')})(), 'The DoublyLinkedList data structure exists.');", - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; if (test.add == undefined) { return false; }; return (typeof test.add == 'function')})(), 'The DoublyLinkedList has a method called add.');", - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; if (test.remove == undefined) { return false; }; return (typeof test.remove == 'function')})(), 'The DoublyLinkedList has a method called remove.');", - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; return (test.remove(100) == null); })(), 'Removing an item from an empty list returns null.');", - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; test.add(5); test.add(6); test.add(723); return (test.print().join('') == '56723'); })(), 'The add method adds items to the list.');", - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; test.add(50); test.add(68); test.add(73); return (test.printReverse().join('') == '736850'); })(), 'Each node keeps track of the previous node.');", - "assert((function() { let 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' ) })(), 'The first item can be removed from the list.');", - "assert((function() { let 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' ) })(), 'The last item can be removed from the list.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "let Node = function(data, prev) {", - " this.data = data;", - " this.prev = prev;", - " this.next = null;", - "};", - "let DoublyLinkedList = function() {", - " this.head = null;", - " this.tail = null;", - " // change code below this line", - " // change code above this line", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; return (typeof test == 'object')})(), 'The DoublyLinkedList data structure exists.');", - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; if (test.add == undefined) { return false; }; return (typeof test.add == 'function')})(), 'The DoublyLinkedList has a method called add.');", - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; if (test.reverse == undefined) { return false; }; return (typeof test.reverse == 'function')})(), 'The DoublyLinkedList has a method called reverse.');", - "assert((function() { let test = false; if (typeof DoublyLinkedList !== 'undefined') { test = new DoublyLinkedList() }; return (test.reverse() == null); })(), 'Reversing an empty list returns null.');", - "assert((function() { let 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'); })(), 'The reverse method reverses the list.');", - "assert((function() { let 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'); })(), '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 {", - " let node = this.head;", - " let prev = null;", - " while (node.next != null) {", - " prev = node;", - " node = node.next;", - " };", - " let newNode = new Node(data, node);", - " node.next = newNode;", - " this.tail = newNode;", - " };", - " },", - " print() {", - " if (this.head == null) {", - " return null;", - " } else {", - " let result = new Array();", - " let 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 {", - " let result = new Array();", - " let node = this.tail;", - " while (node.prev != null) {", - " result.push(node.data);", - " node = node.prev;", - " };", - " result.push(node.data);", - " return result;", - " };", - " }", - "};" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d825a367417b2b2512c89", - "title": " Implement Quick Sort", - "description": [ - "Here we will move on to an intermediate sorting algorithm: quick sort. Quick sort is an", - "efficient, recursive divide-and-conquer approach to sorting an array. In this method, a pivot value is chosen in the original array. The array is then partitioned into two subarrays of values less than and greater than the pivot value. We then combine the result of recursively calling the quick sort algorithm on both sub-arrays. This continues until the base case of an empty or single-item array is reached, which we return. The unwinding of the recursive calls return us the sorted array.", - "Quick sort is a very efficient sorting method, providing O(nlog(n)) performance on average. It is also relatively easy to implement. These attributes make it a popular and useful sorting method.", - "Instructions: Write a function quickSort which takes an array of integers as input and returns an array of these integers in sorted order. While the choice of the pivot value is important, any pivot will do for our purposes here. For simplicity, the first or last element could be used." - ], - "challengeSeed": [ - "// generate a randomly filled array", - "let array = new Array();", - "(function createArray(size = 5) {", - " array.push(+(Math.random() * 100).toFixed(0));", - " return (size > 1) ? createArray(size - 1) : undefined;", - "})(12);", - "function quickSort(array) {", - " // change code below this line", - " // change code above this line", - " return array;", - "};" - ], - "tests": [ - "assert(typeof quickSort == 'function', 'quickSort is a function');", - "assert(isSorted(quickSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'quickSort returns a sorted array');" - ], - "tail": [ - "function isSorted(arr) {", - " let check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", - " return check(0);", - "};" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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 Map object provided here as a wrapper around a JavaScript object. Create the following methods and operations on the Map object:", - "add accepts a key, value pair to add to the map", - "remove accepts a key and removes the associated key, value pair", - "get accepts a key and returns the stored value", - "has returns a boolean for the presence or absence of an item", - "values returns an array of all the values in the map", - "size returns the number of items in the map", - "clear empties the map" - ], - "challengeSeed": [ - "let Map = function() {", - " this.collection = {}", - " // change code below this line", - " // change code above this line", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof Map !== 'undefined') { test = new Map() }; return (typeof test == 'object')})(), 'The Map data structure exists.');", - "assert((function() { let 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')})(), 'The Map object has the following methods: add, remove, get, has, values, clear, and size.');", - "assert((function() { let 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)})(), 'The add method adds items to the map.');", - "assert((function() { let test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add('test','value'); return (test.has('test') && !test.has('false'))})(), 'The has method returns true for added items and false for absent items.');", - "assert((function() { let test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add('abc','def'); return (test.get('abc') == 'def')})(), 'The get method accepts keys as input and returns the associated values.');", - "assert((function() { let test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add('a','b'); test.add('c','d'); test.add('e','f'); let vals = test.values(); return (vals.indexOf('b') != -1 && vals.indexOf('d') != -1 && vals.indexOf('f') != -1)})(), 'The values method returns all the values stored in the map as strings in an array.');", - "assert((function() { let test = false; if (typeof Map !== 'undefined') { test = new Map() }; test.add('b','b'); test.add('c','d'); test.remove('asdfas'); let init = test.size(); test.clear(); return (init == 2 && test.size() == 0)})(), 'The clear method empties the map and the size method returns the number of items present in the map.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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:", - "[ 6, 22, 30, 37, 63, 48, 42, 76 ]", - "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: [ null, 22, 30, 37, 63, 48, 42, 76 ]", - "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": [ - "let MaxHeap = function() {", - " // change code below this line", - " // change code above this line", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() }; return (typeof test == 'object')})(), 'The MaxHeap data structure exists.');", - "assert((function() { let 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() { let 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() { let 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) })(), 'The insert method adds elements according to the max heap property.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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": [ - "let MaxHeap = function() {", - " // change code below this line", - " // change code above this line", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof MaxHeap !== 'undefined') { test = new MaxHeap() }; return (typeof test == 'object')})(), 'The MaxHeap data structure exists.');", - "assert((function() { let 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() { let 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() { let 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() { let 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": 0, - "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) {", - " let check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", - " return check(0);", - "};", - "// generate a randomly filled array", - "let array = new Array();", - "(function createArray(size = 5) {", - " array.push(+(Math.random() * 100).toFixed(0));", - " return (size > 1) ? createArray(size - 1) : undefined;", - "})(25);", - "let MinHeap = function() {", - " // change code below this line", - " // change code above this line", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() }; return (typeof test == 'object')})(), 'The MinHeap data structure exists.');", - "assert((function() { let test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() } else { return false; }; return (typeof test.insert == 'function')})(), 'MinHeap has a method called insert.');", - "assert((function() { let test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() } else { return false; }; return (typeof test.remove == 'function')})(), 'MinHeap has a method called remove.');", - "assert((function() { let test = false; if (typeof MinHeap !== 'undefined') { test = new MinHeap() } else { return false; }; return (typeof test.sort == 'function')})(), 'MinHeap has a method called sort.');", - "assert((function() { let 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); let result = test.sort(); return (isSorted(result)); })(), 'The sort method returns an array containing all items added to the min heap in sorted order.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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:", - ".has(key) returns true or false based on the presence of a key", - ".get(key) returns the value associated with a key", - ".set(key, value) sets a new key, value pair", - ".delete(key) removes a key, value pair", - ".clear() removes all key, value pairs", - ".entries() returns an array of all the keys in insertion order", - ".values() 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 'Free Code Camp', 'Awesome!' to it." - ], - "challengeSeed": [ - "// change code below this line" - ], - "tests": [ - "assert(typeof myMap == 'object', 'The myMap object exists.');", - "assert(myMap.get('Free Code Camp') == 'Awesome!', 'myMap contains the key value pair \\'Free Code Camp\\', \\'Awesome!\\'.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "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!" - ], - "challengeSeed": [ - "let called = 0;", - "let hash = (string) => {", - " called++;", - " let hash = 0;", - " for (let i = 0; i < string.length; i++) { hash += string.charCodeAt(i); };", - " return hash;", - "};", - "let HashTable = function() {", - " this.collection = {};", - " // change code below this line", - " // change code above this line", - "};" - ], - "tests": [ - "assert((function() { let test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; return (typeof test == 'object')})(), 'The HashTable data structure exists.');", - "assert((function() { let test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; return ((typeof test.add) == 'function')})(), 'The HashTable has an add method.');", - "assert((function() { let test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; return ((typeof test.remove) == 'function')})(), 'The HashTable has an remove method.');", - "assert((function() { let test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; return ((typeof test.lookup) == 'function')})(), 'The HashTable has an lookup method.');", - "assert((function() { let test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; test.add('key', 'value'); return (test.lookup('key') == 'value')})(), 'The add method adds key value pairs and the lookup method returns the values associated with a given key.');", - "assert((function() { let test = false; if (typeof HashTable !== 'undefined') { test = new HashTable() }; test.add('key', 'value'); test.remove('key'); return (test.lookup('key') == null)})(), 'The remove method accepts a key as input and removes the associated key value pair.');", - "assert((function() { let 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)})(), 'Items are added using the hash function.');", - "assert((function() { let 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')})(), 'The hash table handles collisions.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d825c367417b2b2512c8f", - "title": "Implement Merge Sort", - "description": [ - "Another intermediate sorting algorithm that is very common is merge sort. Like quick sort, merge sort also uses a divide-and-conquer, recursive methodology to sort an array. It takes advantage of the fact that it is relatively easy to sort two arrays as long as each is sorted in the first place. But we'll start with only one array as input, so how do we get to two sorted arrays from that? Well, we can recursively divide the original input in two until we reach the base case of an array with one item. A single-item array is naturally sorted, so then we can start combining. This combination will unwind the recursive calls that split the original array, eventually producing a final sorted array of all the elements. The steps of merge sort, then, are:", - "Recursively split the input array in half until a sub-array with only one element is produced.", - "Merge each sorted sub-array together to produce the final sorted array.", - "Merge sort is an efficient sorting method, with time complexity of O(nlog(n)). This algorithm is popular because it is performant and relatively easy to implement.", - "As an aside, this will be the last sorting algorithm we cover here. However, later in the section on tree data structures we will describe heap sort, another efficient sorting method that requires a binary heap in its implementation.", - "Instructions: Write a function mergeSort which takes an array of integers as input and returns an array of these integers in sorted order. A good way to implement this is to write one function, for instance merge, which is responsible for merging two sorted arrays, and another function, for instance mergeSort, which is responsible for the recursion that produces single-item arrays to feed into merge. Good luck!" - ], - "challengeSeed": [ - "// generate a randomly filled array", - "let array = new Array();", - "(function createArray(size = 5) {", - " array.push(+(Math.random() * 100).toFixed(0));", - " return (size > 1) ? createArray(size - 1) : undefined;", - "})(25);", - "// change code below this line" - ], - "tests": [ - "assert(typeof mergeSort == 'function', 'mergeSort is a function');", - "assert(isSorted(mergeSort([1,4,2,8,345,123,43,32,5643,63,123,43,2,55,1,234,92])), 'mergeSort returns a sorted array');" - ], - "tail": [ - "function isSorted(arr) {", - " let check = (i) => (i == arr.length - 1) ? true : (arr[i] > arr[i + 1]) ? false : check(i + 1);", - " return check(0);", - "};" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, + "challengeType": 1, "translations": {} }, { @@ -3795,366 +2929,7 @@ ], "type": "waypoint", "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d825c367417b2b2512c91", - "title": "Nodes in a Doubly Linked List", - "description": [ - "A doubly linked list is similar to a linked list. The difference between these data structures is the information in the nodes, which store: the element, a reference to the previous node and a reference to the next node of the list.", - "Imagine that you're a moutaineer in a team that's trying to summit a mountain. There's a rope that connects you to the person in front of you and a rope that connects you to the person after you in the line. However, due to fog, you can only see the person directly ahead of you and the person directly behind you.", - "A doubly linked list node is similar to a person in this line. The person represents the element of the node and the rope connections between the mountaineers represent the references to the previous and next nodes of the list.", - "Instructions", - "We have created two nodes that represent the first two mountaineers (Joe and Cameron).", - "Create one new node that represents a new mountaineer (Donna) and manually add it to the line." - ], - "challengeSeed": [ - "var Node = function(element){", - " this.element = element;", - " this.prev = null;", - " this.next = null;", - "}", - "var Joe = new Node(\"Joe\");", - "var Cameron = new Node(\"Cameron\");", - "Joe.next = Cameron;", - "Cameron.prev = Joe;", - "// only add code below this line", - "// test your code", - "console.log(\"After Joe is \" + Joe.next.element);", - "console.log(\"Before Cameron is \" + Cameron.prev.element);" - ], - "tests": [ - "assert(Cameron.next.element === \"Donna\", 'message: The Cameron node should reference to a Donna next node.');", - "assert(Donna.prev.element === \"Cameron\", 'message: The Donna node should reference to a Cameron prev node.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d825c367417b2b2512c92", - "title": "Inserting Nodes in a Doubly Linked List", - "description": [ - "One of the most important methods in a Doubly Linked List implementation is the insert method. When a new node is inserted into the doubly linked list, the list's size is increased by one.", - "If the new node is the first element of the list, it should be referenced as the next element of the head and the previous element of the tail. If there are already nodes in the list, the new node should be referenced as the previous node of the tail and the next of the penultimate node of the list.", - "Instructions", - "Create a method that inserts nodes to the list.", - "We've created the auxiliary methods:", - "getSizeOfList - returns the size of the list.", - "isListEmpty - returns true if the list is empty.", - "printList - prints the list.", - "getHeadNode - returns the head node of the list.", - "getTailNode - returns the tail node of the list.", - "Note", - "The size of the list should increase when you insert a new node." - ], - "challengeSeed": [ - "function DoublyLinkedList(){", - " var length = 0;", - " var head = new Node(null);", - " var tail = new Node(null);", - " head.next = tail;", - " tail.prev = head;", - " ", - " // constructor for a list node", - " function Node(element){", - " this.element = element;", - " this.next = null;", - " this.prev = null;", - " }", - " ", - " // get the head node of the list", - " this.getHeadNode = function(){", - " return head;", - " }", - " ", - " // get the tail node of the list", - " this.getTailNode = function(){", - " return tail;", - " }", - " // get the size of the list", - " this.getSizeOfList = function(){", - " return length;", - " }", - " ", - " // check if a list is empty", - " this.isListEmpty = function (){", - " return (head.next === tail);", - " }", - " ", - " // only add code below this line", - " ", - " // only add code above this line", - " ", - " // prints the contents of the doubly linked list", - " this.printList = function(){", - " var current = head;", - " var list=\"[\";", - " ", - " while(current){", - " if(current.element!==null){", - " list = list + current.element;", - " if(current.next.element!== null){", - " list=list+", - ";", - " }", - " }", - " current = current.next;", - " }", - " list = list + \"]\";", - " return list;", - " }", - " // test your code", - " var conga = new DoublyLinkedList();", - " conga.insert(\"Joe\");", - " conga.insert(\"Cameron\");", - " conga.printList();", - "}" - ], - "tests": [ - "assert((function(){var conga = new DoublyLinkedList(); return (typeof conga.insert === 'function')}()), 'message: Your DoublyLinkedList class should have a insert method.');", - "assert((function(){var conga = new DoublyLinkedList(); conga.insert('Joe'); return conga.getHeadNode().next.element === 'Joe'}()), 'message: Your DoublyLinkedList class should contain the node Joe.');", - "assert((function(){var conga = new DoublyLinkedList(); conga.insert('Joe'); return conga.isListEmpty() === false}()), 'message: Your DoublyLinkedList should not be empty.');", - "assert((function(){var conga = new DoublyLinkedList(); conga.insert('Joe'); return conga.getSizeOfList() > 0}()), 'message: Your DoublyLinkedList class should contain at least 1 node.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d825c367417b2b2512c93", - "title": "Removing Nodes From a Doubly Linked List", - "description": [ - "A method that removes items is an essential component of every Doubly Linked List implementation.", - "When a node is removed from a Doubly Linked List, the references that connect it to its neighbors should be updated and the number of elements of the list should be decremented.", - "For example:", - "You have the nodes {cat, dog, bird} in a Doubly Linked List and you remove the dog node. The cat.next reference should now point to the bird node and the bird.prev reference should now point to the cat node. Then, the size of the list should decrease by 1.", - "Instructions", - "Create a method that removes a node from the list.", - "We've created the auxiliary methods:", - "getSizeOfList - returns the size of the list", - "isListEmpty - returns true if the list is empty.", - "printList - prints the list.", - "getHeadNode - returns the head node of the list.", - "getTailNode - returns the tail node of the list.", - "Note", - "The size of the list should be decremented when you remove a node." - ], - "challengeSeed": [ - "function DoublyLinkedList(){", - " var length = 0;", - " var head = new Node(null);", - " var tail = new Node(null);", - " head.next = tail;", - " tail.prev = head;", - " ", - " // constructor for a list node", - " function Node(element){", - " this.element = element;", - " this.next = null;", - " this.prev = null;", - " }", - " ", - " // get the head node of the list", - " this.getHeadNode = function(){", - " return head;", - " }", - " ", - " // get the tail node of the list", - " this.getTailNode = function(){", - " return tail;", - " }", - " ", - " // get the size of the list", - " this.getSizeOfList = function(){", - " return length;", - " }", - " ", - " // check if a list is empty", - " this.isListEmpty = function (){", - " return (head.next === tail);", - " }", - " ", - " this.insert = function(element){", - " var node = new Node(element);", - " ", - " var current = head;", - " ", - " while(current.next !== tail){", - " current = current.next; ", - " }", - " ", - " current.next = node;", - " node.prev = current;", - " node.next = tail;", - " tail.prev = node;", - " ", - " length++;", - " }", - " ", - " // only add code below this line", - " ", - " // only add code above this line", - " ", - " // prints the contents of the doubly linked list", - " this.printList = function(){", - " var current = head;", - " var list=\"[\";", - " ", - " while(current){", - " if(current.element!==null){", - " list = list + current.element;", - " if(current.next.element!== null){", - " list=list+", - ";", - " }", - " }", - " current = current.next;", - " }", - " list = list + \"]\";", - " return list;", - " }", - "}", - " // test your code", - " var conga = new DoublyLinkedList();", - " conga.insert(\"Joe\");", - " conga.insert(\"Cameron\");", - " conga.printList();", - "}" - ], - "tests": [ - "assert((function(){var conga = new DoublyLinkedList(); return (typeof conga.remove === 'function')}()), 'message: Your DoublyLinkedList class should have a remove method.');", - "assert((function(){var conga = new DoublyLinkedList(); conga.insert('Joe'); conga.remove('Joe'); return conga.getSizeOfList() === 0}()), 'message: Your DoublyLinkedList class should have 0 elements.');", - "assert((function(){var conga = new DoublyLinkedList(); conga.insert('Joe'); conga.remove('Joe'); return conga.isListEmpty() === true}()), 'message: Your DoublyLinkedList should be empty.');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, - "translations": {} - }, - { - "id": "587d825d367417b2b2512c94", - "title": "Searching For Nodes In a Doubly Linked List", - "description": [ - "When you have a lot of nodes in a Doubly Linked List, you'll likely need a method to search for a node in your list.", - "Since Doubly Linked Lists can be traversed from front to back or from back to front, you can search for nodes in both directions.", - "Instructions", - "Create a method that searches for a node in the list and returns its index on the list.", - "We've created the auxiliary methods:", - "getSizeOfList - returns the size of the list", - "isListEmpty - returns true if the list is empty.", - "printList - prints the list.", - "getHeadNode - returns the head node of the list.", - "getTailNode - returns the tail node of the list.", - "Note", - "If the node you're looking for is not in the Doubly Linked List, your search method should return -1.", - "The head of the list has index 0." - ], - "challengeSeed": [ - "function DoublyLinkedList(){", - " var length = 0;", - " var head = new Node(null);", - " var tail = new Node(null);", - " head.next = tail;", - " tail.prev = head;", - " ", - " // constructor for a list node", - " function Node(element){", - " this.element = element;", - " this.next = null;", - " this.prev = null;", - " }", - " ", - " // get the head node of the list", - " this.getHeadNode = function(){", - " return head;", - " }", - " ", - " // get the tail node of the list", - " this.getTailNode = function(){", - " return tail;", - " }", - " ", - " // get the size of the list", - " this.getSizeOfList = function(){", - " return length;", - " }", - " ", - " // check if a list is empty", - " this.isListEmpty = function (){", - " return (head.next === tail);", - " }", - " // insert an element at the end of the list", - " this.insert = function(element){", - " var node = new Node(element);", - " ", - " var current = head;", - " ", - " while(current.next !== tail){", - " current = current.next; ", - " }", - " ", - " current.next = node;", - " node.prev = current;", - " node.next = tail;", - " tail.prev = node;", - " ", - " length++;", - " }", - " ", - " // remove an element from the list", - " this.remove = function(element){", - " var current = head;", - " ", - " while(current.element !== element){", - " current = current.next;", - " }", - " ", - " current.prev.next = current.next;", - " current.next.prev = current.prev;", - " ", - " length--;", - " }", - " // only add code below this line", - " ", - " // only add code above this line", - " ", - " // prints the contents of the doubly linked list", - " this.printList = function(){", - " var current = head;", - " var list=\"[\";", - " ", - " while(current){", - " if(current.element!==null){", - " list = list + current.element;", - " if(current.next.element!== null){", - " list=list+", - ";", - " }", - " }", - " current = current.next;", - " }", - " list = list + \"]\";", - " return list;", - " }", - "}", - " // test your code", - " var conga = new DoublyLinkedList();", - " conga.insert(\"Joe\");", - " console.log(conga.search(\"Joe\"));", - "}" - ], - "tests": [ - "assert((function(){var conga = new DoublyLinkedList(); return (typeof conga.search === 'function')}()), 'message: Your DoublyLinkedList class should have a search method.');", - "assert((function(){var conga = new DoublyLinkedList(); conga.add('Joe'); return conga.search('Joe') === 1}()), 'message: Your DoublyLinkedList search method should find Joe at index 1.');", - "assert((function(){var conga = new DoublyLinkedList(); conga.add('Joe'); return conga.search('Cameron') === -1}()), 'message: Your DoublyLinkedList search method should return -1 (element not found).');" - ], - "type": "waypoint", - "solutions": [], - "challengeType": 0, + "challengeType": 1, "translations": {} }, { @@ -4191,7 +2966,7 @@ ], "type": "waypoint", "solutions": [], - "challengeType": 0, + "challengeType": 1, "translations": {} } ]