├── .DS_Store ├── modal ├── modal.js ├── modal.html └── modal.css ├── accordion ├── index.js ├── index.css └── index.html ├── star ├── style.css ├── index.html └── main.js ├── clock ├── index.html ├── index.css └── index.js ├── debounce ├── index.js └── index.html ├── query-selector-all └── index.js ├── throttle ├── throttle.js └── index.html ├── function-chain └── index.js ├── memoize └── memoize.js ├── tic-tac-toe ├── index.css ├── index.html └── index.js ├── data-structures ├── trie │ └── trie.js ├── disjoint-set │ └── unionFind.js └── heap │ ├── maxHeap.js │ └── minHeap.js ├── carousel ├── index.html ├── index.js └── index.css ├── custom-promise └── index.js ├── popover └── popover.html └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivakumarsc/frontend-practice-questions/HEAD/.DS_Store -------------------------------------------------------------------------------- /modal/modal.js: -------------------------------------------------------------------------------- 1 | function showModal() { 2 | let modal = document.getElementsByClassName('modal-wrapper')[0]; 3 | 4 | modal.style.display = 'block'; 5 | } 6 | 7 | function hideModal() { 8 | let modal = document.getElementsByClassName('modal-wrapper')[0]; 9 | 10 | modal.style.display = 'none'; 11 | } -------------------------------------------------------------------------------- /accordion/index.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var wrapper = document.getElementsByClassName('accordion-wrapper')[0]; 3 | console.log(wrapper) 4 | 5 | wrapper.addEventListener("click", function(e) { 6 | console.log('*************', e.target.parentElement); 7 | e.target.parentElement.classList.toggle('active'); 8 | }); 9 | 10 | })(); -------------------------------------------------------------------------------- /star/style.css: -------------------------------------------------------------------------------- 1 | rating-container * { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | 6 | ul { 7 | list-style: none; 8 | display: flex; 9 | } 10 | 11 | ul li { 12 | font-size: 40px; 13 | color: grey; 14 | cursor: pointer; 15 | } 16 | 17 | ul li.active, 18 | ul li:hover { 19 | color: gold; 20 | transform: scale(1.2); 21 | } 22 | -------------------------------------------------------------------------------- /clock/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 | 12 |
13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /star/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Star Rating

9 |
10 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /debounce/index.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function (){ 2 | const textBox = document.getElementById("text-box"); 3 | let timerId = null; 4 | 5 | function debounce(callback, delay) { 6 | if (timerId) { 7 | clearTimeout(timerId); 8 | } 9 | 10 | timerId = setTimeout(() => { 11 | callback(arguments[2]) 12 | }, delay); 13 | } 14 | 15 | function printVal(e) { 16 | console.log('********************', e.target.value); 17 | } 18 | 19 | textBox.addEventListener("keypress", function(e){ 20 | debounce(printVal, 200, e); 21 | }); 22 | 23 | 24 | }); -------------------------------------------------------------------------------- /debounce/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | 24 |
25 | 26 |
27 | 28 | -------------------------------------------------------------------------------- /query-selector-all/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement document.querySelectorAll() in JS 3 | */ 4 | 5 | document.prototype.myQuerySelectorAll = function(selector) { 6 | let result = []; 7 | 8 | // Selector will be compared as a regex 9 | function isMatch(node) { 10 | return node.matches(selector); 11 | } 12 | 13 | function traverse(node) { 14 | if (!node) { 15 | return; 16 | } 17 | if (isMatch(node)) { 18 | result.add(node); 19 | } 20 | for (let childNode of node.children) { 21 | traverse(childNode); 22 | } 23 | } 24 | 25 | // To travserse from root HTML element 26 | traverse(this.documentElement); 27 | 28 | return result; 29 | } -------------------------------------------------------------------------------- /modal/modal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 21 |
22 | 23 | -------------------------------------------------------------------------------- /throttle/throttle.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function() { 2 | const wrapper = document.getElementsByClassName('wrapper')[0]; 3 | let timerId = null; 4 | 5 | function throttle(callback, delay) { 6 | if (timerId) { 7 | return; 8 | } 9 | 10 | timerId = setTimeout(function() { 11 | callback(); 12 | 13 | clearTimeout(timerId); 14 | // After clearTimeout also, we will have timerId. So, explicitly making it as null. 15 | timerId = null; 16 | }, delay); 17 | } 18 | 19 | function handleScroll() { 20 | console.log('*****************************'); 21 | } 22 | 23 | 24 | wrapper.addEventListener("scroll", function() { 25 | throttle(handleScroll, 500); 26 | }); 27 | }); -------------------------------------------------------------------------------- /accordion/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | background-color: pink; 9 | } 10 | 11 | .accordion-wrapper { 12 | margin: 50px auto; 13 | width: 500px; 14 | } 15 | 16 | .accordion { 17 | border: 1px solid #000; 18 | border-radius: 4px; 19 | background: #fff; 20 | padding: 10px; 21 | margin: 10px; 22 | } 23 | 24 | .accordion.active .accordion-body { 25 | max-height: 100px; 26 | overflow: auto; 27 | } 28 | 29 | .accordion-title { 30 | border-bottom: 1px solid #000; 31 | cursor: pointer; 32 | } 33 | 34 | .accordion-body { 35 | max-height: 0; 36 | overflow: hidden; 37 | transition: max-height 0.5s ease-out; 38 | } 39 | 40 | .content { 41 | padding: 10px; 42 | } -------------------------------------------------------------------------------- /function-chain/index.js: -------------------------------------------------------------------------------- 1 | ` 2 | Implement a function chain like below snippet 3 | const developer = new DeveloperBuilder('John') 4 | .addSkill('ES6') 5 | .addSkill('TypeScript') 6 | .setFramework('React'); 7 | ` 8 | 9 | class DeveloperBuilder { 10 | constructor(name) { 11 | this.name = name; 12 | this.skills = []; 13 | this.framework = null; 14 | } 15 | 16 | addSkill(skill) { 17 | this.skills.push(skill); 18 | 19 | return this; 20 | } 21 | 22 | setFramework(framework) { 23 | this.framework = framework; 24 | 25 | return this; 26 | } 27 | } 28 | 29 | const developer = new DeveloperBuilder('John') 30 | .addSkill('ES6') 31 | .addSkill('TypeScript') 32 | .setFramework('React'); 33 | 34 | console.log(developer); -------------------------------------------------------------------------------- /memoize/memoize.js: -------------------------------------------------------------------------------- 1 | function multiplyBy2(x) { 2 | return x * 2; 3 | } 4 | 5 | function memoize(func) { 6 | let map = new Map(); 7 | 8 | return function(x) { 9 | if (map.has(x)) { 10 | return `Returned from memory ${map.get(x)}`; 11 | } 12 | 13 | let result = func(x); 14 | map.set(x, result); 15 | 16 | return `Returned from function ${result}`; 17 | } 18 | } 19 | 20 | let wrapper = memoize(multiplyBy2); 21 | console.log(wrapper(2)); // Returned from function 4 22 | console.log(wrapper(4)); // Returned from function 8 23 | console.log(wrapper(5)); // Returned from function 10 24 | console.log(wrapper(2)); // Returned from memory 4 25 | console.log(wrapper(2)); // Returned from memory 4 26 | console.log(wrapper(6)); // Returned from function 12 -------------------------------------------------------------------------------- /throttle/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 27 | 28 | 29 |
30 |

31 |

32 |

33 |

34 |

35 |
36 | 37 | -------------------------------------------------------------------------------- /tic-tac-toe/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | background-color: beige; 9 | } 10 | 11 | .container { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | } 16 | 17 | .board { 18 | width: 300px; 19 | display: grid; 20 | grid-template-columns: repeat(3, auto); 21 | } 22 | 23 | .cell { 24 | width: 100px; 25 | height: 100px; 26 | font-size: 20px; 27 | text-align: center; 28 | background-color: #fff; 29 | border: 1px solid #000; 30 | cursor: pointer; 31 | } 32 | 33 | .game-result { 34 | text-align: center; 35 | display: none; 36 | font-size: 18px; 37 | } 38 | 39 | .restart-game { 40 | padding: 20px; 41 | margin: 10px; 42 | background: #fff; 43 | cursor: pointer; 44 | font-size: 14px; 45 | } -------------------------------------------------------------------------------- /clock/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | background-color: powderblue; 9 | } 10 | 11 | .container { 12 | width: 70%; 13 | margin: 0 auto; 14 | } 15 | 16 | .clock .outer-circle { 17 | border: 5px solid #000; 18 | border-radius: 50%; 19 | width: 200px; 20 | height: 200px; 21 | margin: 100px auto; 22 | position: relative; 23 | } 24 | 25 | .numbers { 26 | position: absolute; 27 | left: 50%; 28 | top: 50%; 29 | height: 100%; 30 | width: 10px; 31 | } 32 | 33 | .hand { 34 | position: absolute; 35 | transform: translateX(-50%) rotate(90deg); 36 | background-color: #000; 37 | bottom: 50%; 38 | left: 50%; 39 | transform-origin: bottom; 40 | } 41 | 42 | .second { 43 | width: 2px; 44 | height: 40%; 45 | } 46 | 47 | .minute { 48 | width: 4px; 49 | height: 40%; 50 | } 51 | 52 | .hour { 53 | width: 6px; 54 | height: 30%; 55 | } -------------------------------------------------------------------------------- /tic-tac-toe/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /modal/modal.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | background-color: aquamarine; 9 | } 10 | 11 | .wrapper { 12 | height: 100%; 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | } 17 | 18 | .wrapper button { 19 | border-radius: 4px; 20 | padding: 8px; 21 | text-align: center; 22 | background-color: #fff; 23 | color: #000; 24 | cursor: pointer; 25 | } 26 | 27 | .modal-wrapper { 28 | position: fixed; 29 | top: 0; 30 | left: 0; 31 | width: 100%; 32 | height: 100%; 33 | background-color: rgba(0, 0, 0, 0.4); 34 | padding-top: 100px; 35 | display: none; 36 | } 37 | 38 | .modal { 39 | background: #fff; 40 | width: 500px; 41 | height: 300px; 42 | margin: auto; 43 | } 44 | 45 | .modal-header { 46 | padding: 20px; 47 | text-align: center; 48 | position: relative; 49 | } 50 | 51 | .close { 52 | position: absolute; 53 | top: -30px; 54 | right: -30px; 55 | font-size: 40px; 56 | cursor: pointer; 57 | } -------------------------------------------------------------------------------- /data-structures/trie/trie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement trie-prefix tree 3 | * https://leetcode.com/problems/implement-trie-prefix-tree/solution/ 4 | */ 5 | 6 | class TrieNode { 7 | constructor() { 8 | this.edges = new Map(); 9 | this.isWordEnd = false; 10 | } 11 | } 12 | 13 | class Trie { 14 | constructor() { 15 | this.root = new TrieNode(); 16 | } 17 | 18 | insert(word) { 19 | let node = this.root; 20 | for(let w of word) { 21 | if (!node.edges.has(w)) { 22 | node.edges.set(w, new TrieNode()); 23 | } 24 | node = node.edges.get(w); 25 | } 26 | node.isWordEnd = true; 27 | } 28 | 29 | traverse(word) { 30 | let node = this.root; 31 | for(let w of word) { 32 | if (!node.edges.has(w)) { 33 | return null; 34 | } 35 | node = node.edges.get(w); 36 | } 37 | return node; 38 | } 39 | 40 | search(word) { 41 | let node = this.traverse(word); 42 | return node !== null && node.isWordEnd; 43 | } 44 | 45 | startsWith(word) { 46 | let node = this.traverse(word); 47 | return node !== null; 48 | } 49 | } -------------------------------------------------------------------------------- /carousel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 33 | 34 | -------------------------------------------------------------------------------- /carousel/index.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function() { 2 | console.log('****** loaded ****'); 3 | 4 | let currentIndex = 0; 5 | const prevButton = document.getElementsByClassName('prev')[0]; 6 | const nextButton = document.getElementsByClassName('next')[0]; 7 | const carouselWrapper = document.getElementsByClassName('carousel-item-wrapper')[0]; 8 | const totalItems = carouselWrapper.children.length; 9 | const percent = 100/totalItems; 10 | console.log(totalItems); 11 | 12 | function handlePrevClick() { 13 | slideTo(currentIndex - 1); 14 | } 15 | 16 | function handleNextClick() { 17 | slideTo(currentIndex + 1); 18 | } 19 | 20 | function slideTo(index) { 21 | let slideIndex = index; 22 | 23 | if (index < 0) { 24 | slideIndex = totalItems - 1; 25 | } else if(index >= carouselWrapper.children.length) { 26 | slideIndex = 0; 27 | } 28 | carouselWrapper.style.transform = `translate(-${slideIndex*percent}%, 0)`; 29 | currentIndex = slideIndex; 30 | } 31 | 32 | prevButton.addEventListener("click", handlePrevClick); 33 | nextButton.addEventListener("click", handleNextClick); 34 | 35 | }); -------------------------------------------------------------------------------- /carousel/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | .carousel-section { 8 | width: 70%; 9 | margin: 0 auto; 10 | } 11 | 12 | .carousel-wrapper { 13 | width: 100%; 14 | position: relative; 15 | overflow: hidden; 16 | } 17 | 18 | .carousel { 19 | width: 500%; 20 | } 21 | 22 | .carousel-item-wrapper { 23 | transition: all 800ms; 24 | } 25 | 26 | .carousel-item { 27 | width: 20%; 28 | float: left; 29 | list-style: none; 30 | } 31 | .carousel-item .item { 32 | width: 100%; 33 | height: 500px; 34 | } 35 | 36 | .item1 { 37 | background-color: red; 38 | } 39 | 40 | .item2 { 41 | background-color: black; 42 | } 43 | 44 | .item3 { 45 | background-color: green; 46 | } 47 | 48 | .item4 { 49 | background-color: orange; 50 | } 51 | 52 | .item5 { 53 | background-color: blue; 54 | } 55 | 56 | .action-btn { 57 | position: absolute; 58 | top: 50%; 59 | padding: 20px; 60 | background-color: pink; 61 | cursor: pointer; 62 | z-index: 1; 63 | border: 0; 64 | transform: translateY(-50%); 65 | font-size: 20px; 66 | } 67 | 68 | .prev { 69 | left: 0; 70 | } 71 | 72 | .next { 73 | right: 0; 74 | } -------------------------------------------------------------------------------- /data-structures/disjoint-set/unionFind.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A QuickFind Implementation 3 | * 4 | * Time Complexity: 5 | * find - O(1) 6 | * union - O(n) 7 | * completed - O(1) 8 | */ 9 | 10 | class UnionFind { 11 | constructor(size) { 12 | this.root = Array.from({ length: size }, (_, i) => i); 13 | } 14 | 15 | find(x) { 16 | return this.root[x]; 17 | } 18 | 19 | union(x, y) { 20 | let rootX = this.find(x); 21 | let rootY = this.find(y); 22 | 23 | if (rootX !== rootY) { 24 | for(let i=0; i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | Accordion 1 13 |
Hello
14 |
15 |
16 | Accordion 2 17 |
Hello 2
18 |
19 |
20 | 21 |
22 |
23 |
Title 1
24 |
25 |
Body 1
26 |
27 |
28 |
29 |
Title 2
30 |
31 |
Body 2
32 |
33 |
34 |
35 |
Title 3
36 |
37 |
Body 3
38 |
39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /star/main.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function() { 2 | const stars = document.getElementsByTagName("li"); 3 | const starContainer = document.getElementsByClassName("rating-container")[0]; 4 | let active = -1; 5 | 6 | starContainer.addEventListener("click", function(el) { 7 | if (el.target.dataset.id) { 8 | let currentIndex = el.target.dataset.id; 9 | active = currentIndex - 1; 10 | 11 | for(let i=0; i {}; 5 | 6 | this.onResolve = this.onResolve.bind(this); 7 | this.onReject = this.onReject.bind(this); 8 | 9 | callback(this.onResolve, this.onReject); 10 | } 11 | 12 | then(onResolve) { 13 | this.promiseChain.push(onResolve); 14 | 15 | return this; 16 | } 17 | 18 | catch(error) { 19 | this.handleError = error; 20 | 21 | return this; 22 | } 23 | 24 | onResolve(value) { 25 | let previousValue = value; 26 | try { 27 | this.promiseChain.forEach(nextFunction => { 28 | previousValue = nextFunction(previousValue); 29 | }); 30 | } catch(e) { 31 | this.onReject(e); 32 | } 33 | } 34 | 35 | onReject(error) { 36 | this.handleError(error); 37 | } 38 | } 39 | 40 | function fakePromise() { 41 | return new CustomPromise((resolve, reject) => { 42 | setTimeout(() => { 43 | const sample = Math.round((Math.random() * 2)); 44 | if (sample % 2 === 0) { 45 | resolve(sample); 46 | } else { 47 | reject(sample); 48 | } 49 | }, 1000); 50 | }); 51 | } 52 | 53 | fakePromise().then((res) => { 54 | console.log(`Response 1 ${res}`); 55 | return res; 56 | }).then((res) => { 57 | console.log(`Response 2 ${res}`); 58 | }).catch((err) => { 59 | console.log(`Error ${err}`); 60 | }); -------------------------------------------------------------------------------- /popover/popover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 45 | 46 | 47 |
48 |
49 | 50 |
51 |
52 |

I am a pop over title

53 |
I am a pop over content
54 |
55 |
56 |
57 |
58 |
59 | 60 | -------------------------------------------------------------------------------- /clock/index.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function() { 2 | let circle = document.getElementsByClassName('outer-circle')[0]; 3 | 4 | function createNumbers() { 5 | let fragment = document.createDocumentFragment(); 6 | let deg = 0; 7 | for(let i=0; i<12; i+=1) { 8 | let div = document.createElement('div'); 9 | div.classList.add('numbers'); 10 | deg += 30; 11 | div.style.transform = `translate(-50%, -50%)rotate(${deg}deg)`; 12 | 13 | let num = document.createElement('div'); 14 | num.textContent = i + 1; 15 | num.style.transform = `rotate(-${deg}deg)`; 16 | div.appendChild(num); 17 | fragment.appendChild(div); 18 | } 19 | circle.appendChild(fragment); 20 | } 21 | 22 | function drawHands(handType) { 23 | let hand = document.createElement('div'); 24 | hand.classList.add('hand', handType); 25 | circle.appendChild(hand); 26 | } 27 | 28 | function setCurrentTime() { 29 | let date = new Date(); 30 | let sec = date.getSeconds() / 60; 31 | let min = (date.getMinutes() + sec) / 60; 32 | let hour = (date.getHours() + min) / 12; 33 | 34 | let hourHand = document.getElementsByClassName('hour')[0]; 35 | let minuteHand = document.getElementsByClassName('minute')[0]; 36 | let secondHand = document.getElementsByClassName('second')[0]; 37 | 38 | console.log(hour, min, sec); 39 | 40 | hourHand.style.transform = "rotate(" + (hour * 360) + "deg)"; 41 | minuteHand.style.transform = "rotate(" + (min * 360) + "deg)"; 42 | secondHand.style.transform = "rotate(" + (sec * 360) + "deg)"; 43 | } 44 | 45 | createNumbers(); 46 | drawHands('second'); 47 | drawHands('minute'); 48 | drawHands('hour'); 49 | setCurrentTime(); 50 | setInterval(setCurrentTime, 1000); 51 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # frontend-practice-questions 2 | Created this repo for practicing some common frontend interview questions. The answers given are **functional but may not follow best practices.** 3 | 4 | ## Common Interview Questions 5 | 6 | * [Accordion](https://github.com/scsiva1991/frontend-practice-questions/tree/master/accordion) 7 | * [Carousel](https://github.com/scsiva1991/frontend-practice-questions/tree/master/carousel) 8 | * [Analog clock](https://github.com/scsiva1991/frontend-practice-questions/tree/master/clock) 9 | * [Memoize](https://github.com/scsiva1991/frontend-practice-questions/tree/master/memoize) 10 | * [Popover](https://github.com/scsiva1991/frontend-practice-questions/tree/master/popover) 11 | * [Star Rating](https://github.com/scsiva1991/frontend-practice-questions/tree/master/star) 12 | * [Debounce](https://github.com/scsiva1991/frontend-practice-questions/tree/master/debounce) 13 | * [Throtlle](https://github.com/scsiva1991/frontend-practice-questions/tree/master/throttle) 14 | * [Custom Promise](https://github.com/scsiva1991/frontend-practice-questions/tree/master/custom-promise) 15 | * [Modal](https://github.com/scsiva1991/frontend-practice-questions/tree/master/modal) 16 | * [Tic-Tac-Toe](https://github.com/scsiva1991/frontend-practice-questions/tree/master/tic-tac-toe) 17 | * [Function chaining](https://github.com/scsiva1991/frontend-practice-questions/tree/master/function-chain) 18 | * [Implement querySelector All](https://github.com/scsiva1991/frontend-practice-questions/tree/master/query-selector-all) 19 | 20 | ## Data Structures 21 | 22 | * [Trie](https://github.com/scsiva1991/frontend-practice-questions/tree/master/data-structures/trie) 23 | * [Heap](https://github.com/scsiva1991/frontend-practice-questions/tree/master/data-structures/heap) 24 | * [Disjoint Set](https://github.com/scsiva1991/frontend-practice-questions/tree/master/data-structures/disjoint-set) -------------------------------------------------------------------------------- /data-structures/heap/maxHeap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * MaxHeap Implementation 3 | * 4 | * Time Complexity 5 | * Insertion - O(log n) 6 | * Deletion - O(log n) 7 | * To get min value - O(1) 8 | */ 9 | class MaxHeap { 10 | constructor(heapSize) { 11 | this.maxHeapSize = heapSize; 12 | this.maxHeap = Array.from({ length: heapSize + 1}, () => 0); 13 | this.currentSize = 0; 14 | } 15 | 16 | add(num) { 17 | this.currentSize += 1; 18 | 19 | if (this.currentSize > this.maxHeapSize) { 20 | this.currentSize -= 1; 21 | throw new Error("Heap size exceeds!!"); 22 | } 23 | 24 | this.maxHeap[this.currentSize] = num; 25 | let index = this.currentSize; 26 | let parentIndex = Math.floor(index / 2); 27 | 28 | while(index > 1 && this.maxHeap[index] > this.maxHeap[parentIndex]) { 29 | [this.maxHeap[index], this.maxHeap[parentIndex]] = [this.maxHeap[parentIndex], this.maxHeap[index]]; 30 | index = parentIndex; 31 | parentIndex = Math.floor(index / 2); 32 | } 33 | } 34 | 35 | peek() { 36 | return this.maxHeap[1]; 37 | } 38 | 39 | pop() { 40 | if (this.currentSize < 1) { 41 | throw new Error("Heap is empty"); 42 | } 43 | let itemToBeRemoved = this.maxHeap[1]; 44 | this.maxHeap[1] = this.maxHeap[this.currentSize]; 45 | this.currentSize -= 1; 46 | let index = 1; 47 | 48 | while (index < this.currentSize && index <= Math.floor(this.currentSize/2)) { 49 | let left = index * 2, right = index * 2 + 1; 50 | if (this.maxHeap[index] < this.maxHeap[left] || this.maxHeap[index] < this.maxHeap[right]) { 51 | if (this.maxHeap[left] > this.maxHeap[right]) { 52 | [this.maxHeap[index], this.maxHeap[left]] = [this.maxHeap[left], this.maxHeap[index]]; 53 | index = left; 54 | } else { 55 | [this.maxHeap[index], this.maxHeap[right]] = [this.maxHeap[right], this.maxHeap[index]]; 56 | index = right; 57 | } 58 | } else { 59 | break; 60 | } 61 | } 62 | 63 | return itemToBeRemoved; 64 | } 65 | 66 | size() { 67 | return this.currentSize; 68 | } 69 | } 70 | 71 | // test case 72 | let maxHeap = new MaxHeap(5); 73 | maxHeap.add(3); 74 | maxHeap.add(1); 75 | maxHeap.add(2); 76 | console.log(maxHeap.peek()); // 3 77 | console.log(maxHeap.pop()); // 3 78 | console.log(maxHeap.pop()); // 2 79 | console.log(maxHeap.pop()); // 1 80 | maxHeap.add(5); 81 | maxHeap.add(4); 82 | console.log(maxHeap.peek()); // 5 -------------------------------------------------------------------------------- /data-structures/heap/minHeap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * MinHeap Implementation 3 | * 4 | * Time Complexity 5 | * Insertion - O(log n) 6 | * Deletion - O(log n) 7 | * To get min value - O(1) 8 | */ 9 | class MinHeap { 10 | constructor(heapSize) { 11 | this.minHeapSize = heapSize; 12 | this.minHeap = Array.from({length: heapSize + 1}, () => 0); 13 | this.currentSize = 0; 14 | } 15 | 16 | add(num) { 17 | this.currentSize += 1; 18 | 19 | if (this.currentSize > this.minHeapSize) { 20 | this.currentSize -= 1; 21 | throw new Error("Heap size exceeds!!"); 22 | } 23 | 24 | this.minHeap[this.currentSize] = num; 25 | 26 | let index = this.currentSize; 27 | let parentIndex = Math.floor(index/2); 28 | 29 | while(index > 1 && this.minHeap[index] < this.minHeap[parentIndex]) { 30 | [this.minHeap[index], this.minHeap[parentIndex]] = [this.minHeap[parentIndex], this.minHeap[index]]; 31 | index = parentIndex; 32 | parentIndex = Math.floor(index/2); 33 | } 34 | } 35 | 36 | peek() { 37 | return this.minHeap[1]; 38 | } 39 | 40 | pop() { 41 | if (this.currentSize < 1) { 42 | throw new Error("Heap is empty"); 43 | } 44 | 45 | let itemToBeRemoved = this.minHeap[1]; 46 | this.minHeap[1] = this.minHeap[this.currentSize]; 47 | this.currentSize -= 1; 48 | let index = 1; 49 | 50 | while(index < this.currentSize && index <= Math.floor(this.currentSize/2)) { 51 | let left = index * 2, right = index * 2 + 1; 52 | if (this.minHeap[index] > this.minHeap[left] || this.minHeap[index] > this.minHeap[right]) { 53 | if (this.minHeap[left] < this.minHeap[right]) { 54 | [this.minHeap[index], this.minHeap[left]] = [this.minHeap[left], this.minHeap[index]]; 55 | index = left; 56 | } else { 57 | [this.minHeap[index], this.minHeap[right]] = [this.minHeap[right], this.minHeap[index]]; 58 | index = right; 59 | } 60 | } else { 61 | break; 62 | } 63 | } 64 | 65 | return itemToBeRemoved; 66 | } 67 | 68 | size() { 69 | return this.currentSize; 70 | } 71 | } 72 | 73 | // test case 74 | let minHeap = new MinHeap(5); 75 | minHeap.add(3); 76 | minHeap.add(1); 77 | minHeap.add(2); 78 | console.log(minHeap.peek()); // 1 79 | console.log(minHeap.pop()); // 1 80 | console.log(minHeap.pop()); // 2 81 | console.log(minHeap.pop()); // 3 82 | minHeap.add(5); 83 | minHeap.add(4); 84 | console.log(minHeap.peek()); // 4 -------------------------------------------------------------------------------- /tic-tac-toe/index.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function() { 2 | let currentPlayer = 'X'; 3 | let gameScores = []; 4 | let board = document.getElementsByClassName('board')[0]; 5 | let gameResult = document.getElementsByClassName('game-result')[0]; 6 | let gameStatus = document.getElementsByClassName('status')[0]; 7 | let restartBtn = document.getElementsByClassName('restart-game')[0]; 8 | 9 | function initGame() { 10 | currentPlayer = 'X'; 11 | gameScores = new Array(9).fill(''); 12 | gameResult.style.display = 'none'; 13 | document.querySelectorAll('.cell').forEach(cell => cell.innerHTML = ""); 14 | } 15 | 16 | function updateGameStatus(isWon) { 17 | if (isWon) { 18 | gameStatus.innerHTML = `Congrats!!. ${currentPlayer} won the game.` 19 | } else { 20 | gameStatus.innerHTML = 'Math drawn.' 21 | } 22 | 23 | gameResult.style.display = 'block'; 24 | } 25 | 26 | function updateCurrentPlayer() { 27 | currentPlayer = currentPlayer === 'X' ? 'O' : 'X'; 28 | } 29 | 30 | function checkGameStatus() { 31 | let validMoves = [ 32 | [0, 1, 2], 33 | [3, 4, 5], 34 | [6, 7, 8], 35 | [0, 3, 6], 36 | [1, 4, 7], 37 | [2, 5, 8], 38 | [0, 4, 8], 39 | [2, 4, 6], 40 | ]; 41 | 42 | let isWon = false; 43 | 44 | for(let i=0; i<=7; i+=1) { 45 | let [a, b, c] = validMoves[i]; 46 | 47 | if (gameScores[a] && gameScores[b] && gameScores[c] && 48 | gameScores[a] === gameScores[b] && gameScores[b] === gameScores[c]) { 49 | updateGameStatus(true); 50 | isWon = true; 51 | break; 52 | } 53 | } 54 | 55 | if (!isWon) { 56 | console.log(gameScores) 57 | let isMoveExists = gameScores.find(score => score === ''); 58 | 59 | console.log(isMoveExists) 60 | if (isMoveExists === undefined) { 61 | updateGameStatus(false); 62 | return; 63 | } 64 | } 65 | 66 | updateCurrentPlayer(); 67 | } 68 | 69 | function handleClick(cell) { 70 | const index = cell.getAttribute("data-index"); 71 | 72 | if (gameScores[index] !== '') { 73 | return; 74 | } 75 | 76 | cell.innerHTML = currentPlayer; 77 | gameScores[index] = currentPlayer; 78 | 79 | checkGameStatus(); 80 | 81 | 82 | } 83 | 84 | board.addEventListener("click", function(e) { 85 | handleClick(e.target); 86 | }); 87 | 88 | restartBtn.addEventListener("click", function() { 89 | initGame(); 90 | }); 91 | 92 | initGame(); 93 | }); --------------------------------------------------------------------------------