- Stack
- Set
- Queue
- Priority queue
- Binary search tree
- Hash tables
- Map
- Linked list
- Trie
- Heap
- Graph
- Big O notation
var myStack = new Stack();
console.log(myStack.length()); // 0
console.log(myStack.push('a')); // undefined (push return no value)
console.log(myStack.push('b')); // undefined
console.log(myStack.push('c')); // undefined
console.log(myStack.pop()); // c
console.log(myStack.top()); // b
console.log(myStack.bottom()); // a
console.log(myStack.length()); // 2
const Stack = function() {
let count = 0;
let content = {};
this.push = function(value) { // add value on top of the stack
content[count] = value;
count++;
};
this.pop = function() { // remove value at top of the stack and return that value
if(count === 0) { return undefined }
count--;
var popped = content[count];
delete content[count];
return popped;
};
this.length = function() { return count } // return number of values in stack
this.top = function() { // return the value on top of the stack
if(count === 0 ) { return undefined }
return content[count-1]
}
this.bottom = function() { // return the value at the bottom of the stack
if(count === 0 ) { return undefined }
return content['0']
}
}
- Javascript array object has all Stack data structure attributes and methods. However, we can always implement a stack data structure by ourself and add more property/method as we want.
const mySet = new Set();
console.log(mySet.add('a')); // a is added to set!
console.log(mySet.exist('a')); // true
console.log(mySet.exist('b')); // false
console.log(mySet.list()); // [ 'a' ]
console.log(mySet.add('b')); // b is added to set!
console.log(mySet.add('c')); // c is added to set!
console.log(mySet.remove('c')); // c is removed from set!
console.log(mySet.list()); // [ 'a', 'b' ]
console.log(mySet.length()); // 2
const mySet2 = new Set();
console.log(mySet2.add('a')); // a is added to set!
console.log(mySet2.add('g')); // g is added to set!
console.log(mySet2.add('h')); // h is added to set!
console.log(mySet2.list()); // [ 'a', 'g', 'h' ]
console.log(mySet.union(mySet2).list()); // [ 'a', 'b', 'g', 'h' ]
console.log(mySet.intersect(mySet2).list()); // [ 'a' ]
console.log(mySet.difference(mySet2).list()); // [ 'b', 'g', 'h' ]
const Set = function() {
let set = [];
this.exist = function(element) { // check if element is in set return true, else return false
return (set.indexOf(element) !== -1)
}
this.add = function(element) { // add element to set if not exist, do not add if it exists
if(!this.exist(element)) {
set.push(element);
return `${element} is added to set!`;
}
}
this.list = function() { // list all element in set
return set;
}
this.remove = function(element) { // remove an element if it's in set
if(this.exist(element)) {
set.splice(set.indexOf(element), 1);
return `${element} is removed from set!`;
} else { return `${element} is not in set!`; }
}
this.length = function() { // return number of element in set
return set.length;
}
this.union = function(setC) { // return the union set of two sets
let union = new Set();
let setA = this.list();
let setB = setC.list();
for(let i = 0; i < setA.length; i++) {
union.add(setA[i]);
}
for(let j = 0; j < setB.length; j++) {
union.add(setB[j]);
}
return union;
}
this.intersect = function(setC) { // return a set of intersection of 2 sets
let intersect = new Set();
let setA = this;
let setB = setC.list();
for(let i = 0; i < setB.length; i++) {
if(setA.exist(setB[i])) {
intersect.add(setB[i])
}
}
return intersect;
}
this.difference = function(setC) { // return a set of difference of 2 sets
let setA = this;
let difference = setA.union(setC);
let setB = setA.intersect(setC).list();
for(let i = 0; i < setB.length; i++) {
difference.remove(setB[i]);
}
return difference;
}
}
const myQueue = new Queue();
myQueue.enqueue('a');
myQueue.enqueue('b');
myQueue.enqueue('c');
myQueue.list(); // [ 'a', 'b', 'c' ]
console.log(myQueue.dequeue()); // a
console.log(myQueue.front()); // b
console.log(myQueue.length()); // 2
console.log(myQueue.isEmpty()); // false
myQueue.list(); // [ 'b', 'c' ]
function Queue() { // first in, first out (FIFO)
const content = [];
this.list = function() { console.log(content) }; // list all queue item
this.enqueue = function(element) { content.push(element) }; // put item to the queue
this.dequeue = function() { return content.shift() }; // get the first item in the queue, remove it from the queue
this.front = function() { return content[0] }; // get the first item in the queue, still let it be in the queue
this.length = function() { return content.length }; // get queue length
this.isEmpty = function() { return (content.length == 0) } // check if queue is empty
}
const myQueue = new priorityQueue();
myQueue.enqueue(['a', 2]);
myQueue.enqueue(['b', 3]);
myQueue.list(); // [ [ 'a', 2 ], [ 'b', 3 ] ]
myQueue.enqueue(['c', 4]);
myQueue.list(); // [ [ 'a', 2 ], [ 'b', 3 ], [ 'c', 4 ] ]
myQueue.enqueue(['d', 3]);
myQueue.list(); // [ [ 'a', 2 ], [ 'b', 3 ], [ 'd', 3 ], [ 'c', 4 ] ]
myQueue.enqueue(['e', 1]);
myQueue.list(); // [ [ 'e', 1 ], [ 'a', 2 ], [ 'b', 3 ], [ 'd', 3 ], [ 'c', 4 ] ]
console.log(myQueue.dequeue()); // [ 'e', 1 ]
console.log(myQueue.front()); // [ 'a', 2 ]
console.log(myQueue.length()); // 4
console.log(myQueue.isEmpty()); // false
function priorityQueue() { // Queue where item with highest priority get out first no matter when added
const content = [];
this.list = function() { console.log(content) }; // list all queue item
this.dequeue = function() { return content.shift() }; // get the first item in the queue, remove it from the queue
this.front = function() { return content[0] }; // get the first item in the queue, still let it be in the queue
this.length = function() { return content.length }; // get queue length
this.isEmpty = function() { return (content.length == 0) } // check if queue is empty
this.enqueue = function(element) { // put item to the queue with priority
if(this.isEmpty()) { return content.push(element) };
var added = false;
for(let i = 0; i < content.length; i ++) { // 1 is highest priority
if(element[1] < content[i][1]) {
content.splice(i, 0, element);
added = true;
break;
}
}
if(!added) { content.push(element) }
};
}
- Search item in tree
fast
O(log n), findmin
andmax
,inOrder
,preOrder
,postOrder
,levelOrder
preOrder
used when need to explore the roots before inspecting any leavespostOrder
used when need to explore all the leaves before any nodesinOrder
used when need to flatten the tree back into its sequence (bottom up)levelOrder
used when need to inspect nodes at height level
const util = require('util')
class Node {
constructor(data, left = null, right = null) {
this.data = data;
this.left = left;
this.right = right;
}
}
class binarySearchTree {
constructor() { this.root = null }
add(data) { // add node to binary tree
const node = this.root;
if(this.root === null) { this.root = new Node(data) } else {
searchLeaf(node);
function searchLeaf(node) {
if(data < node.data) {
if(node.left === null) { return node.left = new Node(data) }
searchLeaf(node.left);
} else if(data > node.data) {
if(node.right === null) { node.right = new Node(data) }
searchLeaf(node.right);
} else { return null }
}
}
}
findMin() { // find min value in binary tree
let current = this.root;
while(current.left !== null) { current = current.left }
return current.data;
}
findMax() { // find max value in binary tree
let current = this.root;
while(current.right !== null) { current = current.right }
return current.data;
}
exist(data) { // check if a data is in binary tree
let current = this.root;
while(current) {
if(current.data === data) { return true }
else if(current.data > data) { current = current.left }
else { current = current.right }
}
return false
}
remove(data) { // remove a node from binary tree
this.root = removeNode(this.root, data);
function removeNode(node, data) {
if(node == null) { return null } // root node/tree has nothing in it
if(data < node.data) { // not match, move to left
node.left = removeNode(node.left, data);
} else if(data > node.data) { // not match, move to right
node.right = removeNode(node.right, data);
} else { // found matching node
if(node.left === null && node.right === null) { return null } // no children
if(node.left === null) { return node.right } // has right child
if(node.right === null) { return node.left } // has left child
let rightThenFarthestLeft = node.right; // has both left, right child
while(rightThenFarthestLeft.left !== null) {
rightThenFarthestLeft = rightThenFarthestLeft.left;
}
node.data = rightThenFarthestLeft.data;
removeNode(node.right, rightThenFarthestLeft.data);
}
return node;
}
}
findMinHeight(node = this.root) { // get min height of tree
if (node == null) { return -1 }
let left = this.findMinHeight(node.left);
let right = this.findMinHeight(node.right);
if (left < right) { return left + 1 }
else { return right + 1 }
}
findMaxHeight(node = this.root) { // get max height of tree
if (node == null) { return -1 }
let left = this.findMaxHeight(node.left);
let right = this.findMaxHeight(node.right);
if (left < right) { return right + 1 }
else { return left + 1 }
}
isBalanced() { // check if tree is balanced
return (this.findMinHeight() >= this.findMaxHeight() - 1)
}
inOrder() { // traverse tree to array min -> max
if (this.root == null) { return null } else {
var result = new Array();
traverseInOrder(this.root);
return result;
function traverseInOrder(node) {
node.left && traverseInOrder(node.left);
result.push(node.data);
node.right && traverseInOrder(node.right);
}
};
}
bottomUp() { return this.inOrder() } // synonym of inOrder
topDown() { // traverse tree to array max -> min
if (this.root == null) {
return null;
} else {
var result = new Array();
traverseInOrder(this.root);
return result;
function traverseInOrder(node) {
node.right && traverseInOrder(node.right);
result.push(node.data);
node.left && traverseInOrder(node.left);
}
};
}
preOrder() {
if (this.root == null) { return null } else {
var result = new Array();
function traversePreOrder(node) {
result.push(node.data);
node.left && traversePreOrder(node.left);
node.right && traversePreOrder(node.right);
};
traversePreOrder(this.root);
return result;
};
}
postOrder() {
if (this.root == null) { return null } else {
var result = new Array();
function traversePostOrder(node) {
node.left && traversePostOrder(node.left);
node.right && traversePostOrder(node.right);
result.push(node.data);
};
traversePostOrder(this.root);
return result;
}
}
levelOrder() {
let result = [];
let Q = [];
if (this.root != null) {
Q.push(this.root);
while(Q.length > 0) {
let node = Q.shift();
result.push(node.data);
if (node.left != null) {
Q.push(node.left);
};
if (node.right != null) {
Q.push(node.right);
};
};
return result;
} else {
return null;
};
};
}
var myBt = new binarySearchTree();
myBt.add(4);
myBt.add(1);
myBt.add(8);
myBt.add(0.5);
myBt.add(2);
myBt.add(1.6);
myBt.add(2.2);
myBt.add(1.4);
myBt.add(1.7);
console.log(util.inspect(myBt, {showHidden: false, depth: null}));
// {
// root : {
// data : 4,
// left : {
// data : 1,
// left : {
// data: 0.5,
// left: null,
// right: null
// },
// right : {
// data : 2,
// left : {
// data : 1.6,
// left : {
// data : 1.4,
// left : null,
// right : null
// },
// right : {
// data : 1.7,
// left : null,
// right : null
// }
// },
// right: {
// data: 2.2,
// left: null,
// right: null
// }
// }
// },
// right : {
// data: 8,
// left: null,
// right: null
// }
// }
// }
console.log(myBt.findMin()); // 0.5
console.log(myBt.findMax()); // 8
console.log(myBt.exist(5)); // false
console.log(myBt.exist(2.2)); // true
console.log(myBt.findMinHeight()); // 1
console.log(myBt.findMaxHeight()); // 4
console.log(myBt.inOrder()); // [ 0.5, 1, 1.4, 1.6, 1.7, 2, 2.2, 4, 8 ]
console.log(myBt.bottomUp()); // [ 0.5, 1, 1.4, 1.6, 1.7, 2, 2.2, 4, 8 ]
console.log(myBt.isBalanced()); // false
console.log(myBt.topDown()); // [ 8, 4, 2.2, 2, 1.7, 1.6, 1.4, 1, 0.5 ]
console.log(myBt.preOrder()); // [ 4, 1, 0.5, 2, 1.6, 1.4, 1.7, 2.2, 8 ]
console.log(myBt.postOrder()); // [ 0.5, 1.4, 1.7, 1.6, 2.2, 2, 1, 8, 4 ]
console.log(myBt.levelOrder()); // [ 4, 1, 8, 0.5, 2, 1.6, 2.2, 1.4, 1.7 ]
console.log(myBt.remove(1));
console.log(util.inspect(myBt, {showHidden: false, depth: null}));
// {
// root : {
// data : 4,
// left : {
// data : 1.4,
// left : {
// data : 0.5,
// left : null,
// right : null
// },
// right : {
// data : 2,
// left : {
// data : 1.6,
// left : null,
// right : {
// data : 1.7,
// left : null,
// right : null
// }
// },
// right : {
// data : 2.2,
// left : null,
// right : null
// }
// }
// },
// right : {
// data : 8,
// left : null,
// right : null
// }
// }
// }
- The average time for each look up is not tied to the number of elements stored in the table
- Time complexity for
search
,insert
anddelete
isO(1)
- But time to add an element is longer, vary to table size
var hash = (string, max) => { // return hash number
var hash = 0;
for (var i = 0; i < string.length; i++) {
hash += string.charCodeAt(i);
}
return hash % max; // simple hash function return value from 0 to max
};
let HashTable = function() {
let storage = [];
const storageLimit = 4; // define hash table size
this.print = function() { console.log(storage) }
this.add = function(key, value) { // add [key, value] to hash table
var index = hash(key, storageLimit);
if (storage[index] === undefined) {
storage[index] = [
[key, value]
];
} else {
var inserted = false;
for (var i = 0; i < storage[index].length; i++) { // collision will be stored in same hash position
if (storage[index][i][0] === key) {
storage[index][i][1] = value;
inserted = true;
}
}
if (inserted === false) {
storage[index].push([key, value]);
}
}
};
this.remove = function(key) { // remove [key, value] from hash table
var index = hash(key, storageLimit);
if (storage[index].length === 1 && storage[index][0][0] === key) {
delete storage[index];
} else {
for (var i = 0; i < storage[index].length; i++) { // check if there is collision
if (storage[index][i][0] === key) {
delete storage[index][i];
}
}
}
};
this.lookup = function(key) { // lookup and return [key, value] in hash table
var index = hash(key, storageLimit);
if (storage[index] === undefined) {
return undefined;
} else {
for (var i = 0; i < storage[index].length; i++) { // check if there is collision
if (storage[index][i][0] === key) {
return storage[index][i][1];
}
}
}
};
};
console.log(hash('quincy', 10)) // 5
let ht = new HashTable();
ht.add('beau', 'person');
ht.add('fido', 'dog');
ht.add('rex', 'dinosour');
ht.add('tux', 'penguin')
console.log(ht.lookup('tux')) // penguin
ht.print(); // [ <1 empty item>,[ [ 'beau', 'person' ], [ 'tux', 'penguin' ] ], [ [ 'fido', 'dog' ] ], [ [ 'rex', 'dinosour' ] ] ]
- In Javascript, object is map
let myMap = function() {
this.collection = {};
this.count = 0;
this.size = function() { // return number of total item in map
return this.count;
};
this.set = function(key, value) { // add (key, value) pair to map
this.collection[key] = value;
this.count++;
};
this.has = function(key) { // check if map contains a given key
return (key in this.collection);
};
this.get = function(key) { // get value given key
return (key in this.collection) ? this.collection[key] : null;
};
this.delete = function(key) { // delete value given key
if (key in this.collection) {
delete this.collection[key];
this.count--;
}
};
this.values = function() { // list all values in map
let result = new Array();
for (let key of Object.keys(this.collection)) {
result.push(this.collection[key]);
};
return (result.length > 0) ? result : null;
};
this.clear = function() { // delete all key value pairs in map
this.collection = {};
this.count = 0;
};
this.log = function() { // log the whole map out (for learning purpose)
console.log(this.collection);
}
};
let map = new myMap();
map.set('arms', 2);
map.set('fingers', 10);
map.set('eyes', 2);
map.set('belley button', 1);
console.log(map.log()); // { arms: 2, fingers: 10, eyes: 2, 'belley button': 1 }
console.log(map.get('fingers')); // 10
console.log(map.size()); // 4
console.log(map.values()); // [ 2, 10, 2, 1 ]
let map2 = new myMap();
map2.set('hands', 'right');
console.log(map2.values()); // [ 'right' ]
console.log(map2.has('hands')); // true
let keyObj = {},
keyFunc = function() {};
map2.set('hello', 'string value');
map2.set(keyObj, 'obj value');
map2.set(keyFunc, 'func value');
map2.set(NaN, 'NaN value')
console.log(map2.size); // [Function]
console.log(map2.get('hello')); // string value
console.log(map2.get(keyObj)); // obj value
console.log(map2.get(keyFunc)); // func value
console.log(map2.get(NaN)); // NaN value
const util = require('util')
function LinkedList() {
let length = 0;
let head = null;
let Node = function(element) { // define a node in the linked list
this.element = element;
this.next = null;
}
this.size = function() { return length } // get linked list length
this.head = function() { return head } // return head of the linked list
this.add = function(element) { // add element to linked list
let node = new Node(element);
if(head === null) { head = node } else {
let currentNode = head;
while(currentNode.next) {
currentNode = currentNode.next;
}
currentNode.next = node;
}
length++;
}
this.remove = function(element) { // remove an element from the linked list by its data
let currentNode = head;
let previousNode;
if(currentNode.element === element) {
head = currentNode.next;
} else {
while(currentNode.element !== element) {
previousNode = currentNode;
currentNode = currentNode.next;
}
previousNode.next = currentNode.next;
}
length--;
}
this.isEmpty = function() { return length == 0 } // check if linked list is empty
this.indexOf = function(element) { // get index of an element
let currentNode = head;
let index = 0; // function return -1 if element is not in the linked list
if(currentNode.element === element) {
return index;
} else {
while(currentNode.element !== element) {
currentNode = currentNode.next;
index++;
}
if(currentNode !== null) { return index } else { return -1 }
}
}
this.elementAt = function(index) { // get element by index
let currentNode = head;
let currentIndex = 0;
while(currentNode && index !== currentIndex) {
currentNode = currentNode.next;
currentIndex++;
}
if(currentNode) { return currentNode.element } else {
return -1;
}
}
this.addAt = function(index, element) { // add element at given index
let node = new Node(element);
let currentNode = head;
let currentIndex = 0;
let previousNode;
if(index > length - 1) { return 'index out of range!' } else {
if(index === 0) {
node.next = head;
head = node;
length++;
return
}
while(index !== currentIndex) {
previousNode = currentNode;
currentNode = currentNode.next;
currentIndex++
}
previousNode.next = node;
node.next = currentNode;
length++;
}
}
this.removeAt = function(index) { // remove element at index
let currentNode = head;
let currentIndex = 0;
let previousNode;
if(index > length - 1) { return 'index out of range!' } else {
if(index === 0) {
head = head.next;
length--;
return
}
while(index !== currentIndex) {
previousNode = currentNode;
currentNode = currentNode.next;
currentIndex++
}
previousNode.next = currentNode.next;
length--;
}
}
}
let myll = new LinkedList();
console.log(myll.size()); // 0
console.log(myll.head()); // null
myll.add(12);
console.log(myll.size()); // 1
myll.add(34);
console.log(myll.size()); // 2
console.log(myll.head()); // Node {element:12,next:Node{ element: 34, next: null } }
myll.add(76);
myll.add(43);
console.log(myll.size()); // 4
myll.remove(76);
console.log(myll.size()); // 3
console.log(myll.indexOf(34)); // 1
console.log(myll.elementAt(4)); // -1
console.log(myll.head()); // Node {element:12,next:Node{element:34,next Node{element:43,next:null}}}
console.log(myll.addAt(7, 'lalala')); // index out of range!
console.log(myll.addAt(0, 'lalala')); // undefined
console.log(myll.addAt(2, 'lolo')); // undefined
console.log(util.inspect(myll.head(), {showHidden: false, depth: null})); // Node {element: 'lalala',next: Node {element: 12,next:Node {element: 'lolo',next: Node { element: 34, next: Node { element: 43, next: null } } } } }
console.log(myll.removeAt(2)); // undefined
console.log(util.inspect(myll.head(), {showHidden: false, depth: null})); // Node {element: 'lalala',next: Node {element: 12,next: Node { element: 34, next: Node { element: 43, next: null } } } }
- An use case is to validate if a word in the dictionary
const Node = function() {
this.keys = new Map();
this.end = false;
this.setEnd = function() { this.end = true };
this.isEnd = function() { return this.end };
}
let Trie = function() {
this.root = new Node();
this.add = function(input, node = this.root) { // add word to trie
if (input.length == 0) {
node.setEnd();
return;
} else if (!node.keys.has(input[0])) {
node.keys.set(input[0], new Node());
return this.add(input.substr(1), node.keys.get(input[0]));
} else {
return this.add(input.substr(1), node.keys.get(input[0]));
};
};
this.isWord = function(word) { // check if word is in trie
let node = this.root;
while (word.length > 1) {
if (!node.keys.has(word[0])) {
return false;
} else {
node = node.keys.get(word[0]);
word = word.substr(1);
};
};
return (node.keys.has(word) && node.keys.get(word).isEnd()) ?
true : false;
};
this.print = function() {
let words = new Array();
let search = function(node, string) {
if (node.keys.size != 0) {
for (let letter of node.keys.keys()) {
search(node.keys.get(letter), string.concat(letter));
};
if (node.isEnd()) {
words.push(string);
};
} else {
string.length > 0 ? words.push(string) : undefined;
return;
};
};
search(this.root, new String());
return words.length > 0 ? words : mo;
};
};
myTrie = new Trie()
myTrie.add('ball');
myTrie.add('bat');
myTrie.add('doll');
myTrie.add('dork');
myTrie.add('do');
myTrie.add('dorm')
myTrie.add('send')
myTrie.add('sense')
console.log(myTrie.isWord('doll')); // true
console.log(myTrie.isWord('dor')) // false
console.log(myTrie.isWord('dorf')) // false
console.log(myTrie.print()); // [ 'ball', 'bat', 'doll', 'dork', 'dorm', 'do', 'send', 'sense' ]
- Partially ordered binary tree which satisfies the heap property.
- Heap property indicates a specific relationship between parent and child node
- Max heap: all parent nodes are greater than or equal to child nodes
- Min heap: all child nodes are greater than or equal to parent nodes
- The order between child nodes in a same level does not matter
- Binary heaps are complete binary trees: all levels of the tree are fully filled (if the last level partially filled, it's filled from left to right)
- In array representation, index start at
1
so index0
isnull
- Node at index
k
has left child node at index2*k
and right child node at index2*k+1
- Node at index
l
has parent node at indexroundDown(l/2)
- Online heap and array representation
- Online interacting heap creation
- Heap valuable feature: min and max sorting. With the worst case performance of
O(nlogn)
const myMinHeap = new MinHeap();
myMinHeap.insert(8);
myMinHeap.insert(32);
myMinHeap.insert(31);
myMinHeap.insert(5);
myMinHeap.print(); // [ null, 5, 8, 31, 32 ]
myMinHeap.insert(7);
myMinHeap.insert(100);
myMinHeap.print(); // [ null, 5, 7, 31, 32, 8, 100 ]
console.log(myMinHeap.sort()); // [ 5, 7, 8, 31, 32, 100 ]
myMinHeap.print(); // [ null ]
const myMaxHeap = new MaxHeap();
myMaxHeap.insert(8);
myMaxHeap.insert(32);
myMaxHeap.insert(31);
myMaxHeap.insert(5);
myMaxHeap.print(); // [ null, 32, 8, 31, 5 ]
myMaxHeap.insert(7);
myMaxHeap.insert(100);
myMaxHeap.print(); // [ null, 100, 8, 32, 5, 7, 31 ]
console.log(myMaxHeap.sort()); // [ 100, 32, 31, 8, 7, 5 ]
myMaxHeap.print(); // [ null ]
function MinHeap() {
let heap = [null];
this.print = function() { console.log(heap) } // print the heap array representation
this.insert = function(num) { // insert a number to heap
heap.push(num);
if (heap.length > 2) {
let idx = heap.length - 1;
while (heap[idx] < heap[Math.floor(idx/2)]) {
if (idx >= 1) {
[heap[Math.floor(idx/2)], heap[idx]] = [heap[idx], heap[Math.floor(idx/2)]];
if (Math.floor(idx/2) > 1) {
idx = Math.floor(idx/2);
} else {
break;
};
};
};
};
};
this.remove = function() { // remove a number from heap
let smallest = heap[1];
if (heap.length > 2) {
heap[1] = heap[heap.length - 1];
heap.splice(heap.length - 1);
if (heap.length == 3) {
if (heap[1] > heap[2]) {
[heap[1], heap[2]] = [heap[2], heap[1]];
};
return smallest;
};
let i = 1;
let left = 2 * i;
let right = 2 * i + 1;
while (heap[i] >= heap[left] || heap[i] >= heap[right]) {
if (heap[left] < heap[right]) {
[heap[i], heap[left]] = [heap[left], heap[i]];
i = 2 * i
} else {
[heap[i], heap[right]] = [heap[right], heap[i]];
i = 2 * i + 1;
};
left = 2 * i;
right = 2 * i + 1;
if (heap[left] == undefined || heap[right] == undefined) {
break;
};
};
} else if (heap.length == 2) {
heap.splice(1, 1);
} else {
return null;
};
return smallest;
};
this.sort = function() {
let result = new Array();
while (heap.length > 1) {
result.push(this.remove());
};
return result;
};
};
function MaxHeap() {
let heap = [null];
this.print = () => { console.log(heap) };
this.insert = function(num) {
heap.push(num);
if (heap.length > 2) {
let idx = heap.length - 1;
while (heap[idx] > heap[Math.floor(idx/2)]) {
if (idx >= 1) {
[heap[Math.floor(idx/2)], heap[idx]] = [heap[idx], heap[Math.floor(idx/2)]];
if (Math.floor(idx/2) > 1) {
idx = Math.floor(idx/2);
} else {
break;
};
};
};
};
};
this.remove = function() {
let smallest = heap[1];
if (heap.length > 2) {
heap[1] = heap[heap.length - 1];
heap.splice(heap.length - 1);
if (heap.length == 3) {
if (heap[1] < heap[2]) {
[heap[1], heap[2]] = [heap[2], heap[1]];
};
return smallest;
};
let i = 1;
let left = 2 * i;
let right = 2 * i + 1;
while (heap[i] <= heap[left] || heap[i] <= heap[right]) {
if (heap[left] > heap[right]) {
[heap[i], heap[left]] = [heap[left], heap[i]];
i = 2 * i
} else {
[heap[i], heap[right]] = [heap[right], heap[i]];
i = 2 * i + 1;
};
left = 2 * i;
right = 2 * i + 1;
if (heap[left] == undefined || heap[right] == undefined) {
break;
};
};
} else if (heap.length == 2) {
heap.splice(1, 1);
} else {
return null;
};
return smallest;
};
this.sort = function() {
let result = new Array();
while (heap.length > 1) {
result.push(this.remove());
};
return result;
};
};
- Graph is collection of things and relationship between them
- Data in graph is called node(vertices) and connection between nodes called edges
- There are two major types of graphs:
directed
andundirected
directed
graph is graph with direction (e.g. social network)undirected
graph is graph with direction (e.g. webpage links)- Representation of graph:
adjacency list
,adjacency matrix
can represent direction,incidence matrix
(row are nodes, column are edges)
const adjacencyList = [
[ 1, 2 ], // node 1
[ 0 ], // node 2
[ 2 ], // node 3
];
const adjacencyMatrix = [
[ 0, 1, 1 ], // node 1
[ 0, 0, 0 ], // node 2
[ 1, 0, 0 ], // node 3
]
const incidenceMatrix = [
[ 0, 1, -1 ],
[ 0, 0, 0 ],
[ -1, 0, 0 ],
]
var exBFSGraph = [ // adjagency representation of a graph
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0],
[1, 1, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 0, 0]
];
console.log(bfs(exBFSGraph, 1)); // distance between node 1 to other nodes
// { '0': 2, '1': 0, '2': 1, '3': 3, '4': Infinity }
function bfs(graph, root) {
var nodesLen = {};
for (var i = 0; i < graph.length; i++) {
nodesLen[i] = Infinity;
}
nodesLen[root] = 0;
var queue = [root];
var current;
while (queue.length != 0) {
current = queue.shift();
var curConnected = graph[current];
var neighborIdx = [];
var idx = curConnected.indexOf(1);
while (idx != -1) {
neighborIdx.push(idx);
idx = curConnected.indexOf(1, idx + 1);
}
for (var j = 0; j < neighborIdx.length; j++) {
if (nodesLen[neighborIdx[j]] == Infinity) {
nodesLen[neighborIdx[j]] = nodesLen[current] + 1;
queue.push(neighborIdx[j]);
}
}
}
return nodesLen;
};
- Simplify analysis of algoritm's efficiency
- Complexity in term of input size, N
- Machine independent, basic computer step
- Analyze time and space of worst case scenario
- Ignore constant
5n -> O(n)
- Cetain terms dominate others
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(2^n) < O(n!)
x = 5 + 15 * 20; // O(1) independent of input size (input are constants)
x = 5 + 15 * 20;
y = 9 - 6;
console.log(x + y) // total time = O(1) + O(1) + O(1) = 3O(1) = O(1) (constants are dropped)
for(let i = 0; i < N; i++) {
console.log(i)
} // total time = N * O(1) = O(N) (input now is N, not constant)
y = 32 + 6
for(let i = 0; i < N; i++) {
console.log(i)
} // total time = O(1) + N * O(1) = O(N) (low order term is dropped)
y = 32 + 6
for(let i = 0; i < N; i++) {
console.log(i)
}
for(let i = 0; i < N; i++) {
for(let j = 0; i < N; i++) {
console.log(j);
}
} // total time = max(O(1), O(N), O(N^2)) = O(N^2) (O(N^2) dominates)
if(x > 0) {
// O(1)
} else if(x < 0) {
// O(logn)
} else {
// O(n^2)
} // total time = O(n^2) (big O considers worst case scenario)