How would I apply these to my day-to-day work? (Click to expand)
- As a programmer, we have to solve problems every day. If you want to solve problems well, then it's good to know about a broad range of solutions. A lot of times, it's more efficient to learn existing resources than stumble upon the answer yourself. The more tools and practice you have, the better. This book helps you understand the tradeoffs among data structures and reason about algorithms performance.
+ As a programmer, we have to solve problems every day. If you want to solve problems well, it's good to know about a broad range of solutions. Often, it's more efficient to learn existing resources than stumble upon the answer yourself. The more tools and practice you have, the better. This book helps you understand the tradeoffs among data structures and reason about algorithms performance.
diff --git a/book/B-self-balancing-binary-search-trees.asc b/book/B-self-balancing-binary-search-trees.asc
index 182bdaa4..d099b8b1 100644
--- a/book/B-self-balancing-binary-search-trees.asc
+++ b/book/B-self-balancing-binary-search-trees.asc
@@ -36,8 +36,8 @@ Let's go one by one.
Right rotation moves a node on the right as a child of another node.
-Take a look at the `@example` in the code below.
-As you can see we have an unbalanced tree `4-3-2-1`.
+Take a look at the examples in the code in the next section.
+As you will see we have an unbalanced tree `4-3-2-1`.
We want to balance the tree, for that we need to do a right rotation of node 3.
So, we move node 3 as the right child of the previous child.
@@ -140,4 +140,3 @@ This rotation is also referred to as `RL rotation`.
=== Self-balancing trees implementations
So far, we have study how to make tree rotations which are the basis for self-balancing trees. There are different implementations of self-balancing trees such a Red-Black Tree and AVL Tree.
-
diff --git a/book/D-interview-questions-solutions.asc b/book/D-interview-questions-solutions.asc
index 1e37568f..1e9a3579 100644
--- a/book/D-interview-questions-solutions.asc
+++ b/book/D-interview-questions-solutions.asc
@@ -438,7 +438,7 @@ The complexity of any of the BFS methods or DFS is similar.
[#hashmap-q-two-sum]
include::content/part02/hash-map.asc[tag=hashmap-q-two-sum]
-// include::content/part03/hashmap.asc[tag=hashmap-q-two-sum]
+// include::content/part02/hash-map.asc[tag=hashmap-q-two-sum]
This simple problem can have many solutions; let's explore some.
@@ -482,7 +482,7 @@ include::interview-questions/two-sum.js[tags=description;solution]
[#hashmap-q-subarray-sum-equals-k]
include::content/part02/hash-map.asc[tag=hashmap-q-subarray-sum-equals-k]
-// include::content/part03/hashmap.asc[tag=hashmap-q-subarray-sum-equals-k]
+// include::content/part02/hash-map.asc[tag=hashmap-q-subarray-sum-equals-k]
This problem has multiple ways to solve it. Let's explore some.
@@ -590,7 +590,7 @@ The sum is 1, however `sum - k` is `0`. If it doesn't exist on the map, we will
[#set-q-most-common-word]
-include::content/part03/set.asc[tag=set-q-most-common-word]
+include::content/part02/hash-set.asc[tag=set-q-most-common-word]
This problem requires multiple steps. We can use a `Set` for quickly looking up banned words. For getting the count of each word, we used a `Map`.
@@ -632,7 +632,7 @@ include::interview-questions/most-common-word.js[tags=explicit]
[#set-q-longest-substring-without-repeating-characters]
-include::content/part03/set.asc[tag=set-q-longest-substring-without-repeating-characters]
+include::content/part02/hash-set.asc[tag=set-q-longest-substring-without-repeating-characters]
One of the most efficient ways to find repeating characters is using a `Map` or `Set`. Use a `Map` when you need to keep track of the count/index (e.g., string -> count) and use a `Set` when you only need to know if there are repeated characters or not.
diff --git a/book/content/colophon.asc b/book/content/colophon.asc
index c6860171..6387ef12 100644
--- a/book/content/colophon.asc
+++ b/book/content/colophon.asc
@@ -9,7 +9,7 @@ For online information and ordering this and other books, please visit https://a
No part of this publication may be produced, store in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without the prior written permission of the publisher.
-While every precaution has been taking in the preparation of this book, the publisher and author assume no responsibility for errors or omissions, or damages resulting from the use of the information contained herein.
+While every precaution has been taking in the preparation of this book, the publisher and author assume no responsibility for errors or omissions, or damages resulting from using the information contained herein.
// {revremark}, {revdate}.
Version {revnumber}, {revdate}.
diff --git a/book/content/dedication.asc b/book/content/dedication.asc
index 069d116c..db104a6d 100644
--- a/book/content/dedication.asc
+++ b/book/content/dedication.asc
@@ -1,4 +1,4 @@
[dedication]
== Dedication
-_To my wife Nathalie who supported me in my long hours of writing and my baby girl Abigail._
+_To my wife Nathalie, who supported me in my long hours of writing, and my baby girl Abigail._
diff --git a/book/content/introduction.asc b/book/content/introduction.asc
index e59de6d1..e7e1167d 100644
--- a/book/content/introduction.asc
+++ b/book/content/introduction.asc
@@ -4,9 +4,9 @@
You are about to become a better programmer and grasp the fundamentals of Algorithms and Data Structures.
Let's take a moment to explain how we are going to do that.
-This book is divided into 4 main parts:
+This book is divided into four main parts:
-In *Part 1*, we will cover the framework to compare and analyze algorithms: Big O notation. When you have multiple solutions to a problem, this framework comes handy to know which solution will scale better.
+In *Part 1*, we will cover the framework to compare and analyze algorithms: Big O notation. When you have multiple solutions to a problem, this framework comes in handy to know which solution will scale better.
In *Part 2*, we will go over linear data structures and trade-offs about using one over another.
After reading this part, you will know how to trade space for speed using Maps, when to use a linked list over an array, or what problems can be solved using a stack over a queue.
diff --git a/book/content/part01/algorithms-analysis.asc b/book/content/part01/algorithms-analysis.asc
index dc7b6893..c2f2dce3 100644
--- a/book/content/part01/algorithms-analysis.asc
+++ b/book/content/part01/algorithms-analysis.asc
@@ -143,7 +143,7 @@ _7n^3^ + 3n^2^ + 5_
You can express it in Big O notation as _O(n^3^)_. The other terms (_3n^2^ + 5_) will become less significant as the input grows bigger.
-Big O notation only cares about the ābiggestā terms in the time/space complexity. It combines what we learn about time and space complexity, asymptotic analysis, and adds a worst-case scenario.
+Big O notation only cares about the ābiggestā terms in the time/space complexity. It combines what we learn about time and space complexity, asymptotic analysis and adds a worst-case scenario.
.All algorithms have three scenarios:
* Best-case scenario: the most favorable input arrangement where the program will take the least amount of operations to complete. E.g., a sorted array is beneficial for some sorting algorithms.
@@ -152,7 +152,7 @@ Big O notation only cares about the ābiggestā terms in the time/space comple
To sum up:
-TIP: Big O only cares about the run time function's highest order on the worst-case scenario.
+TIP: Big O only cares about the run time function's highest order in the worst-case scenario.
WARNING: Don't drop terms that are multiplying other terms. _O(n log n)_ is not equivalent to _O(n)_. However, _O(n + log n)_ is.
diff --git a/book/content/part01/big-o-examples.asc b/book/content/part01/big-o-examples.asc
index 73bfe968..c7755736 100644
--- a/book/content/part01/big-o-examples.asc
+++ b/book/content/part01/big-o-examples.asc
@@ -23,9 +23,9 @@ Before we dive in, hereās a plot with all of them.
.CPU operations vs. Algorithm runtime as the input size grows
// image::image5.png[CPU time needed vs. Algorithm runtime as the input size increases]
-image::big-o-running-time-complexity.png[CPU time needed vs. Algorithm runtime as the input size increases]
+image::time-complexity-manual.png[{half-size}]
-The above chart shows how the algorithm's running time is related to the work the CPU has to perform. As you can see, O(1) and O(log n) is very scalable. However, O(n^2^) and worst can convert your CPU into a furnace š„ for massive inputs.
+The above chart shows how the algorithm's running time is related to the CPU's work. As you can see, O(1) and O(log n) is very scalable. However, O(n^2^) and worst can convert your CPU into a furnace š„ for massive inputs.
[[constant]]
==== Constant
@@ -71,7 +71,9 @@ include::{codedir}/runtimes/02-binary-search.js[tag=binarySearchRecursive]
This binary search implementation is a recursive algorithm, which means that the function `binarySearchRecursive` calls itself multiple times until the program finds a solution. The binary search splits the array in half every time.
-Finding the runtime of recursive algorithms is not very obvious sometimes. It requires some tools like recursion trees or the https://adrianmejia.com/blog/2018/04/24/analysis-of-recursive-algorithms/[Master Theorem]. The `binarySearch` divides the input in half each time. As a rule of thumb, when you have an algorithm that divides the data in half on each call, you are most likely in front of a logarithmic runtime: _O(log n)_.
+Finding the runtime of recursive algorithms is not very obvious sometimes. It requires some approaches like recursion trees or the https://adrianmejia.com/blog/2018/04/24/analysis-of-recursive-algorithms/[Master Theorem].
+
+Since the `binarySearch` divides the input in half each time. As a rule of thumb, when you have an algorithm that divides the data in half on each call, you are most likely in front of a logarithmic runtime: _O(log n)_.
[[linear]]
==== Linear
@@ -171,7 +173,7 @@ Cubic *O(n^3^)* and higher polynomial functions usually involve many nested loop
[[cubic-example]]
===== 3 Sum
-Let's say you want to find 3 items in an array that add up to a target number. One brute force solution would be to visit every possible combination of 3 elements and add them up to see if they are equal to target.
+Let's say you want to find 3 items in an array that add up to a target number. One brute force solution would be to visit every possible combination of 3 elements and add them to see if they are equal to the target.
[source, javascript]
----
diff --git a/book/content/part01/how-to-big-o.asc b/book/content/part01/how-to-big-o.asc
index 951cee6b..26a3358e 100644
--- a/book/content/part01/how-to-big-o.asc
+++ b/book/content/part01/how-to-big-o.asc
@@ -6,7 +6,7 @@ endif::[]
=== How to determine time complexity from code?
In general, you can determine the time complexity by analyzing the program's statements.
-However, you have to be mindful how are the statements arranged. Suppose they are inside a loop or have function calls or even recursion. All these factors affect the runtime of your code. Let's see how to deal with these cases.
+However, you have to be mindful of how are the statements arranged. Suppose they are inside a loop or have function calls or even recursion. All these factors affect the runtime of your code. Let's see how to deal with these cases.
*Sequential Statements*
@@ -114,7 +114,7 @@ If instead of `m`, you had to iterate on `n` again, then it would be `O(n^2)`. A
[[big-o-function-statement]]
*Function call statements*
-When you calculate your programs' time complexity and invoke a function, you need to be aware of its runtime. If you created the function, that might be a simple inspection of the implementation. However, if you are using a library function, you might infer it from the language/library documentation.
+When you calculate your programs' time complexity and invoke a function, you need to be aware of its runtime. If you created the function, that might be a simple inspection of the implementation. However, you might infer it from the language/library documentation if you use a 3rd party function.
Let's say you have the following program:
@@ -210,7 +210,7 @@ graph G {
If you take a look at the generated tree calls, the leftmost nodes go down in descending order: `fn(4)`, `fn(3)`, `fn(2)`, `fn(1)`, which means that the height of the tree (or the number of levels) on the tree will be `n`.
-The total number of calls, in a complete binary tree, is `2^n - 1`. As you can see in `fn(4)`, the tree is not complete. The last level will only have two nodes, `fn(1)` and `fn(0)`, while a complete tree would have 8 nodes. But still, we can say the runtime would be exponential `O(2^n)`. It won't get any worst because `2^n` is the upper bound.
+The total number of calls in a complete binary tree is `2^n - 1`. As you can see in `fn(4)`, the tree is not complete. The last level will only have two nodes, `fn(1)` and `fn(0)`, while a full tree would have eight nodes. But still, we can say the runtime would be exponential `O(2^n)`. It won't get any worst because `2^n` is the upper bound.
==== Summary
diff --git a/book/content/part02/array-vs-list-vs-queue-vs-stack.asc b/book/content/part02/array-vs-list-vs-queue-vs-stack.asc
index b464f17d..1c88b696 100644
--- a/book/content/part02/array-vs-list-vs-queue-vs-stack.asc
+++ b/book/content/part02/array-vs-list-vs-queue-vs-stack.asc
@@ -5,7 +5,7 @@ endif::[]
=== Array vs. Linked List & Queue vs. Stack
-In this part of the book, we explored the most used linear data structures such as Arrays, Linked Lists, Stacks and Queues. We implemented them and discussed the runtime of their operations.
+In this part of the book, we explored the most used linear data structures such as Arrays, Linked Lists, Stacks, and Queues. We implemented them and discussed the runtime of their operations.
.Use Arrays whenā¦
* You need to access data in random order fast (using an index).
@@ -17,7 +17,7 @@ In this part of the book, we explored the most used linear data structures such
* You want constant time to remove/add from extremes of the list.
.Use a Queue when:
-* You need to access your data on a first-come, first served basis (FIFO).
+* You need to access your data on a first-come, first-served basis (FIFO).
* You need to implement a <