diff --git a/clone-graph/yolophg.js b/clone-graph/yolophg.js new file mode 100644 index 000000000..26985886d --- /dev/null +++ b/clone-graph/yolophg.js @@ -0,0 +1,33 @@ +// Time Complexity: O(N + E) N = Node + E = Edge +// Space Complexity: O(N + E) + +var cloneGraph = function (node) { + // to keep track of all the nodes that have already been copied + const map = new Map(); + + // helper to perform the deep copy + const dfs = (currentNode) => { + if (!currentNode) return null; + + // if the node has already been copied, return the copied one + if (map.has(currentNode)) return map.get(currentNode); + + // create a new node with the current node's value + const newNode = new Node(currentNode.val); + + // store this new node in the map + map.set(currentNode, newNode); + + // iterate through each neighbor of the current node + for (const neighbor of currentNode.neighbors) { + // clone the neighbors and add them to the new node's neighbors + newNode.neighbors.push(dfs(neighbor)); + } + + // return the newly created node + return newNode; + }; + + // start the deep copy from the given node + return dfs(node); +}; diff --git a/combination-sum/yolophg.js b/combination-sum/yolophg.js new file mode 100644 index 000000000..6ff2a0db9 --- /dev/null +++ b/combination-sum/yolophg.js @@ -0,0 +1,27 @@ +// Time Complexity: O(n * target * k) +// Space Complexity: O(target) + +var combinationSum = function (candidates, target) { + // initialize dp array to store combinations + const dp = Array(target + 1) + .fill(null) + .map(() => []); + + // sort candidates to ensure uniqueness and optimize processing + candidates.sort((a, b) => a - b); + + // one way to make sum 0 (by choosing no elements) + dp[0] = [[]]; + + // iterate through each candidate + for (let num of candidates) { + // update dp array for current candidate + for (let i = num; i <= target; i++) { + for (let combination of dp[i - num]) { + dp[i].push([...combination, num]); + } + } + } + + return dp[target]; +}; diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/yolophg.js b/construct-binary-tree-from-preorder-and-inorder-traversal/yolophg.js new file mode 100644 index 000000000..0712372b9 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/yolophg.js @@ -0,0 +1,46 @@ +// Time Complexity: O(n) +// Space Complexity: O(n) + +var buildTree = function (preorder, inorder) { + // build a map for quick lookup of index positions in inorder array + // map to store the value -> index relationships + let map = new Map(); + inorder.forEach((value, index) => map.set(value, index)); + + // recursive function to construct the binary tree + function buildTreeHelper(preStart, preEnd, inStart, inEnd) { + // if there are no elements to consider + if (preStart > preEnd || inStart > inEnd) return null; + + // the first element in preorder is the root of the current tree + let rootValue = preorder[preStart]; + // create a new root node + let root = { val: rootValue, left: null, right: null }; + + // find the root in the inorder array to split the tree + let inRootIndex = map.get(rootValue); + // number of nodes in the left subtree + let numsLeft = inRootIndex - inStart; + + // recursively build the left subtree + root.left = buildTreeHelper( + preStart + 1, + preStart + numsLeft, + inStart, + inRootIndex - 1 + ); + + // recursively build the right subtree + root.right = buildTreeHelper( + preStart + numsLeft + 1, + preEnd, + inRootIndex + 1, + inEnd + ); + + return root; + } + + // initiate the recursive construction + return buildTreeHelper(0, preorder.length - 1, 0, inorder.length - 1); +}; diff --git a/course-schedule/yolophg.js b/course-schedule/yolophg.js new file mode 100644 index 000000000..778fb83a1 --- /dev/null +++ b/course-schedule/yolophg.js @@ -0,0 +1,47 @@ +// Time Complexity: O(numCourses * E) E = Edge +// Space Complexity: O(numCourses * E) + +// initialize an array to keep track of the in-degrees for each course +let inDegree = new Array(numCourses).fill(0); +// initialize an adjacency list to represent the graph of courses +let adjList = new Array(numCourses).fill(0).map(() => []); + +// fill the in-degree array and adjacency list based on the prerequisites +for (let [course, prereq] of prerequisites) { + // increment the in-degree of the course + inDegree[course]++; + // add the course to the adjacency list of the prerequisite + adjList[prereq].push(course); +} + +// initialize a queue to keep track of courses with no prerequisites (in-degree of 0) +let queue = []; +for (let i = 0; i < numCourses; i++) { + if (inDegree[i] === 0) { + queue.push(i); + } +} + +// initialize a counter to keep track of the number of courses that have been processed +let count = 0; + +// process the courses with no prerequisites +while (queue.length > 0) { + // get a course from the queue + let current = queue.shift(); + // increment the counter as this course can be taken + count++; + + // iterate through the adjacency list of the current course + for (let neighbor of adjList[current]) { + // decrement the in-degree of the neighboring course + inDegree[neighbor]--; + // if in-degree becomes 0, add it to the queue + if (inDegree[neighbor] === 0) { + queue.push(neighbor); + } + } +} + +// if the count of processed courses equals the total number of courses, return true +return count === numCourses; diff --git a/design-add-and-search-words-data-structure/yoloophg.js b/design-add-and-search-words-data-structure/yoloophg.js new file mode 100644 index 000000000..f94eb58f8 --- /dev/null +++ b/design-add-and-search-words-data-structure/yoloophg.js @@ -0,0 +1,56 @@ +// AddWord Method : Time Complexity: O(L)/Space Complexity: O(L) L = the length of the word being added +// Search Method : Time Complexity: O(M)/Space Complexity: O(1) M = the length of the word being searched +// SearchInNode Method : Time Complexity: O(M)/Space Complexity: O(1) + +var WordDictionary = function () { + this.root = {}; +}; + +WordDictionary.prototype.addWord = function (word) { + // start from the root node + let node = this.root; + for (let char of word) { + if (!node[char]) { + // if the character does not exist, create a new node + node[char] = {}; + } + // move to the next node + node = node[char]; + } + // mark the end of the word + node.isEndOfWord = true; +}; + +WordDictionary.prototype.search = function (word) { + // start searching from the root + return this.searchInNode(word, 0, this.root); +}; + +WordDictionary.prototype.searchInNode = function (word, index, node) { + if (index === word.length) { + // if all characters are checked + return node.isEndOfWord === true; + } + + const char = word[index]; + if (char === ".") { + //iIf the character is '.', check all possible nodes at this part + for (let key in node) { + if ( + key !== "isEndOfWord" && + this.searchInNode(word, index + 1, node[key]) + ) { + // if any path matches, return true + return true; + } + } + // no path matched + return false; + } else if (node[char]) { + // if the character exists in the trie + return this.searchInNode(word, index + 1, node[char]); + } else { + // character path does not exist + return false; + } +}; diff --git a/implement-trie-prefix-tree/yolophg.js b/implement-trie-prefix-tree/yolophg.js new file mode 100644 index 000000000..9bf0497a4 --- /dev/null +++ b/implement-trie-prefix-tree/yolophg.js @@ -0,0 +1,52 @@ +// Insert Method : Time Complexity: O(m)/Space Complexity: O(m) +// Search Method : Time Complexity: O(m)/Space Complexity: O(1) +// Starts With Method : Time Complexity: O(p)/Space Complexity: O(1) + +var Trie = function () { + this.trie = {}; +}; + +// insert a word into the Trie +Trie.prototype.insert = function (word) { + let node = this.trie; + for (let char of word) { + if (!node[char]) { + // create a new node if it doesn't exist + node[char] = {}; + } + // move to the next node + node = node[char]; + } + // mark the end of the word + node.isEnd = true; +}; + +// search for a word in the Trie +Trie.prototype.search = function (word) { + let node = this.trie; + for (let char of word) { + if (!node[char]) { + // character not found + return false; + } + // move to the next node + node = node[char]; + } + // Ccheck if it's the end of a valid word + return node.isEnd === true; +}; + +// check if there's any word in the Trie that starts with the given prefix +Trie.prototype.startsWith = function (prefix) { + let node = this.trie; + for (let char of prefix) { + if (!node[char]) { + // prefix not found + return false; + } + // move to the next node + node = node[char]; + } + // prefix found + return true; +}; diff --git a/kth-smallest-element-in-a-bst/yolophg.js b/kth-smallest-element-in-a-bst/yolophg.js new file mode 100644 index 000000000..2cbe451a4 --- /dev/null +++ b/kth-smallest-element-in-a-bst/yolophg.js @@ -0,0 +1,31 @@ +// Time Complexity: O(n) +// Space Complexity: O(n) + +var kthSmallest = function (root, k) { + let stack = []; + let current = root; + let count = 0; + + while (stack.length > 0 || current !== null) { + // go to the leftmost node + while (current !== null) { + stack.push(current); + current = current.left; + } + + // pop the node from the stack + current = stack.pop(); + count++; + + // if reached the kth node + if (count === k) { + return current.val; + } + + // go to the right node + current = current.right; + } + + // if k is out of the range of the number of in the tree + return null; +}; diff --git a/number-of-islands/yolophg.js b/number-of-islands/yolophg.js new file mode 100644 index 000000000..6a3a1a963 --- /dev/null +++ b/number-of-islands/yolophg.js @@ -0,0 +1,51 @@ +// Time Complexity: O(m * n) m = rows n = cols +// Space Complexity: O(m * n) + +var numIslands = function (grid) { + const m = grid.length; + const n = grid[0].length; + let numIslands = 0; + + // directions arrays for moving up, down, left, and right + const directions = [ + [-1, 0], + [1, 0], + [0, -1], + [0, 1], + ]; + + // BFS function + function bfs(row, col) { + const queue = [[row, col]]; + // mark as visited + grid[row][col] = "0"; + + while (queue.length > 0) { + const [r, c] = queue.shift(); + + for (const [dr, dc] of directions) { + const nr = r + dr; + const nc = c + dc; + + if (nr >= 0 && nr < m && nc >= 0 && nc < n && grid[nr][nc] === "1") { + // mark as visited + grid[nr][nc] = "0"; + queue.push([nr, nc]); + } + } + } + } + + // iterate through each cell in the grid + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (grid[i][j] === "1") { + numIslands++; + // start BFS to mark all connected land cells + bfs(i, j); + } + } + } + + return numIslands; +}; diff --git a/pacific-atlantic-water-flow/yolophg.js b/pacific-atlantic-water-flow/yolophg.js new file mode 100644 index 000000000..edaaa14a1 --- /dev/null +++ b/pacific-atlantic-water-flow/yolophg.js @@ -0,0 +1,76 @@ +// Time Complexity: O(rows + cols) +// Space Complexity: O(rows + cols) + +var pacificAtlantic = function (heights) { + const rows = heights.length; + const cols = heights[0].length; + + // initialize matrices to keep track of cells reachable by Pacific and Atlantic + const pacific = []; + const atlantic = []; + + // create 2D arrays filled with false values + for (let i = 0; i < rows; i++) { + pacific.push(new Array(cols).fill(false)); + atlantic.push(new Array(cols).fill(false)); + } + + // directions for moving up, right, down, left + const directions = [ + [0, 1], + [1, 0], + [0, -1], + [-1, 0], + ]; + + // helper to do DFS + function dfs(row, col, ocean) { + // mark the cell as reachable + ocean[row][col] = true; + for (const [dx, dy] of directions) { + // check all 4 directions + const newRow = row + dx; + const newCol = col + dy; + // continue if the new cell is within bounds, not visited, and has a height greater than or equal to the current cell + if ( + newRow >= 0 && + newRow < rows && + newCol >= 0 && + newCol < cols && + !ocean[newRow][newCol] && + heights[newRow][newCol] >= heights[row][col] + ) { + dfs(newRow, newCol, ocean); + } + } + } + + // start DFS from the Pacific ocean borders + for (let i = 0; i < rows; i++) { + // left edge + dfs(i, 0, pacific); + // right edge + dfs(i, cols - 1, atlantic); + } + + // start DFS from the Atlantic ocean borders + for (let j = 0; j < cols; j++) { + // top edge + dfs(0, j, pacific); + dfs(rows - 1, j, atlantic); // Bottom edge + } + + const result = []; + // check each cell can reach both oceans + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (pacific[i][j] && atlantic[i][j]) { + // if it can reach both + result.push([i, j]); + } + } + } + + // return the final list + return result; +}; diff --git a/word-search/yolophg.js b/word-search/yolophg.js new file mode 100644 index 000000000..24e200891 --- /dev/null +++ b/word-search/yolophg.js @@ -0,0 +1,58 @@ +// Time Complexity: O(rows×cols) +// Space Complexity: O(rows×cols) + +var exist = function (board, word) { + const rows = board.length; + const cols = board[0].length; + + // initialize a queue for BFS, starting from each cell in the board + const queue = []; + + // perform BFS from each cell in the board + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (board[i][j] === word[0]) { + queue.push([[i, j], 0, [[i, j]]]); + } + } + } + + // directions for BFS: up, down, left, right + const directions = [ + [-1, 0], + [1, 0], + [0, -1], + [0, 1], + ]; + + while (queue.length > 0) { + const [[row, col], index, path] = queue.shift(); + + // if the entire word is found, return true + if (index === word.length - 1) { + return true; + } + + // explore neighbors + for (const [dx, dy] of directions) { + const newRow = row + dx; + const newCol = col + dy; + + // check boundaries and character match + if ( + newRow >= 0 && + newRow < rows && + newCol >= 0 && + newCol < cols && + board[newRow][newCol] === word[index + 1] && + !path.some(([r, c]) => r === newRow && c === newCol) + ) { + // enqueue the next cell to explore + queue.push([[newRow, newCol], index + 1, [...path, [newRow, newCol]]]); + } + } + } + + // if no path was found, return false + return false; +};