├── 1-big-o-notation
├── big-o.md
└── graph.png
├── 10-stacks-and-queues
├── 1-stacks.md
└── 2-queues.md
├── 11-binary-search-trees
├── 1-binary-search-trees.md
├── binary_search_tree.svg
├── binary_tree.svg
└── htmltree.png
├── 12-tree-traversal
└── 1-tree-traversal.md
├── 13-binary-heaps
└── 1-binary-heaps.md
├── 14-hash-tables
├── 1-hash-tables.html
└── 1-hash-tables.md
├── 15-graphs
├── 1-graphs.md
├── 1-implementation.md
└── graphs.png
├── 2-performance-of-arrays-and-objects
└── 1-performance-of-arrays-and-objects.md
├── 3-problem-solving-approach
└── 1-problem-solving-approach.md
├── 4-problem-solving-patterns
├── 1-Frequency-Counters.md
├── 2-Multiple-Pointers.md
└── 3-Divide-and-Conquer.md
├── 5-recursion
├── 1-recursion.md
└── call-stack.png
├── 6-searching
├── 1-linear-search.md
├── 2-binary-search.md
└── 3-string-search.md
├── 7-sorting
├── 1-bubble-sort.md
├── 2-selection-sort.md
├── 3-insertion-sort.md
├── 4-merge-sort.md
├── 5-quick-sort.md
└── 6-radix-sort.md
├── 8-data-structures
└── 1-data-structures.md
└── 9-linked-list
├── 1-singly-linked-list.md
└── 2-doubly-linked-list.md
/1-big-o-notation/big-o.md:
--------------------------------------------------------------------------------
1 | # Big O Notation
2 |
3 | ## What
4 | * Compare the performance of two or more algorithms numerically.
5 |
6 | ## An example
7 |
8 | Consider a problem to find the sum of all numbers upto a given number n;
9 | Here are two ways to do that:
10 |
11 | 1. Using a for loop:
12 | ```javascript
13 | function addToSum(num) {
14 | let sum = 0;
15 | for (let i = 1; i <= num; i++) {
16 | sum += i;
17 | }
18 | return sum;
19 | }
20 | ```
21 | 2. Using math formula:
22 | ```javascript
23 | function addToSumFast(num) {
24 | return num * (num + 1) / 2;
25 | }
26 | ```
27 |
28 | If we compare the time required to compute the sum of first billion digits, we find that the second algorithm is about 100 times faster. But time taken for execution is not a very good measure to compare two algorithms as:
29 |
30 | 1. _Time of execution varies_ from machine to machine and even execution to execution on the same machine.
31 | 2. When _comparing two similarly performing algorithms_, the absolute time of execution may not be able to determine the faster algorithm due to lack of precesion.
32 |
33 | ## A Solution:
34 |
35 | Instead of counting time of execution, what if we count the number of operations. But do we mean by an operaion? Operations include the following:
36 |
37 | 1. Mathematical Operations: +, *, -, /, %
38 | 2. Comparisions: ==, <=, >=, ===
39 | 3. Assignment: let i = 5, a = 4
40 |
41 | In the math formula version of our code, for any input size of num, we perform 3 operations. On the other hand in the for loop code, the number of operations we perform depends linearly on n (4n + 1).
42 |
43 | So we can say that the number of operations in the for loop algorithm is _proportional to n_ and in the math algorithm is _proportional to a constant_.
44 |
45 | > Project Idea: Make a js tool that takes in a function as input and plots its time vs n curve
46 |
47 | ## Definition:
48 | The Big O notation allows us to formally talk about the performance of an algorithm.
49 | > Big O : The Big O of an algorithm is **O(f(n))** if the number of operations involved in the algorithm is of the order of some multiple of f(n) as n grows.
50 |
51 | Thus the big O of the for loop example above is **O(n)**
52 | and for the math formula is **O(1)**
53 |
54 | ### Thumb Rules:
55 | 1. Constants don't matter: O(4n + 6) => O(n)
56 | 2. Smaller quantities don't matter: O(4n2 + 3n + 2)=> O(n2)
57 | 3. Analyse the trend as n approaches infinity
58 |
59 | ---
60 |
61 | ## Space Complexity
62 |
63 | Time complexity indicates how much time program takes. Similarly, space complexity indicates how much space a program takes. While estimating the time complexity of an algorithm, we considered the number of operations. Here, while calculating the space complexity, we will consider the number of _declarations_:
64 |
65 | 1. Declaring primitive datatypes takes constant space:
66 | ```javascript
67 | // All have O(1) space
68 | let i = 0;
69 | const a = null;
70 | var isHappy = true;
71 | ```
72 | 2. Strings, Arrays, Sets etc. take linear space proportional to their size:
73 | ```javascript
74 | // All have O(n) space
75 | let arr = [1, 2, 3, 4, 5];
76 | const word = "Hello World!";
77 | ```
78 |
79 | _The same thumb rules apply as the ones for time complexity._
80 |
81 | ## Examples:
82 |
83 | 1. O(1):
84 | ```javascript
85 | function sum(array) {
86 | let total = 0; //first declaration
87 | for (const num of array) { //second declaration
88 | total += num;
89 | }
90 | return total
91 | }
92 | ```
93 | Note: In the for of loop, ```num``` is declared only once but assigned multiple number of times
94 |
95 | 2. O(n):
96 | ```javascript
97 | function doubleArray(array){
98 | let doubleArray = [];
99 | for(const num of array){
100 | doubleArray.push(2*num);
101 | }
102 | return doubleArray;
103 | }
104 | ```
105 | Since we declare an array here, the space complexity depends linearly with its length. Hence the space complexity here is O(n) or O(array.length)
106 |
107 | ## Review
108 | Time complexity counts number of *Operations*
109 | Space complexity counts number of *Declarations*
110 | 
--------------------------------------------------------------------------------
/1-big-o-notation/graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/godcrampy/js-notes-dsa-colt-steele/6aab4183afd794c1166d763ce73e91e345ee72ab/1-big-o-notation/graph.png
--------------------------------------------------------------------------------
/10-stacks-and-queues/1-stacks.md:
--------------------------------------------------------------------------------
1 | # Stacks
2 |
3 | Last in First Out. Used in browser history, call stacks etc.
4 |
5 | ## Complexity
6 | 1. ```push``` and ```pop``` are constant time. (Array implementation is not)
7 |
8 | ## Implementation
9 |
10 | 1. **Using Arrays**
11 | ```javascript
12 | let a = [1, 2, 3];
13 | a.push(4);
14 | a.pop();
15 | // 4
16 | ```
17 |
18 | 2. **Using Classes**
19 | ```javascript
20 | class Node {
21 | constructor(data) {
22 | this.data = data;
23 | this.next = null;
24 | }
25 | }
26 |
27 | class Stack {
28 | constructor() {
29 | this.first = null;
30 | this.last = null;
31 | this.size = 0;
32 | }
33 | push(data) {
34 | let newNode = new Node(data);
35 | if (this.size === 0) {
36 | this.first = newNode;
37 | this.last = newNode;
38 | } else {
39 | newNode.next = this.last;
40 | this.last = newNode;
41 | }
42 | this.size++;
43 | return this.size;
44 | }
45 | pop() {
46 | if (this.size == 0) return undefined;
47 | let temp = this.last;
48 | this.last = this.last.next;
49 | this.size--;
50 | return temp;
51 | }
52 | }
53 | ```
--------------------------------------------------------------------------------
/10-stacks-and-queues/2-queues.md:
--------------------------------------------------------------------------------
1 | # Queues
2 | FIFO. Used in download managers, printing several documents etc.
3 |
4 | ## Complexity
5 | 1. ```queue``` and ```dequeue``` are constant time. (Array implementation is not)
6 |
7 | ## Implementation
8 |
9 | 1. **Using Arrays**
10 | ```javascript
11 | let a = [1, 2, 3];
12 | a.push(4);
13 | a.shift();
14 | // 1
15 | ```
16 |
17 | 2. **Using Classes**
18 | ```javascript
19 | class Node {
20 | constructor(data) {
21 | this.previous = null;
22 | this.data = data;
23 | }
24 | }
25 |
26 | class Queue {
27 | constructor() {
28 | this.first = null;
29 | this.last = null;
30 | this.size = 0;
31 | }
32 | queue(data) {
33 | let newNode = new Node(data);
34 | if (this.size === 0) {
35 | this.first = newNode;
36 | } else this.last.previous = newNode;
37 | this.size++;
38 | this.last = newNode;
39 | return this.size;
40 | }
41 |
42 | dequeue() {
43 | if (this.size == 0) return undefined;
44 | let temp = this.head;
45 | this.head = temp.previous;
46 | this.size--;
47 | return temp;
48 | }
49 | }
50 | ```
--------------------------------------------------------------------------------
/11-binary-search-trees/1-binary-search-trees.md:
--------------------------------------------------------------------------------
1 | # Binary Search Trees
2 |
3 | ## Trees
4 | Trees are a data structure where the nodes are in a parent-child realtionship. They are non-linear unlike arrays and linked lists. Each parent in a tree may have multiple children but each child must have single aprent only. Singly linked list is a special case of tree. Nodes must point at their children only and not their siblings ie must go farther from the root. Root is a node with no parent. There is only one node in a tree.
5 |
6 | 
7 |
8 | ### Definitions
9 | 1. **Root**: Topmost Node of tree with no parents
10 | 2. **Child**: A node directly connected to a node while moving away from the root node
11 | 3. **Parent**: The node to which child is connected to
12 | 4. **Siblings**: Nodes with same parents
13 | 5. **Leaf**: Nodes with no children
14 | 6. **Edge**: The connection between parent and child node
15 |
16 | ### Use
17 | 1. HTML DOM
18 | 2. Network Structure
19 | 3. Syntax Tree
20 | 4. Directory Structure
21 | 5. JSON
22 |
23 | ## Binary Tree
24 | Subset of trees. Every node can have atmost _two_ children.
25 |
26 | 
27 |
28 | ## Binary Search Tree
29 | Subset of Binary Trees. Every child to the left of a node is smaller than parent node and every child to the right is larger than the parent.
30 |
31 | 
32 |
33 | ### Complexity
34 |
35 | 1. Searching and Inserting: (Doubling sized increases one step)
36 | * Best: O(1)
37 | * Average: O(log(n))
38 | * Worst: O(n)
39 |
40 | > Worst occurs if the binary tree is one sided. That is every node has child only on the right(or left) side. In this case, it is basically a linked list.
41 |
42 |
43 | ### Implementation
44 | ```javascript
45 | class Node {
46 | constructor(data) {
47 | this.left = null;
48 | this.right = null;
49 | this.data = data;
50 | }
51 | }
52 |
53 | class BinarySearchTree {
54 | constructor() {
55 | this.root = null;
56 | }
57 | insert(data) {
58 | let newNode = new Node(data)
59 | if (this.root === null) {
60 | this.root = newNode;
61 | return true;
62 | }
63 | let iterator = this.root;
64 | while (true) {
65 | if (data == iterator.data) return false;
66 | if (data > iterator.data) {
67 | // go to the right
68 | if (iterator.right == null) {
69 | iterator.right = newNode;
70 | break;
71 | }
72 | iterator = iterator.right;
73 | } else {
74 | // go to the left
75 | if (iterator.left == null) {
76 | iterator.left = newNode;
77 | break;
78 | }
79 | iterator = iterator.left;
80 | }
81 | }
82 | return true;
83 | }
84 | find(data) {
85 | let iterator = this.root;
86 | while (iterator != null) {
87 | if (data === iterator.data) return true;
88 | else if (data > iterator.data) iterator = iterator.right;
89 | else iterator = iterator.left;
90 | }
91 | return false;
92 | }
93 | }
94 | ```
--------------------------------------------------------------------------------
/11-binary-search-trees/binary_search_tree.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
162 |
164 |
165 |
166 |
167 |
169 | Adobe PDF library 5.00
170 |
171 |
172 |
173 |
174 |
175 |
177 | 2004-01-23T20:04:24-04:00
178 | 2005-12-31T21:30:20Z
179 | Adobe Illustrator 10.0
180 | 2004-01-23T20:25:01-05:00
181 |
182 |
183 | image/svg+xml
186 |
187 |
188 |
189 | 8
212 | 3
235 | 10
258 | 1
281 | 6
304 | 14
327 | 4
350 | 7
373 | 13
396 |
--------------------------------------------------------------------------------
/11-binary-search-trees/binary_tree.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/11-binary-search-trees/htmltree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/godcrampy/js-notes-dsa-colt-steele/6aab4183afd794c1166d763ce73e91e345ee72ab/11-binary-search-trees/htmltree.png
--------------------------------------------------------------------------------
/12-tree-traversal/1-tree-traversal.md:
--------------------------------------------------------------------------------
1 | # Tree Traversal
2 | Traversing a tree means visiting every node on the tree once.
3 |
4 | ## Types:
5 | 1. Breadth First Search
6 | 2. Depth First Search
7 | 1. DFS PreOrder
8 | 2. DFS PostOrder
9 | 3. DFS Inorder
10 |
11 | ##BFS:
12 |
13 | To implement BFS, we use queues. We start by adding the root node to the queue. Then as long as the queue is not empty we follow the following algorithm:
14 | 1. Dequeue a node
15 | 2. Add its value to the list
16 | 3. Queue its left and right parameters to the queue
17 |
18 | ```javascript
19 | // Inside Binary Search Classs from previous section
20 | BFS() {
21 | let queue = [];
22 | let list = [];
23 | queue.push(this.root);
24 | while (queue.length != 0) {
25 | let node = queue.shift();
26 | list.push(node.data);
27 | if(node.left != null)queue.push(node.left)
28 | if(node.right != null)queue.push(node.right)
29 | }
30 | return list
31 | }
32 | ```
33 |
34 | ## DFS:
35 |
36 | We implement DFS recursively. When we encounter a node, we can do things in three different ways
37 |
38 | 1. PreOrder:
39 |
40 | 1. Add the node value to the list.
41 | 2. Encounter the left node
42 | 3. Encounter the right node
43 |
44 | 2. PostOrder
45 |
46 | 1. Encounter the left node
47 | 2. Encounter the right node
48 | 3. Add the node value to the list
49 |
50 | 3. InOrder
51 |
52 | 1. Encounter the left node
53 | 2. Add the node value to the list
54 | 3. Encounter the right node
55 |
56 | ```javascript
57 | // Inside Binary Search Classs from previous section
58 | DFSPreOrder() {
59 | let list = [];
60 |
61 | function searchRecursive(node) {
62 | list.push(node.data);
63 | if (node.left != null) searchRecursive(node.left)
64 | if (node.right != null) searchRecursive(node.right)
65 | }
66 | searchRecursive(this.root);
67 | return list;
68 | }
69 | DFSPostOrder() {
70 | let list = [];
71 |
72 | function searchRecursive(node) {
73 | if (node.left != null) searchRecursive(node.left)
74 | if (node.right != null) searchRecursive(node.right)
75 | list.push(node.data);
76 | }
77 | searchRecursive(this.root);
78 | return list;
79 | }
80 | DFSInOrder() {
81 | let list = [];
82 |
83 | function searchRecursive(node) {
84 | if (node.left != null) searchRecursive(node.left)
85 | list.push(node.data);
86 | if (node.right != null) searchRecursive(node.right)
87 | }
88 | searchRecursive(this.root);
89 | return list;
90 | }
91 | ```
92 |
93 | ## BFS vs DFS
94 | Time complexity for all four methods is same as we visit all the nodes once. But BFS takes more memory if we have a really dense tree due to the queue. When traversing, BFS stores the width in the memory whereas DFS stores the depth in the memory.
--------------------------------------------------------------------------------
/13-binary-heaps/1-binary-heaps.md:
--------------------------------------------------------------------------------
1 | # Binary Heap
2 | Binary Heaps are Binary Trees with some rules. Binary Heaps are as compact as possible
3 | * **Min Binary Heap**: The parent nodes are always smaller than the child nodes
4 | * **MAx Binary Heap**: The parent nodes are always greater than the child nodes
5 |
6 | ## Representation
7 | Since heaps are as compact as possible, we can expresss them as an array:
8 | ```
9 | 8
10 | / \
11 | 7 3
12 | / \ /
13 | 5 6 2
14 | |=> [8, 7, 3, 5, 6, 2]
15 | ```
16 | Thus the child nodes and parent nodes are related by index as follows:
17 | 1. If parent is at index n:
18 | * left child is at index 2n + 1
19 | * right child is at index 2n + 2
20 |
21 | 2. If child is at index n, parent is at Math.floor((n-1)/2)
22 |
23 | ## Implementation
24 | 1. **Insert**: Used to insert a value to the heap. First the value is added to the array using ```push```. Then as long as the parent node is smaller then the current node, they are swapped with each other.
25 | 2. **Remove**: Used to remove a node from the heap. The node to be removed is swapped with the last node on the array and then removed using ```pop```. Then since a smaller element has risen to the top, this node is trickled down by swapping it with the larger child as long as either of the child has larger value than this node.
26 | ```javascript
27 | class MaxBinaryHeap {
28 | constructor() {
29 | this.values = []
30 | }
31 | parentIndex(index) {
32 | if (index === 0) return 0;
33 | return Math.floor((index - 1) / 2)
34 | }
35 | parentValue(index) {
36 | return this.values[this.parentIndex(index)];
37 | }
38 | childIndexLeft(index) {
39 | let final = 2 * index + 1;
40 | if (final >= this.values.length) return index
41 | return final;
42 | }
43 | childIndexRight(index) {
44 | let final = 2 * index + 2;
45 | if (final >= this.values.length) return index
46 | return final;
47 | }
48 | childValueLeft(index) {
49 | return this.values[this.childIndexLeft(index)];
50 | }
51 | childValueRigth(index) {
52 | return this.values[this.childIndexRight(index)];
53 | }
54 | insert(value) {
55 | this.values.push(value)
56 | let currentIndex = this.values.length - 1;
57 | while (this.parentValue(currentIndex) < this.values[currentIndex]) {
58 | [this.values[currentIndex], this.values[this.parentIndex(currentIndex)]] = [this.values[this.parentIndex(currentIndex)], this.values[currentIndex]]
59 | currentIndex = this.parentIndex(currentIndex);
60 | }
61 | }
62 | remove(value) {
63 | let currentIndex = this.values.indexOf(value);
64 | // swap with the last
65 | [this.values[currentIndex], this.values[this.values.length - 1]] = [this.values[this.values.length - 1], this.values[currentIndex]];
66 | this.values.pop();
67 | while (this.values[currentIndex] < this.childValueLeft(currentIndex) || this.values[currentIndex] < this.childValueRigth(currentIndex)) {
68 | if (this.childValueLeft(currentIndex) > this.childValueRigth(currentIndex)) {
69 | //swap with left
70 | [this.values[currentIndex], this.values[this.childIndexLeft(currentIndex)]] = [this.values[this.childIndexLeft(currentIndex)], this.values[currentIndex]];
71 | currentIndex = this.childIndexLeft(currentIndex);
72 | } else {
73 | // swap with right guy
74 | [this.values[currentIndex], this.values[this.childIndexRight(currentIndex)]] = [this.values[this.childIndexRight(currentIndex)], this.values[currentIndex]];
75 | currentIndex = this.childIndexRight(currentIndex);
76 | }
77 | }
78 | }
79 | }
80 | ```
81 |
82 | ## Complexity
83 | 1. Insertion: log(n)
84 | 2. Removal: log(n)
85 | 3. Search: O(n)
86 |
87 | ## Priority Queues
88 | Priority Queues are datastructures in which the elements with higher priority are sered first. These are used by the Operating System to execute important tasks first. Prioritty Wueues are implemented using heaps since the root node is always the larget value. Thus the taks are queued in a heap and when the root task is performed, it is removed as in above implementation and the next higher values comes to the top and so on. If instead arrays were used, we would take O(n) time to find the max which is slower.
89 |
90 | > Project Ideas: Implement a todo list app using priority queue
--------------------------------------------------------------------------------
/14-hash-tables/1-hash-tables.html:
--------------------------------------------------------------------------------
1 |
Suppose we want to store a key value pair. Arrays are already key value pair implementation where keys are the indices. But suppose we want the key to be strings, in such case we need to use a hash table.
1062 | Consider the following object: {"pink" : "#ff6932", "cyan" : "#00fff"}.
1063 | To store this data we need to use a hash function and an array. Suppose hash("pink") is 25. Thus we will store "#ff6932" at the 25 index of our array. Thus next time whenever we want to access the value of key "pink", we simply have to pass the key thru the hash function and the output index will be used to access the value from array.
Hash function is a function that takes any data and converts it into a number.
1066 |
Properties of a good hash function:
1067 | 1. Fast (Constant Time)
1068 | 2. Distributes numbers uniformly
1069 | 3. Returns the same index for the same value everytime
1070 |
Here’s a hash function
1071 |
// size is the size of array to be used
1072 | functionhash(string,size){
1073 | lettotal=0;
1074 | for(charofstring){
1075 | total+=char.charCodeAt(0)
1076 | }
1077 | returntotal%size;
1078 | }
1079 |
1080 | The problem with above function is a bad hash function as its O(n) and not that random.
1081 |
What if two strings lead to a same hash?, We solve it by the following methods.
1107 |
1108 |
Separate Chaining:
1109 | Suppose hash("pink") and hash("orange") gives us a hash of 4. This method stores the key and hash in an array which are stored in an array at the specific index.
1110 |
1111 |
For Example: at index 4, we will have the following array after the above hashing: [["pink", "#ff6932"], ["orange", "#aa6666"]]. Thus we will need to search the above array to get our value.
1112 |
1113 |
Linear Probing:
1114 | In this method, if an index is occupied, we look at the next unoccupied index and store our data there. So for the above example, if at index 4 we have [["pink", "#ff6932"], we will go to index 5 and if it is empty, we will store ["orange", "#aa6666"]. This method exhausts the hash array quickly.
--------------------------------------------------------------------------------
/14-hash-tables/1-hash-tables.md:
--------------------------------------------------------------------------------
1 | # Hash Tables
2 | Objects and Maps in JavaScript, Dictionaries in Python, Maps in Dart are all Hash Table implementation.
3 |
4 | ## Working
5 | Suppose we want to store a key value pair. Arrays are already key value pair implementation where keys are the indices. But suppose we want the key to be strings, in such case we need to use a hash table.
6 | Consider the following object: ```{"pink" : "#ff6932", "cyan" : "#00fff"}```.
7 | To store this data we need to use a hash function and an array. Suppose ```hash("pink")``` is 25. Thus we will store ```"#ff6932"``` at the 25 index of our array. Thus next time whenever we want to access the value of key ```"pink"```, we simply have to pass the key thru the hash function and the output index will be used to access the value from array.
8 |
9 | ## Hash Function
10 | Hash function is a function that takes any data and converts it into a number.
11 |
12 | **Properties of a good hash function:**
13 | 1. Fast (Constant Time)
14 | 2. Distributes numbers uniformly
15 | 3. Returns the same index for the same value everytime
16 |
17 | Here's a hash function
18 | ```javascript
19 | // size is the size of array to be used
20 | function hash(string, size) {
21 | let total = 0;
22 | for (char of string) {
23 | total += char.charCodeAt(0)
24 | }
25 | return total % size;
26 | }
27 | ```
28 | The problem with above function is a bad hash function as its O(n) and not that random.
29 |
30 | ```javascript
31 | // removing O(n)
32 | function hash(string, size) {
33 | let total = 0;
34 | for (let i = 0; i < Math.min(string.length, 100); i++) {
35 | total += string[i].charCodeAt(0)
36 | }
37 | return total % size;
38 | }
39 | ```
40 |
41 | ```javascript
42 | // Increasing randomness
43 | function hash(string, size) {
44 | let total = 0;
45 | const PRIME = 1327;
46 | for (let i = 0; i < Math.min(string.length, 100); i++) {
47 | total = (total * PRIME + string[i].charCodeAt(0)) % size;
48 | }
49 | return total;
50 | }
51 | ```
52 |
53 | > Pro Tip: Use Prime Number length for storing hash tables. https://qr.ae/TWtE8y
54 |
55 | ## Handling Collisions
56 | What if two strings lead to a same hash?, We solve it by the following methods.
57 |
58 | 1. Separate Chaining:
59 | Suppose ```hash("pink")``` and ```hash("orange")``` gives us a hash of 4. This method stores the key and hash in an array which are stored in an array at the specific index.
60 |
61 | For Example: at index 4, we will have the following array after the above hashing: ```[["pink", "#ff6932"], ["orange", "#aa6666"]]```. Thus we will need to search the above array to get our value.
62 |
63 | 2. Linear Probing:
64 | In this method, if an index is occupied, we look at the next unoccupied index and store our data there. So for the above example, if at index 4 we have ```[["pink", "#ff6932"]```, we will go to index 5 and if it is empty, we will store ```["orange", "#aa6666"]```. This method exhausts the hash array quickly.
65 |
66 | ## Implementation
67 | ```javascript
68 | class HashTable {
69 | constructor() {
70 | this.keyMap = new Array(79);
71 | }
72 | _hash(string) {
73 | let total = 0;
74 | const PRIME = 1327;
75 | for (let i = 0; i < Math.min(string.length, 100); i++) {
76 | total = (total * PRIME + string[i].charCodeAt(0)) % 79;
77 | }
78 | return total;
79 | }
80 |
81 | set(key, value) {
82 | let hash = this._hash(key);
83 | if (this.keyMap[hash] == undefined)
84 | this.keyMap[hash] = [
85 | [key, value]
86 | ];
87 | else this.keyMap.push([key, value]);
88 | }
89 |
90 | get(key) {
91 | let hash = this._hash(key);
92 | let chain = this.keyMap[hash];
93 | for (pair of chain)
94 | if (pair[0] === key) return pair[1];
95 | return undefined;
96 | }
97 | pairs() {
98 | let pairs = [];
99 | for (let i = 0; i < 79; i++)
100 | if (banana.keyMap[i] != undefined)
101 | for (let pair of banana.keyMap[i]) pairs.push(pair)
102 | return pairs;
103 | }
104 | }
105 | ```
106 |
107 | ## Complexity
108 | * Insertion: O(1)
109 | * Deletion: O(1)
110 | * Access: O(1)
--------------------------------------------------------------------------------
/15-graphs/1-graphs.md:
--------------------------------------------------------------------------------
1 | # Graphs
2 | Interconnected Nodes. Superset of trees.
3 |
4 | 
5 |
6 | ## Use
7 | 1. Social Network
8 | 2. Networks
9 | 3. Google Maps
10 | 4. Recommendation Engines
11 |
12 | ## Graphs vs Trees:
13 | In trees, for going from one node to another, there is only one path whereas for graphs, there may be multiple paths.
14 |
15 | ## Terminology
16 | 1. **Vertice**: Nodes of the graph
17 | 2. **Edge**: Connections between nodes
18 | 3. **Directed Graph**: Graph in which we can traverse an edge in only one way. Eg: Instagram Followers Graph
19 | 4. **Undirected Graph**: Graph in which nodes can be traversed in both directions. Eg: Facebook Friends Graph
20 | 5. **Unweighted Graph**: Edges have no value associated with them.
21 | 6. **Weighted Graph**: Edges have some value associated with them.
22 |
23 | ## Representing Graphs
24 | There are two ways of representing graphs:
25 |
26 | 1. Adjacency Matrix:
27 |
28 | Is a matrix of size VxV where V is the number of Vertices.
29 | The matrix for the above graph would be:
30 | ```
31 | 0 1 2 3 4
32 | =======================
33 | 0 0 1 0 0 1
34 |
35 | 1 1 0 1 1 1
36 |
37 | 2 0 1 0 1 0
38 |
39 | 3 0 1 1 0 1
40 |
41 | 4 1 1 0 1 0
42 |
43 | ```
44 |
45 | 2. Adjacency List:
46 |
47 | Graph is stored in a list or hashmap:
48 | ```javascript
49 | [
50 | [1, 4],//0
51 | [4, 3, 2],//1
52 | [1, 3],//2
53 | [1, 4, 2],//3
54 | [0, 1, 3],//f
55 | ]
56 |
57 | {
58 | 0 : [1, 4],
59 | 1 : [4, 3, 2],
60 | 2 : [1, 3],
61 | 3 : [1, 4, 1],
62 | 4 : [0, 1, 3],
63 | }
64 | ```
65 |
66 | ## Complexity Comparision
67 | 1. Add Vertex:
68 |
69 | Matrix: O(v2)
70 | List: O(1)
71 |
72 | 2. Add Edge:
73 |
74 | Matrix: O(1)
75 | List: O(1)
76 |
77 | 3. Remove Vertex:
78 |
79 | Matrix: O(v2)
80 | List: O(v + e)
81 |
82 | 4. Remove Edge:
83 |
84 | Matrix: O(1)
85 | List: O(e)
86 |
87 | 5. Query:
88 |
89 | Matrix: O(1)
90 | List: O(v + e)
91 |
92 | 6. Storage:
93 |
94 | Matrix: O(v2)
95 | List: O(v)
--------------------------------------------------------------------------------
/15-graphs/1-implementation.md:
--------------------------------------------------------------------------------
1 | # Graph Implementation
2 |
3 | ```javascript
4 | class Graph {
5 | constructor() {
6 | this.adjacencyList = {};
7 | }
8 | addVertex(vertex) {
9 | if (!this.adjacencyList[vertex])
10 | this.adjacencyList[vertex] = [];
11 | }
12 | addEdge(vertex1, vertex2) {
13 | if (!this.adjacencyList[vertex1].includes(vertex2))
14 | this.adjacencyList[vertex1].push(vertex2);
15 | if (!this.adjacencyList[vertex2].includes(vertex1))
16 | this.adjacencyList[vertex2].push(vertex1);
17 | }
18 | removeEdge(vertex1, vertex2) {
19 | this.adjacencyList[vertex1] = this.adjacencyList[vertex1].filter((value) => value != vertex2);
20 | this.adjacencyList[vertex2] = this.adjacencyList[vertex2].filter((value) => value != vertex1);
21 | }
22 | deleteVertex(vertex) {
23 | let vertices = [...this.adjacencyList[vertex]];
24 | for (let vertexIndex in vertices) {
25 | // Remove vertex from corresponding vertex array
26 | let otherVertex = vertices[vertexIndex];
27 | this.adjacencyList[otherVertex] = this.adjacencyList[otherVertex].filter((value) => value != vertex);
28 | // delete vertex itself
29 | delete this.adjacencyList[vertex];
30 | }
31 | }
32 | DFS(vertex) {
33 | let visited = {};
34 | let result = [];
35 | const adjacencyList = this.adjacencyList;
36 |
37 | function searchDepthFirst(vertex) {
38 | if (!vertex) return null;
39 | visited[vertex] = true;
40 | result.push(vertex);
41 | adjacencyList[vertex].forEach(function (nextVertex) {
42 | if (!visited[nextVertex])
43 | return searchDepthFirst(nextVertex);
44 | })
45 | }
46 | searchDepthFirst(vertex)
47 | return result;
48 | }
49 | BFS(vertex) {
50 | let visited = {};
51 | let result = [];
52 | let queue = [vertex];
53 | visited[vertex] = true;
54 | let currentVertex;
55 | while (queue.length) {
56 | currentVertex = queue.shift;
57 | result.push(currentVertex);
58 |
59 | this.adjacencyList[currentVertex].forEach((nextVertex) => {
60 | if (!visited[nextVertex]) {
61 | visited[nextVertex] = true;
62 | queue.push(neighbor);
63 | }
64 | });
65 | }
66 | return result;
67 | }
68 | }
69 | ```
--------------------------------------------------------------------------------
/15-graphs/graphs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/godcrampy/js-notes-dsa-colt-steele/6aab4183afd794c1166d763ce73e91e345ee72ab/15-graphs/graphs.png
--------------------------------------------------------------------------------
/2-performance-of-arrays-and-objects/1-performance-of-arrays-and-objects.md:
--------------------------------------------------------------------------------
1 | # Performance of Objects and Arrays
2 |
3 | ## Objects
4 | Given a key, that specific key value pair of the object can be accessed in constant time.
5 |
6 | 1. Searching **O(n)**: Search if a value exists in the object
7 | 2. Accessing **O(1)**: Return the value given a key
8 | 3. Deleting **O(1)**: Removal of key value pair given a key
9 | 4. Insertion **O(1)**: Insertion of a new key value pair
10 |
11 | ### Object Methods
12 | Consider a ```banana``` object:
13 |
14 | ```javascript
15 | let banana = {
16 | color : "yellow",
17 | taste : "sweet",
18 | calories : 100,
19 | isFav : true
20 | }
21 | ```
22 | 1. Get an array of all the keys **O(n)**
23 | ```javascript
24 | Object.keys(banana);
25 | // ["color", "taste", "calories", "isFav"]
26 | // O(n) as we need to access all the keys
27 | ```
28 | 2. Get an array of all the values **O(n)**
29 | ```javascript
30 | Object.values(banana);
31 | // ["yellow", "sweet", 100, true]
32 | // O(n) as we need to access all the values
33 | ```
34 | 3. Get all the key-value pair **O(n)**
35 | ```javascript
36 | Object.entries(banana);
37 | // [["color", "yellow"], ["taste", "sweet"], ["calories", 100], ["isFav", true]]
38 | // O(n) as we need to access all the key-value pairs
39 | ```
40 | 4. Check if a key exists **O(1)**
41 | ```javascript
42 | banana.hasOwnProperty("color");
43 | // true
44 | banana.hasOwnProperty("yellow");
45 | // false
46 | ```
47 |
48 | ## Arrays
49 | Given an index, the thing at that index can be accessed in constant time.
50 |
51 | 1. Searching **O(n)**: Search if a value exists in the array
52 | 2. Accessing **O(1)**: Return the value at given index
53 | 3. Deleting:
54 | * Delete last element: **O(1)**
55 | * Delete first of middle element: **O(n)** (as it involves reassigning the indices)
56 | 4. Insertion:
57 | * Insert last element: **O(1)**
58 | * Insert first of middle element: **O(n)** (as it involves reassigning the indices)
59 |
60 | ### Array Methods:
61 | Consider the ```groceries``` array:
62 | ```javascript
63 | let groceries = ['milk', 'egg', 'bread', 'bananas'];
64 | ```
65 |
66 | 1. Insert and Delete at the last index **O(1)**:
67 | ```javascript
68 | groceries.pop();
69 | // ['milk', 'egg', 'bread']
70 | groceries.push('apples');
71 | //['milk', 'egg', 'bread', 'apples']
72 | ```
73 | 2. Insert and Delete at the first index **O(n)**:
74 | ```javascript
75 | groceries.shift();
76 | // [egg', 'bread', 'bananas']
77 | groceries.unshift('beer');
78 | //['beer', 'egg', 'bread', 'apples']
79 | ```
80 | 3. Insert and Delete at the middle of array **O(n)**:
81 | ```javascript
82 | groceries.splice(1, 1, 'pizza', 'meat');
83 | // ['milk', 'pizza', 'meat', 'bread', 'bananas']
84 | ```
85 | 4. Concatenation of two arrays **O(a + b)**:
86 | ```javascript
87 | let a = [1, 2, 3];
88 | let b = [4, 5, 6];
89 | a.concat(b);
90 | // [1, 2, 3, 4, 5, 6]
91 | ```
92 | 5. Slicing an array **O(n)**:
93 | ```javascript
94 | let a = [1, 2, 3, 4, 5, 6];
95 | a.slice(2, 4);
96 | // [3, 4]
97 | ```
98 | 6. Sorting an array **O(nlong(n))**:
99 | ```javascript
100 | let a = [4, 6, 1, 2, 5, 3]
101 | a.sort();
102 | // [1, 2, 3, 4, 5, 6]
103 | ```
104 | 7. Iteration over an array **O(n)**:
105 | ```javascript
106 | let a = [1, 2, 3, 4, 5, 6]
107 | a.forEach((value)=>console.log(value));
108 | // 1
109 | // 2
110 | // 3
111 | // 4
112 | // 5
113 | // 6
114 | a.map((value)=>2*value);
115 | // [2, 4, 6, 8, 10, 12]
116 |
117 | a.filter((value)=>value%2==0);
118 | // [2, 4, 6]
119 |
120 | a.reduce((accumulator, value)=>accumulator + value);
121 | // 21
122 | ```
--------------------------------------------------------------------------------
/3-problem-solving-approach/1-problem-solving-approach.md:
--------------------------------------------------------------------------------
1 | # Problem Solving Approach
2 |
3 | ## Algorithm
4 | A _process_ or _sequence of tasks_ to accomplish a certain task
5 |
6 | ## General Approach
7 | 1. Understand the Problem
8 | 2. Explore Concrete Examples
9 | 3. Break it down
10 | 4. Solve/Simplify
11 | 5. Look Back and Refactor
12 |
13 | ## 1. Understand the problem:
14 | 1. Restate the problem in your own words.
15 | 2. What are the inputs? and their constraints?
16 | 3. What are the outputs? and their constraints?
17 | 4. Do I have enough informantion to determine the ouputs from inputs?(You may not know the answer to this right away!)
18 | 5. How should I label the important pieces of information that are a part of this problem?
19 |
20 | ## 2. Explore Concrete Examples:
21 | 1. Try a simple case
22 | 2. Try a boundary case
23 | 3. Try empty case (inputs not passed into function)
24 | 4. Try invalid cases (strings passed instead of numbers)
25 |
26 | ## 3. Break it Down:
27 | * Break the problem into smaller tasks and write each task as a comment
28 |
29 | ## 4. Solve or Simplify:
30 | * Solve each of the smaller tasks if you can.
31 | * If you can't:
32 | 1. Break down this task into even smaller task or
33 | 2. Try ignoring the difficulty in the problem and solving the problem and then incorporating the difficulty back again
34 |
35 | ## 5. Look Back and Refactor:
36 | 1. See if your solution works on a sample case?
37 | 2. How can this problem be approached differently?
38 | 3. How can every smaller task approached differently?
39 | 4. Is the code understandable in a glance?
40 | 5. Clean the code
41 |
42 | ## Example:
43 | Q: Write a function that takes in a string and returns counts of each charecter in the string as an object
44 |
45 | ### 1. Understand the Question:
46 | 1. Restate the function in your own words:
47 | >Write down a function that takes a string as input, and return an object with the count of each charecter in the string
48 |
49 | 2. What are the inputs?
50 | > The input is a string always
51 |
52 | 3. What are the outputs?
53 | > The output needs to be an object with key of charecters paired up with their count as values
54 |
55 | 4. Are the inputs and outputs related?
56 | > Yes.
57 |
58 | 5. How do I label the important pieces of information in the problem?
59 | > Let's call the input as string and the output Object as table.
60 |
61 | ### 2. Explore Concrete Examples:
62 | 1. Simple
63 | > "Hello" => {h : 1, e : 1, l : 2, o : 1}
64 | > Upper case and lowercase are same
65 | > 0 counts are not returned
66 | 2. Boundary:
67 | > "Hello hi" => {h : 2, e : 1, l : 2, o : 1, i : 1}
68 | > Spaces don't count, only alphanumeric do
69 | 3. Empty:
70 | > "" => {}
71 | 4. Invalid:
72 | > No Invalid Cases will be provided
73 |
74 | ### 3. Break Down the Problem:
75 | ```javascript
76 | function getCountObject(string){
77 | // define table
78 | // do the counting
79 | //Iterate over the string
80 | //if char is alpa numeric:
81 | // if char in table, value++
82 | // else add char to the table with value 1
83 | // return table
84 | }
85 | ```
86 | >TODO: Learn Regex !important
87 | ### 4. Solve or Simplify:
88 | ```javascript
89 | function getCountObject(string){
90 | // define table
91 | let table = {};
92 | // do the counting
93 | //Iterate over the string
94 | for(char of table){
95 | //if char is alpa numeric:
96 | if(isAlphaNumeric(char)){
97 | // if char in table, value++
98 | if(table[char]){
99 | table[char]++;
100 | }
101 | // else add char to the table with value 1
102 | else{
103 | table[char] = 1;
104 | }
105 | }
106 | }
107 | // return table
108 | return table;
109 | }
110 | function isAlphaNumeric(char){
111 | if(/[a-z0-9]/.text(char)){
112 | return true;
113 | }
114 | return false;
115 | }
116 | ```
117 |
118 | ### 5. Look Back and Refactor
119 | 1. Does it work?
120 | ```javascript
121 | //Corrected Code
122 | function getCountObject(string) {
123 | // define table
124 | let table = {};
125 | // do the counting
126 | //Iterate over the string
127 | for (char of string) {
128 | char = char.toLowerCase();
129 | //if char is alpa numeric:
130 | if (isAlphaNumeric(char)) {
131 | // if char in table, value++
132 | if (table[char]) {
133 | table[char]++;
134 | }
135 | // else add char to the table with value 1
136 | else {
137 | table[char] = 1;
138 | }
139 | }
140 | }
141 | // return table
142 | return table;
143 | }
144 |
145 | function isAlphaNumeric(char) {
146 | if (/[a-z0-9]/.test(char)) {
147 | return true;
148 | }
149 | return false;
150 | }
151 | ```
152 | 2. How can this problem be approached differently?
153 | > Maybe there is some builtin function which does this
154 |
155 | 3. How can the smaller parts be approached differently?
156 | > 1. lowercase the whole string first
157 | > 2. using ascii values instead of regex
158 |
159 | 4. Is this code understandable at glance?
160 | > can be refactored
161 |
162 | 5. Refactor the code
163 | ```javascript
164 | //Final Code
165 | function getCountObject(string) {
166 | let table = {};
167 | string.toLowerCase();
168 | for (char of string) {
169 | if (isAlphaNumeric(char)) {
170 | table[char] = ++table[char] || 1;
171 | }
172 | }
173 | // return table
174 | return table;
175 | }
176 |
177 | function isAlphaNumeric(char) {
178 | return /[a-z0-9]/.test(char);
179 | }
180 | ```
--------------------------------------------------------------------------------
/4-problem-solving-patterns/1-Frequency-Counters.md:
--------------------------------------------------------------------------------
1 | # Frequency Counters
2 |
3 | If there is a loop in loop resulting in a time complexity of _O(n2)_, break down into two seperate for loops by making frequency object structures and then use these structures to do the furthur manipulations as accessing objects is faster than accessing arrays and strings
4 |
5 | Used when there are linear datstructures such as arrays, linked lists and structs and the position of elements is of no use in the problem.
6 |
7 | ## Example
8 |
9 | Write a function same that accepts two arrays. The function should return true if every value in the array has it's corresponding value squared in the second array. The frequency values must be same.
10 |
11 | ### Naive Approach
12 |
13 | 1. Iterate over first loop
14 | 2. For every value in first loop, iterate over the second loop
15 | 3. if the value in second loop is square of value in the first loop, delete that value in the second loop(to account for frequency as well)
16 |
17 | ### Frequency Counter + General Approach from previous chapter
18 |
19 | #### Understand the problem
20 |
21 | 1. Restate the question in own words
22 | > There are two arrays. Return true if for every value the first array, there exists a unique square value in the second array.
23 |
24 | 2. What are the Inputs?
25 | > Two arrays of numbers
26 |
27 | 3. What are the outputs?
28 | > Boolean
29 |
30 | 4. Are Inputs and Outputs related?
31 | > Yes
32 |
33 | 5. What labels should be given for important data
34 | > Let's call the first array as initialArray and the second array as finalArray. Let's call the two tables as initialTable and finalTable.
35 |
36 | #### Explore Concrete Examples
37 | 1. Simple case
38 | > [1, 2, 3], [1, 4, 9] => true]
39 | > Oreder does'nt matter
40 |
41 | 2. Boundary case
42 | > [1, 1, 2], [1, 4, 1] => true
43 | > frequency does matter
44 |
45 | 3. Zero case
46 | > [], [] => true
47 |
48 | 4. Invalid case
49 | > always valid ie arrays
50 |
51 | #### Break Down the Problem
52 | ```javascript
53 | // Use Frequency counter approach to get two arrays first
54 | function same(initialArray, finalArray){
55 | // make empty tables
56 | // copy array into tables
57 | // compare tables
58 | // return
59 | }
60 | ```
61 |
62 | #### Solve/Simplify
63 | ```javascript
64 | // Use Frequency counter approach to get two arrays first
65 | function same(initialArray, finalArray){
66 | // make empty tables
67 | // copy array into tables
68 | let initialTable = arrayToTable(initialArray);
69 | let finalTable = arrayToTable(finalArray);
70 | for(key in initialTable){
71 | if(initialTable[key] != finalTable[key**2]){
72 | return false;
73 | }
74 | }
75 | // compare tables
76 | return true;
77 | }
78 |
79 | function arrayToTable(array){
80 | let table = {};
81 | //[1, 1, 2, 3] => {1 : 2, 2: 1, 3 : 1}
82 | for(number of array){
83 | table[number] = ++table[number] || 1;
84 | }
85 | return table;
86 | }
87 | ```
88 |
89 | #### Look Back and Refactor
90 | 1. Does it run:
91 | > Yes
92 | 2. Ways to approach the problem
93 | > Naive : two for loops
94 | 3. Ways to apporach the parts
95 | > NOt using the second function
96 | 4. Is the code understandable at first glance
97 | > Mostly
98 | 5. Clean
99 | ```javascript
100 | //Final Code
101 | function same(initialArray, finalArray){
102 | // convert array to frequency tables
103 | let initialTable = arrayToTable(initialArray);
104 | let finalTable = arrayToTable(finalArray);
105 | // compare tables
106 | for(key in initialTable){
107 | if(initialTable[key] != finalTable[key**2]){
108 | return false;
109 | }
110 | }
111 | return true;
112 | }
113 |
114 | function arrayToTable(array){
115 | let table = {};
116 | //[1, 1, 2, 3] => {1 : 2, 2: 1, 3 : 1}
117 | array.forEach((value)=>table[value] = ++table[value] || 1);
118 | return table;
119 | }
120 | ```
--------------------------------------------------------------------------------
/4-problem-solving-patterns/2-Multiple-Pointers.md:
--------------------------------------------------------------------------------
1 | # Multiple Pointers
2 |
3 | Used when there is a linear **sorted** datastructure and the problem requires comparision between two members. Naive approach would give O(n2), while this method gives O(n)
4 |
5 | This method involves using two pointers to traverse the array. One traverses from the left while the other traverses from the right.
6 |
7 | ## Example:
8 | Write a function sumZero which accepts a sorted array of integers and return the number of pairs whose sum is zero.
9 |
10 | ### Naive Approach
11 | 1. Use a for loop to iterate over the array
12 | 2. For every number, iterate over the array again and find a partner number with sum zero
13 |
14 | > Insight: The informatin that the array is sorted is not used
15 |
16 | ### Multiple Pointers + General Approach
17 | #### Understand the Problem
18 | 1. Restate
19 | > Write a function that takes a sorted array and returns the number of pairs such that the sum of the paired numbers is 0
20 | 2. Input
21 | > a sorted array of numbers
22 | 3. Output
23 | > Number of sumZero pairs
24 | 4. are input and output related
25 | > Yes
26 | 5. Labels for useful fata
27 | > Let's call the input array as simply array and the frequency of pair counter as pairCounter. Let the pointers be called left and righ
28 |
29 | #### Explore Concrete Examples
30 | 1. Simple Case:
31 | > [-3, -2, -1, 0, 1, 2, 3] => 3
32 | > the pointer should move towards each other until they cross each other
33 | 2. Boundary Case:
34 | >[-4, 4, 5,6 , 34, 50] => 1
35 | > the pointer whose absolute value is higher should move
36 | 3. Empty case:
37 | >[] =>0
38 | 4. Invalid Case:
39 | > array assured
40 |
41 | #### Break Down the Problem
42 | ```javascript
43 | function sumZero(array){
44 | // define pointers and counter
45 | //while pointers dont cross:
46 | // compare numbers
47 | // if pair found move both pointers, counter++
48 | // else move the pointer with higher value
49 | }
50 | ```
51 |
52 | #### Solve/Simplify
53 | ```javascript
54 | function sumZero(array){
55 | // define pointers and counter
56 | let left = 0;
57 | let right = array.length - 1;
58 | let pairCounter = 0;
59 | //while pointers dont cross:
60 | while(left < right){
61 | // compare numbers
62 | if(array[left] + array[right] === 0){
63 | // if pair found move both pointers, counter++
64 | pairCounter++;
65 | left++;
66 | right--;
67 | }
68 | else{
69 | // else move the pointer with higher value
70 | if(Math.abs(array[left] > Math.abs(right))){
71 | // left is larger
72 | left++;
73 | }
74 | else{
75 | right--;
76 | }
77 | }
78 | return pairCounter;
79 | }
80 | ```
81 |
82 | #### Look Back and Refactor
83 | 1. Does it work?
84 | ```javascript
85 | //Corrected code
86 | function sumZero(array) {
87 | // define pointers and counter
88 | let left = 0;
89 | let right = array.length - 1;
90 | let pairCounter = 0;
91 | //while pointers dont cross:
92 | while (left < right) {
93 | // compare numbers
94 | if (array[left] + array[right] === 0) {
95 | // if pair found move both pointers, counter++
96 | pairCounter++;
97 | left++;
98 | right--;
99 | } else {
100 | // else move the pointer with higher value
101 | if (Math.abs(array[left]) > Math.abs(array[right])) {
102 | // left is larger
103 | left++;
104 | } else {
105 | right--;
106 | }
107 | }
108 | }
109 | return pairCounter;
110 | }
111 | ```
112 | 2. Other ways to approach problem?
113 | > Naive way or two for loops
114 | 3. Other ways to approach smaller parts
115 | > Not sure
116 | 4. Is code Readable
117 | > Mostly
118 | 5. Refactor
119 | ```javascript
120 | function sumZero(array) {
121 | let left = 0;
122 | let right = array.length - 1;
123 | let pairCounter = 0;
124 | //while pointers dont cross:
125 | while (left < right) {
126 | // compare numbers
127 | if (array[left] + array[right] === 0) {
128 | // if pair found move both pointers, counter++
129 | pairCounter++;
130 | left++;
131 | right--;
132 | } else {
133 | // move the pointer with higher value
134 | Math.abs(array[left]) > Math.abs(array[right]) ? left++ : right--;
135 | }
136 | }
137 | return pairCounter;
138 | }
139 | ```
--------------------------------------------------------------------------------
/4-problem-solving-patterns/3-Divide-and-Conquer.md:
--------------------------------------------------------------------------------
1 | # Divide and Conquer
2 | Divide the dataset into smaller parts and reapeating the process for the smaller parts.
3 | Eg: Binary Search, Merge Sort, Quick Sort
--------------------------------------------------------------------------------
/5-recursion/1-recursion.md:
--------------------------------------------------------------------------------
1 | # Recursion
2 | A recursive function is one which calls itself while executing. It works on the divide and conquer strategy. Every recursive function must have a base case to end the recursiveness.
3 |
4 | ## Example
5 | **Sum of Numbers**
6 | ```javascript
7 | function sumOfNum(number){
8 | if(number === 1){
9 | return 1;
10 | }
11 | return number + sumOfNum(number-1);
12 | }
13 | ```
14 |
15 | ## Call Stack
16 | Call stack is a stack data structure which handles the function execution in javascript. It works on Last in First Out (LIFO)
17 |
18 | ### Experiment
19 | Try out the following code in the chrome dev console as a snippet and watch the call stack. Add a breakpoint at ```startEngine()``` and then step one by one and observe the call stack.
20 | ```javascript
21 | var ignition = true;
22 | function startEngine(){
23 | passInFuel();
24 | passInAir();
25 | let a = runStarterMotor();
26 | // let a = runStarterMotor;
27 | // Try both and see their effect on call stack
28 | if(ignition){
29 | console.log("Engine Started");
30 | }
31 | else{
32 | startEngine();
33 | }
34 | }
35 |
36 | function passInAir(){
37 | return "Passing in Air";
38 | }
39 |
40 | function passInFuel(){
41 | return "Passing in Fuel";
42 | }
43 |
44 | function runStarterMotor(){
45 | if(Math.ceil(Math.random()*2) - 1){
46 | ignition = true;
47 | }
48 | }
49 |
50 | startEngine();
51 | ```
52 | 
53 | > Note: Call Stacks have a limit to their size which is browser dependent. After the call stack size exceeds the limit, the execition will stop and throw an error. This is known as **Stack Overflow!**
54 |
55 | ## Helper Method Recursion
56 | In this type, recursive method is defined and called inside a main method.
57 | ### Example
58 | Write a function that takes in an array and outputs an array with only odd numbers of the previous array.
59 | 1. With Helper Method Recursion
60 |
61 | ```javascript
62 | function getEven(array){
63 | let result = [];
64 | function addIfEven(array){
65 | if(array.length == 0)return;
66 | if(array[0]%2==0)result.push(array[0]);
67 | addIfOdd(array.slice(1));
68 | }
69 | addIfOdd(array);
70 | return result;
71 | }
72 | ```
73 |
74 | 2. With Pure Recursion Method
75 | ```javascript
76 | function getEven(array){
77 | let newArray = []
78 | if(array.length==0)return [];
79 | if(array[0]%2==0)newArray.push(array[0]);
80 | newArray = newArray.concat(getEven(array.slice(1)));
81 | return newArray;
82 | }
83 | ````
84 |
--------------------------------------------------------------------------------
/5-recursion/call-stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/godcrampy/js-notes-dsa-colt-steele/6aab4183afd794c1166d763ce73e91e345ee72ab/5-recursion/call-stack.png
--------------------------------------------------------------------------------
/6-searching/1-linear-search.md:
--------------------------------------------------------------------------------
1 | # Linear Search
2 | Compares every element one by one till target is reached.
3 |
4 | ## Complexity
5 | 1. Time
6 |
7 | Best: O(1)
8 |
9 | Average: O(n)
10 |
11 | Worst: O(n)
12 |
13 | 2. Space: O(1)
14 |
15 | ## Implementation
16 | ```javascript
17 | function linearSearch(array, target){
18 | for(index in array){
19 | if(array[index] == target)return parseInt(index);
20 | }
21 | return -1;
22 | }
23 | ```
24 |
25 | ## JavaScript inbuilt search functions
26 | ```javascript
27 | let a = [3, 45, 65, 20, 1];
28 | ```
29 | **1. find:**
30 |
31 | Takes in a function and return the first value that satisfies the condition in function. The function should return true or false.
32 |
33 | ```javascript
34 | a.find(function(value){
35 | return value > 40;
36 | });
37 | //returns 45
38 |
39 | // ES6 Version
40 | a.find((value)=>value > 40);
41 | //returns 45
42 | ```
43 |
44 | **2. findIndex:**
45 |
46 | Same like find but instead of returning value, it returns index of the values that satisfies the condition
47 | ```javascript
48 | a.findIndex(function(value){
49 | return value > 40;
50 | })
51 | // returns 1
52 |
53 | // ES6 version
54 | a.findIndex((value)=> value > 40)
55 | // returns 1
56 | ```
57 |
58 | **3. indexOf**
59 |
60 | Returns the index of first occurance of the given value
61 | ```javascript
62 | a.indexOf(45)
63 | // returns 1
64 | ```
65 |
66 | **4. includes**
67 |
68 | Returns tru if the given value exists in the array else returns false
69 | ```javascript
70 | a.includes(45)
71 | // returns true
72 |
73 | a.includes(455)
74 | // returns false
75 | ```
--------------------------------------------------------------------------------
/6-searching/2-binary-search.md:
--------------------------------------------------------------------------------
1 | # Binary Search
2 |
3 | Requires sorted array. Unlike linear search which eliminates one number at a time, binary search eliminates half of the array at a time. Works on _divide and conquer._
4 |
5 | ## Complexity
6 | 1. Time
7 |
8 | Best: O(1)
9 |
10 | Average: O(log(n))
11 |
12 | Best: O(log(n))
13 |
14 | 2. Space: O(1)
15 |
16 | ### Explanation
17 | Suppose we have 16 length array. Is we perform binary search, we half the array overtime repeatedly. So in the worst case 16=>8=>4=>2=>1. Now if we doubled the size of array we will require one extra step. 32=>16=>8=>4=>2=>1. Thus by if complexity is f(n), by doubling n the steps increase by one. Thus f(n) = log(n). Thus O(log(n))
18 |
19 | ## Implementation
20 | ```javascript
21 | //Using recursion
22 | function binarySearch(array, target, left = 0, right = array.length - 1) {
23 | // find the middle index
24 | let middle = Math.floor((left + right) / 2)
25 | if (array[middle] == target) {
26 | return middle;
27 | } else if (right == left) {
28 | return -1;
29 | } else if (target < array[middle]) {
30 | // target is in the left array
31 | return binarySort(array, target, left, middle - 1);
32 | } else {
33 | // target is in the right array
34 | return binarySort(array, target, middle + 1, right);
35 | }
36 | }
37 |
38 | //Using While Loop
39 | function binarySearch(array, target) {
40 | // Set inital pointers
41 | let left = 0;
42 | let right = array.length;
43 | while (true) {
44 | let middle = Math.floor((left + right) / 2);
45 | if (array[middle] === target) return middle;
46 | if (array.length === 1) return -1;
47 | if (array[middle] < target) {
48 | // target belongs to the right;
49 | left = middle + 1;
50 | } else {
51 | // target belongs to the left
52 | right = middle - 1
53 | }
54 | }
55 | }
56 | ```
--------------------------------------------------------------------------------
/6-searching/3-string-search.md:
--------------------------------------------------------------------------------
1 | # String Search
2 | Given a string and a target string, find if the target string is a substring of the main string. Return the number of times the target string mathes with the substring
3 |
4 | ## Naive Approach
5 | ```javascript
6 | // Loop over both the strings and compare charecter by charecter
7 | function stringSearch(string, target) {
8 | let counter = 0
9 | let isSubstring = false;
10 | for (let i = 0; i < string.length; i++) {
11 | isSubstring = true;
12 | for (let j = 0; j < target.length; j++) {
13 | if (string[i + j] != target[j]) {
14 | isSubstring = false;
15 | break;
16 | }
17 | }
18 | if (isSubstring) counter++;
19 | }
20 | return counter;
21 | }
22 | ```
23 |
24 | ### Complexity
25 | 1. Time: O(n2)
26 | 2. Space: O(1)
--------------------------------------------------------------------------------
/7-sorting/1-bubble-sort.md:
--------------------------------------------------------------------------------
1 | # Bubble Sort
2 | Uses two for loop. In every pass, the largest value of the unsorted array is added to the sorted array.
3 |
4 | ## Complexity
5 | 1. Time
6 |
7 | * Normal Version
8 | * All: O(n2)
9 |
10 | * Optimized Version
11 | * Best: O(n)
12 | * Avg: O(n2)
13 | * Worst: O(n2)
14 |
15 |
16 | 2. Space
17 | * All: O(1)
18 |
19 | ## Implemantation
20 | ```javascript
21 | function bubbleSort(array) {
22 | for (let i = 0; i < array.length; i++)
23 | for (j = array.length - 1; j > i; j--)
24 | if (array[j] < array[j - 1])
25 | [array[j], array[j - 1]] = [array[j - 1], array[j]];
26 | // ES6 Swap ^
27 | }
28 | ```
29 |
30 | ```javascript
31 | // Optimized
32 | function bubbleSort(array) {
33 | let didDoSwapping = false;
34 | for (let i = 0; i < array.length; i++) {
35 | didDoSwapping = false;
36 | for (j = array.length - 1; j > i; j--) {
37 | if (array[j] < array[j - 1]) {
38 | [array[j], array[j - 1]] = [array[j - 1], array[j]];
39 | didDoSwapping = true;
40 | }
41 | }
42 | if (!didDoSwapping) break;
43 | }
44 | }
45 | ```
--------------------------------------------------------------------------------
/7-sorting/2-selection-sort.md:
--------------------------------------------------------------------------------
1 | # Selection Sort
2 | We go thru the unsorted array and put the smallest value at the beginning and include it in the sorted array. The only place where selection sort is better than bubble is that it has less number of swaps.
3 |
4 | ## Complexity
5 | 1. Time
6 | * Best: O(n)
7 | * Avg: O(n2)
8 | * Worst: O(n2)
9 |
10 |
11 | 2. Space
12 | * All: O(1)
13 |
14 | ## Implementaion
15 | ```javascript
16 | function selectionSort(array) {
17 | let minPosition;
18 | for (let i = 0; i < array.length - 1; i++) {
19 | //i represents the position to swap with
20 | minPosition = i;
21 | for (let j = i + 1; j < array.length; j++)
22 | if (array[j] < array[minPosition])
23 | minPosition = j;
24 | [array[i], array[minPosition]] = [array[minPosition], array[i]];
25 | }
26 | }
27 | ```
--------------------------------------------------------------------------------
/7-sorting/3-insertion-sort.md:
--------------------------------------------------------------------------------
1 | # Insertion Sort
2 | We take one element and insert it in the right place in the sorted part. One advantage over bubble and selection sort is that the it is good when data is getting appended over time.
3 |
4 | ## Complexity
5 | 1. Time
6 | * Best: O(n)
7 | * Avg: O(n2)
8 | * Worst: O(n2)
9 |
10 |
11 | 2. Space
12 | * All: O(1)
13 |
14 | ## Implementation
15 | ```javascript
16 | function insertionSort(array) {
17 | for (let i = 0; i < array.length - 1; i++)
18 | for (let j = i + 1; j > 0; j--)
19 | if (array[j] < array[j - 1])[array[j - 1], array[j]] = [array[j], array[j - 1]];
20 | else break;
21 | }
22 | ```
--------------------------------------------------------------------------------
/7-sorting/4-merge-sort.md:
--------------------------------------------------------------------------------
1 | # Merge Sort
2 | Works on divide and conquer algorithm. General strategy is to keep on splitting the arrays into halves then merging the halves one by one.
3 |
4 | ## Complexity
5 | 1. Time
6 | * All: O(nlog(n))
7 | > Suppose some set of data takes time t. Now if we double the data, we have to make one more splitting step. This is log(n) pattern. But for this additional step we have to make n comparisions as well in the merging step. Thus we get O(nlog(n))
8 |
9 | 2. Space
10 | * All : O(n)
11 | > More double length of left and right arrays created as n doubles
12 |
13 | > Coding Pattern: Avoid while true with breaks inside as it makes the code harder to understand.
14 |
15 | ### Implementation
16 | ```javascript
17 | function mergeSort(array) {
18 | if (array.length <= 1) return array;
19 | let midpoint = Math.floor(array.length / 2)
20 | let left = mergeSort(array.slice(0, midpoint));
21 | let right = mergeSort(array.slice(midpoint));
22 | return mergeArrays(left, right);
23 | }
24 |
25 | function mergeArrays(array1, array2) {
26 | // Given two functions, this function constructs a single sorted array
27 | let sortedArray = [],
28 | pointer1 = 0,
29 | pointer2 = 0;
30 | while (pointer1 != array1.length && pointer2 != array2.length)
31 | if (array1[pointer1] > array2[pointer2]) {
32 | sortedArray.push(array2[pointer2]);
33 | pointer2++;
34 | } else {
35 | sortedArray.push(array1[pointer1]);
36 | pointer1++;
37 | }
38 | if (pointer1 == array1.length)
39 | sortedArray = sortedArray.concat(array2.slice(pointer2));
40 | else
41 | sortedArray = sortedArray.concat(array1.slice(pointer1));
42 | return sortedArray
43 | }
44 | ```
--------------------------------------------------------------------------------
/7-sorting/5-quick-sort.md:
--------------------------------------------------------------------------------
1 | # Quick Sort
2 | Sorts elements one by one by placing a pivot element in its correct place and then repeating for the left and right elements recursively.
3 |
4 | ## Complexity
5 | 1. Time
6 |
7 | * Best, Avg: O(nlog(n))
8 | * Worst: O(n2)
9 |
10 | > If certain data takes time t and we double the data, we need to make one morebreaking od array into left and right and for that one extra array we need to compare n times. Thus O(nlog(n)). Worst case comes when the array is already sorted. The pivot will be at its position and we will make n comparisions to move to the next postion which will be at its postion as well and so on for n times. Thus we get O(n2). I way to avoid this is by using ```isSorted(array)``` which checks if an array is sorted.
11 |
12 | 2. Space
13 |
14 | * All: O(log(n))
15 |
16 | > When array size is doubled, call stack increases by one (Not satisfied with the answer coz this reasoning fails at merge sort)
17 |
18 | ## Implementation
19 | ```javascript
20 | function quickSort(array) {
21 | if (array.length <= 1) return array;
22 | let pivot = array[0];
23 | let pivotPosition = 0;
24 | for (let i = 1; i < array.length; i++)
25 | if (array[i] < pivot) {
26 | pivotPosition++;
27 | [array[pivotPosition], array[i]] = [array[i], array[pivotPosition]];
28 | }
29 | [array[0], array[pivotPosition]] = [array[pivotPosition], array[0]];
30 | let left = quickSort(array.slice(0, pivotPosition));
31 | let right = quickSort(array.slice(pivotPosition + 1));
32 | return [...left, pivot, ...right];
33 | }
34 | ```
--------------------------------------------------------------------------------
/7-sorting/6-radix-sort.md:
--------------------------------------------------------------------------------
1 | # Radix Sort
2 |
3 | Radix sort doesn't use comparisons for sorting instead uses properties of numbers. This sort can be done on integers only. The algorithm groups numbers according to the numbers at units, tens, hundreds and so on places.
4 |
5 | ### Note on JavaScript Methods
6 | >NOTE: primitive type variables like strings and numbers are always passed by value.
7 | Arrays and Objects are passed by reference or by value based on these conditions:
8 | > 1. if you are setting the value of an object or array it is Pass by Value.
9 |
10 | ```javascript
11 | object1 = {prop: "car"};
12 | array1 = [1,2,3];
13 | ```
14 |
15 | >2. if you are changing a property value of an object or array then it is Pass by Reference.
16 |
17 | ```javascript
18 | object1.prop = "car";
19 | array1[0] = 9;
20 | ```
21 | ```javascript
22 | function passVar(obj1, obj2, num) {
23 | obj1.prop = "laptop"; // will CHANGE original
24 | obj2 = { prop: "computer" }; //will NOT affect original
25 | num = num + 1; // will NOT affect original
26 | }
27 |
28 | var object1 = {
29 | prop: "car"
30 | };
31 | var object2 = {
32 | prop: "bike"
33 | };
34 | var number1 = 10;
35 |
36 | passVar(object1, object2, number1);
37 | console.log(object1); //output: Object {item:"laptop"}
38 | console.log(object2); //output: Object {item:"bike"}
39 | console.log(number1); //ouput: 10
40 | ```
41 |
42 | ## Complexity
43 | > k is the base on numbers (10 in our case)
44 | 1. Time: O(kn)
45 | 2. Space: O(k + n)
46 |
47 | ## Implementation
48 | ```javascript
49 | function getDigit(number, place) {
50 | // returns the number at the given place 124, 3 => 1
51 | return Math.floor(Math.abs(number) / Math.pow(10, place)) % 10;
52 | }
53 |
54 | function digitCount(number) {
55 | if (number == 0) return 1;
56 | return Math.floor(Math.log10(Math.abs(number))) + 1;
57 | }
58 |
59 | function mostDigits(array) {
60 | let digits = 0;
61 | for (number of array)
62 | digits = Math.max(digitCount(number), number);
63 | return digits;
64 | }
65 |
66 | function radixSort(array) {
67 | let iterations = mostDigits(array);
68 | for (let i = 0; i < iterations; i++) {
69 | let bucket = Array.from({
70 | length: 10
71 | }, () => []);
72 | for (number of array)
73 | bucket[getDigit(number, i)].push(number);
74 | array = [].concat(...bucket)
75 | }
76 | return array;
77 | }
78 | ```
--------------------------------------------------------------------------------
/8-data-structures/1-data-structures.md:
--------------------------------------------------------------------------------
1 | # Data Structures
2 | Data structures are collection of values, the relationship between them and the functions or the operations that can be applied to the data.
3 |
4 | ## ES2015 Classes
5 | ```javascript
6 | class Student {
7 | static count = 0;
8 | // ES6 Does'nt support static properties :(.Only Chrome console does
9 | constructor(firstname, lastname, year) {
10 | this.firstname = firstname;
11 | this.lastname = lastname;
12 | this.grade = year;
13 | this.scores = [];
14 | Student.count++;
15 | }
16 | fullName() {
17 | return `${this.firstname} ${this.lastname}`;
18 | }
19 | addScore(score) {
20 | this.scores.push(score);
21 | }
22 | averageScore() {
23 | let sum = this.scores.reduce((acc, val) => acc + val);
24 | return sum / this.scores.length;
25 | }
26 | }
27 |
28 | let emily = new Student("Emily", "Wolowitz");
29 | // grade remains undefined as we don't provide it
30 |
31 | emily.fullName()
32 | // Emily Wolowitz
33 |
34 | emily.addScore(92);
35 | emily.addScore(90);
36 | emily.scores;
37 | // [92, 91]
38 |
39 | emily.averageScore();
40 | // 91
41 | ```
--------------------------------------------------------------------------------
/9-linked-list/1-singly-linked-list.md:
--------------------------------------------------------------------------------
1 | # Singly Linked List
2 | Collection of nodes each node containing a value and pointer to the next node.
3 |
4 | >In JavaScript, ```undefined``` means a variable has been declared but has not yet been assigned a value.
5 | ```null``` is an assignment value. It can be assigned to a variable as a representation of no value
6 |
7 | >Javascript is garbage collected, you don't need to delete objects themselves - they will be removed when there is no way to refer to them anymore.
8 |
9 | ## Complexity
10 | 1. Search: O(n)
11 | 2. Access: O(n)
12 | 3. Delete: O(n) or O(1)
13 | 4. Insert: O(n) or O(1)
14 |
15 | > Singly Linked List are better than arrays when insertion and deletion at the end and at the start is required. Accessing is difficult in linked lists
16 |
17 | ## Methods
18 | 1. ```push(data)```: Works same as ```push``` of array
19 | 2. ```pop()```: Works same as ```pop``` of array
20 | 3. ```shift()```: Works same as ```shift``` of array
21 | 4. ```unshift(data)```: Works same as ```unshift``` of array
22 | 5. ```get(index)```: Gets the node at given index
23 | 6. ```set(index)```: Sets the node data at given index
24 | 7. ```insert(index, data)```: Inserts a node with given data at the given index
25 | 8. ```remove(index)```: Removes the node at the index
26 | 9. ```reverse()```: Reverses the list
27 | 10. ```traverse()```: Logs the list on the console
28 |
29 |
30 | ## Implementation
31 | ```javascript
32 | class Node {
33 | constructor(data) {
34 | this.data = data;
35 | this.next = null;
36 | }
37 | }
38 |
39 | class SinglyLinkedList {
40 | constructor() {
41 | this.length = null;
42 | this.head = null;
43 | this.tail = null;
44 | }
45 |
46 | push(data) {
47 | let newNode = new Node(data);
48 | if (!this.head) {
49 | // Empty list
50 | this.head = newNode;
51 | this.tail = newNode;
52 | } else {
53 | this.tail.next = newNode;
54 | this.tail = newNode;
55 | }
56 | this.length++;
57 | return this.length;
58 | }
59 | pop() {
60 | if (!this.length) return undefined;
61 | // Find the second last node
62 | let iterator = this.head;
63 | while (iterator.next != this.tail) {
64 | iterator = iterator.next;
65 | }
66 | let final = this.tail.data;
67 | this.tail = iterator;
68 | this.tail.next = null;
69 | this.length--;
70 | if (this.length === 0) {
71 | this.head = null;
72 | this.tail = null;
73 | }
74 | return final
75 | }
76 | shift() {
77 | if (!this.length) return undefined;
78 | let newHead = this.head.next;
79 | let final = this.head.data;
80 | this.head = newHead;
81 | this.length--;
82 | if (this.length === 0) {
83 | this.head = null;
84 | this.tail = null;
85 | }
86 | return final;
87 | }
88 | unshift(data) {
89 | let newHead = new Node(data)
90 | if (this.length === 0) {
91 | this.head = newHead;
92 | this.tail = newHead;
93 | } else {
94 | newHead.next = this.head;
95 | this.head = newHead;
96 | }
97 | this.length++;
98 | return this.length;
99 | }
100 | traverse() {
101 | let iterator = this.head;
102 | while (iterator) {
103 | console.log(iterator.data);
104 | iterator = iterator.next;
105 | }
106 | }
107 | get(index = 0) {
108 | let iteratorPostion = 0;
109 | let iterator = this.head;
110 | if (index >= this.length || index < 0) return null
111 | while (iteratorPostion != index) {
112 | iterator = iterator.next;
113 | iteratorPostion++;
114 | }
115 | return iterator;
116 | }
117 | set(index = 0, data = undefined) {
118 | let iterator = this.get(index);
119 | if (!iterator) return false;
120 | iterator.data = data;
121 | return true;
122 | }
123 | insert(index = 0, data = undefined) {
124 | let prevNode = this.get(index - 1);
125 | let nextNode = this.get(index);
126 | let newNode = new Node(data);
127 | if (this.length === 0 || !nextNode) this.push(data)
128 | else if (!prevNode) this.unshift(data)
129 | else {
130 | prevNode.next = newNode;
131 | newNode.next = nextNode;
132 | this.length++;
133 | }
134 | return this.length;
135 | }
136 | remove(index = 0) {
137 | let prevNode = this.get(index - 1);
138 | let nextNode = this.get(index + 1);
139 | let currentNode = this.get(index);
140 | if (this.length === 0) return null;
141 | else if (!prevNode) this.shift()
142 | else if (!nextNode) this.pop()
143 | else {
144 | prevNode.next = nextNode;
145 | this.length--;
146 | return currentNode.data;
147 | }
148 | }
149 | reverse() {
150 | // flip head and tail
151 | let temp = this.head;
152 | this.head = this.tail;
153 | this.tail = temp;
154 | let node = this.tail;
155 | let prevNode = null;
156 | let nextNode = node.next;
157 | while (node != null) {
158 | node.next = prevNode;
159 | prevNode = node;
160 | node = nextNode;
161 | if (node != null) nextNode = node.next;
162 | }
163 | }
164 | }
165 | ```
--------------------------------------------------------------------------------
/9-linked-list/2-doubly-linked-list.md:
--------------------------------------------------------------------------------
1 | # Double Linked List
2 | Collection of nodes each node containing a value and pointer to the next and the previous node.
3 |
4 | > If a parameter is not passed to a function, that parameter gets the value of undefined.
5 | >https://www.freecodecamp.org/news/copying-stuff-in-javascript-how-to-differentiate-between-deep-and-shallow-copies-b6d8c1ef09cd/
6 |
7 | ## Complexity
8 | 1. Search: O(n)
9 | 2. Access: O(n)
10 | 3. Delete: O(n) or O(1)
11 | 4. Insert: O(n) or O(1)
12 |
13 | > Faster than or same speed as singly linked list
14 |
15 | ## Methods
16 | 1. ```push(data)```: Works same as ```push``` of array
17 | 2. ```pop()```: Works same as ```pop``` of array
18 | 3. ```shift()```: Works same as ```shift``` of array
19 | 4. ```unshift(data)```: Works same as ```unshift``` of array
20 | 5. ```get(index)```: Gets the node at given index
21 | 6. ```set(index)```: Sets the node data at given index
22 | 7. ```insert(index, data)```: Inserts a node with given data at the given index
23 | 8. ```remove(index)```: Removes the node at the index
24 | 9. ```reverse()```: Reverses the list
25 | 10. ```traverse()```: Logs the list on the console
26 |
27 | ## Implemetation
28 | ```javascript
29 | class Node {
30 | constructor(data) {
31 | this.data = data;
32 | this.previous = null;
33 | this.next = null;
34 |
35 | }
36 | }
37 |
38 | class DoublyLinkedList {
39 | constructor() {
40 | this.head = null;
41 | this.tail = null;
42 | this.length = 0;
43 | }
44 |
45 | push(data) {
46 | let newNode = new Node(data);
47 | if (this.length === 0) {
48 | this.head = newNode;
49 | } else {
50 | this.tail.next = newNode;
51 | newNode.previous = this.tail;
52 | }
53 | this.tail = newNode;
54 | this.length++;
55 | return this.length;
56 | }
57 | pop() {
58 | let final;
59 | if (this.length === 0) final = null;
60 | else if (this.length === 1) {
61 | final = this.head.data;
62 | this.head = null;
63 | this.tail = null;
64 | this.length = 0;
65 | } else {
66 | final = this.tail.data;
67 | this.tail = this.tail.previous;
68 | this.tail.next = null;
69 | this.length--;
70 | }
71 | return final;
72 | }
73 | shift() {
74 | let final;
75 | if (this.length === 0) final = null;
76 | else if (this.length === 1) {
77 | final = this.head.data;
78 | this.head = null;
79 | this.tail = null;
80 | this.length = 0;
81 | } else {
82 | final = this.head.data;
83 | this.head = this.head.next;
84 | this.head.previous = null;
85 | this.length--
86 | }
87 | return final;
88 | }
89 | unshift(data) {
90 | let newNode = new Node(data);
91 | if (this.length === 0) {
92 | this.tail = newNode;
93 | } else {
94 | this.head.previous = newNode;
95 | newNode.next = this.head;
96 | }
97 | this.head = newNode;
98 | this.length++;
99 | return this.length;
100 | }
101 | get(index) {
102 | if (index < 0 || index >= this.length) return null;
103 | let iterator;
104 | if (index > this.length / 2) {
105 | // Traverse from behind
106 | iterator = this.tail;
107 | let iteratorPosition = this.length - 1;
108 | while (iteratorPosition != index) {
109 | iterator = iterator.previous;
110 | iteratorPosition--;
111 | }
112 | } else {
113 | //travers from front
114 | iterator = this.head;
115 | let iteratorPosition = 0;
116 | while (iteratorPosition != index) {
117 | iterator = iterator.next;
118 | iteratorPosition++;
119 | }
120 | }
121 | return iterator;
122 | }
123 | set(index, data) {
124 | let iterator = this.get(index);
125 | if (!iterator) return false;
126 | iterator.data = data;
127 | return true;
128 | }
129 | insert(index, data) {
130 | let previousNode = this.get(index - 1);
131 | let nextNode = this.get(index);
132 | let newNode = new Node(data);
133 | if (index === 0 || this.length === 0) this.unshift(data)
134 | else if (index === this.length) this.push(data);
135 | else {
136 | previousNode.next = newNode;
137 | newNode.previous = previousNode;
138 | newNode.next = nextNode;
139 | nextNode.previous = newNode;
140 | this.length++;
141 | }
142 | return this.length;
143 | }
144 | remove(index) {
145 | if (index < 0 || index >= this.length) return undefined;
146 | if (index === 0) this.shift()
147 | else if (index === this.length - 1) this.pop()
148 | else {
149 | let final = this.get(index).data;
150 | let previousNode = this.get(index - 1);
151 | let nextNode = this.get(index + 1);
152 | previousNode.next = nextNode;
153 | nextNode.previous = previousNode;
154 | this.length--;
155 | return final;
156 | }
157 | }
158 | reverse() {
159 | let iterator = this.head;
160 | while(iterator){
161 | let temp = iterator.next;
162 | iterator.next = iterator.previous;
163 | iterator.previous = temp;
164 | iterator = iterator.previous;
165 | }
166 | let temp = this.head;
167 | this.head = this.tail;
168 | this.tail = temp;
169 | }
170 | traverse() {
171 | let iterator = this.head;
172 | while (iterator) {
173 | console.log(iterator.data);
174 | iterator = iterator.next;
175 | }
176 | return this.length;
177 | }
178 | }
179 | ```
--------------------------------------------------------------------------------