UNPKG

ds-algo-study

Version:

Just experimenting with publishing a package

803 lines (801 loc) 224 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="description" content="A description of the page and its contents" /> <link rel="stylesheet" href="styles.css" /> <title>Page Title</title> <link rel="stylesheet" href="./../../../assets/style.css" /> <link rel="stylesheet" href="./../../../assets/prism.css" /> <script async src="./../../../assets/prism.js"></script> </head> <body> <h1 id="section"><img src="ds-cheat-sheet-operations.png" /></h1> <blockquote> <p>Its always good to have a look at worst-case time complexities of common data structure operations frequently. </p> </blockquote> <p>Its always good to have a look at worst-case time complexities of common data structure operations frequently.</p> <figure> <img src="https://miro.medium.com/max/60/1*6NpRbTpekXG_1l5hh1XeIQ.png?q=20" alt="Image for post" /> <figcaption>Image for post</figcaption> </figure> <figure> <img src="https://miro.medium.com/max/3572/1*6NpRbTpekXG_1l5hh1XeIQ.png" alt="Image for post" /> <figcaption>Image for post</figcaption> </figure> <p>Arrays are one of the basic and important data structures to learn, They take constant time to read and Insert elements at the end and takes a linear time for the remaining.</p> <figure> <img src="https://miro.medium.com/max/60/1*vFbcvaNX-aWr5-wERwKFIA.png?q=20" alt="Image for post" /> <figcaption>Image for post</figcaption> </figure> <figure> <img src="https://miro.medium.com/max/3746/1*vFbcvaNX-aWr5-wERwKFIA.png" alt="Image for post" /> <figcaption>Image for post</figcaption> </figure> <p>Stack takes constant time for Push, Pop &amp; Peek operations.</p> <figure> <img src="https://miro.medium.com/max/60/1*HgkpbE06UCWm2G3U54J8ew.png?q=20" alt="Image for post" /> <figcaption>Image for post</figcaption> </figure> <figure> <img src="https://miro.medium.com/max/2512/1*HgkpbE06UCWm2G3U54J8ew.png" alt="Image for post" /> <figcaption>Image for post</figcaption> </figure> <p>In Queue for Enqueue, Dequeue &amp; Peek operations it takes only Constant time.</p> <figure> <img src="https://miro.medium.com/max/60/1*amq4OYYapQjaN2QXIG5eUw.png?q=20" alt="Image for post" /> <figcaption>Image for post</figcaption> </figure> <figure> <img src="https://miro.medium.com/max/3942/1*amq4OYYapQjaN2QXIG5eUw.png" alt="Image for post" /> <figcaption>Image for post</figcaption> </figure> <p>Here we are considering we are using tails for all single linked lists (Some implementations might not have it).<br /> Linked List is the data structure that comes with a lot of different operational scenarios, we have to think about head &amp; tail usage in every operation we are doing. And operation logic and complexity changes at the head, tail, and middle. Typically insertion at head &amp; tail takes constant time and insertion in middle takes linear time. Search can take linear time. Deletion at the head takes constant time and it can take linear time in remaining scenarios.</p> <hr /> <h2 id="trees-basic-concepts"><a class="btn" href="#Trees-basic-concepts" title="Trees: basic concepts"></a>Trees: basic concepts </h2> <p>A tree is a data structure where a node can have zero or more children. Each node contains a <strong>value</strong>. Like graphs, the connection between nodes is called <strong>edges</strong>. A tree is a type of graph, but not all of them are trees (more on that later). </p> <p>These data structures are called "trees" because the data structure resembles a tree 🌳. It starts with a <strong>root</strong> node and <strong>branch</strong> off with its descendants, and finally, there are <strong>leaves</strong>. </p> <p><img src="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/images/tree-parts.jpg" /></p> <p>Here are some properties of trees:</p> <ul> <li>The top-most node is called <strong>root</strong>.</li> <li>A node without children is called <strong>leaf</strong> node or <strong>terminal</strong> node.</li> <li><strong>Height</strong> (<em>h</em>) of the tree is the distance (edge count) between the farthest leaf to the root. <ul> <li><code>A</code> has a height of 3</li> <li><code>I</code> has a height of 0</li> </ul> </li> <li><strong>Depth</strong> or <strong>level</strong> of a node is the distance between the root and the node in question. <ul> <li><code>H</code> has a depth of 2</li> <li><code>B</code> has a depth of 1</li> </ul> </li> </ul> <h3 id="implementing-a-simple-tree-data-structure"><a class="btn" href="#Implementing-a-simple-tree-data-structure" title="Implementing a simple tree data structure"></a>Implementing a simple tree data structure</h3> <p>As we saw earlier, a tree node is just a data structure that has a value and has links to their descendants.</p> <p>Here's an example of a tree node:</p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span><span><span>class</span> <span>TreeNode</span> </span>{</span><br><span> <span>constructor</span>(value) {</span><br><span> <span>this</span>.value = value;</span><br><span> <span>this</span>.descendents = [];</span><br><span> }</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>We can create a tree with 3 descendents as follows:</p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br><span>9</span><br><span>10</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span></span><br><span><span>const</span> abe = <span>new</span> TreeNode(<span>'Abe'</span>);</span><br><span><span>const</span> homer = <span>new</span> TreeNode(<span>'Homer'</span>);</span><br><span><span>const</span> bart = <span>new</span> TreeNode(<span>'Bart'</span>);</span><br><span><span>const</span> lisa = <span>new</span> TreeNode(<span>'Lisa'</span>);</span><br><span><span>const</span> maggie = <span>new</span> TreeNode(<span>'Maggie'</span>);</span><br><span></span><br><span></span><br><span>abe.descendents.push(homer);</span><br><span>homer.descendents.push(bart, lisa, maggie);</span><br></pre> </td> </tr> </tbody> </table> <p>That's all; we have a tree data structure!</p> <p><img src="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/images/simpson2-tree.jpg" title="Simpson tree data structure"/></p> <p>The node <code>abe</code> is the <strong>root</strong> and <code>bart</code>, <code>lisa</code> and <code>maggie</code> are the <strong>leaf</strong> nodes of the tree. Notice that the tree's node can have a different number of descendants: 0, 1, 3, or any other value. </p> <p>Tree data structures have many applications such as:</p> <ul> <li><a href="https://adrianmejia.com/blog/2018/04/28/data-structures-time-complexity-for-beginners-arrays-hashmaps-linked-lists-stacks-queues-tutorial/#HashMaps">Maps</a> </li> <li><a href="https://adrianmejia.com/blog/2018/04/28/data-structures-time-complexity-for-beginners-arrays-hashmaps-linked-lists-stacks-queues-tutorial/#Sets">Sets</a> </li> <li>Databases</li> <li>Priority Queues</li> <li>Querying an LDAP (Lightweight Directory Access Protocol)</li> <li>Representing the Document Object Model (DOM) for HTML on Websites.</li> </ul> <h2 id="binary-trees"><a class="btn" href="#Binary-Trees" title="Binary Trees"></a>Binary Trees</h2> <p>Trees nodes can have zero or more children. However, when a tree has at the most two children, then it's called <strong>binary tree</strong>. </p> <h3 id="full-complete-and-perfect-binary-trees"><a class="btn" href="#Full-Complete-and-Perfect-binary-trees" title="Full, Complete and Perfect binary trees"></a>Full, Complete and Perfect binary trees</h3> <p>Depending on how nodes are arranged in a binary tree, it can be <strong>full</strong>, <strong>complete</strong> and <strong>perfect</strong>:</p> <ul> <li><strong>Full binary tree</strong>: each node has exactly 0 or 2 children (but never 1).</li> <li><strong>Complete binary tree</strong>: when all levels except the last one are <strong>full</strong> with nodes. </li> <li><strong>Perfect binary tree</strong>: when all the levels (including the last one) are full of nodes.</li> </ul> <p>Look at these examples:</p> <p><img src="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/images/full-complete-perfect-binary-tree.jpg" title="Full vs. Complete vs. Perfect Binary Tree"/></p> <p>These properties are not always mutually exclusive. You can have more than one:</p> <ul> <li>A perfect tree is <strong>always</strong> complete and full. <ul> <li>Perfect binary trees have precisely 2k-1 nodes, where <em><code>k</code></em> is the last level of the tree (starting with 1).</li> </ul> </li> <li>A complete tree is <strong>not</strong> always <code>full</code>. <ul> <li>Like in our "complete" example, since it has a parent with only one child. If we remove the rightmost gray node, then we would have a <strong>complete</strong> and <strong>full</strong> tree but not perfect.</li> </ul> </li> <li>A full tree is not always complete and perfect.</li> </ul> <h2 id="binary-search-tree-bst"><a class="btn" href="#Binary-Search-Tree-BST" title="Binary Search Tree (BST)"></a>Binary Search Tree (BST)</h2> <p>Binary Search Trees or BST for short are a particular application of binary trees. BST has at most two nodes (like all binary trees). However, the values are in such a way that the left children value must be less than the parent, and the right children is must be higher.</p> <p><strong>Duplicates:</strong> Some BST doesn't allow duplicates while others add the same values as a right child. Other implementations might keep a count on a case of the duplicity (we are going to do this one later).</p> <p>Let's implement a Binary Search Tree!</p> <h3 id="bst-implementation"><a class="btn" href="#BST-Implementation" title="BST Implementation"></a>BST Implementation </h3> <p>BST are very similar to our previous <a class="btn" href="#Implementing-a-simple-tree-data-structure">implementation of a tree</a>. However, there are some differences:</p> <ul> <li>Nodes can have at most, only two children: left and right.</li> <li>Nodes values has to be ordered as <code>left &lt; parent &lt; right</code>.</li> </ul> <p>Here's the tree node. Very similar to what we did before, but we added some handy getters and setters for left and right children. Notice that is also keeping a reference to the parent and we update it every time add children.</p> <p>TreeNode.js<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/tree-node.js">Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br><span>9</span><br><span>10</span><br><span>11</span><br><span>12</span><br><span>13</span><br><span>14</span><br><span>15</span><br><span>16</span><br><span>17</span><br><span>18</span><br><span>19</span><br><span>20</span><br><span>21</span><br><span>22</span><br><span>23</span><br><span>24</span><br><span>25</span><br><span>26</span><br><span>27</span><br><span>28</span><br><span>29</span><br><span>30</span><br><span>31</span><br><span>32</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span><span>const</span> LEFT = <span>0</span>;</span><br><span><span>const</span> RIGHT = <span>1</span>;</span><br><span></span><br><span><span><span>class</span> <span>TreeNode</span> </span>{</span><br><span> <span>constructor</span>(value) {</span><br><span> <span>this</span>.value = value;</span><br><span> <span>this</span>.descendents = [];</span><br><span> <span>this</span>.parent = <span>null</span>;</span><br><span> }</span><br><span></span><br><span> <span>get</span> <span>left</span>() {</span><br><span> <span>return</span> <span>this</span>.descendents[LEFT];</span><br><span> }</span><br><span></span><br><span> <span>set</span> <span>left</span>(<span>node</span>) {</span><br><span> <span>this</span>.descendents[LEFT] = node;</span><br><span> <span>if</span> (node) {</span><br><span> node.parent = <span>this</span>;</span><br><span> }</span><br><span> }</span><br><span></span><br><span> <span>get</span> <span>right</span>() {</span><br><span> <span>return</span> <span>this</span>.descendents[RIGHT];</span><br><span> }</span><br><span></span><br><span> <span>set</span> <span>right</span>(<span>node</span>) {</span><br><span> <span>this</span>.descendents[RIGHT] = node;</span><br><span> <span>if</span> (node) {</span><br><span> node.parent = <span>this</span>;</span><br><span> }</span><br><span> }</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>Ok, so far we can add a left and right child. Now, let's do the BST class that enforces the <code>left &lt; parent &lt; right</code> rule. </p> <p>BinarySearchTree.js linkUrl linkText</p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br><span>9</span><br><span>10</span><br><span>11</span><br><span>12</span><br><span>13</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span></span><br><span><span><span>class</span> <span>BinarySearchTree</span> </span>{</span><br><span> <span>constructor</span>() {</span><br><span> <span>this</span>.root = <span>null</span>;</span><br><span> <span>this</span>.size = <span>0</span>;</span><br><span> }</span><br><span></span><br><span> add(value) { }</span><br><span> find(value) { }</span><br><span> remove(value) { }</span><br><span> getMax() { }</span><br><span> getMin() { }</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>Let's implementing insertion.</p> <h3 id="bst-node-insertion"><a class="btn" href="#BST-Node-Insertion" title="BST Node Insertion"></a>BST Node Insertion </h3> <p>To insert a node in a binary tree, we do the following:</p> <ol type="1"> <li>If a tree is empty, the first node becomes the <strong>root</strong> and you are done.</li> <li>Compare root/parent's value if it's <em>higher</em> go <strong>right</strong>, if it's <em>lower</em> go <strong>left</strong>. If it's the same, then the value already exists so you can increase the duplicate count (multiplicity). </li> <li>Repeat #2 until we found an empty slot to insert the new node.</li> </ol> <p>Let's do an illustration how to insert 30, 40, 10, 15, 12, 50:</p> <p><img src="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/images/bst2.gif" title="Inserting nodes on a Binary Search Tree (BST)"/></p> <p>We can implement insert as follows:</p> <p>BinarySearchTree.prototype.add<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js#L11">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br><span>9</span><br><span>10</span><br><span>11</span><br><span>12</span><br><span>13</span><br><span>14</span><br><span>15</span><br><span>16</span><br><span>17</span><br><span>18</span><br><span>19</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>add(value) {</span><br><span> <span>const</span> newNode = <span>new</span> TreeNode(value);</span><br><span></span><br><span> <span>if</span> (<span>this</span>.root) {</span><br><span> <span>const</span> { found, parent } = <span>this</span>.findNodeAndParent(value);</span><br><span> <span>if</span> (found) { </span><br><span> found.meta.multiplicity = (found.meta.multiplicity || <span>1</span>) + <span>1</span>;</span><br><span> } <span>else</span> <span>if</span> (value &lt; parent.value) {</span><br><span> parent.left = newNode;</span><br><span> } <span>else</span> {</span><br><span> parent.right = newNode;</span><br><span> }</span><br><span> } <span>else</span> {</span><br><span> <span>this</span>.root = newNode;</span><br><span> }</span><br><span></span><br><span> <span>this</span>.size += <span>1</span>;</span><br><span> <span>return</span> newNode;</span><br><span>}</span><br> </pre> </td> </tr> </tbody> </table> <p>We are using a helper function called <code>findNodeAndParent</code>. If we found that the node already exists in the tree, then we increase the <code>multiplicity</code> counter. Let's see how this function is implemented:</p> <p>BinarySearchTree.prototype.findNodeAndParent<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js#L44">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br><span>9</span><br><span>10</span><br><span>11</span><br><span>12</span><br><span>13</span><br><span>14</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>findNodeAndParent(value) {</span><br><span> <span>let</span> node = <span>this</span>.root;</span><br><span> <span>let</span> parent;</span><br><span></span><br><span> <span>while</span> (node) {</span><br><span> <span>if</span> (node.value === value) {</span><br><span> <span>break</span>;</span><br><span> }</span><br><span> parent = node;</span><br><span> node = ( value &gt;= node.value) ? node.right : node.left;</span><br><span> }</span><br><span></span><br><span> <span>return</span> { <span>found</span>: node, parent };</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p><code>findNodeAndParent</code> goes through the tree searching for the value. It starts at the root (line 2) and then goes left or right based on the value (line 10). If the value already exists, it will return the node <code>found</code> and also the parent. In case that the node doesn't exist, we still return the <code>parent</code>. </p> <h3 id="bst-node-deletion"><a class="btn" href="#BST-Node-Deletion" title="BST Node Deletion"></a>BST Node Deletion </h3> <p>We know how to insert and search for value. Now, we are going to implement the delete operation. It's a little trickier than adding, so let's explain it with the following cases:</p> <p><strong>Deleting a leaf node (0 children)</strong></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span> 30 30</span><br><span> / \ remove(12) / \</span><br><span>10 40 ---------&gt; 10 40</span><br><span> \ / \ \ / \</span><br><span> 15 35 50 15 35 50</span><br><span> /</span><br><span>12*</span><br></pre> </td> </tr> </tbody> </table> <p>We just remove the reference from node's parent (15) to be null.</p> <p><strong>Deleting a node with one child.</strong></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span> 30 30</span><br><span> / \ remove(10) / \</span><br><span>10* 40 ---------&gt; 15 40</span><br><span> \ / \ / \</span><br><span> 15 35 50 35 50</span><br> </pre> </td> </tr> </tbody> </table> <p>In this case, we go to the parent (30) and replace the child (10), with a child's child (15).</p> <p><strong>Deleting a node with two children</strong></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span> 30 30</span><br><span> / \ remove(40) / \</span><br><span>15 40* ---------&gt; 15 50</span><br><span> / \ /</span><br><span> 35 50 35</span><br></pre> </td> </tr> </tbody> </table> <p>We are removing node 40, that has two children (35 and 50). We replace the parent's (30) child (40) with the child's right child (50). Then we keep the left child (35) in the same place it was before, so we have to make it the left child of 50.</p> <p>Another way to do it to remove node 40, is to move the left child (35) up and then keep the right child (50) where it was.</p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span> 30</span><br><span> / \</span><br><span>15 35</span><br><span> \</span><br><span> 50</span><br></pre> </td> </tr> </tbody> </table> <p>Either way is ok as long as you keep the binary search tree property: <code>left &lt; parent &lt; right</code>.</p> <p><strong>Deleting the root.</strong></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span> 30* 50</span><br><span> / \ remove(30) / \</span><br><span>15 50 ---------&gt; 15 35</span><br><span> /</span><br><span> 35</span><br></pre> </td> </tr> </tbody> </table> <p>Deleting the root is very similar to removing nodes with 0, 1, or 2 children that we discussed earlier. The only difference is that afterward, we need to update the reference of the root of the tree.</p> <p>Here's an animation of what we discussed.</p> <p><img src="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/images/bst-remove.gif" title="Removing a node with 0, 1, 2 children from a binary search tree"/></p> <p>In the animation, it moves up the left child/subtree and keeps the right child/subtree in place.</p> <p>Now that we have a good idea how it should work, let's implement it:</p> <p>BinarySearchTree.prototype.remove<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js#L89">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br><span>9</span><br><span>10</span><br><span>11</span><br><span>12</span><br><span>13</span><br><span>14</span><br><span>15</span><br><span>16</span><br><span>17</span><br><span>18</span><br><span>19</span><br><span>20</span><br><span>21</span><br><span>22</span><br><span>23</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>remove(value) {</span><br><span> <span>const</span> nodeToRemove = <span>this</span>.find(value);</span><br><span> <span>if</span> (!nodeToRemove) <span>return</span> <span>false</span>;</span><br><span></span><br><span> </span><br><span> <span>const</span> nodeToRemoveChildren = <span>this</span>.combineLeftIntoRightSubtree(nodeToRemove);</span><br><span></span><br><span> <span>if</span> (nodeToRemove.meta.multiplicity &amp;&amp; nodeToRemove.meta.multiplicity &gt; <span>1</span>) {</span><br><span> nodeToRemove.meta.multiplicity -= <span>1</span>; </span><br><span> } <span>else</span> <span>if</span> (nodeToRemove === <span>this</span>.root) {</span><br><span> </span><br><span> <span>this</span>.root = nodeToRemoveChildren;</span><br><span> <span>this</span>.root.parent = <span>null</span>; </span><br><span> } <span>else</span> {</span><br><span> <span>const</span> side = nodeToRemove.isParentLeftChild ? <span>'left'</span> : <span>'right'</span>;</span><br><span> <span>const</span> { parent } = nodeToRemove; </span><br><span> </span><br><span> parent[side] = nodeToRemoveChildren;</span><br><span> }</span><br><span></span><br><span> <span>this</span>.size -= <span>1</span>;</span><br><span> <span>return</span> <span>true</span>;</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>Here are some highlights of the implementation:</p> <ul> <li>First, we search if the node exists. If it doesn't, we return false and we are done!</li> <li>If the node to remove exists, then combine left and right children into one subtree.</li> <li>Replace node to delete with the combined subtree.</li> </ul> <p>The function that combines left into right subtree is the following:</p> <p>BinarySearchTree.prototype.combineLeftIntoRightSubtree<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js#L89">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>combineLeftIntoRightSubtree(node) {</span><br><span> <span>if</span> (node.right) {</span><br><span> <span>const</span> leftmost = <span>this</span>.getLeftmost(node.right);</span><br><span> leftmost.left = node.left;</span><br><span> <span>return</span> node.right;</span><br><span> }</span><br><span> <span>return</span> node.left;</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>For instance, let's say that we want to combine the following tree and we are about to delete node <code>30</code>. We want to mix 30's left subtree into the right one. The result is this:</p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span> 30* 40</span><br><span> / \ / \</span><br><span>10 40 combine(30) 35 50</span><br><span> \ / \ -----------&gt; /</span><br><span> 15 35 50 10</span><br><span> \</span><br><span> 15</span><br></pre> </td> </tr> </tbody> </table> <p>Now, and if we make the new subtree the root, then node <code>30</code> is no more!</p> <h2 id="binary-tree-transversal"><a class="btn" href="#Binary-Tree-Transversal" title="Binary Tree Transversal"></a>Binary Tree Transversal</h2> <p>There are different ways of traversing a Binary Tree, depending on the order that the nodes are visited: in-order, pre-order, and post-order. Also, we can use them <a href="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/blog/2018/05/14/Data-Structures-for-Beginners-Graphs-Time-Complexity-tutorial/#Depth-first-search-DFS-Graph-search">DFS</a> and <a href="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/blog/2018/05/14/Data-Structures-for-Beginners-Graphs-Time-Complexity-tutorial/#Breadth-frirst-search-BFS-Graph-search">BFS</a> that we learned from the <a href="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/blog/2018/05/14/Data-Structures-for-Beginners-Graphs-Time-Complexity-tutorial/">graph post.</a> Let's go through each one.</p> <p><strong>In-Order Traversal</strong></p> <p>In-order traversal visit nodes on this order: left, parent, right.</p> <p>BinarySearchTree.prototype.inOrderTraversal<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>* inOrderTraversal(node = <span>this</span>.root) {</span><br><span> <span>if</span> (node.left) { <span>yield</span>* <span>this</span>.inOrderTraversal(node.left); }</span><br><span> <span>yield</span> node;</span><br><span> <span>if</span> (node.right) { <span>yield</span>* <span>this</span>.inOrderTraversal(node.right); }</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>Let's use this tree to make the example:</p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span> 10</span><br><span> / \</span><br><span> 5 30</span><br><span> / / \</span><br><span> 4 15 40</span><br><span> /</span><br><span>3</span><br></pre> </td> </tr> </tbody> </table> <p>In-order traversal would print out the following values: <code>3, 4, 5, 10, 15, 30, 40</code>. If the tree is a BST, then the nodes will be sorted in ascendent order as in our example.</p> <p><strong>Post-Order Traversal</strong></p> <p>Post-order traversal visit nodes on this order: left, right, parent.</p> <p>BinarySearchTree.prototype.postOrderTraversal<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>* postOrderTraversal(node = <span>this</span>.root) {</span><br><span> <span>if</span> (node.left) { <span>yield</span>* <span>this</span>.postOrderTraversal(node.left); }</span><br><span> <span>if</span> (node.right) { <span>yield</span>* <span>this</span>.postOrderTraversal(node.right); }</span><br><span> <span>yield</span> node;</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>Post-order traversal would print out the following values: <code>3, 4, 5, 15, 40, 30, 10</code>.</p> <p><strong>Pre-Order Traversal and DFS</strong></p> <p>In-order traversal visit nodes on this order: parent, left, right.</p> <p>BinarySearchTree.prototype.preOrderTraversal<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>* preOrderTraversal(node = <span>this</span>.root) {</span><br><span> <span>yield</span> node;</span><br><span> <span>if</span> (node.left) { <span>yield</span>* <span>this</span>.preOrderTraversal(node.left); }</span><br><span> <span>if</span> (node.right) { <span>yield</span>* <span>this</span>.preOrderTraversal(node.right); }</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>Pre-order traversal would print out the following values: <code>10, 5, 4, 3, 30, 15, 40</code>. This order of numbers is the same result that we would get if we run the Depth-First Search (DFS).</p> <p>BinarySearchTree.prototype.dfs<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br><span>9</span><br><span>10</span><br><span>11</span><br><span>12</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>* dfs() {</span><br><span> <span>const</span> stack = <span>new</span> Stack();</span><br><span></span><br><span> stack.add(<span>this</span>.root);</span><br><span></span><br><span> <span>while</span> (!stack.isEmpty()) {</span><br><span> <span>const</span> node = stack.remove();</span><br><span> <span>yield</span> node;</span><br><span> </span><br><span> node.descendents.reverse().forEach(<span><span>child</span> =&gt;</span> stack.add(child));</span><br><span> }</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>If you need a refresher on DFS, we covered in details on <a href="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/blog/2018/05/14/Data-Structures-for-Beginners-Graphs-Time-Complexity-tutorial/#Depth-first-search-DFS-Graph-search">Graph post</a>.</p> <p><strong>Breadth-First Search (BFS)</strong></p> <p>Similar to DFS, we can implement a BFS by switching the <code>Stack</code> by a <code>Queue</code>:</p> <p>BinarySearchTree.prototype.bfs<a href="https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/trees/binary-search-tree.js">Full Code</a></p> <table> <tbody> <tr> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>1</span><br><span>2</span><br><span>3</span><br><span>4</span><br><span>5</span><br><span>6</span><br><span>7</span><br><span>8</span><br><span>9</span><br><span>10</span><br><span>11</span><br> </pre> </td> <td> pre data-role="codeBlock" data-info="js" class="language-javascript"><span>* bfs() {</span><br><span> <span>const</span> queue = <span>new</span> Queue();</span><br><span></span><br><span> queue.add(<span>this</span>.root);</span><br><span></span><br><span> <span>while</span> (!queue.isEmpty()) {</span><br><span> <span>const</span> node = queue.remove();</span><br><span> <span>yield</span> node;</span><br><span> node.descendents.forEach(<span><span>child</span> =&gt;</span> queue.add(child));</span><br><span> }</span><br><span>}</span><br></pre> </td> </tr> </tbody> </table> <p>The BFS order is: <code>10, 5, 30, 4, 15, 40, 3</code></p> <h2 id="balanced-vs.-non-balanced-trees"><a class="btn" href="#Balanced-vs-Non-balanced-Trees" title="Balanced vs. Non-balanced Trees"></a>Balanced vs. Non-balanced Trees</h2> <p>So far, we have discussed how to <code>add</code>, <code>remove</code> and <code>find</code> elements. However, we haven't talked about runtimes. Let's think about the worst-case scenarios.</p> <p>Let's say that we want to add numbers in ascending order.</p> <p><img src="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/images/bst-asc.gif" title="Inserting values in ascending order in a Binary Search Tree"/></p> <p>We will end up with all the nodes on the left side! This unbalanced tree is no better than a LinkedList, so finding an element would take <em>O(n)</em>. 😱</p> <p>Looking for something in an unbalanced tree is like looking for a word in the dictionary page by page. When the tree is balanced, you can open the dictionary in the middle and from there you know if you have to go left or right depending on the alphabet and the word you are looking for.</p> <p>We need to find a way to balance the tree!</p> <p>If the tree was <strong>balanced</strong>, then we could find elements in <em>O(log n)</em> instead of going through each node. Let's talk about what balanced tree means.</p> <p><img src="chrome-extension://cjedbglnccaioiolemnfhjncicchinao/images/balanced-vs-non-balanced-tree.jpg" title="Balanced vs unbalanced Tree"/></p> <p>If we are searching for <code>7</code> in the non-balanced tree, we have to go from 1 to 7. However, in the balanced tree, we visit: <code>4</code>, <code>6</code>, and <code>7</code>. It gets even worse with larger trees. If you have one million nodes, searching for a non-existing element might require to visit all million while on a balanced tree it just requires 20 visits! That's a huge difference!</p> <p>We are going to solve this issue in the next post using self-balanced trees (AVL trees).</p> <h2 id="big-o-notation">Big O Notation</h2> <h3 id="time-complexity">time complexity</h3> <p>it allow us to talk formally about how the runtime of an algorithm grows as the input grows.</p> <p>n = number of operation the computer has to do can be: f(n) = n f(n) = n^2 f(n) = 1</p> <p>f(n) = could be something entirely different !</p> <p>O(n):</p> <div id=">pre data-role=" codeBlock" data-info="js" class="language-javascript"><code><a title=" 1"><span>function</span> <span>addUpToSimple</span>(n<span>:</span> number) <span>{</span></a> <a title="2"> <span>let</span> total <span>=</span> <span>0;</span></a> <a title="3"> <span>for</span> (<span>let</span> i <span>=</span> <span>0;</span> i <span>&lt;</span> n<span>;</span> i<span>++</span>) <span>{</span></a> <a title="4"> total <span>+=</span> i<span>;</span></a> <a title="5"> <span>}</span></a> <a id=-6" title="6"> <span>return</span> total<span>;</span></a> <a title="7"><span>}</span></a></code></pre> </div> <p>O(1):</p> <div id=">pre data-role=" codeBlock" data-info="js" class="language-javascript"><code><a title=" 1"><span>function</span> <span>addUpComplex</span>(n<span>:</span> number) <span>{</span></a> <a id=-2" title="2"> <span>return</span> (n <span>*</span> (n <span>+</span> <span>1</span>)) / <span>2;</span></a> <a id=-3" title="3"><span>}</span></a></code></pre> </div> <p>O(n): maybe thinking O(2n) but we see big picture! BigONotation doesn't care about precision only about general trends <em>linear? quadric? constant?</em></p> <div id=">pre data-role=" codeBlock" data-info="js" class="language-javascript"><code><a title=" 1"><span>function</span> <span>printUpAndDown</span>(n<span>:</span> number) <span>{</span></a> <a title="2"> <span>console</span>.<span>log</span>(<span>&quot;Going up&quot;</span>)<span>;</span></a> <a title="3"> <span>for</span> (<span>let</span> i <span>=</span> <span>0;</span> i <span>&lt;</span> n<span>;</span> i<span>++</span>) <span>{</span></a> <a id=-4" title="4"> <span>console</span>.<span>log</span>(i)<span>;</span></a> <a id=-5" title="5"> <span>}</span></a> <a id=-6" title="6"> <span>console</span>.<span>log</span>(<span>&quot;Going down&quot;</span>)<span>;</span></a> <a id=-7" title="7"> <span>for</span> (<span>let</span> j <span>=</span> n <span>-</span> <span>1;</span> j <span>&gt;</span> <span>0;</span> j<span>--</span>) <span>{</span></a> <a id=-8" title="8"> <span>console</span>.<span>log</span>(j)<span>;</span></a> <a id=-9" title="9"> <span>}</span></a> <a id=-10" title="10"><span>}</span></a></code></pre> </div> <p>O(n^2)</p> <div id=">pre data-role=" codeBlock" data-info="js" class="language-javascript"><code><a title=" 1"><span>function</span> <span>printAllPairs</span>(n<span>:</span> number) <span>{</span></a> <a id=-2" title="2"> <span>for</span> (<span>let</span> i <span>=</span> <span>0;</span> i <span>&lt;</span> n<span>;</span> i<span>++</span>) <span>{</span></a> <a id=-3" title="3"> <span>console</span>.<span>log</span>(i)<span>;</span></a> <a id=-4" title="4"> <span>for</span> (<span>let</span> j <span>=</span> <span>0;</span> j <span>&lt;</span> n<span>;</span> j<span>++</span>) <span>{</span></a> <a id=-5" title="5"> <span>console</span>.<span>log</span>(j)<span>;</span></a> <a id=-6" title="6"> <span>}</span></a> <a id=-7" title="7"> <span>}</span></a> <a id=-8" title="8"><span>}</span></a></code></pre> </div> <p>O(n) : cuz as soon as n grows complexity grows too</p> <div> pre data-role="codeBlock" data-info="js" class="language-javascript"><code><a title="1"><span >function</span> <span >logAtLeastFive</span>(n<span >:</span> number) <span >{</span></a> <a id=-2" title="2"> <span >for</span> (<span >let</span> i <span >=</span> <span >0;</span> i <span >&lt;=</span> <span >Math</span>.<span >max</span>(<span >5,</span> n)<span >;</span> i<span >++</span>) <span >{</span></a> <a id=-3" title="3"> <span >console</span>.<span >log</span>(i)<span >;</span></a> <a id=-4" title="4"> <span >}</span></a> <a id=-5" title="5"><span >}</span></a></code></pre> </div> <p>O(1)</p> <div id=">pre data-role=" codeBlock" data-info="js" class="language-javascript"><code><a title=" 1"><span>function</span> <span>logAtMostFive</span>(n<span>:</span> number) <span>{</span></a> <a id=-2" title="2"> <span>for</span> (<span>let</span> i <span>=</span> <span>0;</span> i <span>&lt;=</span> <span>Math</span>.<span>min</span>(<span>5,</span> n)<span>;</span> i<span>++</span>) <span>{</span></a> <a id=-3" title="3"> <span>console</span>.<span>log</span>(i)<span>;</span></a> <a id=-4" title="4"> <span>}</span></a> <a id=-5" title="5"><span>}</span></a></code></pre> </div> <h3 id="space-complexity">space complexity</h3> <p>Rules of Thumb - &lt;==(<em><strong>most primitive booleans numbers undefined null are constant space</strong></em>)==&gt;. - &lt;==(<em><strong>strings and reference types like objects an arrays require O(n) space <em>n is string length or number of keys</em></strong></em>)==&gt;</p> <p>O(1)</p> <div id="cb7"> pre data-role="codeBlock" data-info="js" class="language-javascript"><code><a title="1"><span >function</span> <span >sum</span>(arr<span >:</span> number[]) <span >{</span></a> <a id="cb7-2" title="2"> <span >let</span> total <span >=</span> <span >0;</span></a> <a id="cb7-3" title="3"> <span >for</span> (<span >let</span> i <span >=</span> <span >0;</span> i <span >&lt;</span> <span >arr</span>.<span >length;</span> i<span >++</span>) <span >{</span></a> <a id="cb7-4" title="4"> total <span >+=</span> arr[i]<span >;</span></a> <a id="cb7-5" title="5"> <span >}</span></a> <a id="cb7-6" title="6"><span >}</span></a></code></pre> </div> <p>O(n)</p> <div id="cb8"> pre data-role="codeBlock" data-info="js" class="language-javascript"><code><a title="1"><span >function</span> <span >double</span>(arr<span >:</span> number[]) <span >{</span></a> <a id="cb8-2" title="2"> <span >const</span> newArr <span >=</span> []<span >;</span></a> <a id="cb8-3" title="3"> <span >for</span> (<span >let</span> i <span >=</span> <span >0;</span> i <span >&lt;</span> <span >arr</span>.<span >length;</span> i<span >++</span>) <span >{</span></a> <a id="cb8-4" title="4"> <span >array</span>.<span >push</span>(arr[i] <span >*</span> <span >2</span>)<span >;</span></a> <a id="cb8-5" title="5"> <span >}</span></a> <a id="cb8-6" title="6"> <span >return</span> newArr<span >;</span></a> <a id="cb8-7" title="7"><span >}</span></a></code></pre> </div> <h3 id="quick-note-around-object-array-through-bigo-lens">quick note around object, array through BigO lens!</h3> <ul> <li>object:</li> </ul> <div id="cb9"> pre data-role="codeBlock" data-info="js" class="language-javascript"><code><a title="1"><span >const</span> person <span >=</span> <span >{</span> <span >name:</span> <span >&quot;John&quot;,</span> <span >age:</span> <span >22,</span> <span >hobbies:</span> [<span >&quot;reading&quot;,</span> <span >&quot;sleeping&quot;</span>] <span >};</span></a> <a id="cb9-2" title="2"></a> <a id="cb9-3" title="3"><span >Object</span>.<span >keys</span>(person)<span >;</span> <span >// [&quot;name&quot;, &quot;age&quot;, &quot;hobbies&quot;] ---&gt; O(n)</span></a> <a id="cb9-4" title="4"><span >Object</span>.<span >values</span>(person)<span >;</span> <span >// [&quot;John&quot;, 22, Array(2)]---&gt; O(n)</span></a> <a id="cb9-5" title="5"><span >Object</span>.<span >entries</span>(person)<span >;</span> <span >// [Array(2), Array(2), Array(2)]---&gt; O(n)</span></a> <a id="cb9-6" title="6"><span >person</span>.<span >hasOwnProperty</span>(<span >&quot;name&quot;</span>)<span >;</span> <span >// true ---&gt; O(1)</span></a></code></pre> </div> <ul> <li>array: <strong><em>push() and pop()</em> are always faster than <em>unshift() and shift()</em> because inserting or removing element from beginning of an array requires reIndexing all elements</strong></li> </ul> <h2 id="common-patterns">Common Patterns</h2> <div id=0"> pre data-role="codeBlock" data-info="js" class="language-javascript"><code><a id=0-1" title="1"><span >function</span> <span >binarySearch</span>(sortedArr<span >:</span> number[]<span >,</span> value<span >:</span> number)<span >:</span> number <span >{</span></a> <a id=0-2" title="2"> <span >let</span> min <span >=</span> <span >0;</span></a> <a id=0-3" title="3"> <span >let</span> max <span >=</span> <span >sortedArr</span>.<span >length</span> <span >-</span> <span >1;</span></a> <a id=0-4" title="4"></a> <a id=0-5" title="5"> <span >while</span> (min <span >&lt;=</span> max) <span >{</span></a> <a id=0-6" title="6"> <span >let</span> middle <span >=</span> <span >Math</span>.<span >floor</span>((min <span >+</span> max) / <span >2</span>)<span >;</span></a> <a id=0-7" title="7"> <span >if</span> (sortedArr[middle] <span >&lt;</span> value) <span >{</span></a> <a id=0-8" title="8"> min <span >=</span> middle <span >+</span> <span >1;</span></a> <a id=0-9" title="9"> <span >}</span> <span >else</span> <span >if</span> (sortedArr[middle] <span >&gt;</span> value) <span >{</span></a> <a id=0-10" title