├── README.md ├── arrays ├── range-query │ └── 303. Range Sum Query - Immutable │ │ ├── Solution.cs │ │ ├── notes.md │ │ └── solution.py └── sliding-window │ └── 209. Minimum Size Subarray Sum │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── backtracking ├── 967. Numbers With Same Consecutive Differences │ ├── Solution.cs │ └── notes.md ├── 216. Combination Sum III │ ├── Solution.cs │ └── notes.md ├── 22. Generate Parentheses │ ├── Solution.cs │ └── notes.md ├── 39. Combination Sum │ ├── Solution.cs │ └── notes.md ├── 40. Combination Sum II │ ├── Solution.cs │ └── notes.md ├── 46. Permutations │ ├── Solution.cs │ └── notes.md ├── 47. Permutations II │ ├── Solution.cs │ └── notes.md ├── 51. N-Queens │ ├── notes.md │ └── solution.py ├── 78. Subsets │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 797. All Paths From Source to Target │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 93. Restore IP Addresses │ ├── notes.md │ └── solution.py └── 967. Numbers With Same Consecutive Differences │ └── notes.md ├── binary-search-trees ├── 270. Closest Binary Search Tree Value │ ├── notes.md │ └── solution.cs └── 701. Insert into a Binary Search Tree │ ├── Solution.cs │ └── notes.md ├── binary-search ├── 1231. Divide Chocolate ├── 1283. Find the Smallest Divisor Given a Threshold │ ├── Solution.cs │ └── notes.md ├── 2389. Longest Subsequence With Limited Sum │ ├── Solution.cs │ └── notes.md └── 35. Search Insert Position │ ├── Solution.cs │ └── notes.md ├── dynamic-programming ├── 309. Best Time to Buy and Sell Stock with Cooldown │ ├── Solution.cs │ └── notes.md ├── 322. Coin Change │ ├── Solution.cs │ └── notes.md └── 70. Climbing Stairs │ ├── Solution.cs │ └── notes.md ├── graphs ├── 127. Word Ladder │ ├── Solution.cs │ └── notes.md ├── 1306. Jump Game III │ ├── Solution.cs │ └── notes.md ├── 1971. Find if Path Exists in Graph │ ├── Solution.cs │ └── notes.md ├── 2101. Detonate the Maximum Bombs │ ├── Solution.cs │ └── notes.md ├── 2368. Reachable Nodes With Restrictions │ ├── Solution.cs │ └── notes.md ├── 323. Number of Connected Components in an Undirected Graph │ ├── Solution.cs │ └── notes.md ├── 433. Minimum Genetic Mutation │ ├── Solution.cs │ └── notes.md ├── 695. Max Area of Island │ ├── Solution.cs │ └── notes.md └── 909. Snakes and Ladders │ ├── Solution.cs │ └── notes.md ├── greedy ├── 1196. How Many Apples Can You Put into the Basket │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 1323. Maximum 69 Number │ ├── Solution.cs │ └── notes.md ├── 1338. Reduce Array Size to The Half │ ├── Solution.cs │ └── notes.md └── 1710. Maximum Units on a Truck │ ├── Solution.cs │ └── notes.md ├── hash-tables ├── 1426. Counting Elements │ ├── notes.md │ └── solution.py ├── 1832. Check if the Sentence Is Pangram │ ├── notes.md │ └── solution.py └── 560. Subarray Sum Equals K │ ├── notes.md │ └── solution.py ├── heaps ├── 1167. Minimum Cost to Connect Sticks │ ├── Solution.cs │ └── notes.md ├── 1962. Remove Stones to Minimize the Total │ ├── Solution.cs │ └── notes.md ├── 215. Kth Largest Element in an Array │ ├── Solution.cs │ └── notes.md ├── 703. Kth Largest Element in a Stream │ ├── Solution.cs │ └── notes.md └── 973. K Closest Points to Origin │ ├── Solution.cs │ └── notes.md ├── leetcode75 ├── 208. Implement Trie (Prefix Tree) │ ├── Solution.cs │ └── notes.md ├── 1137. N-th Tribonacci Number │ ├── Solution.cs │ └── notes.md ├── 1143. Longest Common Subsequence │ ├── Solution.cs │ └── notes.md ├── 1268. Search Suggestions System │ ├── Solution.cs │ └── notes.md ├── 1318. Minimum Flips to Make a OR b Equal to c │ ├── Solution.cs │ └── notes.md ├── 136. Single Number │ ├── Solution.cs │ └── notes.md ├── 1431. Kids With the Greatest Number of Candies │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 1466. Reorder Routes to Make All Paths Lead to the City Zero │ ├── Solution.cs │ └── notes.md ├── 151. Reverse Words in a String │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 162. Find Peak Element │ ├── Solution.cs │ └── notes.md ├── 17. Letter Combinations of a Phone Number │ ├── Solution.cs │ └── notes.md ├── 1768. Merge Strings Alternately │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 1926. Nearest Exit from Entrance in Maze │ ├── Solution.cs │ └── notes.md ├── 198. House Robber │ ├── Solution.cs │ └── notes.md ├── 215. Kth Largest Element in an Array │ ├── Solution.cs │ └── notes.md ├── 216. Combination Sum III │ ├── Solution.cs │ └── notes.md ├── 2300. Successful Pairs of Spells and Potions │ ├── Solution.cs │ └── notes.md ├── 2336. Smallest Number in Infinite Set │ ├── Solutions.cs │ └── notes.md ├── 238. Product of Array Except Self │ ├── Solution.cd │ ├── notes.md │ └── solution.py ├── 2462. Total Cost to Hire K Workers │ ├── Solution.cs │ └── notes.md ├── 2542. Maximum Subsequence Score │ ├── Solution.cs │ └── notes.md ├── 283. Move Zeroes │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 334. Increasing Triplet Subsequence │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 338. Counting Bits │ ├── Solution.cs │ └── notes.md ├── 345. Reverse Vowels of a String │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 374. Guess Number Higher or Lower │ ├── Solution.cs │ └── notes.md ├── 392. Is Subsequence │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 399. Evaluate Division │ ├── Solution.cs │ └── notes.md ├── 435. Non-overlapping Intervals │ ├── Solution.cs │ └── notes.md ├── 452. Minimum Number of Arrows to Burst Balloons │ ├── Solution.cs │ └── notes.md ├── 605. Can Place Flowers │ ├── Solution.cs │ ├── notes.md │ └── solution.py ├── 62. Unique Paths │ ├── Solution.cs │ └── notes.md ├── 714. Best Time to Buy and Sell Stock with Transaction Fee │ ├── Solution.cs │ └── notes.md ├── 72. Edit Distance │ ├── Solution.cs │ └── notes.md ├── 739. Daily Temperatures │ ├── Solution.cs │ └── notes.md ├── 746. Min Cost Climbing Stairs │ ├── Solution.cs │ └── notes.md ├── 790. Domino and Tromino Tiling │ ├── Solution.cs │ └── notes.md ├── 875. Koko Eating Bananas │ ├── Solution.cs │ └── notes.md ├── 901. Online Stock Span │ ├── Solution.cs │ └── notes.md └── 994. Rotting Oranges │ ├── Solution.cs │ └── notes.md ├── stack └── 20. Valid Parentheses │ ├── Solution.cs │ └── notes.md └── templates └── arrays-and-strings ├── template.cs └── template.py /README.md: -------------------------------------------------------------------------------- 1 | Coding Interview Preparation 2 | ======== 3 | 4 | This repository contains a guide to Software Engineering coding interviews. 5 | 6 | For each problem are available: 7 | 8 | * a solution in multiple programming languages 9 | * the thinking process needed to find the solution 10 | * the time and space complexity analysis of the solution 11 | 12 | ### Problems Categories 13 | 14 | Here are the solved problems divided by category: 15 | 16 | * [Arrays]([https://leetcode.com/problems/roman-to-integer/description/]([https://github.com/FrancoFernando/leetcode/blob/main/LinkedList/linked-list.md](https://github.com/FrancoFernando/leetcode/tree/main/HashTable))) 17 | -------------------------------------------------------------------------------- /arrays/range-query/303. Range Sum Query - Immutable/Solution.cs: -------------------------------------------------------------------------------- 1 | public class NumArray { 2 | 3 | private readonly int[] _prefixSum; 4 | 5 | public NumArray(int[] nums) { 6 | _prefixSum = new int[nums.Length]; 7 | _prefixSum[0] = nums[0]; 8 | for (int i = 1; i < nums.Length; i++) { 9 | _prefixSum[i] = _prefixSum[i-1] + nums[i]; 10 | } 11 | } 12 | 13 | public int SumRange(int left, int right) { 14 | return (left > 0) ? _prefixSum[right] - _prefixSum[left-1] : _prefixSum[right]; 15 | } 16 | } 17 | 18 | /** 19 | * Your NumArray object will be instantiated and called as such: 20 | * NumArray obj = new NumArray(nums); 21 | * int param_1 = obj.SumRange(left,right); 22 | */ 23 | -------------------------------------------------------------------------------- /arrays/range-query/303. Range Sum Query - Immutable/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer array nums, handle multiple queries of the following type: 4 | 5 | Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. 6 | Implement the NumArray class: 7 | 8 | NumArray(int[] nums) Initializes the object with the integer array nums. 9 | int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive (i.e. nums[left] + nums[left + 1] + ... + nums[right]). 10 | 11 | 12 | Example 1: 13 | 14 | Input 15 | ["NumArray", "sumRange", "sumRange", "sumRange"] 16 | [[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]] 17 | Output 18 | [null, 1, -1, -3] 19 | 20 | Explanation 21 | NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]); 22 | numArray.sumRange(0, 2); // return (-2) + 0 + 3 = 1 23 | numArray.sumRange(2, 5); // return 3 + (-5) + 2 + (-1) = -1 24 | numArray.sumRange(0, 5); // return (-2) + 0 + 3 + (-5) + 2 + (-1) = -3 25 | 26 | 27 | Constraints: 28 | 29 | 1 <= nums.length <= 104 30 | -105 <= nums[i] <= 105 31 | 0 <= left <= right < nums.length 32 | At most 104 calls will be made to sumRange. 33 | 34 | # Thought Process 35 | 36 | Build a prefix sum array with the input let efficiently answer each query. 37 | 38 | # Complexity 39 | 40 | O(n) preprocessing time and O(1) query time 41 | -------------------------------------------------------------------------------- /arrays/range-query/303. Range Sum Query - Immutable/solution.py: -------------------------------------------------------------------------------- 1 | class NumArray: 2 | 3 | def __init__(self, nums: List[int]): 4 | self.prefix_sum = [0] 5 | for num in nums: 6 | self.prefix_sum.append(num + self.prefix_sum[-1]) 7 | 8 | 9 | def sumRange(self, left: int, right: int) -> int: 10 | return self.prefix_sum[right + 1] - self.prefix_sum[left] 11 | 12 | 13 | 14 | # Your NumArray object will be instantiated and called as such: 15 | # obj = NumArray(nums) 16 | # param_1 = obj.sumRange(left,right) 17 | -------------------------------------------------------------------------------- /arrays/sliding-window/209. Minimum Size Subarray Sum/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MinSubArrayLen(int target, int[] nums) { 3 | int minLength = int.MaxValue; 4 | for(int right = 0, left = 0, sum = 0; right < nums.Length; right++){ 5 | sum += nums[right]; 6 | while (sum >= target) { 7 | minLength = Math.Min(minLength, right-left+1); 8 | sum -= nums[left++]; 9 | } 10 | } 11 | return minLength == int.MaxValue ? 0 : minLength; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /arrays/sliding-window/209. Minimum Size Subarray Sum/notes.md: -------------------------------------------------------------------------------- 1 | Given an array of positive integers nums and a positive integer target, return the minimal length of a subarray whose sum is greater than or equal to target. If there is no such subarray, return 0 instead. 2 | 3 | 4 | 5 | Example 1: 6 | 7 | Input: target = 7, nums = [2,3,1,2,4,3] 8 | Output: 2 9 | Explanation: The subarray [4,3] has the minimal length under the problem constraint. 10 | Example 2: 11 | 12 | Input: target = 4, nums = [1,4,4] 13 | Output: 1 14 | Example 3: 15 | 16 | Input: target = 11, nums = [1,1,1,1,1,1,1,1] 17 | Output: 0 18 | 19 | 20 | Constraints: 21 | 22 | 1 <= target <= 109 23 | 1 <= nums.length <= 105 24 | 1 <= nums[i] <= 104 25 | 26 | 27 | Follow up: If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log(n 28 | 29 | # Thought process 30 | 31 | Use sliding window: right expand at every step, left shrinks every time the sum inside the window exceed the target. 32 | 33 | # Complexity 34 | 35 | O(n) time complexity. The space complexity is O(1) as we're only using a constant amount of extra space. 36 | -------------------------------------------------------------------------------- /arrays/sliding-window/209. Minimum Size Subarray Sum/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minSubArrayLen(self, target: int, nums: List[int]) -> int: 3 | min_length = float('inf') 4 | window_sum = 0 5 | left = 0 6 | for right, num in enumerate(nums): 7 | window_sum += num 8 | while window_sum >= target: 9 | min_length = min(min_length, right-left+1) 10 | window_sum -= nums[left] 11 | left += 1 12 | 13 | return min_length if min_length != float('inf') else 0 14 | 15 | -------------------------------------------------------------------------------- /backtracking/ 967. Numbers With Same Consecutive Differences/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int[] NumsSameConsecDiff(int n, int k) { 3 | var result = new List(); 4 | for (int digit = 1; digit <= 9; digit++) { 5 | GenerateNums(result, digit, n-1, k); 6 | } 7 | return result.ToArray(); 8 | } 9 | 10 | private void GenerateNums(List result, int current, int remainingDigits, int k) { 11 | 12 | if (remainingDigits == 0) { 13 | result.Add(current); 14 | return; 15 | } 16 | 17 | int lastDigit = current % 10; 18 | int nextDigitUp = lastDigit + k; 19 | if (nextDigitUp < 10) { 20 | int num = current * 10 + nextDigitUp; 21 | GenerateNums(result, num, remainingDigits-1, k); 22 | } 23 | 24 | int nextDigitDown = lastDigit - k; 25 | if (nextDigitDown >=0 && k != 0) { 26 | int num = current * 10 + nextDigitDown; 27 | GenerateNums(result, num, remainingDigits-1, k); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backtracking/ 967. Numbers With Same Consecutive Differences/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given two integers n and k, return an array of all the integers of length n where the difference between every two consecutive digits is k. You may return the answer in any order. 4 | 5 | Note that the integers should not have leading zeros. Integers as 02 and 043 are not allowed. 6 | 7 | Example 1: 8 | 9 | Input: n = 3, k = 7 10 | Output: [181,292,707,818,929] 11 | Explanation: Note that 070 is not a valid number, because it has leading zeroes. 12 | Example 2: 13 | 14 | Input: n = 2, k = 1 15 | Output: [10,12,21,23,32,34,43,45,54,56,65,67,76,78,87,89,98] 16 | 17 | 18 | Constraints: 19 | 20 | 2 <= n <= 9 21 | 0 <= k <= 9 22 | 23 | # Thought process 24 | 25 | Since we need to build numbers digit by digit with constraints, this suggests a recursive or backtracking approach. 26 | Each digit we add has limited options based on the previous digit: 27 | 28 | - For each position, given the previous digit d, we can only use d+k or d-k as the next digit (if they are valid digits 0-9). 29 | - We need to start with a non-zero digit. 30 | - We need to track the number we're building as we go. 31 | 32 | # Complexity 33 | 34 | Time Complexity: O(2^n) in the worst case, as each position could have at most 2 choices. 35 | Space Complexity: O(n) for the recursion stack, plus O(2^n) for storing the results. 36 | -------------------------------------------------------------------------------- /backtracking/216. Combination Sum III/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private IList> _result; 4 | private IList _current; 5 | 6 | public IList> CombinationSum3(int k, int n) { 7 | _current = new List(); 8 | _result = new List>(); 9 | Backtrack(k, n, 1); 10 | return _result; 11 | } 12 | 13 | public void Backtrack(int k, int remainingSum, int start) { 14 | 15 | //base case 16 | if (k == 0 && remainingSum == 0) { 17 | _result.Add(new List(_current)); 18 | return; 19 | } 20 | 21 | //pruning 22 | if (k <= 0 || remainingSum <= 0) { 23 | return; 24 | } 25 | 26 | for(int i = start; i <= 9 && i <= remainingSum; ++i) { 27 | _current.Add(i); 28 | Backtrack(k-1, remainingSum-i, i+1); 29 | _current.RemoveAt(_current.Count - 1); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /backtracking/216. Combination Sum III/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Find all valid combinations of k numbers that sum up to n such that the following conditions are true: 4 | 5 | Only numbers 1 through 9 are used. 6 | Each number is used at most once. 7 | Return a list of all possible valid combinations. The list must not contain the same combination twice, and the combinations may be returned in any order. 8 | 9 | Example 1: 10 | 11 | Input: k = 3, n = 7 12 | Output: [[1,2,4]] 13 | Explanation: 14 | 1 + 2 + 4 = 7 15 | There are no other valid combinations. 16 | Example 2: 17 | 18 | Input: k = 3, n = 9 19 | Output: [[1,2,6],[1,3,5],[2,3,4]] 20 | Explanation: 21 | 1 + 2 + 6 = 9 22 | 1 + 3 + 5 = 9 23 | 2 + 3 + 4 = 9 24 | There are no other valid combinations. 25 | Example 3: 26 | 27 | Input: k = 4, n = 1 28 | Output: [] 29 | Explanation: There are no valid combinations. 30 | Using 4 different numbers in the range [1,9], the smallest sum we can get is 1+2+3+4 = 10 and since 10 > 1, there are no valid combination. 31 | 32 | 33 | Constraints: 34 | 35 | 2 <= k <= 9 36 | 1 <= n <= 60 37 | 38 | # Thought Process 39 | 40 | # Complexity 41 | -------------------------------------------------------------------------------- /backtracking/22. Generate Parentheses/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private IList _result; 4 | 5 | public IList GenerateParenthesis(int n) { 6 | 7 | _result = new List(); 8 | BackTrack (n, 0, 0, new char[n * 2], 0); 9 | return _result; 10 | } 11 | 12 | public void BackTrack (int n, int openCount, int closeCount, char[] current, int index) { 13 | 14 | if (index == current.Length) { 15 | _result.Add(new string(current)); 16 | return; 17 | } 18 | 19 | if (openCount < n) { 20 | current[index] = '('; 21 | BackTrack (n, openCount+1, closeCount, current, index+1); 22 | } 23 | 24 | if (closeCount < openCount) { 25 | current[index] = ')'; 26 | BackTrack (n, openCount, closeCount+1, current, index+1); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backtracking/22. Generate Parentheses/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. 4 | 5 | Example 1: 6 | 7 | Input: n = 3 8 | Output: ["((()))","(()())","(())()","()(())","()()()"] 9 | Example 2: 10 | 11 | Input: n = 1 12 | Output: ["()"] 13 | 14 | 15 | Constraints: 16 | 17 | 1 <= n <= 8 18 | 19 | # Thought process 20 | 21 | Once you put an open parenthesis you have two option: put another open parenthesis (if the maximum of n has not been reached) or put a closed parenthesis. 22 | 23 | # Complexity 24 | 25 | Time complexity is O(2^(2n)). For each position in the string (of length 2n), we have at most 2 choices: Add an opening parenthesis '(' or add a closing parenthesis ')'. 26 | 27 | Space complexity is O(n) for the maximum depth of recursion and storing the current combination being built 28 | -------------------------------------------------------------------------------- /backtracking/39. Combination Sum/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | private IList> _result; 3 | private IList _current; 4 | public IList> CombinationSum(int[] candidates, int target) { 5 | _current = new List(); 6 | _result = new List>(); 7 | Array.Sort(candidates); 8 | Backtrack(candidates, 0, target); 9 | return _result; 10 | } 11 | 12 | private void Backtrack(int[] candidates, int startIndex, int remaining) { 13 | 14 | if (remaining == 0) { 15 | _result.Add(new List(_current)); 16 | return; 17 | } 18 | 19 | for (int i = startIndex; i < candidates.Length; i++) { 20 | int num = candidates[i]; 21 | if (num > remaining) break; 22 | _current.Add(num); 23 | Backtrack(candidates, i, remaining - num); 24 | _current.RemoveAt(_current.Count - 1); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backtracking/39. Combination Sum/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order. 4 | 5 | The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different. 6 | 7 | The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: candidates = [2,3,6,7], target = 7 14 | Output: [[2,2,3],[7]] 15 | Explanation: 16 | 2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times. 17 | 7 is a candidate, and 7 = 7. 18 | These are the only two combinations. 19 | Example 2: 20 | 21 | Input: candidates = [2,3,5], target = 8 22 | Output: [[2,2,2,2],[2,3,3],[3,5]] 23 | Example 3: 24 | 25 | Input: candidates = [2], target = 1 26 | Output: [] 27 | 28 | 29 | Constraints: 30 | 31 | 1 <= candidates.length <= 30 32 | 2 <= candidates[i] <= 40 33 | All elements of candidates are distinct. 34 | 1 <= target <= 40 35 | 36 | # Thought Process 37 | 38 | As long as the sum of the choosen numbers is less than target you can pick another number. 39 | When you find a valid combination you need to backtrack to look for another combination. 40 | Sorting the input is critical to avoid duplicate combination ad pruning the search as soon as the current sum exceed target. 41 | 42 | # Complexity 43 | 44 | Time complexity is O(n * 2^n) since for each of the n candidates we can include it or not in the solurion. 45 | Since numbers are integers this could be simplified to O(target). 46 | Space complexity is O(n), simplified to O(target) 47 | -------------------------------------------------------------------------------- /backtracking/40. Combination Sum II/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | private IList> _result; 3 | private IList _current; 4 | public IList> CombinationSum2(int[] candidates, int target) { 5 | _current = new List(); 6 | _result = new List>(); 7 | Array.Sort(candidates); 8 | Backtrack(candidates, 0, target); 9 | return _result; 10 | } 11 | 12 | private void Backtrack(int[] candidates, int startIndex, int remaining) { 13 | 14 | if (remaining == 0) { 15 | _result.Add(new List(_current)); 16 | return; 17 | } 18 | 19 | for (int i = startIndex; i < candidates.Length; i++) { 20 | int num = candidates[i]; 21 | if (i > startIndex && candidates[i] == candidates[i - 1]) { 22 | continue; 23 | } 24 | if (num > remaining) break; 25 | _current.Add(num); 26 | Backtrack(candidates, i+1, remaining - num); 27 | _current.RemoveAt(_current.Count - 1); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backtracking/40. Combination Sum II/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target. 4 | 5 | Each number in candidates may only be used once in the combination. 6 | 7 | Note: The solution set must not contain duplicate combinations. 8 | 9 | 10 | Example 1: 11 | 12 | Input: candidates = [10,1,2,7,6,1,5], target = 8 13 | Output: 14 | [ 15 | [1,1,6], 16 | [1,2,5], 17 | [1,7], 18 | [2,6] 19 | ] 20 | Example 2: 21 | 22 | Input: candidates = [2,5,2,1,2], target = 5 23 | Output: 24 | [ 25 | [1,2,2], 26 | [5] 27 | ] 28 | 29 | 30 | Constraints: 31 | 32 | 1 <= candidates.length <= 100 33 | 1 <= candidates[i] <= 50 34 | 1 <= target <= 30 35 | 36 | # Thought process 37 | 38 | Modify Combinations solution by adding a logic to skip duplicated 39 | -------------------------------------------------------------------------------- /backtracking/46. Permutations/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private IList> _result; 4 | 5 | private bool[] _used; 6 | 7 | public IList> Permute(int[] nums) { 8 | 9 | _used = new bool[nums.Length]; 10 | _result = new List>(); 11 | GeneratePermutations(nums, 0, new int[nums.Length]); 12 | return _result; 13 | } 14 | 15 | private void GeneratePermutations(int[] nums, int index, int[] current) { 16 | 17 | if (index == nums.Length) { 18 | _result.Add(new List(current)); 19 | return; 20 | } 21 | 22 | for (int i = 0; i < nums.Length; ++i) { 23 | if (!_used[i]) { 24 | _used[i] = true; 25 | current[index] = nums[i]; 26 | GeneratePermutations(nums, index+1, current); 27 | _used[i] = false; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /backtracking/46. Permutations/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. 4 | 5 | Example 1: 6 | 7 | Input: nums = [1,2,3] 8 | Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 9 | Example 2: 10 | 11 | Input: nums = [0,1] 12 | Output: [[0,1],[1,0]] 13 | Example 3: 14 | 15 | Input: nums = [1] 16 | Output: [[1]] 17 | 18 | 19 | Constraints: 20 | 21 | 1 <= nums.length <= 6 22 | -10 <= nums[i] <= 10 23 | All the integers of nums are unique. 24 | 25 | # Thought process 26 | 27 | Given a position in the permuted array, each number of the input that has not been already used can be put in that position. 28 | The idea is to use backtracking to put an input number in a position, mark it as already used and move to the next position. 29 | 30 | # 31 | -------------------------------------------------------------------------------- /backtracking/47. Permutations II/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private IList> _result; 4 | private bool[] _used; 5 | private int[] _current; 6 | 7 | public IList> PermuteUnique(int[] nums) { 8 | 9 | _result = new List>(); 10 | _current = new int[nums.Length]; 11 | _used = new bool[nums.Length]; 12 | GeneratePermutations(nums, 0); 13 | return _result; 14 | } 15 | 16 | private void GeneratePermutations(int[] nums, int index) { 17 | 18 | if (index == nums.Length) { 19 | _result.Add(new List(_current)); 20 | return; 21 | } 22 | 23 | // track values used at each recursion level to avoid using the same multiple times 24 | var usedValues = new HashSet(); 25 | 26 | for (int i = 0; i < nums.Length; i++) { 27 | if (!_used[i] && !usedValues.Contains(nums[i])) { 28 | _used[i] = true; 29 | _current[index] = nums[i]; 30 | usedValues.Add(nums[i]); 31 | GeneratePermutations(nums, index + 1); 32 | _used[i] = false; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /backtracking/47. Permutations II/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given a collection of numbers, nums, that might contain duplicates, return all possible unique permutations in any order. 4 | 5 | Example 1: 6 | 7 | Input: nums = [1,1,2] 8 | Output: 9 | [[1,1,2], 10 | [1,2,1], 11 | [2,1,1]] 12 | Example 2: 13 | 14 | Input: nums = [1,2,3] 15 | Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 16 | 17 | 18 | Constraints: 19 | 20 | 1 <= nums.length <= 8 21 | -10 <= nums[i] <= 10 22 | 23 | # Thought Process 24 | 25 | The same reasonong to Permutation problem applies. 26 | The only change is that we want to avoid using multiple times the same value at the same recursion level. 27 | A local hash set in the backtrack function does the job. 28 | 29 | # Complexity 30 | 31 | Same as Permutation. 32 | -------------------------------------------------------------------------------- /backtracking/51. N-Queens/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other. 4 | 5 | Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order. 6 | 7 | Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | 14 | Input: n = 4 15 | Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]] 16 | Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above 17 | Example 2: 18 | 19 | Input: n = 1 20 | Output: [["Q"]] 21 | 22 | 23 | Constraints: 24 | 25 | 1 <= n <= 9 26 | -------------------------------------------------------------------------------- /backtracking/51. N-Queens/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def solveNQueens(self, n): 3 | def is_valid(row, col): 4 | for i in range(row): 5 | if board[i] == col or \ 6 | board[i] - i == col - row or \ 7 | board[i] + i == col + row: 8 | return False 9 | return True 10 | 11 | def backtrack(row): 12 | if row == n: 13 | result.append(["".join('Q' if c == j else '.' for j in range(n)) for c in board]) 14 | return 15 | for col in range(n): 16 | if is_valid(row, col): 17 | board[row] = col 18 | backtrack(row + 1) 19 | 20 | board = [-1] * n 21 | result = [] 22 | backtrack(0) 23 | return result 24 | -------------------------------------------------------------------------------- /backtracking/78. Subsets/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private IList> result; 4 | 5 | public IList> Subsets(int[] nums) { 6 | 7 | result = new List>(); 8 | GenerateSubsets(nums, 0, new List()); 9 | return result; 10 | } 11 | 12 | private void GenerateSubsets(int[] nums, int index, IList current) { 13 | 14 | if (index == nums.Length) { // is solution? 15 | result.Add(new List(current)); 16 | return; 17 | } 18 | 19 | current.Add(nums[index]); // make move 20 | GenerateSubsets(nums, index+1, current); 21 | current.RemoveAt(current.Count - 1); // unmake move 22 | GenerateSubsets(nums, index+1, current); 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backtracking/78. Subsets/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer array nums of unique elements, return all possible subsets (the power set). 4 | 5 | The solution set must not contain duplicate subsets. Return the solution in any order. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: nums = [1,2,3] 12 | Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] 13 | Example 2: 14 | 15 | Input: nums = [0] 16 | Output: [[],[0]] 17 | 18 | 19 | Constraints: 20 | 21 | 1 <= nums.length <= 10 22 | -10 <= nums[i] <= 10 23 | All the numbers of nums are unique. 24 | 25 | # Thought Process 26 | 27 | The idea is to use DFS + backtracking approach to explore all possible combinations of including or excluding each element. 28 | Once an element is added to the current subset and the recursive call is done, the element is then removed. 29 | 30 | # Complexity 31 | 32 | The time complexity is (O(n * 2^n), where n is the length of the input array. (there are 2^n possible subsets, generating each subset takes O(n)). 33 | The space complexity is O(n) for the recursion stack and current subset. 34 | -------------------------------------------------------------------------------- /backtracking/78. Subsets/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def subsets(self, nums: List[int]) -> List[List[int]]: 3 | def backtrack(index, current): 4 | if index == len(nums): 5 | result.append(current[:]) 6 | return 7 | 8 | current.append(nums[index]) 9 | backtrack(index + 1, current) 10 | current.pop() 11 | backtrack(index + 1, current) 12 | 13 | result = [] 14 | backtrack(0, []) 15 | return result 16 | -------------------------------------------------------------------------------- /backtracking/797. All Paths From Source to Target/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private IList> paths; 4 | 5 | public IList> AllPathsSourceTarget(int[][] graph) { 6 | paths = new List>(); 7 | Dfs(graph, 0, graph.Length-1, new List()); 8 | return paths; 9 | } 10 | 11 | private void Dfs(int[][] graph, int currentNode, int targetNode, IList path) { 12 | 13 | path.Add(currentNode); 14 | if (currentNode == targetNode) { 15 | paths.Add(new List(path)); 16 | } 17 | else { 18 | foreach (int neighbor in graph[currentNode]) { 19 | Dfs(graph, neighbor, targetNode, path); 20 | } 21 | } 22 | path.RemoveAt(path.Count-1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backtracking/797. All Paths From Source to Target/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given a directed acyclic graph (DAG) of n nodes labeled from 0 to n - 1, find all possible paths from node 0 to node n - 1 and return them in any order. 4 | 5 | The graph is given as follows: graph[i] is a list of all nodes you can visit from node i (i.e., there is a directed edge from node i to node graph[i][j]). 6 | 7 | Example 1: 8 | 9 | Input: graph = [[1,2],[3],[3],[]] 10 | Output: [[0,1,3],[0,2,3]] 11 | Explanation: There are two paths: 0 -> 1 -> 3 and 0 -> 2 -> 3. 12 | Example 2: 13 | 14 | Input: graph = [[4,3,1],[3,2,4],[3],[4],[]] 15 | Output: [[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]] 16 | 17 | Constraints: 18 | 19 | n == graph.length 20 | 2 <= n <= 15 21 | 0 <= graph[i][j] < n 22 | graph[i][j] != i (i.e., there will be no self-loops). 23 | All the elements of graph[i] are unique. 24 | The input graph is guaranteed to be a DAG. 25 | 26 | # Thought Process 27 | 28 | The easiest way to find all the paths is to use DFS and backtrack every time you reach the end node. 29 | It's not necessary to keep track of the visited nodes since each node may need to be visited multiple times and the graph is a dag. 30 | 31 | # Complexity 32 | 33 | O(2^N * N) time complexity in the worst case, where N is the number of nodes (2^N is the number of vertices). The space complexity is O(N) for the recursion stack and path storage. 34 | -------------------------------------------------------------------------------- /backtracking/797. All Paths From Source to Target/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]: 3 | def backtrack(node, path): 4 | if node == len(graph) - 1: 5 | result.append(path[:]) 6 | return 7 | for next_node in graph[node]: 8 | path.append(next_node) 9 | backtrack(next_node, path) 10 | path.pop() 11 | 12 | result = [] 13 | backtrack(0, [0]) 14 | return result 15 | -------------------------------------------------------------------------------- /backtracking/93. Restore IP Addresses/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | A valid IP address consists of exactly four integers separated by single dots. Each integer is between 0 and 255 (inclusive) and cannot have leading zeros. 4 | 5 | For example, "0.1.2.201" and "192.168.1.1" are valid IP addresses, but "0.011.255.245", "192.168.1.312" and "192.168@1.1" are invalid IP addresses. 6 | Given a string s containing only digits, return all possible valid IP addresses that can be formed by inserting dots into s. You are not allowed to reorder or remove any digits in s. You may return the valid IP addresses in any order. 7 | 8 | 9 | 10 | Example 1: 11 | 12 | Input: s = "25525511135" 13 | Output: ["255.255.11.135","255.255.111.35"] 14 | Example 2: 15 | 16 | Input: s = "0000" 17 | Output: ["0.0.0.0"] 18 | Example 3: 19 | 20 | Input: s = "101023" 21 | Output: ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"] 22 | 23 | 24 | Constraints: 25 | 26 | 1 <= s.length <= 20 27 | s consists of digits only. 28 | 29 | # Thought process 30 | 31 | Backtracking problem where at each recursion step we have to validate the current octet. 32 | 33 | # Complexity 34 | 35 | The time complexity of this solution is O(1) because an IP address always has 4 parts, and each part can have at most 3 digits. 36 | 37 | The space complexity is O(1) for the same reason - the maximum depth of recursion is 4. 38 | 39 | -------------------------------------------------------------------------------- /backtracking/93. Restore IP Addresses/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | 3 | MAX_OCTETS = 4 4 | MAX_OCTET_VALUE = 255 5 | 6 | def restoreIpAddresses(self, s: str) -> List[str]: 7 | result = [] 8 | current = [] 9 | self.backtrack(s, current, 0, result) 10 | return result 11 | 12 | def backtrack(self, s, current, octet, result): 13 | 14 | if (octet == self.MAX_OCTETS and not s): 15 | result.append(".".join(current)) 16 | return 17 | 18 | # impossible to get a valid result 19 | if (octet == self.MAX_OCTETS or not s): 20 | return 21 | 22 | current.append(s[0]) 23 | self.backtrack(s[1:], current, octet+1, result) 24 | current.pop() 25 | 26 | if len(s) >= 2 and self.is_valid_octet(s[:2]): 27 | current.append(s[:2]) 28 | self.backtrack(s[2:], current, octet+1, result) 29 | current.pop() 30 | 31 | if len(s) >= 3 and self.is_valid_octet(s[:3]): 32 | current.append(s[:3]) 33 | self.backtrack(s[3:], current, octet+1, result) 34 | current.pop() 35 | 36 | @staticmethod 37 | def is_valid_octet(octet: str) -> bool: 38 | if len(octet) > 1 and octet[0] == '0': 39 | return False 40 | return len(octet) > 0 and int(octet) <= Solution.MAX_OCTET_VALUE 41 | -------------------------------------------------------------------------------- /backtracking/967. Numbers With Same Consecutive Differences/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given two integers n and k, return an array of all the integers of length n where the difference between every two consecutive digits is k. You may return the answer in any order. 4 | 5 | Note that the integers should not have leading zeros. Integers as 02 and 043 are not allowed. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: n = 3, k = 7 12 | Output: [181,292,707,818,929] 13 | Explanation: Note that 070 is not a valid number, because it has leading zeroes. 14 | Example 2: 15 | 16 | Input: n = 2, k = 1 17 | Output: [10,12,21,23,32,34,43,45,54,56,65,67,76,78,87,89,98] 18 | 19 | 20 | Constraints: 21 | 22 | 2 <= n <= 9 23 | 0 <= k <= 9 24 | -------------------------------------------------------------------------------- /binary-search-trees/270. Closest Binary Search Tree Value/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given the root of a binary search tree and a target value, return the value in the BST that is closest to the target. If there are multiple answers, print the smallest. 4 | 5 | Example 1: 6 | 7 | 8 | Input: root = [4,2,5,1,3], target = 3.714286 9 | Output: 4 10 | Example 2: 11 | 12 | Input: root = [1], target = 4.428571 13 | Output: 1 14 | 15 | 16 | Constraints: 17 | 18 | The number of nodes in the tree is in the range [1, 104]. 19 | 0 <= Node.val <= 109 20 | -109 <= target <= 109 21 | 22 | # Thought Process 23 | 24 | Use the BST property to efficiently navigate the tree and find the closest value. 25 | When you meet a number as close to the the target as the current closest, pick the smallest. 26 | 27 | # Complexity 28 | 29 | O(logN) time for a balanced BST or O(h) otherwise, O(1) space 30 | -------------------------------------------------------------------------------- /binary-search-trees/270. Closest Binary Search Tree Value/solution.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * public class TreeNode { 4 | * public int val; 5 | * public TreeNode left; 6 | * public TreeNode right; 7 | * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { 8 | * this.val = val; 9 | * this.left = left; 10 | * this.right = right; 11 | * } 12 | * } 13 | */ 14 | public class Solution { 15 | public int ClosestValue(TreeNode root, double target) { 16 | 17 | int closest = root.val; 18 | 19 | while (root != null) { 20 | double diffToCurrent = Math.Abs(root.val-target); 21 | double diffToClosest = Math.Abs(closest-target); 22 | if ((diffToCurrent < diffToClosest) || (diffToCurrent == diffToClosest && root.val < closest)){ 23 | closest = root.val; 24 | } 25 | 26 | root = target < root.val ? root.left : root.right; 27 | } 28 | return closest; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /binary-search-trees/701. Insert into a Binary Search Tree/Solution.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * public class TreeNode { 4 | * public int val; 5 | * public TreeNode left; 6 | * public TreeNode right; 7 | * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { 8 | * this.val = val; 9 | * this.left = left; 10 | * this.right = right; 11 | * } 12 | * } 13 | */ 14 | public class Solution { 15 | public TreeNode InsertIntoBST(TreeNode root, int val) { 16 | 17 | if (root == null) return new TreeNode(val); 18 | 19 | if (val < root.val) { 20 | root.left = InsertIntoBST(root.left, val); 21 | } 22 | else { 23 | root.right = InsertIntoBST(root.right, val); 24 | } 25 | 26 | return root; 27 | 28 | } 29 | } 30 | 31 | // iterative 32 | public TreeNode InsertIntoBST(TreeNode root, int val) { 33 | 34 | if (root == null) return new TreeNode(val); 35 | 36 | while (root != null) { 37 | if (val < root.val) { 38 | if (root.left == null) { 39 | root.left = new TreeNode(val); 40 | break; 41 | } 42 | root = root.left; 43 | } 44 | else { 45 | if (root.right == null) { 46 | root.right = new TreeNode(val); 47 | break; 48 | } 49 | root = root.right; 50 | } 51 | } 52 | 53 | return root; 54 | } 55 | -------------------------------------------------------------------------------- /binary-search-trees/701. Insert into a Binary Search Tree/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST. 4 | 5 | Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can return any of them. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | 12 | Input: root = [4,2,7,1,3], val = 5 13 | Output: [4,2,7,1,3,5] 14 | Explanation: Another accepted tree is: 15 | 16 | Example 2: 17 | 18 | Input: root = [40,20,60,10,30,50,70], val = 25 19 | Output: [40,20,60,10,30,50,70,null,null,25] 20 | Example 3: 21 | 22 | Input: root = [4,2,7,1,3,null,null,null,null,null,null], val = 5 23 | Output: [4,2,7,1,3,5] 24 | 25 | 26 | Constraints: 27 | 28 | The number of nodes in the tree will be in the range [0, 104]. 29 | -108 <= Node.val <= 108 30 | All the values Node.val are unique. 31 | -108 <= val <= 108 32 | It's guaranteed that val does not exist in the original BST. 33 | 34 | # Thought Process 35 | 36 | The problem is simple because there is no requirement on where to inser the node. You can reuse the standard search algorithm and append the new node as a leaf. 37 | 38 | # Complexity 39 | 40 | O(logN) time, space is O(N) recursive, O(1) iterative 41 | -------------------------------------------------------------------------------- /binary-search/1231. Divide Chocolate: -------------------------------------------------------------------------------- 1 | # Description 2 | -------------------------------------------------------------------------------- /binary-search/1283. Find the Smallest Divisor Given a Threshold/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int SmallestDivisor(int[] nums, int threshold) { 3 | 4 | int left = 1, right = nums.Max(); 5 | 6 | while (left < right) { 7 | 8 | int mid = (left + right) / 2; 9 | if (SumOfDivisions(nums, mid) <= threshold) { 10 | right = mid; 11 | } 12 | else { 13 | left = mid + 1; 14 | } 15 | } 16 | return left; 17 | } 18 | 19 | private int SumOfDivisions(int[] nums, int divisor) { 20 | // ceiling withou floating points 21 | return nums.Sum(n => (n + divisor - 1) / divisor); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /binary-search/1283. Find the Smallest Divisor Given a Threshold/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an array of integers nums and an integer threshold, we will choose a positive integer divisor, divide all the array by it, and sum the division's result. Find the smallest divisor such that the result mentioned above is less than or equal to threshold. 4 | 5 | Each result of the division is rounded to the nearest integer greater than or equal to that element. (For example: 7/3 = 3 and 10/2 = 5). 6 | 7 | The test cases are generated so that there will be an answer. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: nums = [1,2,5,9], threshold = 6 14 | Output: 5 15 | Explanation: We can get a sum to 17 (1+2+5+9) if the divisor is 1. 16 | If the divisor is 4 we can get a sum of 7 (1+1+2+3) and if the divisor is 5 the sum will be 5 (1+1+1+2). 17 | Example 2: 18 | 19 | Input: nums = [44,22,33,11,1], threshold = 5 20 | Output: 44 21 | 22 | 23 | Constraints: 24 | 25 | 1 <= nums.length <= 5 * 104 26 | 1 <= nums[i] <= 106 27 | nums.length <= threshold <= 106 28 | 29 | # Thought Process 30 | 31 | The possible divisors for the solution are for sure between 1 and the maximum nuber in nums. 32 | Use binary search to find the smallest divisor. 33 | 34 | # Complexity 35 | 36 | The time complexity is (O(n log m), where n is the length of nums and m is the maximum value in nums. Space is O(1). 37 | 38 | -------------------------------------------------------------------------------- /binary-search/2389. Longest Subsequence With Limited Sum/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int[] AnswerQueries(int[] nums, int[] queries) { 3 | 4 | var prefixSum = (int[])nums.Clone(); 5 | Array.Sort(prefixSum); 6 | 7 | for (int i = 1; i < prefixSum.Length; i++) { 8 | prefixSum[i] += prefixSum[i - 1]; 9 | } 10 | 11 | int[] result = new int[queries.Length]; 12 | 13 | for (int i = 0; i < queries.Length; i++) { 14 | int index = Array.BinarySearch(prefixSum, queries[i]); 15 | result[i] = index < 0 ? ~index : index + 1; 16 | } 17 | 18 | return result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /binary-search/2389. Longest Subsequence With Limited Sum/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an integer array nums of length n, and an integer array queries of length m. 4 | 5 | Return an array answer of length m where answer[i] is the maximum size of a subsequence that you can take from nums such that the sum of its elements is less than or equal to queries[i]. 6 | 7 | A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: nums = [4,5,2,1], queries = [3,10,21] 14 | Output: [2,3,4] 15 | Explanation: We answer the queries as follows: 16 | - The subsequence [2,1] has a sum less than or equal to 3. It can be proven that 2 is the maximum size of such a subsequence, so answer[0] = 2. 17 | - The subsequence [4,5,1] has a sum less than or equal to 10. It can be proven that 3 is the maximum size of such a subsequence, so answer[1] = 3. 18 | - The subsequence [4,5,2,1] has a sum less than or equal to 21. It can be proven that 4 is the maximum size of such a subsequence, so answer[2] = 4. 19 | Example 2: 20 | 21 | Input: nums = [2,3,4,5], queries = [1] 22 | Output: [0] 23 | Explanation: The empty subsequence is the only subsequence that has a sum less than or equal to 1, so answer[0] = 0. 24 | 25 | 26 | Constraints: 27 | 28 | n == nums.length 29 | m == queries.length 30 | 1 <= n, m <= 1000 31 | 1 <= nums[i], queries[i] <= 106 32 | 33 | # Thought Process 34 | 35 | For a subsequence, the order of the element doesnn't matter -> sort the input 36 | Sorting the input allow to take smaller elements first which maximize the subsequence length 37 | Binary search can speed up each query 38 | 39 | # Complexity 40 | 41 | Time complexity is (O(n log n + m log n), where n is the length of nums and m is the length of queries (sorting + m queries in log time). 42 | Space complexity is O(n) for the prefix sum array. 43 | -------------------------------------------------------------------------------- /binary-search/35. Search Insert Position/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int SearchInsert(int[] nums, int target) { 3 | 4 | int left = 0, right = nums.Length-1; 5 | 6 | while (left <= right) { 7 | int mid = left + (right - left) / 2; 8 | if (nums[mid] == target) { 9 | return mid; 10 | } 11 | 12 | if (nums[mid] < target) { 13 | left = mid+1; 14 | } 15 | else { 16 | right = mid-1; 17 | } 18 | } 19 | return left; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /binary-search/35. Search Insert Position/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. 4 | 5 | You must write an algorithm with O(log n) runtime complexity. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: nums = [1,3,5,6], target = 5 12 | Output: 2 13 | Example 2: 14 | 15 | Input: nums = [1,3,5,6], target = 2 16 | Output: 1 17 | Example 3: 18 | 19 | Input: nums = [1,3,5,6], target = 7 20 | Output: 4 21 | 22 | 23 | Constraints: 24 | 25 | 1 <= nums.length <= 104 26 | -104 <= nums[i] <= 104 27 | nums contains distinct values sorted in ascending order. 28 | -104 <= target <= 104 29 | 30 | # Thought Process 31 | 32 | Classic binary search implementation for array with distinct elements: 33 | 34 | - stop condition left <= right 35 | - advance left, decrease right 36 | 37 | Test: [1,3] target 0, 2, 4 38 | 39 | # Complexity 40 | 41 | The time complexity is O(log n), and the space complexity is O(1). 42 | -------------------------------------------------------------------------------- /dynamic-programming/309. Best Time to Buy and Sell Stock with Cooldown/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private Dictionary<(int, bool), int> memo; 4 | 5 | public int MaxProfit(int[] prices) { 6 | memo = new Dictionary<(int, bool), int>(); 7 | return Dfs(prices, 0, true); 8 | } 9 | 10 | private int Dfs(int[] prices, int day, bool canBuy) { 11 | 12 | if (day >= prices.Length) { 13 | return 0; 14 | } 15 | 16 | if (memo.ContainsKey((day,canBuy))) { 17 | return memo[(day,canBuy)]; 18 | } 19 | 20 | int doNothing = Dfs(prices, day+1, canBuy); 21 | int sellOrBuy; 22 | if (canBuy) { 23 | sellOrBuy = Dfs(prices, day+1, false) - prices[day]; 24 | } 25 | else { 26 | sellOrBuy = Dfs(prices, day+2, true) + prices[day]; 27 | } 28 | 29 | int maxProfit = Math.Max(doNothing, sellOrBuy); 30 | memo[(day,canBuy)] = maxProfit; 31 | return maxProfit; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /dynamic-programming/309. Best Time to Buy and Sell Stock with Cooldown/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | ou are given an array prices where prices[i] is the price of a given stock on the ith day. 4 | 5 | Find the maximum profit you can achieve. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times) with the following restrictions: 6 | 7 | After you sell your stock, you cannot buy stock on the next day (i.e., cooldown one day). 8 | Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again). 9 | 10 | 11 | 12 | Example 1: 13 | 14 | Input: prices = [1,2,3,0,2] 15 | Output: 3 16 | Explanation: transactions = [buy, sell, cooldown, buy, sell] 17 | Example 2: 18 | 19 | Input: prices = [1] 20 | Output: 0 21 | 22 | 23 | Constraints: 24 | 25 | 1 <= prices.length <= 5000 26 | 0 <= prices[i] <= 1000 27 | 28 | # Thought Process 29 | 30 | This is a dynamic programming problem: we need to try all the possible combinations of buying and selling stocks and subproblems overlap. 31 | 32 | At any given day, we can be in one of two states: 33 | a) Holding a stock 34 | b) Not holding a stock 35 | 36 | If we're holding a stock, we can: 37 | a) Continue holding it 38 | b) Sell it (and jump the next day) 39 | If we're not holding a stock, we can: 40 | a) Continue not holding 41 | b) Buy a stock 42 | 43 | The top-down recursive solution uses the following structure: 44 | 45 | - index (current day) 46 | - canBuy (boolean indicating if we can buy a stock) 47 | 48 | At each step (day) you can decide if do nothing or sell/buy. After we sell we skip two indexes. 49 | 50 | The bottom-up solution uses the following recurrence: 51 | 52 | When we don't own a stock (dp[i][0]), we have two options: 53 | 54 | Do nothing (stay without stock): dp[i-1][0] 55 | Swll a stock we owned yesterday, considering cooldown: dp[i-1][1] + prices[i] 56 | So: dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) 57 | 58 | When we own a stock (dp[i][1]), we have two options: 59 | 60 | Do nothing (keep holding): dp[i-1][1] 61 | Buy a stock, but due to cooldown, we must not have sold a stock yesterday: dp[i-2][0] - prices[i] 62 | So: dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i]) 63 | 64 | where 65 | 66 | hold[i]: Maximum profit on day i if we're holding a stock 67 | notHold[i]: Maximum profit on day i if we're not holding a stock 68 | -------------------------------------------------------------------------------- /dynamic-programming/322. Coin Change/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int CoinChange(int[] coins, int amount) { 3 | 4 | var dp = new int[amount + 1]; 5 | Array.Fill(dp, amount + 1); 6 | dp[0] = 0; 7 | 8 | for (int i = 1; i <= amount; i++) { 9 | foreach (var coin in coins) { 10 | if (i - coin >= 0) { 11 | dp[i] = Math.Min(dp[i], 1+dp[i-coin]); 12 | } 13 | } 14 | } 15 | return dp[amount] > amount ? -1 : dp[amount] ; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /dynamic-programming/322. Coin Change/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money. 4 | 5 | Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1. 6 | 7 | You may assume that you have an infinite number of each kind of coin. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: coins = [1,2,5], amount = 11 14 | Output: 3 15 | Explanation: 11 = 5 + 5 + 1 16 | Example 2: 17 | 18 | Input: coins = [2], amount = 3 19 | Output: -1 20 | Example 3: 21 | 22 | Input: coins = [1], amount = 0 23 | Output: 0 24 | 25 | 26 | Constraints: 27 | 28 | 1 <= coins.length <= 12 29 | 1 <= coins[i] <= 231 - 1 30 | 0 <= amount <= 104 31 | -------------------------------------------------------------------------------- /dynamic-programming/70. Climbing Stairs/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int ClimbStairs(int n) { 3 | 4 | if (n <= 2) return n; 5 | 6 | int oneStepBefore = 2; 7 | int twoStepsBefore = 1; 8 | 9 | for (int i = 3; i <=n; i++) { 10 | int tmp = oneStepBefore; 11 | oneStepBefore += twoStepsBefore; 12 | twoStepsBefore = tmp; 13 | } 14 | return oneStepBefore; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dynamic-programming/70. Climbing Stairs/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are climbing a staircase. It takes n steps to reach the top. 4 | 5 | Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: n = 2 12 | Output: 2 13 | Explanation: There are two ways to climb to the top. 14 | 1. 1 step + 1 step 15 | 2. 2 steps 16 | Example 2: 17 | 18 | Input: n = 3 19 | Output: 3 20 | Explanation: There are three ways to climb to the top. 21 | 1. 1 step + 1 step + 1 step 22 | 2. 1 step + 2 steps 23 | 3. 2 steps + 1 step 24 | 25 | 26 | Constraints: 27 | 28 | 1 <= n <= 45 29 | 30 | # Thought process 31 | 32 | The number of ways to reach the current step are the sum of the number of ways to reach thhe two steps before. 33 | Using this recurrence the problem can be solved with DP. 34 | 35 | # Complexity 36 | 37 | O(n) time complexity and O(1) space complexity 38 | -------------------------------------------------------------------------------- /graphs/127. Word Ladder/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int LadderLength(string beginWord, string endWord, IList wordList) { 3 | if (!wordList.Contains(endWord)) return 0; 4 | 5 | var wordSet = new HashSet(wordList); 6 | var queue = new Queue(); 7 | var visited = new HashSet(); 8 | 9 | queue.Enqueue(beginWord); 10 | visited.Add(beginWord); 11 | int length = 1; 12 | 13 | while (queue.Count > 0) { 14 | int levelSize = queue.Count; 15 | for (int i = 0; i < levelSize; i++) { 16 | var currentWord = queue.Dequeue(); 17 | if (currentWord == endWord) return length; 18 | // look for possible edges 19 | for (int j = 0; j < currentWord.Length; j++) { 20 | char[] chars = currentWord.ToCharArray(); 21 | for (char c = 'a'; c <= 'z'; c++) { 22 | chars[j] = c; 23 | string newWord = new string(chars); 24 | if (wordSet.Contains(newWord) && !visited.Contains(newWord)) { 25 | queue.Enqueue(newWord); 26 | visited.Add(newWord); 27 | } 28 | } 29 | } 30 | } 31 | length++; 32 | } 33 | return 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /graphs/1306. Jump Game III/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public bool CanReach(int[] arr, int start) { 3 | var queue = new Queue(); 4 | int length = arr.Length; 5 | var visited = new bool[length]; 6 | 7 | queue.Enqueue(start); 8 | visited[start] = true; 9 | 10 | while (queue.Count > 0) { 11 | int index = queue.Dequeue(); 12 | if (arr[index] == 0) return true; 13 | 14 | int[] nextIndices = { index + arr[index], index - arr[index] }; 15 | foreach (int nextIndex in nextIndices) { 16 | if (nextIndex >= 0 && nextIndex < length && !visited[nextIndex]) { 17 | queue.Enqueue(nextIndex); 18 | visited[nextIndex] = true; 19 | } 20 | } 21 | } 22 | return false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /graphs/1306. Jump Game III/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an array of non-negative integers arr, you are initially positioned at start index of the array. When you are at index i, you can jump to i + arr[i] or i - arr[i], check if you can reach any index with value 0. 4 | 5 | Notice that you can not jump outside of the array at any time. 6 | 7 | Example 1: 8 | 9 | Input: arr = [4,2,3,0,3,1,2], start = 5 10 | Output: true 11 | Explanation: 12 | All possible ways to reach at index 3 with value 0 are: 13 | index 5 -> index 4 -> index 1 -> index 3 14 | index 5 -> index 6 -> index 4 -> index 1 -> index 3 15 | Example 2: 16 | 17 | Input: arr = [4,2,3,0,3,1,2], start = 0 18 | Output: true 19 | Explanation: 20 | One possible way to reach at index 3 with value 0 is: 21 | index 0 -> index 4 -> index 1 -> index 3 22 | Example 3: 23 | 24 | Input: arr = [3,0,2,1,2], start = 2 25 | Output: false 26 | Explanation: There is no way to reach at index 1 with value 0. 27 | 28 | 29 | Constraints: 30 | 31 | 1 <= arr.length <= 5 * 104 32 | 0 <= arr[i] < arr.length 33 | 0 <= start < arr.length 34 | 35 | # Thought process 36 | 37 | Each integer can be seen as a node of a graph, having two edges to positions i+val and i-val. Both bfs and dfs solve the problem. 38 | 39 | # Complexity 40 | 41 | O(N) time and space 42 | -------------------------------------------------------------------------------- /graphs/1971. Find if Path Exists in Graph/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public bool ValidPath(int n, int[][] edges, int source, int destination) { 3 | 4 | var graph = BuildGraph(n, edges); 5 | var visited = new bool[n]; 6 | return Dfs(graph, source, destination, visited); 7 | } 8 | 9 | private bool Dfs(IList[] graph, int vertex, int destination, bool[] visited) { 10 | 11 | if (vertex == destination) { 12 | return true; 13 | } 14 | 15 | visited[vertex] = true; 16 | 17 | foreach (int adjacent in graph[vertex]) { 18 | if (!visited[adjacent] && Dfs(graph, adjacent, destination, visited)) { 19 | return true; 20 | } 21 | } 22 | 23 | return false; 24 | } 25 | 26 | private List[] BuildGraph(int n, int[][] edges) { 27 | var graph = new List[n]; 28 | for (int i = 0; i < n; i++) { 29 | graph[i] = new List(); 30 | } 31 | 32 | foreach (var edge in edges) { 33 | graph[edge[0]].Add(edge[1]); 34 | graph[edge[1]].Add(edge[0]); 35 | } 36 | 37 | return graph; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /graphs/1971. Find if Path Exists in Graph/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | There is a bi-directional graph with n vertices, where each vertex is labeled from 0 to n - 1 (inclusive). The edges in the graph are represented as a 2D integer array edges, where each edges[i] = [ui, vi] denotes a bi-directional edge between vertex ui and vertex vi. Every vertex pair is connected by at most one edge, and no vertex has an edge to itself. 4 | 5 | You want to determine if there is a valid path that exists from vertex source to vertex destination. 6 | 7 | Given edges and the integers n, source, and destination, return true if there is a valid path from source to destination, or false otherwise. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | 14 | Input: n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2 15 | Output: true 16 | Explanation: There are two paths from vertex 0 to vertex 2: 17 | - 0 → 1 → 2 18 | - 0 → 2 19 | Example 2: 20 | 21 | 22 | Input: n = 6, edges = [[0,1],[0,2],[3,5],[5,4],[4,3]], source = 0, destination = 5 23 | Output: false 24 | Explanation: There is no path from vertex 0 to vertex 5. 25 | 26 | 27 | Constraints: 28 | 29 | 1 <= n <= 2 * 105 30 | 0 <= edges.length <= 2 * 105 31 | edges[i].length == 2 32 | 0 <= ui, vi <= n - 1 33 | ui != vi 34 | 0 <= source, destination <= n - 1 35 | There are no duplicate edges. 36 | There are no self edges. 37 | 38 | # Thought Process 39 | 40 | Simple search on an undirected graph. The only thing to do is converting the list of edge representation into an adjacency list. 41 | 42 | # Complexity 43 | 44 | The time complexity is O(V + E), where V is the number of vertices and E is the number of edges. The space complexity is also O(V + E) for the graph representation and the visited array. 45 | -------------------------------------------------------------------------------- /graphs/2101. Detonate the Maximum Bombs/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MaximumDetonation(int[][] bombs) { 3 | 4 | int maxBombs = 0; 5 | int n = bombs.Length; 6 | var graph = BuildGraph(bombs); 7 | 8 | for (int i = 0; i < n; i++) { 9 | var visited = new BitArray(n); 10 | maxBombs = Math.Max(maxBombs, Dfs(graph, i, visited)); 11 | if (maxBombs == n) break; 12 | } 13 | return maxBombs; 14 | } 15 | 16 | private int Dfs(List[] graph, int nodeIndex, BitArray visited) { 17 | 18 | int reachedNodes = 1; 19 | visited[nodeIndex] = true; 20 | foreach (var adjacent in graph[nodeIndex]) { 21 | if (!visited[adjacent]) { 22 | reachedNodes += Dfs(graph, adjacent, visited); 23 | } 24 | } 25 | return reachedNodes; 26 | } 27 | 28 | private List[] BuildGraph(int[][] bombs) { 29 | 30 | int n = bombs.Length; 31 | var graph = new List[n]; 32 | 33 | for (int i = 0; i < n; i++) { 34 | graph[i] = new List(); 35 | for (int j = 0; j < n; j++) { 36 | if (i != j && CanDetonate(bombs[i], bombs[j])) { 37 | graph[i].Add(j); 38 | } 39 | } 40 | } 41 | 42 | return graph; 43 | } 44 | 45 | private bool CanDetonate(int[] bomb1, int[] bomb2) { 46 | long dx = bomb1[0] - bomb2[0]; 47 | long dy = bomb1[1] - bomb2[1]; 48 | long r = bomb1[2]; 49 | return dx * dx + dy * dy <= r * r; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /graphs/2101. Detonate the Maximum Bombs/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given a list of bombs. The range of a bomb is defined as the area where its effect can be felt. This area is in the shape of a circle with the center as the location of the bomb. 4 | 5 | The bombs are represented by a 0-indexed 2D integer array bombs where bombs[i] = [xi, yi, ri]. xi and yi denote the X-coordinate and Y-coordinate of the location of the ith bomb, whereas ri denotes the radius of its range. 6 | 7 | You may choose to detonate a single bomb. When a bomb is detonated, it will detonate all bombs that lie in its range. These bombs will further detonate the bombs that lie in their ranges. 8 | 9 | Given the list of bombs, return the maximum number of bombs that can be detonated if you are allowed to detonate only one bomb. 10 | 11 | Example 1: 12 | 13 | Input: bombs = [[2,1,3],[6,1,4]] 14 | Output: 2 15 | Explanation: 16 | The above figure shows the positions and ranges of the 2 bombs. 17 | If we detonate the left bomb, the right bomb will not be affected. 18 | But if we detonate the right bomb, both bombs will be detonated. 19 | So the maximum bombs that can be detonated is max(1, 2) = 2. 20 | Example 2: 21 | 22 | 23 | Input: bombs = [[1,1,5],[10,10,5]] 24 | Output: 1 25 | Explanation: 26 | Detonating either bomb will not detonate the other bomb, so the maximum number of bombs that can be detonated is 1. 27 | Example 3: 28 | 29 | 30 | Input: bombs = [[1,2,3],[2,3,1],[3,4,2],[4,5,3],[5,6,4]] 31 | Output: 5 32 | Explanation: 33 | The best bomb to detonate is bomb 0 because: 34 | - Bomb 0 detonates bombs 1 and 2. The red circle denotes the range of bomb 0. 35 | - Bomb 2 detonates bomb 3. The blue circle denotes the range of bomb 2. 36 | - Bomb 3 detonates bomb 4. The green circle denotes the range of bomb 3. 37 | Thus all 5 bombs are detonated. 38 | 39 | 40 | Constraints: 41 | 42 | 1 <= bombs.length <= 100 43 | bombs[i].length == 3 44 | 1 <= xi, yi, ri <= 105 45 | 46 | # Thought process 47 | 48 | Bombs can be seen as nodes of a directed graph. The outgoing edges from a node are connecting it to other bombs in its range. 49 | 50 | If we build the graph, we find the total number of bombs that will be detonated if we start from a fixed bomb using dfs. 51 | 52 | But how do we build the graph? 53 | 54 | We check every possible combination of centers and we use Pythagoras to see if thier distance is less than the radius. 55 | 56 | # Complexity 57 | 58 | The time complexity is O(n^2) for building the graph and O(n^2) in the worst case for DFS (if all bombs are connected), where n is the number of bombs. 59 | The space complexity is O(n^2) for the graph and O(n) for the recursion stack and visited array. 60 | -------------------------------------------------------------------------------- /graphs/2368. Reachable Nodes With Restrictions/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | private HashSet _restrictedSet; 3 | 4 | public int ReachableNodes(int n, int[][] edges, int[] restricted) { 5 | var tree = BuildTree(n, edges); 6 | _restrictedSet = new HashSet(restricted); 7 | return Dfs(tree, 0, -1); 8 | } 9 | 10 | private int Dfs(List[] tree, int node, int parent) { 11 | if (_restrictedSet.Contains(node)) { 12 | return 0; 13 | } 14 | 15 | int reachable = 1; 16 | foreach (var adjacent in tree[node]) { 17 | if (adjacent != parent) { 18 | reachable += Dfs(tree, adjacent, node); 19 | } 20 | } 21 | return reachable; 22 | } 23 | 24 | private List[] BuildTree(int n, int[][] edges) { 25 | var tree = new List[n]; 26 | for (int i = 0; i < n; i++) { 27 | tree[i] = new List(); 28 | } 29 | 30 | foreach (var edge in edges) { 31 | tree[edge[0]].Add(edge[1]); 32 | tree[edge[1]].Add(edge[0]); 33 | } 34 | return tree; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /graphs/2368. Reachable Nodes With Restrictions/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | There is an undirected tree with n nodes labeled from 0 to n - 1 and n - 1 edges. 4 | 5 | You are given a 2D integer array edges of length n - 1 where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the tree. You are also given an integer array restricted which represents restricted nodes. 6 | 7 | Return the maximum number of nodes you can reach from node 0 without visiting a restricted node. 8 | 9 | Note that node 0 will not be a restricted node. 10 | 11 | Example 1: 12 | 13 | Input: n = 7, edges = [[0,1],[1,2],[3,1],[4,0],[0,5],[5,6]], restricted = [4,5] 14 | Output: 4 15 | Explanation: The diagram above shows the tree. 16 | We have that [0,1,2,3] are the only nodes that can be reached from node 0 without visiting a restricted node. 17 | Example 2: 18 | 19 | 20 | Input: n = 7, edges = [[0,1],[0,2],[0,5],[0,4],[3,2],[6,5]], restricted = [4,2,1] 21 | Output: 3 22 | Explanation: The diagram above shows the tree. 23 | We have that [0,5,6] are the only nodes that can be reached from node 0 without visiting a restricted node. 24 | 25 | 26 | Constraints: 27 | 28 | 2 <= n <= 105 29 | edges.length == n - 1 30 | edges[i].length == 2 31 | 0 <= ai, bi < n 32 | ai != bi 33 | edges represents a valid tree. 34 | 1 <= restricted.length < n 35 | 1 <= restricted[i] < n 36 | All the values of restricted are unique. 37 | 38 | # Thought Process 39 | 40 | The problem can be solved with a standard DFS approach where the restricted nodes can be considered as already visited. 41 | Since the input is a tree and not a graph, it is not necessary to mark the current node as visited, but it is sufficient to be suro not to visit the parent of a node. 42 | 43 | # Complexity 44 | 45 | The time complexity is O(N), where N is the number of nodes in the tree, as we visit each node once. The space complexity is O(N) for the tree representation and the call stack in the worst case. 46 | -------------------------------------------------------------------------------- /graphs/323. Number of Connected Components in an Undirected Graph/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int CountComponents(int n, int[][] edges) { 3 | var graph = BuildGraph(n, edges); 4 | var visited = new bool[n]; 5 | int components = 0; 6 | for (int i = 0; i < n; i++) { 7 | if (!visited[i]) { 8 | components += 1; 9 | Dfs(graph, i, visited); 10 | } 11 | } 12 | return components; 13 | } 14 | 15 | private void Dfs(List[] graph, int node, bool[] visited) { 16 | 17 | visited[node] = true; 18 | foreach (int adjacent in graph[node]) { 19 | if (!visited[adjacent]) { 20 | Dfs(graph, adjacent, visited); 21 | } 22 | } 23 | } 24 | 25 | private List[] BuildGraph(int n, int[][] edges) { 26 | var graph = new List[n]; 27 | for (int i = 0; i < n; i++) { 28 | graph[i] = new List(); 29 | } 30 | 31 | foreach (var edge in edges) { 32 | graph[edge[0]].Add(edge[1]); 33 | graph[edge[1]].Add(edge[0]); 34 | } 35 | return graph; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /graphs/323. Number of Connected Components in an Undirected Graph/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You have a graph of n nodes. You are given an integer n and an array edges where edges[i] = [ai, bi] indicates that there is an edge between ai and bi in the graph. 4 | 5 | Return the number of connected components in the graph. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | 12 | Input: n = 5, edges = [[0,1],[1,2],[3,4]] 13 | Output: 2 14 | Example 2: 15 | 16 | 17 | Input: n = 5, edges = [[0,1],[1,2],[2,3],[3,4]] 18 | Output: 1 19 | 20 | 21 | Constraints: 22 | 23 | 1 <= n <= 2000 24 | 1 <= edges.length <= 5000 25 | edges[i].length == 2 26 | 0 <= ai <= bi < n 27 | ai != bi 28 | There are no repeated edges. 29 | 30 | # Though Process 31 | 32 | The problem can be solved by running dfs algo on all the graph vertices. Every time you start a dfs on an unvisited node, you discover a new component. 33 | 34 | # Complexity 35 | 36 | The time complexity is O(V + E), where V is the number of vertices and E is the number of edges. The space complexity is also O(V + E) for the graph representation and the visited array. 37 | -------------------------------------------------------------------------------- /graphs/433. Minimum Genetic Mutation/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | private static readonly char[] Choices = {'A', 'C', 'G', 'T'}; 3 | public int MinMutation(string startGene, string endGene, string[] bank) { 4 | 5 | if (!bank.Contains(endGene)) return -1; 6 | 7 | int mutations = 0; 8 | var queue = new Queue(); 9 | queue.Enqueue(startGene); 10 | var discovered = new HashSet(); 11 | discovered.Add(startGene); 12 | 13 | while (queue.Count > 0) { 14 | int levelSize = queue.Count; 15 | for (int i = 0; i < levelSize; i++) { 16 | var currentGene = queue.Dequeue(); 17 | if (currentGene == endGene) { 18 | return mutations; 19 | } 20 | foreach (var gene in FindValidMutations(currentGene, bank, discovered)) { 21 | discovered.Add(gene); 22 | queue.Enqueue(gene); 23 | } 24 | } 25 | mutations++; 26 | } 27 | return -1; 28 | } 29 | 30 | private List FindValidMutations(string gene, string[] bank, HashSet discovered){ 31 | var mutations = new List(); 32 | char[] geneArray = gene.ToCharArray(); 33 | for (int i = 0; i < geneArray.Length; i++) { 34 | var originalChar = geneArray[i]; 35 | foreach (char c in Choices) { 36 | if (c != originalChar) { 37 | geneArray[i] = c; 38 | var newGene = new string(geneArray); 39 | if (bank.Contains(newGene) && !discovered.Contains(newGene)) { 40 | mutations.Add(newGene); 41 | } 42 | } 43 | } 44 | geneArray[i] = originalChar; 45 | } 46 | return mutations; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /graphs/433. Minimum Genetic Mutation/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | A gene string can be represented by an 8-character long string, with choices from 'A', 'C', 'G', and 'T'. 4 | 5 | Suppose we need to investigate a mutation from a gene string startGene to a gene string endGene where one mutation is defined as one single character changed in the gene string. 6 | 7 | For example, "AACCGGTT" --> "AACCGGTA" is one mutation. 8 | There is also a gene bank bank that records all the valid gene mutations. A gene must be in bank to make it a valid gene string. 9 | 10 | Given the two gene strings startGene and endGene and the gene bank bank, return the minimum number of mutations needed to mutate from startGene to endGene. If there is no such a mutation, return -1. 11 | 12 | Note that the starting point is assumed to be valid, so it might not be included in the bank. 13 | 14 | 15 | 16 | Example 1: 17 | 18 | Input: startGene = "AACCGGTT", endGene = "AACCGGTA", bank = ["AACCGGTA"] 19 | Output: 1 20 | Example 2: 21 | 22 | Input: startGene = "AACCGGTT", endGene = "AAACGGTA", bank = ["AACCGGTA","AACCGCTA","AAACGGTA"] 23 | Output: 2 24 | 25 | 26 | Constraints: 27 | 28 | 0 <= bank.length <= 10 29 | startGene.length == endGene.length == bank[i].length == 8 30 | startGene, endGene, and bank[i] consist of only the characters ['A', 'C', 'G', 'T']. 31 | 32 | # Thought process 33 | 34 | To get from startGene to endGene we can only use the string in bank. 35 | Each gene string can be considered as a node in an undirected grapf, and the problem can be solved using Bfs. 36 | Implementation details: no need to build explicitely the graph since the bank is small. Just loox for the next valid mutation at each gene. 37 | 38 | # Complexity 39 | 40 | O(NM) time where N is the length of the gene string and M is the number of genes in the bank. 41 | O(NM) space 42 | -------------------------------------------------------------------------------- /graphs/695. Max Area of Island/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | private static readonly (int x, int y)[] Directions = { (1, 0), (0, 1), (-1, 0), (0, -1) }; 3 | 4 | public int MaxAreaOfIsland(int[][] grid) { 5 | if (grid == null || grid.Length == 0 || grid[0].Length == 0) 6 | return 0; 7 | 8 | int maxArea = 0; 9 | int m = grid.Length, n = grid[0].Length; 10 | 11 | for (int i = 0; i < m; i++) { 12 | for (int j = 0; j < n; j++) { 13 | if (grid[i][j] == 1) { 14 | maxArea = Math.Max(maxArea, Dfs(grid, i, j)); 15 | } 16 | } 17 | } 18 | return maxArea; 19 | } 20 | 21 | private int Dfs(int[][] grid, int i, int j) { 22 | if (i < 0 || j < 0 || i >= grid.Length || j >= grid[0].Length || grid[i][j] != 1) { 23 | return 0; 24 | } 25 | 26 | grid[i][j] = 0; // Mark as visited 27 | int area = 1; 28 | 29 | foreach (var (dx, dy) in Directions) { 30 | area += Dfs(grid, i + dx, j + dy); 31 | } 32 | return area; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /graphs/695. Max Area of Island/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an m x n binary matrix grid. An island is a group of 1's (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water. 4 | 5 | The area of an island is the number of cells with a value 1 in the island. 6 | 7 | Return the maximum area of an island in grid. If there is no island, return 0. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | 14 | Input: grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]] 15 | Output: 6 16 | Explanation: The answer is not 11, because the island must be connected 4-directionally. 17 | Example 2: 18 | 19 | Input: grid = [[0,0,0,0,0,0,0,0]] 20 | Output: 0 21 | 22 | 23 | Constraints: 24 | 25 | m == grid.length 26 | n == grid[i].length 27 | 1 <= m, n <= 50 28 | grid[i][j] is either 0 or 1. 29 | 30 | # Thought Process 31 | 32 | Iterate through the matrix and start Dfs at each unvisited cell with 1. Count the number of other 1s reachable from there and take the max. 33 | 34 | # Complexity 35 | 36 | O(NM) time, space may be O(1) reusing the input to mark visited cells 37 | -------------------------------------------------------------------------------- /graphs/909. Snakes and Ladders/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int SnakesAndLadders(int[][] board) { 3 | int n = board.Length; 4 | var q = new Queue<(int position, int moves)>(); 5 | q.Enqueue((1,0)); 6 | var visited = new HashSet(); 7 | var flatBoard = FlattenBoard(board); 8 | 9 | while (q.Count > 0) { 10 | 11 | var (position, moves) = q.Dequeue(); 12 | 13 | // this check is necessary because a position could 14 | // inserted multiple time in the queue before getting visited 15 | if (visited.Contains(position)) { 16 | continue; 17 | } 18 | 19 | visited.Add(position); 20 | 21 | foreach (var nextPosition in RollDice(flatBoard, position)) { 22 | if (nextPosition == n*n) { 23 | return moves+1; 24 | } 25 | 26 | if (!visited.Contains(nextPosition)) { 27 | q.Enqueue((nextPosition,moves+1)); 28 | } 29 | } 30 | } 31 | return -1; 32 | } 33 | 34 | private int[] FlattenBoard(int[][] board) { 35 | int n = board.Length; 36 | var flatBoard = new int[n * n + 1]; 37 | flatBoard[1] = -1; 38 | int position = 1; 39 | 40 | bool goingRight = true; 41 | 42 | for (int row = n - 1; row >= 0; row--) { 43 | if (goingRight) { 44 | for (int col = 0; col < n; col++) { 45 | flatBoard[position++] = board[row][col]; 46 | } 47 | } else { 48 | for (int col = n - 1; col >= 0; col--) { 49 | flatBoard[position++] = board[row][col]; 50 | } 51 | } 52 | goingRight = !goingRight; 53 | } 54 | return flatBoard; 55 | } 56 | 57 | private List RollDice(int[] board, int position) { 58 | 59 | var reachedSquares = new List(); 60 | for (int i = 1; i <= 6 && (position+i < board.Length); i++) { 61 | reachedSquares.Add(board[position+i] == -1 ? position+i : board[position+i]); 62 | } 63 | return reachedSquares; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /greedy/1196. How Many Apples Can You Put into the Basket/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MaxNumberOfApples(int[] weight) { 3 | 4 | Array.Sort(weight); 5 | int remainingCapacity = 5000; 6 | int count = 0; 7 | 8 | foreach (int appleWeight in weight) { 9 | if (appleWeight > remainingCapacity) 10 | break; 11 | 12 | remainingCapacity -= appleWeight; 13 | count++; 14 | } 15 | 16 | return count; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /greedy/1196. How Many Apples Can You Put into the Basket/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You have some apples and a basket that can carry up to 5000 units of weight. 4 | 5 | Given an integer array weight where weight[i] is the weight of the ith apple, return the maximum number of apples you can put in the basket. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: weight = [100,200,150,1000] 12 | Output: 4 13 | Explanation: All 4 apples can be carried by the basket since their sum of weights is 1450. 14 | Example 2: 15 | 16 | Input: weight = [900,950,800,1000,700,800] 17 | Output: 5 18 | Explanation: The sum of weights of the 6 apples exceeds 5000 so we choose any 5 of them. 19 | 20 | 21 | Constraints: 22 | 23 | 1 <= weight.length <= 103 24 | 1 <= weight[i] <= 103 25 | 26 | # Thought process 27 | 28 | To maximize the number of apple we need to pick the ones with minor weight first. Sorting the inout array is how to do it. 29 | 30 | # Complexity 31 | 32 | The time complexity is O(n log n) due to the sorting operation, where n is the number of apples. 33 | The space complexity is O(1) as we're not using any additional data structures that scale with the input size. 34 | -------------------------------------------------------------------------------- /greedy/1196. How Many Apples Can You Put into the Basket/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxNumberOfApples(self, weight: List[int]) -> int: 3 | cumulative_weights = list(accumulate(sorted(weight))) 4 | 5 | for i, total_weight in enumerate(cumulative_weights): 6 | if total_weight > 5000: 7 | return i 8 | 9 | return len(weight) 10 | -------------------------------------------------------------------------------- /greedy/1323. Maximum 69 Number/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int Maximum69Number (int num) { 3 | char[] digits = num.ToString().ToCharArray(); 4 | 5 | for (int i = 0; i < digits.Length; i++) { 6 | if (digits[i] == '6') { 7 | digits[i] = '9'; 8 | return int.Parse(new string(digits)); 9 | } 10 | } 11 | 12 | return num; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /greedy/1323. Maximum 69 Number/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given a positive integer num consisting only of digits 6 and 9. 4 | 5 | Return the maximum number you can get by changing at most one digit (6 becomes 9, and 9 becomes 6). 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: num = 9669 12 | Output: 9969 13 | Explanation: 14 | Changing the first digit results in 6669. 15 | Changing the second digit results in 9969. 16 | Changing the third digit results in 9699. 17 | Changing the fourth digit results in 9666. 18 | The maximum number is 9969. 19 | Example 2: 20 | 21 | Input: num = 9996 22 | Output: 9999 23 | Explanation: Changing the last digit 6 to 9 results in the maximum number. 24 | Example 3: 25 | 26 | Input: num = 9999 27 | Output: 9999 28 | Explanation: It is better not to apply any change. 29 | 30 | 31 | Constraints: 32 | 33 | 1 <= num <= 104 34 | num consists of only 6 and 9 digits. 35 | 36 | # Thought Process 37 | 38 | The maximum number we can obtain is by changing the leftmost '6' digit into a '9'. Converting the input to string or char array makes things simpler. 39 | 40 | # Complexity 41 | 42 | The time complexity is O(n), where n is the number of digits in the number. The space complexity is also O(N). 43 | -------------------------------------------------------------------------------- /greedy/1338. Reduce Array Size to The Half/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MinSetSize(int[] arr) { 3 | 4 | var frequencies = new Dictionary(); 5 | foreach (int num in arr) { 6 | if (frequencies.ContainsKey(num)) { 7 | frequencies[num]++; 8 | } else { 9 | frequencies[num] = 1; 10 | } 11 | } 12 | 13 | var pq = new PriorityQueue(Comparer.Create((x, y) => y.CompareTo(x))); 14 | foreach (int freq in frequencies.Values) { 15 | pq.Enqueue(freq, freq); 16 | } 17 | 18 | int targetLength = arr.Length / 2; 19 | int remainingLength = arr.Length; 20 | int setSize = 0; 21 | 22 | while (remainingLength > targetLength) { 23 | remainingLength -= pq.Dequeue(); 24 | setSize++; 25 | } 26 | 27 | return setSize; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /greedy/1338. Reduce Array Size to The Half/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an integer array arr. You can choose a set of integers and remove all the occurrences of these integers in the array. 4 | 5 | Return the minimum size of the set so that at least half of the integers of the array are removed. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: arr = [3,3,3,3,5,5,5,2,2,7] 12 | Output: 2 13 | Explanation: Choosing {3,7} will make the new array [5,5,5,2,2] which has size 5 (i.e equal to half of the size of the old array). 14 | Possible sets of size 2 are {3,5},{3,2},{5,2}. 15 | Choosing set {2,7} is not possible as it will make the new array [3,3,3,3,5,5,5] which has a size greater than half of the size of the old array. 16 | Example 2: 17 | 18 | Input: arr = [7,7,7,7,7,7] 19 | Output: 1 20 | Explanation: The only possible set you can choose is {7}. This will make the new array empty. 21 | 22 | 23 | Constraints: 24 | 25 | 2 <= arr.length <= 105 26 | arr.length is even. 27 | 1 <= arr[i] <= 105 28 | 29 | # ThoughtProcess 30 | 31 | The problem can be solved using a combo of data structures: 32 | 33 | - dictionary to count frequencies 34 | - priorityQueue to extract the greatest frequencies first 35 | 36 | # Complexity 37 | 38 | Time complexity is O(n log n), where n is the length of the input array. 39 | The space complexity is O(n) in the worst case if all elements are unique. 40 | -------------------------------------------------------------------------------- /greedy/1710. Maximum Units on a Truck/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MaximumUnits(int[][] boxTypes, int truckSize) { 3 | 4 | Array.Sort(boxTypes, (x,y) => y[1].CompareTo(x[1])); 5 | int totalUnits = 0; 6 | 7 | foreach (var box in boxTypes) { 8 | int boxCount = box[0]; 9 | int unitsPerBox = box[1]; 10 | 11 | int boxesToTake = Math.Min(boxCount, truckSize); 12 | totalUnits += boxesToTake * unitsPerBox; 13 | truckSize -= boxesToTake; 14 | 15 | if (truckSize == 0) 16 | break; 17 | } 18 | 19 | return totalUnits; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /greedy/1710. Maximum Units on a Truck/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are assigned to put some amount of boxes onto one truck. You are given a 2D array boxTypes, where boxTypes[i] = [numberOfBoxesi, numberOfUnitsPerBoxi]: 4 | 5 | numberOfBoxesi is the number of boxes of type i. 6 | numberOfUnitsPerBoxi is the number of units in each box of the type i. 7 | You are also given an integer truckSize, which is the maximum number of boxes that can be put on the truck. You can choose any boxes to put on the truck as long as the number of boxes does not exceed truckSize. 8 | 9 | Return the maximum total number of units that can be put on the truck. 10 | 11 | 12 | 13 | Example 1: 14 | 15 | Input: boxTypes = [[1,3],[2,2],[3,1]], truckSize = 4 16 | Output: 8 17 | Explanation: There are: 18 | - 1 box of the first type that contains 3 units. 19 | - 2 boxes of the second type that contain 2 units each. 20 | - 3 boxes of the third type that contain 1 unit each. 21 | You can take all the boxes of the first and second types, and one box of the third type. 22 | The total number of units will be = (1 * 3) + (2 * 2) + (1 * 1) = 8. 23 | Example 2: 24 | 25 | Input: boxTypes = [[5,10],[2,5],[4,7],[3,9]], truckSize = 10 26 | Output: 91 27 | 28 | 29 | Constraints: 30 | 31 | 1 <= boxTypes.length <= 1000 32 | 1 <= numberOfBoxesi, numberOfUnitsPerBoxi <= 1000 33 | 1 <= truckSize <= 106 34 | 35 | # Thought Process 36 | 37 | To maximize the number of units it's necessary to put on the truck the boxes with more units first. 38 | Sorting the types of boxes according to the number of units, makes this straightforward. 39 | Optimization: don't use a nested loop but for each type of box takes the maximum allowed by the remaining capacity of the truck. 40 | 41 | # Complexity 42 | 43 | The time complexity is O(n log n) due to the sorting operation, where n is the number of box types. 44 | The space complexity is O(1) as we're not using any additional data structures that scale with the input size. 45 | -------------------------------------------------------------------------------- /hash-tables/1426. Counting Elements/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer array arr, count how many elements x there are, such that x + 1 is also in arr. If there are duplicates in arr, count them separately. 4 | 5 | 6 | 7 | Example 1: 8 | 9 | Input: arr = [1,2,3] 10 | Output: 2 11 | Explanation: 1 and 2 are counted cause 2 and 3 are in arr. 12 | Example 2: 13 | 14 | Input: arr = [1,1,3,3,5,5,7,7] 15 | Output: 0 16 | Explanation: No numbers are counted, cause there is no 2, 4, 6, or 8 in arr. 17 | 18 | 19 | Constraints: 20 | 21 | 1 <= arr.length <= 1000 22 | 0 <= arr[i] <= 1000 23 | -------------------------------------------------------------------------------- /hash-tables/1426. Counting Elements/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def countElements(self, arr: List[int]) -> int: 3 | nums = set(arr) 4 | count = 0 5 | for x in arr: 6 | if x+1 in nums: 7 | count += 1 8 | return count 9 | -------------------------------------------------------------------------------- /hash-tables/1832. Check if the Sentence Is Pangram/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | A pangram is a sentence where every letter of the English alphabet appears at least once. 4 | 5 | Given a string sentence containing only lowercase English letters, return true if sentence is a pangram, or false otherwise. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: sentence = "thequickbrownfoxjumpsoverthelazydog" 12 | Output: true 13 | Explanation: sentence contains at least one of every letter of the English alphabet. 14 | Example 2: 15 | 16 | Input: sentence = "leetcode" 17 | Output: false 18 | 19 | # Thought process 20 | 21 | Build a hash-set with the string characters and check if its size is 26 22 | 23 | # Complexity 24 | 25 | O(N) time to build the set, O(1) space 26 | -------------------------------------------------------------------------------- /hash-tables/1832. Check if the Sentence Is Pangram/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def checkIfPangram(self, sentence: str) -> bool: 3 | return len(set(sentence)) == 26 4 | -------------------------------------------------------------------------------- /hash-tables/560. Subarray Sum Equals K/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. 4 | 5 | A subarray is a contiguous non-empty sequence of elements within an array. 6 | 7 | Example 1: 8 | 9 | Input: nums = [1,1,1], k = 2 10 | Output: 2 11 | Example 2: 12 | 13 | Input: nums = [1,2,3], k = 3 14 | Output: 2 15 | 16 | Constraints: 17 | 18 | 1 <= nums.length <= 2 * 104 19 | -1000 <= nums[i] <= 1000 20 | -107 <= k <= 107 21 | 22 | # Thought process 23 | 24 | Brute force is to check each subarray, calculate its sum and see if it's equal to k. O(nˆ3) 25 | Optimization is to precalculate prefix sums to do the check. O(nˆ2) 26 | Optimal solution comes from observing that Prefix[j]-Prefix[i] = k => Prefix[j] - k = Prefix[i] 27 | If prefix[i] was already found increase the resut by how many times. Prefixes can be stored in a hash table counting frequencies. 28 | 29 | # Complexity 30 | 31 | Time complexity is O(N) and also space complexity to store the hash map. 32 | -------------------------------------------------------------------------------- /hash-tables/560. Subarray Sum Equals K/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def subarraySum(self, nums: List[int], k: int) -> int: 3 | count = {0: 1} 4 | curr_sum = 0 5 | result = 0 6 | 7 | for num in nums: 8 | curr_sum += num 9 | if curr_sum - k in count: 10 | result += count[curr_sum - k] 11 | count[curr_sum] = count.get(curr_sum, 0) + 1 12 | 13 | return result 14 | -------------------------------------------------------------------------------- /heaps/1167. Minimum Cost to Connect Sticks/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int ConnectSticks(int[] sticks) { 3 | 4 | var minHeap = new PriorityQueue(); 5 | foreach (var stick in sticks) { 6 | minHeap.Enqueue(stick, stick); 7 | } 8 | 9 | int totalCost = 0; 10 | while (minHeap.Count > 1) { 11 | int firstStick = minHeap.Dequeue(); 12 | int secondStick = minHeap.Dequeue(); 13 | int combinedStick = firstStick + secondStick; 14 | totalCost += combinedStick; 15 | minHeap.Enqueue(combinedStick, combinedStick); 16 | } 17 | 18 | return totalCost; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /heaps/1167. Minimum Cost to Connect Sticks/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You have some number of sticks with positive integer lengths. These lengths are given as an array sticks, where sticks[i] is the length of the ith stick. 4 | 5 | You can connect any two sticks of lengths x and y into one stick by paying a cost of x + y. You must connect all the sticks until there is only one stick remaining. 6 | 7 | Return the minimum cost of connecting all the given sticks into one stick in this way. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: sticks = [2,4,3] 14 | Output: 14 15 | Explanation: You start with sticks = [2,4,3]. 16 | 1. Combine sticks 2 and 3 for a cost of 2 + 3 = 5. Now you have sticks = [5,4]. 17 | 2. Combine sticks 5 and 4 for a cost of 5 + 4 = 9. Now you have sticks = [9]. 18 | There is only one stick left, so you are done. The total cost is 5 + 9 = 14. 19 | Example 2: 20 | 21 | Input: sticks = [1,8,3,5] 22 | Output: 30 23 | Explanation: You start with sticks = [1,8,3,5]. 24 | 1. Combine sticks 1 and 3 for a cost of 1 + 3 = 4. Now you have sticks = [4,8,5]. 25 | 2. Combine sticks 4 and 5 for a cost of 4 + 5 = 9. Now you have sticks = [9,8]. 26 | 3. Combine sticks 9 and 8 for a cost of 9 + 8 = 17. Now you have sticks = [17]. 27 | There is only one stick left, so you are done. The total cost is 4 + 9 + 17 = 30. 28 | Example 3: 29 | 30 | Input: sticks = [5] 31 | Output: 0 32 | Explanation: There is only one stick, so you don't need to do anything. The total cost is 0. 33 | 34 | 35 | Constraints: 36 | 37 | 1 <= sticks.length <= 104 38 | 1 <= sticks[i] <= 104 39 | 40 | # Thought Process 41 | 42 | The idea is to simulate the process of joiining each time the two shorter sticks. A minHeap is the perfect data structure for this. 43 | 44 | # Complexity 45 | 46 | The time complexity is O(n log n), where n is the number of sticks, due to the heap operations. 47 | The space complexity is O(n) for storing all sticks in the heap. 48 | -------------------------------------------------------------------------------- /heaps/1962. Remove Stones to Minimize the Total/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MinStoneSum(int[] piles, int k) { 3 | 4 | var maxHeap = new PriorityQueue(Comparer.Create((x, y) => y.CompareTo(x))); 5 | for (int i = 0; i < piles.Length; i++) { 6 | maxHeap.Enqueue(i, piles[i]); 7 | } 8 | 9 | for (; k > 0; k--) { 10 | var index = maxHeap.Dequeue(); 11 | piles[index] -= piles[index] / 2; 12 | maxHeap.Enqueue(index, piles[index]); 13 | } 14 | return piles.Sum(); 15 | } 16 | } 17 | 18 | // optimization without changing the input array 19 | public class Solution { 20 | public int MinStoneSum(int[] piles, int k) { 21 | var maxHeap = new PriorityQueue(Comparer.Create((x, y) => y.CompareTo(x))); 22 | int totalStones = 0; 23 | 24 | foreach (int pile in piles) { 25 | maxHeap.Enqueue(pile, pile); 26 | totalStones += pile; 27 | } 28 | 29 | while (k > 0 && maxHeap.TryDequeue(out int maxPile, out _)) { 30 | int removed = maxPile / 2; 31 | totalStones -= removed; 32 | int newPile = maxPile - removed; 33 | 34 | if (newPile > 0) { 35 | maxHeap.Enqueue(newPile, newPile); 36 | } 37 | 38 | k--; 39 | } 40 | 41 | return totalStones; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /heaps/1962. Remove Stones to Minimize the Total/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given a 0-indexed integer array piles, where piles[i] represents the number of stones in the ith pile, and an integer k. You should apply the following operation exactly k times: 4 | 5 | Choose any piles[i] and remove floor(piles[i] / 2) stones from it. 6 | Notice that you can apply the operation on the same pile more than once. 7 | 8 | Return the minimum possible total number of stones remaining after applying the k operations. 9 | 10 | floor(x) is the greatest integer that is smaller than or equal to x (i.e., rounds x down). 11 | 12 | Example 1: 13 | 14 | Input: piles = [5,4,9], k = 2 15 | Output: 12 16 | Explanation: Steps of a possible scenario are: 17 | - Apply the operation on pile 2. The resulting piles are [5,4,5]. 18 | - Apply the operation on pile 0. The resulting piles are [3,4,5]. 19 | The total number of stones in [3,4,5] is 12. 20 | Example 2: 21 | 22 | Input: piles = [4,3,6,7], k = 3 23 | Output: 12 24 | Explanation: Steps of a possible scenario are: 25 | - Apply the operation on pile 2. The resulting piles are [4,3,3,7]. 26 | - Apply the operation on pile 3. The resulting piles are [4,3,3,4]. 27 | - Apply the operation on pile 0. The resulting piles are [2,3,3,4]. 28 | The total number of stones in [2,3,3,4] is 12. 29 | 30 | 31 | Constraints: 32 | 33 | 1 <= piles.length <= 105 34 | 1 <= piles[i] <= 104 35 | 1 <= k <= 105 36 | 37 | # Thought Process 38 | 39 | The idea is to simulate the process of greedely pick the greatestPile and renoving stones from it. 40 | A max heap is the perfect data structure for this. 41 | 42 | # Complexity 43 | 44 | The time complexity is O(n log n + k log n), where n is the number of piles: 45 | - O(n log n) for building the initial heap 46 | - O(k log n) for k operations on the heap 47 | - 48 | The space complexity is O(n) for the heap. 49 | -------------------------------------------------------------------------------- /heaps/215. Kth Largest Element in an Array/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int FindKthLargest(int[] nums, int k) { 3 | 4 | PriorityQueue pq = new(); //MinHeap by default 5 | 6 | for (int i = 0; i < k; ++i) { 7 | pq.Enqueue(nums[i],nums[i]); 8 | } 9 | 10 | for (int i = k; i < nums.Length; ++i) { 11 | if (nums[i] > pq.Peek()) { //keep the k largest in the min heap 12 | pq.Dequeue(); 13 | pq.Enqueue(nums[i],nums[i]); 14 | } 15 | } 16 | return pq.Peek(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /heaps/215. Kth Largest Element in an Array/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer array nums and an integer k, return the kth largest element in the array. 4 | 5 | Note that it is the kth largest element in the sorted order, not the kth distinct element. 6 | 7 | Can you solve it without sorting? 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: nums = [3,2,1,5,6,4], k = 2 14 | Output: 5 15 | Example 2: 16 | 17 | Input: nums = [3,2,3,1,2,4,5,5,6], k = 4 18 | Output: 4 19 | 20 | 21 | Constraints: 22 | 23 | 1 <= k <= nums.length <= 105 24 | -104 <= nums[i] <= 104 25 | 26 | # Thought process 27 | 28 | The idea is to use a minHeap to store the largest k numbers. Every time a numbers is greater than the smallest int the heao, remove the smallest and insert the new one. 29 | 30 | # Complexity 31 | 32 | Time complexity is O(NlogK) to insert N numbers into a heap of size K. 33 | Space complexity is O(K) for the heap. 34 | -------------------------------------------------------------------------------- /heaps/703. Kth Largest Element in a Stream/Solution.cs: -------------------------------------------------------------------------------- 1 | public class KthLargest { 2 | 3 | private readonly PriorityQueue minHeap; 4 | private readonly int k; 5 | 6 | public KthLargest(int k, int[] nums) { 7 | this.k = k; 8 | minHeap = new PriorityQueue(); 9 | foreach (int num in nums) { 10 | Add(num); 11 | } 12 | } 13 | 14 | public int Add(int val) { 15 | if (minHeap.Count < k) { 16 | minHeap.Enqueue(val, val); 17 | } else if (val > minHeap.Peek()) { 18 | minHeap.Enqueue(val, val); 19 | minHeap.Dequeue(); 20 | } 21 | 22 | return minHeap.Peek(); 23 | } 24 | } 25 | 26 | /** 27 | * Your KthLargest object will be instantiated and called as such: 28 | * KthLargest obj = new KthLargest(k, nums); 29 | * int param_1 = obj.Add(val); 30 | */ 31 | -------------------------------------------------------------------------------- /heaps/703. Kth Largest Element in a Stream/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are part of a university admissions office and need to keep track of the kth highest test score from applicants in real-time. This helps to determine cut-off marks for interviews and admissions dynamically as new applicants submit their scores. 4 | 5 | You are tasked to implement a class which, for a given integer k, maintains a stream of test scores and continuously returns the kth highest test score after a new score has been submitted. More specifically, we are looking for the kth highest score in the sorted list of all scores. 6 | 7 | Implement the KthLargest class: 8 | 9 | KthLargest(int k, int[] nums) Initializes the object with the integer k and the stream of test scores nums. 10 | int add(int val) Adds a new test score val to the stream and returns the element representing the kth largest element in the pool of test scores so far. 11 | 12 | 13 | Example 1: 14 | 15 | Input: 16 | ["KthLargest", "add", "add", "add", "add", "add"] 17 | [[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]] 18 | 19 | Output: [null, 4, 5, 5, 8, 8] 20 | 21 | Explanation: 22 | 23 | KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]); 24 | kthLargest.add(3); // return 4 25 | kthLargest.add(5); // return 5 26 | kthLargest.add(10); // return 5 27 | kthLargest.add(9); // return 8 28 | kthLargest.add(4); // return 8 29 | 30 | Example 2: 31 | 32 | Input: 33 | ["KthLargest", "add", "add", "add", "add"] 34 | [[4, [7, 7, 7, 7, 8, 3]], [2], [10], [9], [9]] 35 | 36 | Output: [null, 7, 7, 7, 8] 37 | 38 | Explanation: 39 | 40 | KthLargest kthLargest = new KthLargest(4, [7, 7, 7, 7, 8, 3]); 41 | kthLargest.add(2); // return 7 42 | kthLargest.add(10); // return 7 43 | kthLargest.add(9); // return 7 44 | kthLargest.add(9); // return 8 45 | 46 | 47 | Constraints: 48 | 49 | 0 <= nums.length <= 104 50 | 1 <= k <= nums.length + 1 51 | -104 <= nums[i] <= 104 52 | -104 <= val <= 104 53 | At most 104 calls will be made to add. 54 | 55 | # Thought Process 56 | 57 | Use a minheap to keep the k largst element. For each dequeue an element if it's largest than the new one. 58 | 59 | # Complexity 60 | 61 | The time complexity remains O(log k) for each Add operation. 62 | 63 | The space complexity is O(k), as we only ever keep at most k elements in the heap. 64 | -------------------------------------------------------------------------------- /heaps/973. K Closest Points to Origin/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int[][] KClosest(int[][] points, int k) { 3 | 4 | var maxHeap = new PriorityQueue(k, Comparer.Create((a, b) => b.CompareTo(a))); 5 | 6 | foreach (var point in points) { 7 | double distanceSquared = point[0] * point[0] + point[1] * point[1]; 8 | if (maxHeap.Count < k) { 9 | maxHeap.Enqueue(point, distanceSquared); 10 | } else if (maxHeap.TryPeek(out _, out double priority) && distanceSquared < priority) { 11 | maxHeap.Dequeue(); 12 | maxHeap.Enqueue(point, distanceSquared); 13 | } 14 | } 15 | 16 | return maxHeap.UnorderedItems.Select(item => item.Element).ToArray(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /heaps/973. K Closest Points to Origin/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). 4 | 5 | The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). 6 | 7 | You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). 8 | 9 | 10 | 11 | Example 1: 12 | 13 | 14 | Input: points = [[1,3],[-2,2]], k = 1 15 | Output: [[-2,2]] 16 | Explanation: 17 | The distance between (1, 3) and the origin is sqrt(10). 18 | The distance between (-2, 2) and the origin is sqrt(8). 19 | Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. 20 | We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. 21 | Example 2: 22 | 23 | Input: points = [[3,3],[5,-1],[-2,4]], k = 2 24 | Output: [[3,3],[-2,4]] 25 | Explanation: The answer [[-2,4],[3,3]] would also be accepted. 26 | 27 | 28 | Constraints: 29 | 30 | 1 <= k <= points.length <= 104 31 | -104 <= xi, yi <= 104 32 | 33 | # Thought Process 34 | 35 | The idea is to iterate through the array of points and keep the k closest to the origin. 36 | A maxHeap is the ideal data structure for this. Elements are the points and priority is the distance to the origin. 37 | 38 | # Complexity 39 | 40 | The time complexity remains O(n log k), where n is the number of points. 41 | The space complexity is O(k) for storing the k closest points in the heap. 42 | -------------------------------------------------------------------------------- /leetcode75/ 208. Implement Trie (Prefix Tree)/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Trie { 2 | 3 | private const int AlphabetSize = 26; 4 | private readonly Node _root; 5 | 6 | private class Node { 7 | public Node[] Children { get; } 8 | public bool IsEndOfWord { get; set; } 9 | 10 | public Node() { 11 | Children = new Node[AlphabetSize]; 12 | IsEndOfWord = false; 13 | } 14 | } 15 | 16 | public Trie() { 17 | _root = new Node(); 18 | } 19 | 20 | public void Insert(string word) { 21 | Node current = _root; 22 | foreach (char c in word) { 23 | int index = c - 'a'; 24 | current.Children[index] ??= new Node(); 25 | current = current.Children[index]; 26 | } 27 | current.IsEndOfWord = true; 28 | } 29 | 30 | public bool Search(string word) { 31 | Node node = FindNode(word); 32 | return node != null && node.IsEndOfWord; 33 | } 34 | 35 | public bool StartsWith(string prefix) { 36 | return FindNode(prefix) != null; 37 | } 38 | 39 | private Node FindNode(string s) { 40 | Node current = _root; 41 | foreach (char c in s) { 42 | int index = c - 'a'; 43 | if (current.Children[index] == null) { 44 | return null; 45 | } 46 | current = current.Children[index]; 47 | } 48 | return current; 49 | } 50 | } 51 | 52 | /** 53 | * Your Trie object will be instantiated and called as such: 54 | * Trie obj = new Trie(); 55 | * obj.Insert(word); 56 | * bool param_2 = obj.Search(word); 57 | * bool param_3 = obj.StartsWith(prefix); 58 | */ 59 | -------------------------------------------------------------------------------- /leetcode75/ 208. Implement Trie (Prefix Tree)/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker. 4 | 5 | Implement the Trie class: 6 | 7 | Trie() Initializes the trie object. 8 | void insert(String word) Inserts the string word into the trie. 9 | boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. 10 | boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise. 11 | 12 | 13 | Example 1: 14 | 15 | Input 16 | ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] 17 | [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] 18 | Output 19 | [null, null, true, false, true, null, true] 20 | 21 | Explanation 22 | Trie trie = new Trie(); 23 | trie.insert("apple"); 24 | trie.search("apple"); // return True 25 | trie.search("app"); // return False 26 | trie.startsWith("app"); // return True 27 | trie.insert("app"); 28 | trie.search("app"); // return True 29 | 30 | 31 | Constraints: 32 | 33 | 1 <= word.length, prefix.length <= 2000 34 | word and prefix consist only of lowercase English letters. 35 | At most 3 * 104 calls in total will be made to insert, search, and startsWith. 36 | 37 | # Thought Process 38 | 39 | Implementation of the Trie data structure. 40 | 41 | # Complexity 42 | 43 | O(N) time for all the three operations 44 | -------------------------------------------------------------------------------- /leetcode75/1137. N-th Tribonacci Number/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int Tribonacci(int n) { 3 | if (n == 0) return 0; 4 | if (n <= 2) return 1; 5 | 6 | int a = 0, b = 1, c = 1; 7 | for (int i = 3; i <= n; i++) { 8 | int temp = a + b + c; 9 | a = b; 10 | b = c; 11 | c = temp; 12 | } 13 | 14 | return c; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leetcode75/1137. N-th Tribonacci Number/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | The Tribonacci sequence Tn is defined as follows: 4 | 5 | T0 = 0, T1 = 1, T2 = 1, and Tn+3 = Tn + Tn+1 + Tn+2 for n >= 0. 6 | 7 | Given n, return the value of Tn. 8 | 9 | Example 1: 10 | 11 | Input: n = 4 12 | Output: 4 13 | Explanation: 14 | T_3 = 0 + 1 + 1 = 2 15 | T_4 = 1 + 1 + 2 = 4 16 | Example 2: 17 | 18 | Input: n = 25 19 | Output: 1389537 20 | 21 | 22 | Constraints: 23 | 24 | 0 <= n <= 37 25 | The answer is guaranteed to fit within a 32-bit integer, ie. answer <= 2^31 - 1. 26 | 27 | # Thought Process 28 | 29 | Classic DP problem like fibonacci number or stair climbing. 30 | 31 | # Complexity 32 | 33 | Time Complexity: O(n), as we iterate from 3 to n. 34 | Space Complexity: O(1) for just a few individual variables. 35 | -------------------------------------------------------------------------------- /leetcode75/1143. Longest Common Subsequence/Solution.cs: -------------------------------------------------------------------------------- 1 | // Top-down 2 | public class Solution { 3 | 4 | private int[,] _cache; 5 | private string _text1, _text2; 6 | 7 | public int LongestCommonSubsequence(string text1, string text2) { 8 | _text1 = text1; 9 | _text2 = text2; 10 | _cache = new int[text1.Length, text2.Length]; 11 | for (int i = 0; i < _cache.GetLength(0); i++) 12 | for (int j = 0; j < _cache.GetLength(1); j++) 13 | _cache[i, j] = -1; 14 | return LCS(0, 0); 15 | } 16 | 17 | private int LCS(int i, int j) { 18 | 19 | if (i == _text1.Length || j == _text2.Length) { 20 | return 0; 21 | } 22 | 23 | if (_cache[i, j] != -1) 24 | return _cache[i, j]; 25 | 26 | int result; 27 | if (_text1[i] == _text2[j]) { 28 | result = 1 + LCS(i+1, j+1); 29 | } 30 | else { 31 | result = Math.Max(LCS(i+1, j), LCS(i, j+1)); 32 | } 33 | 34 | _cache[i, j] = result; 35 | return result; 36 | } 37 | } 38 | 39 | //bottom-up 40 | public class Solution { 41 | 42 | public int LongestCommonSubsequence(string text1, string text2) { 43 | int m = text1.Length; 44 | int n = text2.Length; 45 | int[,] dp = new int[m + 1, n + 1]; 46 | 47 | for (int i = 1; i <= m; ++i) { 48 | for (int j = 1; j <= n; ++j) { 49 | if (text1[i-1] == text2[j-1]) 50 | dp[i,j] = 1 + dp[i-1,j-1]; 51 | else 52 | dp[i,j] = Math.Max(dp[i-1,j], dp[i,j-1]); 53 | } 54 | } 55 | return dp[m,n]; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /leetcode75/1143. Longest Common Subsequence/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0. 4 | 5 | A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters. 6 | 7 | For example, "ace" is a subsequence of "abcde". 8 | A common subsequence of two strings is a subsequence that is common to both strings. 9 | 10 | 11 | 12 | Example 1: 13 | 14 | Input: text1 = "abcde", text2 = "ace" 15 | Output: 3 16 | Explanation: The longest common subsequence is "ace" and its length is 3. 17 | Example 2: 18 | 19 | Input: text1 = "abc", text2 = "abc" 20 | Output: 3 21 | Explanation: The longest common subsequence is "abc" and its length is 3. 22 | Example 3: 23 | 24 | Input: text1 = "abc", text2 = "def" 25 | Output: 0 26 | Explanation: There is no such common subsequence, so the result is 0. 27 | 28 | 29 | Constraints: 30 | 31 | 1 <= text1.length, text2.length <= 1000 32 | text1 and text2 consist of only lowercase English characters. 33 | 34 | # Thought Process 35 | 36 | The problem is a classic dynamic programming problem. 37 | 38 | The key observation is: 39 | - If the last characters of both strings match, they contribute to the LCS. 40 | - If they don't match, the LCS will be the longer of the LCS excluding the last/next character of text1 and the LCS excluding the last/next character of text2 41 | 42 | In the top-down approavh dp[i,j] represents the length of the longest common subsequence starting from text1[i...] and text2[j...]. We solve the problem recursively, breaking it down into smaller subproblems. 43 | In the Bottom-up Approach dp[i,j] represents the length of the longest common subsequence of text1[0...i-1] and text2[0...j-1]. We build our solution from smaller subproblems to larger ones. 44 | 45 | The bottom up recurrrence is: 46 | - If text1[i] == text2[j]: LCS[i][j] = 1 + LCS[i-1][j-1] 47 | - Else: LCS[i][j] = max(LCS[i-1][j], LCS[i][j-1]) 48 | 49 | Complexity 50 | 51 | - Time Complexity is O(MN) 52 | - SPace Complexity is O(MN) 53 | 54 | Since we only use i-1, j-1, space can be optimized 55 | 56 | 57 | -------------------------------------------------------------------------------- /leetcode75/1268. Search Suggestions System/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | public class Trie { 4 | private class Node { 5 | public Node[] Children { get; } = new Node[26]; 6 | public SortedSet Words { get; } = new SortedSet(); 7 | 8 | public Node() { 9 | Children = new Node[26]; 10 | Words = new SortedSet(); 11 | } 12 | } 13 | 14 | private Node _root; 15 | 16 | public Trie() { 17 | _root = new Node(); 18 | } 19 | 20 | public void Insert (string word) { 21 | var current = _root; 22 | foreach (char c in word) { 23 | int index = c-'a'; 24 | current.Children[index] ??= new Node(); 25 | current = current.Children[index]; 26 | current.Words.Add(word); 27 | if (current.Words.Count > 3) { 28 | current.Words.Remove(current.Words.Last()); 29 | } 30 | } 31 | } 32 | 33 | public List Search (string prefix) { 34 | var current = _root; 35 | foreach (char c in prefix) { 36 | int index = c-'a'; 37 | current = current.Children[index]; 38 | if (current == null) 39 | return new List(); 40 | } 41 | return current.Words.ToList(); 42 | } 43 | } 44 | 45 | public IList> SuggestedProducts(string[] products, string searchWord) { 46 | 47 | var result = new List>(); 48 | var trie = new Trie(); 49 | 50 | foreach(var product in products) { 51 | trie.Insert(product); 52 | } 53 | 54 | var prefix = new StringBuilder(); 55 | 56 | foreach(char c in searchWord) { 57 | prefix.Append(c); 58 | var currentSuggestion = trie.Search(prefix.ToString()); 59 | if (currentSuggestion != null) 60 | result.Add(currentSuggestion); 61 | else { 62 | while (result.Count < searchWord.Length) result.Add(new List()); 63 | break; 64 | } 65 | } 66 | return result; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /leetcode75/1268. Search Suggestions System/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an array of strings products and a string searchWord. 4 | 5 | Design a system that suggests at most three product names from products after each character of searchWord is typed. Suggested products should have common prefix with searchWord. If there are more than three products with a common prefix return the three lexicographically minimums products. 6 | 7 | Return a list of lists of the suggested products after each character of searchWord is typed. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: products = ["mobile","mouse","moneypot","monitor","mousepad"], searchWord = "mouse" 14 | Output: [["mobile","moneypot","monitor"],["mobile","moneypot","monitor"],["mouse","mousepad"],["mouse","mousepad"],["mouse","mousepad"]] 15 | Explanation: products sorted lexicographically = ["mobile","moneypot","monitor","mouse","mousepad"]. 16 | After typing m and mo all products match and we show user ["mobile","moneypot","monitor"]. 17 | After typing mou, mous and mouse the system suggests ["mouse","mousepad"]. 18 | Example 2: 19 | 20 | Input: products = ["havana"], searchWord = "havana" 21 | Output: [["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]] 22 | Explanation: The only word "havana" will be always suggested while typing the search word. 23 | 24 | 25 | Constraints: 26 | 27 | 1 <= products.length <= 1000 28 | 1 <= products[i].length <= 3000 29 | 1 <= sum(products[i].length) <= 2 * 104 30 | All the strings of products are unique. 31 | products[i] consists of lowercase English letters. 32 | 1 <= searchWord.length <= 1000 33 | searchWord consists of lowercase English letters. 34 | 35 | Thought Process: 36 | 37 | This is a string search and prefix matching problem. It requires efficient retrieval of words with a common prefix. 38 | Trie (Prefix Tree) is an excellent data structure for prefix-based operations. 39 | Build a Trie with all products. 40 | For each prefix of the search word, traverse the Trie and collect matching words. 41 | Sort the matching words and take the top three. 42 | Consider storing words directly in the Trie Nodes to avoid repeated search. 43 | Consider using a data structure to keep the words sorted to simplify the code. 44 | 45 | Complexity 46 | 47 | Time Complexity: 48 | 49 | - Insertion: O(L * log 3) for each word, where L is the word length. 50 | - Overall building of Trie: O(N * L * log 3), where N is the number of products. 51 | - Suggestion retrieval: O(M * 3), where M is the length of searchWord. 52 | Space Complexity: 53 | - O(N * L) in the worst case, but potentially more space-efficient due to the limit of 3 words per node. 54 | -------------------------------------------------------------------------------- /leetcode75/1318. Minimum Flips to Make a OR b Equal to c/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MinFlips(int a, int b, int c) { 3 | 4 | int flips = 0; 5 | 6 | while (a != 0 || b != 0 || c != 0) { 7 | int bitA = a & 1; 8 | int bitB = b & 1; 9 | int bitC = c & 1; 10 | 11 | if ((bitA | bitB) != bitC) { 12 | // c = 1 => a,b=0,0 one flip is enough 13 | // c = 0 => a,b=1,1 or 1,0 or 0,1 flips can be 1 or 2 14 | flips += bitC == 0 ? bitA + bitB : 1; 15 | } 16 | a >>= 1; 17 | b >>= 1; 18 | c >>= 1; 19 | } 20 | return flips; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /leetcode75/1318. Minimum Flips to Make a OR b Equal to c/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given 3 positives numbers a, b and c. Return the minimum flips required in some bits of a and b to make ( a OR b == c ). (bitwise OR operation). 4 | Flip operation consists of change any single bit 1 to 0 or change the bit 0 to 1 in their binary representation. 5 | 6 | 7 | 8 | Example 1: 9 | 10 | 11 | 12 | Input: a = 2, b = 6, c = 5 13 | Output: 3 14 | Explanation: After flips a = 1 , b = 4 , c = 5 such that (a OR b == c) 15 | Example 2: 16 | 17 | Input: a = 4, b = 2, c = 7 18 | Output: 1 19 | Example 3: 20 | 21 | Input: a = 1, b = 2, c = 3 22 | Output: 0 23 | 24 | 25 | Constraints: 26 | 27 | 1 <= a <= 10^9 28 | 1 <= b <= 10^9 29 | 1 <= c <= 10^9 30 | 31 | # Thought process 32 | 33 | There is nothing fancy here. You only need to check bit by bit c and see if a and b need to be flipped. 34 | If the last bit of a or the last bit of b is different from the last bit of c there are two possibiities: 35 | 1. c = 1 => a,b=0,0 one flip is enough 36 | 2. c = 0 => a,b=1,1 or 1,0 or 0,1 flips can be 1 or 2 37 | 38 | # Complexity 39 | 40 | O(log(max(a,b,c))) time since every time we divide by two, O(1) space 41 | -------------------------------------------------------------------------------- /leetcode75/136. Single Number/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int SingleNumber(int[] nums) { 3 | int result = 0; 4 | foreach (int num in nums){ 5 | result ^= num; 6 | } 7 | return result; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /leetcode75/136. Single Number/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given a non-empty array of integers nums, every element appears twice except for one. Find that single one. 4 | 5 | You must implement a solution with a linear runtime complexity and use only constant extra space. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: nums = [2,2,1] 12 | 13 | Output: 1 14 | 15 | Example 2: 16 | 17 | Input: nums = [4,1,2,1,2] 18 | 19 | Output: 4 20 | 21 | Example 3: 22 | 23 | Input: nums = [1] 24 | 25 | Output: 1 26 | 27 | 28 | 29 | Constraints: 30 | 31 | 1 <= nums.length <= 3 * 104 32 | -3 * 104 <= nums[i] <= 3 * 104 33 | Each element in the array appears twice except for one element which appears only once. 34 | 35 | # Thought Process 36 | 37 | Brute force is to use a nested loop to check for each number if it's repeated. 38 | Better solution is to use a hash table to check duplicates but with O(N) space. 39 | Optimal solution is to use the xor property. 40 | 41 | # Complexity 42 | 43 | O(N) time and O(1) space for the optimal solution. 44 | -------------------------------------------------------------------------------- /leetcode75/1431. Kids With the Greatest Number of Candies/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public IList KidsWithCandies(int[] candies, int extraCandies) { 3 | 4 | var result = new bool[candies.Length]; 5 | int maxCandiesForKid = candies.Max(); 6 | 7 | for(int i = 0; i < candies.Length; ++i){ 8 | result[i] = (candies[i] + extraCandies >= maxCandiesForKid) ? true : false; 9 | } 10 | return result.ToList(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /leetcode75/1431. Kids With the Greatest Number of Candies/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | There are n kids with candies. You are given an integer array candies, where each candies[i] represents the number of candies the ith kid has, and an integer extraCandies, denoting the number of extra candies that you have. 4 | 5 | Return a boolean array result of length n, where result[i] is true if, after giving the ith kid all the extraCandies, they will have the greatest number of candies among all the kids, or false otherwise. 6 | 7 | Note that multiple kids can have the greatest number of candies. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: candies = [2,3,5,1,3], extraCandies = 3 14 | Output: [true,true,true,false,true] 15 | Explanation: If you give all extraCandies to: 16 | - Kid 1, they will have 2 + 3 = 5 candies, which is the greatest among the kids. 17 | - Kid 2, they will have 3 + 3 = 6 candies, which is the greatest among the kids. 18 | - Kid 3, they will have 5 + 3 = 8 candies, which is the greatest among the kids. 19 | - Kid 4, they will have 1 + 3 = 4 candies, which is not the greatest among the kids. 20 | - Kid 5, they will have 3 + 3 = 6 candies, which is the greatest among the kids. 21 | Example 2: 22 | 23 | Input: candies = [4,2,1,1,2], extraCandies = 1 24 | Output: [true,false,false,false,false] 25 | Explanation: There is only 1 extra candy. 26 | Kid 1 will always have the greatest number of candies, even if a different kid is given the extra candy. 27 | Example 3: 28 | 29 | Input: candies = [12,1,12], extraCandies = 10 30 | Output: [true,false,true] 31 | 32 | 33 | Constraints: 34 | 35 | n == candies.length 36 | 2 <= n <= 100 37 | 1 <= candies[i] <= 100 38 | 1 <= extraCandies <= 50 39 | 40 | # Thought Process 41 | 42 | Find the kid having the maximum number of candies, and check for each other kid if it cross that threeshold with the extra candies. 43 | 44 | # Complexity 45 | 46 | Time complexity: O(n), where n is the number of kids (length of the candies list). 47 | Space complexity: O(n) for the resulting boolean list. 48 | -------------------------------------------------------------------------------- /leetcode75/1431. Kids With the Greatest Number of Candies/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def kidsWithCandies(self, candies: List[int], extraCandies: int) -> List[bool]: 3 | 4 | max_candies = max(candies) 5 | return [curr_candies + extraCandies >= max_candies for curr_candies in candies] 6 | -------------------------------------------------------------------------------- /leetcode75/1466. Reorder Routes to Make All Paths Lead to the City Zero/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | Dictionary> graph; 4 | bool[] visited; 5 | 6 | public int MinReorder(int n, int[][] connections) { 7 | 8 | graph = new Dictionary>(n); 9 | visited = new bool[n]; 10 | 11 | foreach (var edge in connections) 12 | { 13 | AddEdge(edge[0], edge[1], true); 14 | AddEdge(edge[1], edge[0], false); 15 | } 16 | 17 | return Dfs(0); 18 | } 19 | 20 | private void AddEdge(int from, int to, bool isOutgoing) { 21 | if (!graph.ContainsKey(from)) { 22 | graph[from] = new List<(int, bool)>(); 23 | } 24 | graph[from].Add((to, isOutgoing)); 25 | } 26 | 27 | private int Dfs(int vertex) { 28 | 29 | if(visited[vertex]) 30 | return 0; 31 | 32 | visited[vertex] = true; 33 | 34 | int result = 0; 35 | 36 | foreach(var neighbor in graph[vertex]) { 37 | if (!visited[neighbor.vertex] && neighbor.isOutgoing) 38 | result += 1 + Dfs(neighbor.Item1); 39 | else 40 | result += Dfs(neighbor.Item1); 41 | } 42 | 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /leetcode75/1466. Reorder Routes to Make All Paths Lead to the City Zero/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | There are n cities numbered from 0 to n - 1 and n - 1 roads such that there is only one way to travel between two different cities (this network form a tree). Last year, The ministry of transport decided to orient the roads in one direction because they are too narrow. 4 | 5 | Roads are represented by connections where connections[i] = [ai, bi] represents a road from city ai to city bi. 6 | 7 | This year, there will be a big event in the capital (city 0), and many people want to travel to this city. 8 | 9 | Your task consists of reorienting some roads such that each city can visit the city 0. Return the minimum number of edges changed. 10 | 11 | It's guaranteed that each city can reach city 0 after reorder. 12 | 13 | Example 1: 14 | 15 | Input: n = 6, connections = [[0,1],[1,3],[2,3],[4,0],[4,5]] 16 | Output: 3 17 | Explanation: Change the direction of edges show in red such that each node can reach the node 0 (capital). 18 | Example 2: 19 | 20 | 21 | Input: n = 5, connections = [[1,0],[1,2],[3,2],[3,4]] 22 | Output: 2 23 | Explanation: Change the direction of edges show in red such that each node can reach the node 0 (capital). 24 | Example 3: 25 | 26 | Input: n = 3, connections = [[1,0],[2,0]] 27 | Output: 0 28 | 29 | Constraints: 30 | 31 | 2 <= n <= 5 * 104 32 | connections.length == n - 1 33 | connections[i].length == 2 34 | 0 <= ai, bi <= n - 1 35 | ai != bi 36 | 37 | # Thought process 38 | 39 | Treat the graph as undirected. Start a dfs from the root, if you come across an edge in the forward direction, you need to reverse the edge. 40 | 41 | # Complexity 42 | 43 | O(N) time and space, where N is the number of vertices 44 | 45 | -------------------------------------------------------------------------------- /leetcode75/151. Reverse Words in a String/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public string ReverseWords(string s) { 3 | 4 | var words = s.Split(' ', StringSplitOptions.RemoveEmptyEntries); 5 | return string.Join(' ',words.Reverse()); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /leetcode75/151. Reverse Words in a String/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an input string s, reverse the order of the words. 4 | 5 | A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. 6 | 7 | Return a string of the words in reverse order concatenated by a single space. 8 | 9 | Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces. 10 | 11 | 12 | 13 | Example 1: 14 | 15 | Input: s = "the sky is blue" 16 | Output: "blue is sky the" 17 | Example 2: 18 | 19 | Input: s = " hello world " 20 | Output: "world hello" 21 | Explanation: Your reversed string should not contain leading or trailing spaces. 22 | Example 3: 23 | 24 | Input: s = "a good example" 25 | Output: "example good a" 26 | Explanation: You need to reduce multiple spaces between two words to a single space in the reversed string. 27 | 28 | 29 | Constraints: 30 | 31 | 1 <= s.length <= 104 32 | s contains English letters (upper-case and lower-case), digits, and spaces ' '. 33 | There is at least one word in s. 34 | 35 | 36 | Follow-up: If the string data type is mutable in your language, can you solve it in-place with O(1) extra space? 37 | 38 | # Thought Process 39 | 40 | Split the input into an array of words and reverse it. 41 | 42 | # Complexity 43 | 44 | O(n) time and space 45 | -------------------------------------------------------------------------------- /leetcode75/151. Reverse Words in a String/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseWords(self, s: str) -> str: 3 | words = s.split() 4 | return ' '.join(reversed(words)) 5 | -------------------------------------------------------------------------------- /leetcode75/162. Find Peak Element/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int FindPeakElement(int[] nums) { 3 | int left = 0, right = nums.Length - 1; 4 | 5 | while (left < right) { 6 | int mid = left + (right - left) / 2; 7 | if (nums[mid] < nums[mid + 1]) { 8 | left = mid + 1; 9 | } else { 10 | right = mid; 11 | } 12 | } 13 | 14 | return left; 15 | } 16 | } 17 | 18 | 19 | // In the solution below I explicitely checked if the mid is the PeakElement. 20 | // But this is unncessary since binary search will naturally converge to a peak element 21 | public class Solution { 22 | public int FindPeakElement(int[] nums) { 23 | int left = 0, right = nums.Length-1; 24 | 25 | while (left <= right) { 26 | int mid = left + (right - left) / 2; 27 | if (Isx(nums, mid)) { 28 | return mid; 29 | } 30 | else if (nums[mid + 1] > nums[mid]) { 31 | left = mid + 1; 32 | } 33 | else { 34 | right = mid - 1; 35 | } 36 | } 37 | // never reach this point 38 | return -1; 39 | } 40 | 41 | private bool IsPeakElement(int[] nums, int index) { 42 | long leftElement = index == 0 ? Int64.MinValue : nums[index-1]; 43 | long rightElement = index == nums.Length-1 ? Int64.MinValue : nums[index+1]; 44 | return nums[index] > leftElement && nums[index] > rightElement; 45 | } 46 | -------------------------------------------------------------------------------- /leetcode75/162. Find Peak Element/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | A peak element is an element that is strictly greater than its neighbors. 4 | 5 | Given a 0-indexed integer array nums, find a peak element, and return its index. If the array contains multiple peaks, return the index to any of the peaks. 6 | 7 | You may imagine that nums[-1] = nums[n] = -∞. In other words, an element is always considered to be strictly greater than a neighbor that is outside the array. 8 | 9 | You must write an algorithm that runs in O(log n) time. 10 | 11 | 12 | 13 | Example 1: 14 | 15 | Input: nums = [1,2,3,1] 16 | Output: 2 17 | Explanation: 3 is a peak element and your function should return the index number 2. 18 | Example 2: 19 | 20 | Input: nums = [1,2,1,3,5,6,4] 21 | Output: 5 22 | Explanation: Your function can return either index number 1 where the peak element is 2, or index number 5 where the peak element is 6. 23 | 24 | 25 | Constraints: 26 | 27 | 1 <= nums.length <= 1000 28 | -231 <= nums[i] <= 231 - 1 29 | nums[i] != nums[i + 1] for all valid i. 30 | 31 | # Thought Process 32 | 33 | The brute force approach is the linear search solution, which is to examine each num and compare it with its two neighbours. 34 | 35 | But the straightforward linear scan solution isn't using all of the clues given in the question. These clues, including 36 | 37 | - No adjacent two numbers are the same 38 | - the two end of the arrays are -∞ 39 | - You can return any peak. 40 | 41 | If you use all these clues you can com to binary search. Binary search has two important aspects. 42 | 43 | - A sorted array. 44 | - Splitting an array into two halves. 45 | - 46 | We do not have the first one here. Let us think about the second point. This requires a little brainstroming. 47 | 48 | If the mid element is the local maximum. Return. 49 | 50 | But if it isn't, and no two adjacent elements are equal, then either of the elements (or maybe both of them) will be greater than the mid element. 51 | 52 | Now whichever side has the greater element, you can go to that side and continue the same process. 53 | 54 | Why, you ask ? 55 | 56 | Let us say, the element at [mid+1] is greater. 57 | 58 | Both extremes have numbers smaller than the edge numbers - the right end is already given in the question, and the left end has just been checked by you. 59 | That means we can certainly find a local maximum in the right array. 60 | 61 | # Complexity 62 | 63 | O(logN) time and O(1) space 64 | -------------------------------------------------------------------------------- /leetcode75/17. Letter Combinations of a Phone Number/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private static readonly Dictionary NumToLetters = new Dictionary { 4 | {'2', "abc"}, 5 | {'3', "def"}, 6 | {'4', "ghi"}, 7 | {'5', "jkl"}, 8 | {'6', "mno"}, 9 | {'7', "pqrs"}, 10 | {'8', "tuv"}, 11 | {'9', "wxyz"} 12 | }; 13 | 14 | public IList LetterCombinations(string digits) { 15 | 16 | if (string.IsNullOrEmpty(digits)) 17 | return new List(); 18 | 19 | var result = new List(); 20 | Backtrack(digits, 0, new char[digits.Length], result); 21 | return result; 22 | } 23 | 24 | public void Backtrack(string digits, int index, char[] current, List result) { 25 | 26 | if (index == digits.Length) { 27 | result.Add(new string(current)); 28 | return; 29 | } 30 | 31 | foreach (char letter in NumToLetters[digits[index]]) { 32 | current[index] = letter; 33 | Backtrack(digits, index+1, current, result); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /leetcode75/17. Letter Combinations of a Phone Number/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order. 4 | 5 | A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters. 6 | 7 | 8 | 9 | 10 | Example 1: 11 | 12 | Input: digits = "23" 13 | Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"] 14 | Example 2: 15 | 16 | Input: digits = "" 17 | Output: [] 18 | Example 3: 19 | 20 | Input: digits = "2" 21 | Output: ["a","b","c"] 22 | 23 | 24 | Constraints: 25 | 26 | 0 <= digits.length <= 4 27 | digits[i] is a digit in the range ['2', '9']. 28 | 29 | # Thought Process 30 | 31 | Classic backtracking problem with brute force solution. We need to generate all possible combinations. 32 | 33 | # Complexity 34 | 35 | - O(4^N) where 4 is the max number of digits mapping to a number and N is the input size 36 | - O(N) space for recursion 37 | -------------------------------------------------------------------------------- /leetcode75/1768. Merge Strings Alternately/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public string MergeAlternately(string word1, string word2) { 3 | 4 | int length1 = word1.Length; 5 | int length2 = word2.Length; 6 | var result = new char[length1 + length2]; 7 | int i = 0, j = 0, k = 0; 8 | 9 | while (i < length1 && j < length2) { 10 | result[k++] = word1[i++]; 11 | result[k++] = word2[j++]; 12 | } 13 | 14 | while(i < length1) result[k++] = word1[i++]; 15 | while(j < length2) result[k++] = word2[j++]; 16 | 17 | return new string(result); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /leetcode75/1768. Merge Strings Alternately/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given two strings word1 and word2. Merge the strings by adding letters in alternating order, starting with word1. If a string is longer than the other, append the additional letters onto the end of the merged string. 4 | 5 | Return the merged string. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: word1 = "abc", word2 = "pqr" 12 | Output: "apbqcr" 13 | Explanation: The merged string will be merged as so: 14 | word1: a b c 15 | word2: p q r 16 | merged: a p b q c r 17 | Example 2: 18 | 19 | Input: word1 = "ab", word2 = "pqrs" 20 | Output: "apbqrs" 21 | Explanation: Notice that as word2 is longer, "rs" is appended to the end. 22 | word1: a b 23 | word2: p q r s 24 | merged: a p b q r s 25 | Example 3: 26 | 27 | Input: word1 = "abcd", word2 = "pq" 28 | Output: "apbqcd" 29 | Explanation: Notice that as word1 is longer, "cd" is appended to the end. 30 | word1: a b c d 31 | word2: p q 32 | merged: a p b q c d 33 | 34 | 35 | Constraints: 36 | 37 | 1 <= word1.length, word2.length <= 100 38 | word1 and word2 consist of lowercase English letters. 39 | 40 | # Thought Process 41 | 42 | Iterate over the length of the smaller string to alternate the characters, then append the rest of the longer string 43 | 44 | # Complexity 45 | 46 | O(N) time and space 47 | -------------------------------------------------------------------------------- /leetcode75/1768. Merge Strings Alternately/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def mergeAlternately(self, word1: str, word2: str) -> str: 3 | result = ''.join(a + b for a, b in zip(word1, word2)) 4 | return result + word1[len(word2):] + word2[len(word1):] 5 | 6 | # or 7 | 8 | class Solution: 9 | def mergeAlternately(self, word1: str, word2: str) -> str: 10 | result = [] 11 | 12 | for a,b in zip(word1, word2): 13 | result.append(a) 14 | result.append(b) 15 | 16 | result.extend(word1[len(word2):]) 17 | result.extend(word2[len(word1):]) 18 | 19 | return "".join(result) 20 | -------------------------------------------------------------------------------- /leetcode75/1926. Nearest Exit from Entrance in Maze/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private static readonly (int dx, int dy)[] Directions = { (1, 0), (0, 1), (-1, 0), (0, -1) }; 4 | 5 | public int NearestExit(char[][] maze, int[] entrance) { 6 | int rows = maze.Length; 7 | int cols = maze[0].Length; 8 | 9 | var q = new Queue<(int row, int col, int steps)>(); 10 | var visited = new HashSet<(int, int)>(); 11 | 12 | q.Enqueue((entrance[0], entrance[1], 0)); 13 | visited.Add((entrance[0], entrance[1])); 14 | 15 | while(q.Count > 0) { 16 | 17 | var (row, col, steps) = q.Dequeue(); 18 | 19 | foreach (var (dx, dy) in Directions) { 20 | int newRow = row + dx; 21 | int newCol = col + dy; 22 | 23 | if (!IsValidCell(newRow, newCol, rows, cols) || 24 | visited.Contains((newRow, newCol)) || 25 | maze[newRow][newCol] == '+') { 26 | continue; 27 | } 28 | 29 | if (IsExit(newRow, newCol, rows, cols)) 30 | return steps+1; 31 | 32 | visited.Add((newRow,newCol)); 33 | q.Enqueue((newRow,newCol,steps+1)); 34 | } 35 | } 36 | 37 | return -1; 38 | } 39 | 40 | private bool IsValidCell(int row, int col, int rows, int cols) { 41 | return row >= 0 && row < rows && col >= 0 && col < cols; 42 | } 43 | 44 | private bool IsExit(int row, int col, int rows, int cols) { 45 | return row == 0 || row == rows - 1 || col == 0 || col == cols - 1; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /leetcode75/1926. Nearest Exit from Entrance in Maze/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an m x n matrix maze (0-indexed) with empty cells (represented as '.') and walls (represented as '+'). You are also given the entrance of the maze, where entrance = [entrancerow, entrancecol] denotes the row and column of the cell you are initially standing at. 4 | 5 | In one step, you can move one cell up, down, left, or right. You cannot step into a cell with a wall, and you cannot step outside the maze. Your goal is to find the nearest exit from the entrance. An exit is defined as an empty cell that is at the border of the maze. The entrance does not count as an exit. 6 | 7 | Return the number of steps in the shortest path from the entrance to the nearest exit, or -1 if no such path exists. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | 14 | Input: maze = [["+","+",".","+"],[".",".",".","+"],["+","+","+","."]], entrance = [1,2] 15 | Output: 1 16 | Explanation: There are 3 exits in this maze at [1,0], [0,2], and [2,3]. 17 | Initially, you are at the entrance cell [1,2]. 18 | - You can reach [1,0] by moving 2 steps left. 19 | - You can reach [0,2] by moving 1 step up. 20 | It is impossible to reach [2,3] from the entrance. 21 | Thus, the nearest exit is [0,2], which is 1 step away. 22 | Example 2: 23 | 24 | 25 | Input: maze = [["+","+","+"],[".",".","."],["+","+","+"]], entrance = [1,0] 26 | Output: 2 27 | Explanation: There is 1 exit in this maze at [1,2]. 28 | [1,0] does not count as an exit since it is the entrance cell. 29 | Initially, you are at the entrance cell [1,0]. 30 | - You can reach [1,2] by moving 2 steps right. 31 | Thus, the nearest exit is [1,2], which is 2 steps away. 32 | Example 3: 33 | 34 | 35 | Input: maze = [[".","+"]], entrance = [0,0] 36 | Output: -1 37 | Explanation: There are no exits in this maze. 38 | 39 | 40 | Constraints: 41 | 42 | maze.length == m 43 | maze[i].length == n 44 | 1 <= m, n <= 100 45 | maze[i][j] is either '.' or '+'. 46 | entrance.length == 2 47 | 0 <= entrancerow < m 48 | 0 <= entrancecol < n 49 | entrance will always be an empty cell. 50 | 51 | # Thought process 52 | 53 | Which type of traversal lets you find the distance from a point? Breadth First Search. 54 | 55 | # Complexity 56 | 57 | O(N*M) time and space, where M,N is the grid size 58 | -------------------------------------------------------------------------------- /leetcode75/198. House Robber/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int Rob(int[] nums) { 3 | if (nums.Length == 1) 4 | return nums[0]; 5 | 6 | int prevMax = 0, currMax = nums[0]; 7 | 8 | for (int i = 1; i < nums.Length; ++i) { 9 | int tmp = currMax; 10 | currMax = Math.Max(nums[i] + prevMax, currMax); 11 | prevMax = tmp; 12 | 13 | } 14 | return currMax; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leetcode75/198. House Robber/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night. 4 | 5 | Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: nums = [1,2,3,1] 12 | Output: 4 13 | Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). 14 | Total amount you can rob = 1 + 3 = 4. 15 | Example 2: 16 | 17 | Input: nums = [2,7,9,3,1] 18 | Output: 12 19 | Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1). 20 | Total amount you can rob = 2 + 9 + 1 = 12. 21 | 22 | 23 | Constraints: 24 | 25 | 1 <= nums.length <= 100 26 | 0 <= nums[i] <= 400 27 | 28 | # Thought process 29 | 30 | Classic dynamic programming problem on 1d arrays. The recurrence relationship is dp[i] = Max(dp[i-1], dp[i-2]+nums[i]). 31 | Since only the last two dp values are used to use the current one, the space complexity can be improved. 32 | dp[i] currentMax, dp[i-1] = prevMax => currentMax = Max(prevMax, currentMax+nums[i]) 33 | 34 | # Complexity 35 | 36 | Time Complexity: O(n), where n is the length of the nums array. 37 | Space Complexity: Improved from O(n) to O(1), as we now only use a constant amount of extra space. 38 | -------------------------------------------------------------------------------- /leetcode75/215. Kth Largest Element in an Array/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int FindKthLargest(int[] nums, int k) { 3 | 4 | PriorityQueue pq = new(); //MinHeap by default 5 | 6 | for (int i = 0; i < k; ++i) { 7 | pq.Enqueue(nums[i],nums[i]); 8 | } 9 | 10 | for (int i = k; i < nums.Length; ++i) { 11 | if (nums[i] > pq.Peek()) { //keep the k largest in the min heap 12 | pq.Dequeue(); 13 | pq.Enqueue(nums[i],nums[i]); 14 | } 15 | } 16 | return pq.Peek(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /leetcode75/215. Kth Largest Element in an Array/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer array nums and an integer k, return the kth largest element in the array. 4 | 5 | Note that it is the kth largest element in the sorted order, not the kth distinct element. 6 | 7 | Can you solve it without sorting? 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: nums = [3,2,1,5,6,4], k = 2 14 | Output: 5 15 | Example 2: 16 | 17 | Input: nums = [3,2,3,1,2,4,5,5,6], k = 4 18 | Output: 4 19 | 20 | 21 | Constraints: 22 | 23 | 1 <= k <= nums.length <= 105 24 | -104 <= nums[i] <= 104 25 | 26 | # Thought Process 27 | 28 | Use a MinHeap with the invariant that it keeps the k largest elements seen so far. 29 | 30 | # Complexity 31 | 32 | O(NlogK) time, since it does N insertion/removal to/frpm the queue and each is logarithmic 33 | O(K) space to store K elements in the queue 34 | -------------------------------------------------------------------------------- /leetcode75/216. Combination Sum III/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public IList> CombinationSum3(int k, int n) { 3 | List> result = new List>(); 4 | Backtrack(k, n, 1, new List(), result); 5 | return result; 6 | } 7 | 8 | public void Backtrack(int k, int remainingSum, int start, IList current, List> result) { 9 | 10 | if (k == 0 && remainingSum == 0) { 11 | result.Add(new List(current)); 12 | return; 13 | } 14 | 15 | if (k <= 0 || remainingSum <= 0) { 16 | return; 17 | } 18 | 19 | for(int i = start; i <= 9 && i <= remainingSum; ++i) { 20 | current.Add(i); 21 | Backtrack(k-1, remainingSum-i, i+1, current, result); 22 | current.RemoveAt(current.Count - 1); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /leetcode75/216. Combination Sum III/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Find all valid combinations of k numbers that sum up to n such that the following conditions are true: 4 | 5 | Only numbers 1 through 9 are used. 6 | Each number is used at most once. 7 | Return a list of all possible valid combinations. The list must not contain the same combination twice, and the combinations may be returned in any order. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: k = 3, n = 7 14 | Output: [[1,2,4]] 15 | Explanation: 16 | 1 + 2 + 4 = 7 17 | There are no other valid combinations. 18 | Example 2: 19 | 20 | Input: k = 3, n = 9 21 | Output: [[1,2,6],[1,3,5],[2,3,4]] 22 | Explanation: 23 | 1 + 2 + 6 = 9 24 | 1 + 3 + 5 = 9 25 | 2 + 3 + 4 = 9 26 | There are no other valid combinations. 27 | Example 3: 28 | 29 | Input: k = 4, n = 1 30 | Output: [] 31 | Explanation: There are no valid combinations. 32 | Using 4 different numbers in the range [1,9], the smallest sum we can get is 1+2+3+4 = 10 and since 10 > 1, there are no valid combination. 33 | 34 | 35 | Constraints: 36 | 37 | 2 <= k <= 9 38 | 1 <= n <= 60 39 | 40 | # Thought Process 41 | 42 | Classic backtracking problem where you need to generate all the possible combinations. 43 | The only possible optimization: 44 | - run through each digit in increasing order to early return as soon the current sum is too large 45 | 46 | # Complexity 47 | Time Complexity: O(C(9,k)), where C(n,k) is the binomial coefficient. 48 | Space Complexity: O(k) for the recursion stack and the current list, 49 | -------------------------------------------------------------------------------- /leetcode75/2300. Successful Pairs of Spells and Potions/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int[] SuccessfulPairs(int[] spells, int[] potions, long success) { 3 | 4 | int n = spells.Length, m = potions.Length; 5 | var result = new int[n]; 6 | Array.Sort(potions); 7 | 8 | for (int i = 0; i < n; ++i) { 9 | //long minRequiredPotion = (success + spells[i] - 1) / spells[i]; //less readable way to calculate ceil division 10 | long minRequiredPotion = (long)Math.Ceiling((double)success / spells[i]); 11 | result[i] = m-BinarySearch(potions, minRequiredPotion); 12 | } 13 | return result; 14 | } 15 | 16 | int BinarySearch(int[] potions, long minRequiredPotion) { 17 | 18 | // initialize right to potions.Length allow to consider the case where no potion fits the requirement 19 | int left = 0, right = potions.Length; 20 | 21 | while (left < right) { 22 | int mid = left + (right-left) / 2; 23 | if (potions[mid] >= minRequiredPotion) { 24 | right = mid; 25 | } 26 | else { 27 | left = mid+1; 28 | } 29 | } 30 | 31 | return right; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /leetcode75/2300. Successful Pairs of Spells and Potions/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given two positive integer arrays spells and potions, of length n and m respectively, where spells[i] represents the strength of the ith spell and potions[j] represents the strength of the jth potion. 4 | 5 | You are also given an integer success. A spell and potion pair is considered successful if the product of their strengths is at least success. 6 | 7 | Return an integer array pairs of length n where pairs[i] is the number of potions that will form a successful pair with the ith spell. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: spells = [5,1,3], potions = [1,2,3,4,5], success = 7 14 | Output: [4,0,3] 15 | Explanation: 16 | - 0th spell: 5 * [1,2,3,4,5] = [5,10,15,20,25]. 4 pairs are successful. 17 | - 1st spell: 1 * [1,2,3,4,5] = [1,2,3,4,5]. 0 pairs are successful. 18 | - 2nd spell: 3 * [1,2,3,4,5] = [3,6,9,12,15]. 3 pairs are successful. 19 | Thus, [4,0,3] is returned. 20 | Example 2: 21 | 22 | Input: spells = [3,1,2], potions = [8,5,8], success = 16 23 | Output: [2,0,2] 24 | Explanation: 25 | - 0th spell: 3 * [8,5,8] = [24,15,24]. 2 pairs are successful. 26 | - 1st spell: 1 * [8,5,8] = [8,5,8]. 0 pairs are successful. 27 | - 2nd spell: 2 * [8,5,8] = [16,10,16]. 2 pairs are successful. 28 | Thus, [2,0,2] is returned. 29 | 30 | 31 | Constraints: 32 | 33 | n == spells.length 34 | m == potions.length 35 | 1 <= n, m <= 105 36 | 1 <= spells[i], potions[i] <= 105 37 | 1 <= success <= 1010 38 | 39 | # Thought process 40 | 41 | 1. if a spell and potion pair is successful, then the spell and all stronger potions will be successful too. 42 | 2. for each spell, we need to find the potion with the least strength that will form a successful pair. 43 | 3. We can do this by sorting the potions based on strength and using binary search. 44 | 45 | # Complexity 46 | 47 | O(Plog(P) + Slog(P)) time: sorting + binary search 48 | O(1) space 49 | -------------------------------------------------------------------------------- /leetcode75/2336. Smallest Number in Infinite Set/Solutions.cs: -------------------------------------------------------------------------------- 1 | public class SmallestInfiniteSet { 2 | private SortedSet addedNumbers; 3 | private int currentSmallest; 4 | 5 | public SmallestInfiniteSet() { 6 | addedNumbers = new SortedSet(); 7 | currentSmallest = 1; 8 | } 9 | 10 | public int PopSmallest() { 11 | if (addedNumbers.Count > 0) { 12 | int smallest = addedNumbers.Min; 13 | addedNumbers.Remove(smallest); 14 | return smallest; 15 | } 16 | return currentSmallest++; 17 | } 18 | 19 | public void AddBack(int num) { 20 | if (num < currentSmallest) { 21 | addedNumbers.Add(num); 22 | } 23 | } 24 | } 25 | 26 | /** 27 | * Your SmallestInfiniteSet object will be instantiated and called as such: 28 | * SmallestInfiniteSet obj = new SmallestInfiniteSet(); 29 | * int param_1 = obj.PopSmallest(); 30 | * obj.AddBack(num); 31 | */ 32 | -------------------------------------------------------------------------------- /leetcode75/2336. Smallest Number in Infinite Set/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | ou have a set which contains all positive integers [1, 2, 3, 4, 5, ...]. 4 | 5 | Implement the SmallestInfiniteSet class: 6 | 7 | SmallestInfiniteSet() Initializes the SmallestInfiniteSet object to contain all positive integers. 8 | int popSmallest() Removes and returns the smallest integer contained in the infinite set. 9 | void addBack(int num) Adds a positive integer num back into the infinite set, if it is not already in the infinite set. 10 | 11 | 12 | Example 1: 13 | 14 | Input 15 | ["SmallestInfiniteSet", "addBack", "popSmallest", "popSmallest", "popSmallest", "addBack", "popSmallest", "popSmallest", "popSmallest"] 16 | [[], [2], [], [], [], [1], [], [], []] 17 | Output 18 | [null, null, 1, 2, 3, null, 1, 4, 5] 19 | 20 | Explanation 21 | SmallestInfiniteSet smallestInfiniteSet = new SmallestInfiniteSet(); 22 | smallestInfiniteSet.addBack(2); // 2 is already in the set, so no change is made. 23 | smallestInfiniteSet.popSmallest(); // return 1, since 1 is the smallest number, and remove it from the set. 24 | smallestInfiniteSet.popSmallest(); // return 2, and remove it from the set. 25 | smallestInfiniteSet.popSmallest(); // return 3, and remove it from the set. 26 | smallestInfiniteSet.addBack(1); // 1 is added back to the set. 27 | smallestInfiniteSet.popSmallest(); // return 1, since 1 was added back to the set and 28 | // is the smallest number, and remove it from the set. 29 | smallestInfiniteSet.popSmallest(); // return 4, and remove it from the set. 30 | smallestInfiniteSet.popSmallest(); // return 5, and remove it from the set. 31 | 32 | 33 | Constraints: 34 | 35 | 1 <= num <= 1000 36 | At most 1000 calls will be made in total to popSmallest and addBack. 37 | 38 | # Though Process 39 | 40 | Two possible coices: hashset (to handle duplicates) + priority queue (min heap) or sorted set (balanced tree) 41 | Since the idea is to handle an infinite stream of integer, we ignore the contraint and do not intialize the data structure with all the possible elements. 42 | Rather we save the minimum element in the infinite set and store in the data structure only the elements less than it 43 | 44 | # Complexity 45 | 46 | O(logN) time for each operaton where N is the number of elements in the data structure 47 | O(N) space 48 | -------------------------------------------------------------------------------- /leetcode75/238. Product of Array Except Self/Solution.cd: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int[] ProductExceptSelf(int[] nums) { 3 | 4 | var length = nums.Length; 5 | var result = new int[length]; 6 | 7 | int leftProduct = 1; 8 | 9 | for (int i = 0; i < length; ++i) 10 | { 11 | result[i] = leftProduct; 12 | leftProduct *= nums[i]; 13 | 14 | } 15 | 16 | int rightProduct = 1; 17 | 18 | for (int i = length - 1; i >= 0; i--) 19 | { 20 | result[i] *= rightProduct; 21 | rightProduct *= nums[i]; 22 | } 23 | 24 | return result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /leetcode75/238. Product of Array Except Self/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements of nums except nums[i]. 4 | 5 | The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. 6 | 7 | You must write an algorithm that runs in O(n) time and without using the division operation. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: nums = [1,2,3,4] 14 | Output: [24,12,8,6] 15 | Example 2: 16 | 17 | Input: nums = [-1,1,0,-3,3] 18 | Output: [0,0,9,0,0] 19 | 20 | 21 | Constraints: 22 | 23 | 2 <= nums.length <= 105 24 | -30 <= nums[i] <= 30 25 | The input is generated such that answer[i] is guaranteed to fit in a 32-bit integer. 26 | 27 | 28 | Follow up: Can you solve the problem in O(1) extra space complexity? (The output array does not count as extra space for space complexity analysis.) 29 | 30 | # Thought process 31 | 32 | The solution is quite straightforward. First calculate the product of all elements to the left of each element (forward iteration). 33 | Then, in a second pass, multiplying each element by the product of all elements to its right (backward iteration). 34 | 35 | # Complexity 36 | 37 | The time complexity is O(n), where n is the length of the input array. The space complexity is O(1), as we're not using any extra space that scales with the input size. 38 | -------------------------------------------------------------------------------- /leetcode75/238. Product of Array Except Self/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def productExceptSelf(self, nums: List[int]) -> List[int]: 3 | n = len(nums) 4 | result = [1] * n 5 | 6 | left_product = 1 7 | for i in range(n): 8 | result[i] = left_product 9 | left_product *= nums[i] 10 | 11 | right_product = 1 12 | for i in range(n - 1, -1, -1): 13 | result[i] *= right_product 14 | right_product *= nums[i] 15 | 16 | return result 17 | -------------------------------------------------------------------------------- /leetcode75/2462. Total Cost to Hire K Workers/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public long TotalCost(int[] costs, int k, int candidates) { 3 | 4 | int left = 0, right = costs.Length - 1; 5 | var leftMinHeap = new PriorityQueue(); 6 | var rightMinHeap = new PriorityQueue(); 7 | 8 | for (; left < candidates; left++) { 9 | leftMinHeap.Enqueue(costs[left], costs[left]); 10 | } 11 | 12 | for (; right >= left && right >= costs.Length - candidates; right--) { 13 | rightMinHeap.Enqueue(costs[right], costs[right]); 14 | } 15 | 16 | long result = 0; 17 | 18 | while (k > 0) { 19 | int leftCost = leftMinHeap.TryPeek(out int cost, out _) ? cost : Int32.MaxValue; 20 | int rightCost = rightMinHeap.TryPeek(out cost, out _) ? cost : Int32.MaxValue; 21 | 22 | if (leftCost <= rightCost) { 23 | result += leftMinHeap.Dequeue(); 24 | if (left <= right) { 25 | leftMinHeap.Enqueue(costs[left], costs[left]); 26 | left++; 27 | } 28 | } 29 | else { 30 | result += rightMinHeap.Dequeue(); 31 | if (left <= right) { 32 | rightMinHeap.Enqueue(costs[right], costs[right]); 33 | right--; 34 | } 35 | } 36 | k--; 37 | } 38 | return result; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /leetcode75/2542. Maximum Subsequence Score/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public long MaxScore(int[] nums1, int[] nums2, int k) { 3 | 4 | int n = nums1.Length; 5 | var pairs = new (int num1, int num2)[n]; 6 | for (int i = 0; i < n; i++) { 7 | pairs[i] = (nums1[i], nums2[i]); 8 | } 9 | 10 | Array.Sort(pairs, (a, b) => b.num2.CompareTo(a.num2)); 11 | 12 | var minHeap = new PriorityQueue(); 13 | long sumNums1 = 0, result = 0; 14 | for (int i = 0; i < n; i++) 15 | { 16 | if (i < k) 17 | { 18 | minHeap.Enqueue(pairs[i].Item1, pairs[i].num1); 19 | sumNums1 += pairs[i].num1; 20 | if (i == k - 1) { 21 | result = sumNums1 * pairs[i].num2; 22 | } 23 | } 24 | else { 25 | sumNums1 = sumNums1 - minHeap.Dequeue() + pairs[i].num1; 26 | result = Math.Max(result, pairs[i].num2 * sumNums1); 27 | minHeap.Enqueue(pairs[i].num1, pairs[i].num1); 28 | } 29 | } 30 | 31 | return result; 32 | } 33 | } 34 | 35 | // Top k elements seen sofar of nums1 36 | // to maximize new min remove the minimum of nums 1 37 | -------------------------------------------------------------------------------- /leetcode75/2542. Maximum Subsequence Score/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given two 0-indexed integer arrays nums1 and nums2 of equal length n and a positive integer k. You must choose a subsequence of indices from nums1 of length k. 4 | 5 | For chosen indices i0, i1, ..., ik - 1, your score is defined as: 6 | 7 | The sum of the selected elements from nums1 multiplied with the minimum of the selected elements from nums2. 8 | It can defined simply as: (nums1[i0] + nums1[i1] +...+ nums1[ik - 1]) * min(nums2[i0] , nums2[i1], ... ,nums2[ik - 1]). 9 | Return the maximum possible score. 10 | 11 | A subsequence of indices of an array is a set that can be derived from the set {0, 1, ..., n-1} by deleting some or no elements. 12 | 13 | 14 | 15 | Example 1: 16 | 17 | Input: nums1 = [1,3,3,2], nums2 = [2,1,3,4], k = 3 18 | Output: 12 19 | Explanation: 20 | The four possible subsequence scores are: 21 | - We choose the indices 0, 1, and 2 with score = (1+3+3) * min(2,1,3) = 7. 22 | - We choose the indices 0, 1, and 3 with score = (1+3+2) * min(2,1,4) = 6. 23 | - We choose the indices 0, 2, and 3 with score = (1+3+2) * min(2,3,4) = 12. 24 | - We choose the indices 1, 2, and 3 with score = (3+3+2) * min(1,3,4) = 8. 25 | Therefore, we return the max score, which is 12. 26 | Example 2: 27 | 28 | Input: nums1 = [4,2,3,1,1], nums2 = [7,5,10,9,6], k = 1 29 | Output: 30 30 | Explanation: 31 | Choosing index 2 is optimal: nums1[2] * nums2[2] = 3 * 10 = 30 is the maximum possible score. 32 | 33 | 34 | Constraints: 35 | 36 | n == nums1.length == nums2.length 37 | 1 <= n <= 105 38 | 0 <= nums1[i], nums2[j] <= 105 39 | 1 <= k <= n 40 | 41 | # Thought Process 42 | 43 | // Top k elements seen sofar of nums1 44 | // to maximize new min remove the minimum of nums 1 45 | 46 | # Complexity 47 | 48 | Time Complexity: O(n log n) due to the sorting operation. 49 | Space Complexity: O(n) for the pairs array and O(k) for the min heap. 50 | -------------------------------------------------------------------------------- /leetcode75/283. Move Zeroes/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public void MoveZeroes(int[] nums) { 3 | 4 | int nonZeroIndex = 0; 5 | 6 | for (int i = 0; i < nums.Length; ++i) { 7 | if (nums[i] != 0) { 8 | nums[nonZeroIndex++] = nums[i]; 9 | } 10 | } 11 | 12 | for (int i = nonZeroIndex; i < nums.Length; ++i) { 13 | nums[i] = 0; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leetcode75/283. Move Zeroes/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements. 4 | 5 | Note that you must do this in-place without making a copy of the array. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: nums = [0,1,0,3,12] 12 | Output: [1,3,12,0,0] 13 | Example 2: 14 | 15 | Input: nums = [0] 16 | Output: [0] 17 | 18 | 19 | Constraints: 20 | 21 | 1 <= nums.length <= 104 22 | -231 <= nums[i] <= 231 - 1 23 | 24 | 25 | Follow up: Could you minimize the total number of operations done? 26 | 27 | # Thought process 28 | 29 | Keep an index for non-zero elements and use it to place non-zero numbers while iterating the input array. 30 | Pad the remaining elements with zeros. 31 | 32 | # Complexity 33 | The time complexity is O(n), the space complexity remains O(1) as we're modifying the array in-place without using any additional data structures. 34 | -------------------------------------------------------------------------------- /leetcode75/283. Move Zeroes/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def moveZeroes(self, nums: List[int]) -> None: 3 | """ 4 | Do not return anything, modify nums in-place instead. 5 | """ 6 | non_zero_index = 0 7 | 8 | # Move all non-zero elements to the front 9 | for num in nums: 10 | if num != 0: 11 | nums[non_zero_index] = num 12 | non_zero_index += 1 13 | 14 | # Fill the rest with zeroes 15 | nums[non_zero_index:] = [0] * (len(nums) - non_zero_index) 16 | -------------------------------------------------------------------------------- /leetcode75/334. Increasing Triplet Subsequence/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public bool IncreasingTriplet(int[] nums) { 3 | 4 | var first = int.MaxValue; 5 | var second = int.MaxValue; 6 | 7 | foreach (int num in nums) 8 | { 9 | if (num <= first) 10 | { 11 | first = num; 12 | } 13 | else if (num <= second) 14 | { 15 | second = num; 16 | } 17 | else 18 | { 19 | return true; 20 | } 21 | } 22 | return false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /leetcode75/334. Increasing Triplet Subsequence/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer array nums, return true if there exists a triple of indices (i, j, k) such that i < j < k and nums[i] < nums[j] < nums[k]. If no such indices exists, return false. 4 | 5 | 6 | 7 | Example 1: 8 | 9 | Input: nums = [1,2,3,4,5] 10 | Output: true 11 | Explanation: Any triplet where i < j < k is valid. 12 | Example 2: 13 | 14 | Input: nums = [5,4,3,2,1] 15 | Output: false 16 | Explanation: No triplet exists. 17 | Example 3: 18 | 19 | Input: nums = [2,1,5,0,4,6] 20 | Output: true 21 | Explanation: The triplet (3, 4, 5) is valid because nums[3] == 0 < nums[4] == 4 < nums[5] == 6. 22 | 23 | 24 | Constraints: 25 | 26 | 1 <= nums.length <= 5 * 105 27 | -231 <= nums[i] <= 231 - 1 28 | 29 | 30 | Follow up: Could you implement a solution that runs in O(n) time complexity and O(1) space complexity? 31 | 32 | # Description 33 | 34 | The idea is to iterate the input and use two variables: 35 | 36 | - first which keeps track of the smallest number seen so far. 37 | - second which keeps track of the smallest number that is greater than first. 38 | If we find a number greater than second, we have found an increasing triplet. 39 | 40 | # Complexity 41 | 42 | The time complexity is O(n) and the space complexity is O(1). 43 | -------------------------------------------------------------------------------- /leetcode75/334. Increasing Triplet Subsequence/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def increasingTriplet(self, nums: List[int]) -> bool: 3 | 4 | first = float('inf') 5 | second = float('inf') 6 | 7 | for num in nums: 8 | if num <= first: 9 | first = num 10 | elif num <= second: 11 | second = num 12 | else: 13 | return True 14 | 15 | return False 16 | 17 | 18 | -------------------------------------------------------------------------------- /leetcode75/338. Counting Bits/Solution.cs: -------------------------------------------------------------------------------- 1 | // O(NlgN) solutiion 2 | public class Solution { 3 | public int[] CountBits(int n) { 4 | 5 | var result = new int[n+1]; 6 | 7 | for (int i = 0; i <= n; ++i) { 8 | result[i] = countOnes(i); 9 | } 10 | 11 | return result; 12 | } 13 | 14 | private int countOnes(int n) { 15 | int ones = 0; 16 | 17 | while (n > 0) { 18 | ones += n % 2; 19 | n /= 2; 20 | } 21 | 22 | return ones; 23 | } 24 | } 25 | 26 | //O(N) solution 27 | public class Solution { 28 | public int[] CountBits(int n) { 29 | 30 | var result = new int[n+1]; 31 | 32 | for (int i = 1, nextPower = 2; i <= n; ++i) { 33 | if (i == nextPower) { 34 | result[i] = 1; 35 | nextPower *= 2; 36 | } 37 | else { 38 | result[i] = result[i >> 1] + (i & 1); 39 | //result[i] = result[i/2] + (i % 2); 40 | } 41 | } 42 | return result; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /leetcode75/338. Counting Bits/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an integer n, return an array ans of length n + 1 such that for each i (0 <= i <= n), ans[i] is the number of 1's in the binary representation of i. 4 | 5 | Example 1: 6 | 7 | Input: n = 2 8 | Output: [0,1,1] 9 | Explanation: 10 | 0 --> 0 11 | 1 --> 1 12 | 2 --> 10 13 | Example 2: 14 | 15 | Input: n = 5 16 | Output: [0,1,1,2,1,2] 17 | Explanation: 18 | 0 --> 0 19 | 1 --> 1 20 | 2 --> 10 21 | 3 --> 11 22 | 4 --> 100 23 | 5 --> 101 24 | 25 | 26 | Constraints: 27 | 28 | 0 <= n <= 105 29 | 30 | 31 | Follow up: 32 | 33 | It is very easy to come up with a solution with a runtime of O(n log n). Can you do it in linear time O(n) and possibly in a single pass? 34 | Can you do it without using any built-in function (i.e., like __builtin_popcount in C++)? 35 | 36 | # Thought process 37 | 38 | The brute force approach is easy: for each number from 0 to n, convert it to binary and count the 1's. 39 | To convert a number to binary: 40 | - Repeatedly divide by 2 41 | - Keep track of the remainders (0 or 1) 42 | - The remainders in reverse order give the binary representation 43 | 44 | The optimal approach is based on two observations. 45 | 46 | Observation 1: binary representations follow a pattern: 47 | 0 (0) -> 0 48 | 1 (1) -> 1 49 | 2 (10) -> 1 50 | 3 (11) -> 2 51 | 4 (100) -> 1 52 | 5 (101) -> 2 53 | 6 (110) -> 2 54 | 7 (111) -> 3 55 | 56 | Observation 2: When we reach a power of 2, the count resets to 1 and then follows the previous pattern plus 1. 57 | 58 | If x is even: bits(x) = bits(x/2) 59 | If x is odd: bits(x) = bits(x/2) + 1 60 | 61 | # Complexity 62 | 63 | - O(nlgn) for the brute force (conversion to binary is O(lgn) for each number 64 | - O(n) for the optimal approach 65 | -------------------------------------------------------------------------------- /leetcode75/345. Reverse Vowels of a String/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | private static readonly HashSet Vowels = new HashSet { 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U' }; 3 | 4 | public string ReverseVowels(string s) { 5 | return string.Create(s.Length, s, (span, original) => { 6 | int left = 0, right = s.Length - 1; 7 | 8 | while (left < right) { 9 | while (left < right && !Vowels.Contains(original[left])) { 10 | span[left] = original[left]; 11 | left++; 12 | } 13 | 14 | while (left < right && !Vowels.Contains(original[right])) { 15 | span[right] = original[right]; 16 | right--; 17 | } 18 | 19 | if (left < right) { 20 | span[left] = original[right]; 21 | span[right] = original[left]; 22 | left++; 23 | right--; 24 | } 25 | } 26 | 27 | // Copy any remaining characters 28 | while (left <= right) { 29 | span[left] = original[left]; 30 | left++; 31 | } 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /leetcode75/345. Reverse Vowels of a String/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given a string s, reverse only all the vowels in the string and return it. 4 | 5 | The vowels are 'a', 'e', 'i', 'o', and 'u', and they can appear in both lower and upper cases, more than once. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: s = "IceCreAm" 12 | 13 | Output: "AceCreIm" 14 | 15 | Explanation: 16 | 17 | The vowels in s are ['I', 'e', 'e', 'A']. On reversing the vowels, s becomes "AceCreIm". 18 | 19 | Example 2: 20 | 21 | Input: s = "leetcode" 22 | 23 | Output: "leotcede" 24 | 25 | 26 | 27 | Constraints: 28 | 29 | 1 <= s.length <= 3 * 105 30 | s consist of printable ASCII characters. 31 | 32 | # thought process 33 | -------------------------------------------------------------------------------- /leetcode75/345. Reverse Vowels of a String/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseVowels(self, s: str) -> str: 3 | vowels = set('aeiouAEIOU') 4 | chars = list(s) # Convert string to list of characters 5 | left, right = 0, len(s) - 1 6 | 7 | while left < right: 8 | while left < right and chars[left] not in vowels: 9 | left += 1 10 | 11 | while left < right and chars[right] not in vowels: 12 | right -= 1 13 | 14 | if left < right: 15 | chars[left], chars[right] = chars[right], chars[left] 16 | left += 1 17 | right -= 1 18 | 19 | return ''.join(chars) 20 | -------------------------------------------------------------------------------- /leetcode75/374. Guess Number Higher or Lower/Solution.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Forward declaration of guess API. 3 | * @param num your guess 4 | * @return -1 if num is higher than the picked number 5 | * 1 if num is lower than the picked number 6 | * otherwise return 0 7 | * int guess(int num); 8 | */ 9 | 10 | public class Solution : GuessGame { 11 | public int GuessNumber(int n) { 12 | 13 | int left = 1, right = n; 14 | 15 | while (left <= right) { 16 | int mid = left + (right - left) / 2; 17 | int answer = guess(mid); 18 | if (answer < 0) { 19 | right = mid-1; 20 | } 21 | else if (answer > 0) { 22 | left = mid+1; 23 | } 24 | else { 25 | return mid; 26 | } 27 | } 28 | 29 | //should never reach this point 30 | return -1; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /leetcode75/374. Guess Number Higher or Lower/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | We are playing the Guess Game. The game is as follows: 4 | 5 | I pick a number from 1 to n. You have to guess which number I picked. 6 | 7 | Every time you guess wrong, I will tell you whether the number I picked is higher or lower than your guess. 8 | 9 | You call a pre-defined API int guess(int num), which returns three possible results: 10 | 11 | -1: Your guess is higher than the number I picked (i.e. num > pick). 12 | 1: Your guess is lower than the number I picked (i.e. num < pick). 13 | 0: your guess is equal to the number I picked (i.e. num == pick). 14 | Return the number that I picked. 15 | 16 | 17 | 18 | Example 1: 19 | 20 | Input: n = 10, pick = 6 21 | Output: 6 22 | Example 2: 23 | 24 | Input: n = 1, pick = 1 25 | Output: 1 26 | Example 3: 27 | 28 | Input: n = 2, pick = 1 29 | Output: 1 30 | 31 | 32 | Constraints: 33 | 34 | 1 <= n <= 231 - 1 35 | 1 <= pick <= n 36 | 37 | # Though process 38 | 39 | Classic binary search implementation. 40 | 41 | # Complexity 42 | 43 | O(logN) time, O(1) space 44 | -------------------------------------------------------------------------------- /leetcode75/392. Is Subsequence/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public bool IsSubsequence(string s, string t) { 3 | 4 | if (string.IsNullOrEmpty(s)) return true; 5 | 6 | int sIndex = 0; 7 | 8 | for (int tIndex = 0; tIndex < t.Length && sIndex < s.Length; ++tIndex) 9 | { 10 | if (s[sIndex] == t[tIndex]) 11 | { 12 | sIndex++; 13 | } 14 | } 15 | return sIndex == s.Length; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /leetcode75/392. Is Subsequence/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given two strings s and t, return true if s is a subsequence of t, or false otherwise. 4 | 5 | A subsequence of a string is a new string that is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (i.e., "ace" is a subsequence of "abcde" while "aec" is not). 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: s = "abc", t = "ahbgdc" 12 | Output: true 13 | Example 2: 14 | 15 | Input: s = "axc", t = "ahbgdc" 16 | Output: false 17 | 18 | 19 | Constraints: 20 | 21 | 0 <= s.length <= 100 22 | 0 <= t.length <= 104 23 | s and t consist only of lowercase English letters. 24 | 25 | 26 | Follow up: Suppose there are lots of incoming s, say s1, s2, ..., sk where k >= 109, and you want to check one by one to see if t has its subsequence. In this scenario, how would you change your code? 27 | 28 | # Thought Process 29 | 30 | This idea is to check if s is a subsequence of t by iterating through both strings once, advancing the pointer for s only when a matching character is found in t. The final check of sIndex against s.Length determines if all characters in s were found in order in t. 31 | 32 | # Complexity 33 | 34 | O(n) time complexity, where n is the length of string t. The space complexity is O(1) as we're only using a constant amount of extra space. 35 | -------------------------------------------------------------------------------- /leetcode75/392. Is Subsequence/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isSubsequence(self, s: str, t: str) -> bool: 3 | 4 | s_index, t_index = 0, 0 5 | 6 | while s_index < len(s) and t_index < len(t): 7 | if s[s_index] == t[t_index]: 8 | s_index += 1 9 | t_index += 1 10 | 11 | return s_index == len(s) 12 | -------------------------------------------------------------------------------- /leetcode75/399. Evaluate Division/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | //queries = paths from from start to end 4 | //values = weights 5 | //equations = edges 6 | 7 | Dictionary> graph; 8 | 9 | public double[] CalcEquation(IList> equations, double[] values, IList> queries) { 10 | 11 | graph = new Dictionary>(); 12 | BuildGraph(equations, values); 13 | 14 | var result = new double[queries.Count]; 15 | for (int i = 0; i < queries.Count; ++i) { 16 | result[i] = CalcQuery(queries[i][0], queries[i][1]); 17 | } 18 | return result; 19 | } 20 | 21 | private void BuildGraph(IList> equations, double[] values) { 22 | for (int i = 0; i < equations.Count; i++) { 23 | var (from, to) = (equations[i][0], equations[i][1]); 24 | AddEdge(from, to, values[i]); 25 | AddEdge(to, from, 1 / values[i]); 26 | } 27 | } 28 | 29 | private void AddEdge(string from, string to, double weight) { 30 | if (!graph.TryGetValue(from, out var neighbors)) { 31 | neighbors = new List<(string, double)>(); 32 | graph[from] = neighbors; 33 | } 34 | neighbors.Add((to, weight)); 35 | } 36 | 37 | private double CalcQuery(string start, string end) { 38 | if (!graph.ContainsKey(start) || !graph.ContainsKey(end)) { 39 | return -1.0; 40 | } 41 | 42 | var visited = new HashSet(); 43 | return Dfs(start, end, visited); 44 | } 45 | 46 | private double Dfs(string current, string end, HashSet visited) { 47 | 48 | if (current == end) { 49 | return 1.0; 50 | } 51 | 52 | visited.Add(current); 53 | 54 | foreach(var (neighbor, weight) in graph[current]) { 55 | if(!visited.Contains(neighbor)) { 56 | var result = Dfs(neighbor, end, visited); 57 | if(result >= 0) 58 | return weight * result; 59 | } 60 | } 61 | visited.Remove(current); 62 | return -1.0; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /leetcode75/399. Evaluate Division/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an array of variable pairs equations and an array of real numbers values, where equations[i] = [Ai, Bi] and values[i] represent the equation Ai / Bi = values[i]. Each Ai or Bi is a string that represents a single variable. 4 | 5 | You are also given some queries, where queries[j] = [Cj, Dj] represents the jth query where you must find the answer for Cj / Dj = ?. 6 | 7 | Return the answers to all queries. If a single answer cannot be determined, return -1.0. 8 | 9 | Note: The input is always valid. You may assume that evaluating the queries will not result in division by zero and that there is no contradiction. 10 | 11 | Note: The variables that do not occur in the list of equations are undefined, so the answer cannot be determined for them. 12 | 13 | 14 | 15 | Example 1: 16 | 17 | Input: equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]] 18 | Output: [6.00000,0.50000,-1.00000,1.00000,-1.00000] 19 | Explanation: 20 | Given: a / b = 2.0, b / c = 3.0 21 | queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? 22 | return: [6.0, 0.5, -1.0, 1.0, -1.0 ] 23 | note: x is undefined => -1.0 24 | Example 2: 25 | 26 | Input: equations = [["a","b"],["b","c"],["bc","cd"]], values = [1.5,2.5,5.0], queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]] 27 | Output: [3.75000,0.40000,5.00000,0.20000] 28 | Example 3: 29 | 30 | Input: equations = [["a","b"]], values = [0.5], queries = [["a","b"],["b","a"],["a","c"],["x","y"]] 31 | Output: [0.50000,2.00000,-1.00000,-1.00000] 32 | 33 | 34 | Constraints: 35 | 36 | 1 <= equations.length <= 20 37 | equations[i].length == 2 38 | 1 <= Ai.length, Bi.length <= 5 39 | values.length == equations.length 40 | 0.0 < values[i] <= 20.0 41 | 1 <= queries.length <= 20 42 | queries[i].length == 2 43 | 1 <= Cj.length, Dj.length <= 5 44 | Ai, Bi, Cj, Dj consist of lower case English letters and digits. 45 | 46 | # Thought Process 47 | 48 | See the problem as a graph where: 49 | - queries = paths from from start to end 50 | - values = weights 51 | - equations = edges 52 | 53 | # Complexity 54 | 55 | O(N*M) time and space where N is the number of variable and M of equations 56 | -------------------------------------------------------------------------------- /leetcode75/435. Non-overlapping Intervals/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | public int EraseOverlapIntervals(int[][] intervals) { 4 | 5 | Array.Sort(intervals, (x,y) => x[1].CompareTo(y[1])); 6 | 7 | int count = 0; 8 | var endOfInterval = Int32.MinValue; 9 | foreach (var interval in intervals) { 10 | if (interval[0] >= endOfInterval) { 11 | endOfInterval = interval[1]; 12 | } 13 | else { 14 | count += 1; 15 | } 16 | } 17 | return count; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /leetcode75/435. Non-overlapping Intervals/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping. 4 | 5 | Note that intervals which only touch at a point are non-overlapping. For example, [1, 2] and [2, 3] are non-overlapping. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: intervals = [[1,2],[2,3],[3,4],[1,3]] 12 | Output: 1 13 | Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping. 14 | Example 2: 15 | 16 | Input: intervals = [[1,2],[1,2],[1,2]] 17 | Output: 2 18 | Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping. 19 | Example 3: 20 | 21 | Input: intervals = [[1,2],[2,3]] 22 | Output: 0 23 | Explanation: You don't need to remove any of the intervals since they're already non-overlapping. 24 | 25 | 26 | Constraints: 27 | 28 | 1 <= intervals.length <= 105 29 | intervals[i].length == 2 30 | -5 * 104 <= starti < endi <= 5 * 104 31 | 32 | # Thought Process 33 | 34 | Brute Force would be to try all possible combinations of intervals. This would be extremely inefficient (exponential time complexity). 35 | 36 | Sorting often helps in interval problems, but there are different sorting criteria: 37 | a) By start time 38 | b) By end time 39 | c) By interval length 40 | 41 | The key insight that leads to the greedy approach is recognizing that by always choosing the interval with the earliest end time, we maximize our options for the rest of the intervals. 42 | 43 | # Complexity 44 | 45 | O(N) time, O(1) space 46 | -------------------------------------------------------------------------------- /leetcode75/452. Minimum Number of Arrows to Burst Balloons/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int FindMinArrowShots(int[][] points) { 3 | 4 | Array.Sort(points, (x, y) => x[1].CompareTo(y[1])); 5 | 6 | int arrows = 1; 7 | long end = points[0][1]; 8 | 9 | for (int i = 1; i < points.Length; i++) { 10 | if (points[i][0] > end) { 11 | arrows++; 12 | end = points[i][1]; 13 | } 14 | } 15 | 16 | return arrows; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /leetcode75/452. Minimum Number of Arrows to Burst Balloons/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | There are some spherical balloons taped onto a flat wall that represents the XY-plane. The balloons are represented as a 2D integer array points where points[i] = [xstart, xend] denotes a balloon whose horizontal diameter stretches between xstart and xend. You do not know the exact y-coordinates of the balloons. 4 | 5 | Arrows can be shot up directly vertically (in the positive y-direction) from different points along the x-axis. A balloon with xstart and xend is burst by an arrow shot at x if xstart <= x <= xend. There is no limit to the number of arrows that can be shot. A shot arrow keeps traveling up infinitely, bursting any balloons in its path. 6 | 7 | Given the array points, return the minimum number of arrows that must be shot to burst all balloons. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: points = [[10,16],[2,8],[1,6],[7,12]] 14 | Output: 2 15 | Explanation: The balloons can be burst by 2 arrows: 16 | - Shoot an arrow at x = 6, bursting the balloons [2,8] and [1,6]. 17 | - Shoot an arrow at x = 11, bursting the balloons [10,16] and [7,12]. 18 | Example 2: 19 | 20 | Input: points = [[1,2],[3,4],[5,6],[7,8]] 21 | Output: 4 22 | Explanation: One arrow needs to be shot for each balloon for a total of 4 arrows. 23 | Example 3: 24 | 25 | Input: points = [[1,2],[2,3],[3,4],[4,5]] 26 | Output: 2 27 | Explanation: The balloons can be burst by 2 arrows: 28 | - Shoot an arrow at x = 2, bursting the balloons [1,2] and [2,3]. 29 | - Shoot an arrow at x = 4, bursting the balloons [3,4] and [4,5]. 30 | 31 | 32 | Constraints: 33 | 34 | 1 <= points.length <= 105 35 | points[i].length == 2 36 | -231 <= xstart < xend <= 231 - 1 37 | 38 | # Thought process 39 | 40 | The Brute Force is to try all possible combinations of arrow placements. This would be extremely inefficient. 41 | Imagine the balloons laid on the x axis. An optimal solution would involve shooting arrows at points that intersect with multiple balloons. 42 | The key insight is that if we shoot an arrow, it's optimal to shoot it at the rightmost end point of the overlapping balloons. 43 | [3,4] [1,7] -> 4 [3,5] [4,7] -> 5 44 | This ensures we hit as many balloons as possible with each arrow. 45 | Sorting by end points gives an advantage: it allows us to process balloons in the order they end. 46 | This means we can always shoot at the end point of the current balloon and be sure we're not missing any earlier opportunities. 47 | If the current balloon starts after the last arrow shot, we need a new arrow. Otherwise, the current balloon is burst by the previous arrow. 48 | 49 | # Complexity 50 | 51 | O(N) time, O(1) space 52 | 53 | -------------------------------------------------------------------------------- /leetcode75/605. Can Place Flowers/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public bool CanPlaceFlowers(int[] flowerbed, int n) { 3 | 4 | int length = flowerbed.Length; 5 | 6 | for (int i = 0; i < length && n > 0; ++i) { 7 | 8 | if (flowerbed[i] == 1) 9 | continue; 10 | 11 | bool emptyLeftPlot = i-1 < 0 || flowerbed[i-1] == 0; 12 | bool emptyRightPlot = i+1 >= length || flowerbed[i+1] == 0; 13 | 14 | if (emptyLeftPlot && emptyRightPlot) { 15 | n = n-1; 16 | flowerbed[i] = 1; 17 | } 18 | } 19 | return n == 0; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /leetcode75/605. Can Place Flowers/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You have a long flowerbed in which some of the plots are planted, and some are not. However, flowers cannot be planted in adjacent plots. 4 | 5 | Given an integer array flowerbed containing 0's and 1's, where 0 means empty and 1 means not empty, and an integer n, return true if n new flowers can be planted in the flowerbed without violating the no-adjacent-flowers rule and false otherwise. 6 | 7 | 8 | 9 | Example 1: 10 | 11 | Input: flowerbed = [1,0,0,0,1], n = 1 12 | Output: true 13 | Example 2: 14 | 15 | Input: flowerbed = [1,0,0,0,1], n = 2 16 | Output: false 17 | 18 | 19 | Constraints: 20 | 21 | 1 <= flowerbed.length <= 2 * 104 22 | flowerbed[i] is 0 or 1. 23 | There are no two adjacent flowers in flowerbed. 24 | 0 <= n <= flowerbed.length 25 | 26 | # Thought Process 27 | 28 | Iterate the input array and set to 1 each 0 value whose adjacent values are 0. 29 | Return true when the number of toggled 0s is n or return false at the end. 30 | -------------------------------------------------------------------------------- /leetcode75/605. Can Place Flowers/solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: 3 | 4 | if n == 0: 5 | return True 6 | 7 | length = len(flowerbed) 8 | 9 | for i in range(length): 10 | 11 | if flowerbed[i] == 1: 12 | continue 13 | 14 | is_left_empty = (i == 0) or (flowerbed[i - 1] == 0) 15 | is_right_empty = (i == length - 1) or (flowerbed[i + 1] == 0) 16 | 17 | if is_left_empty and is_right_empty: 18 | n -= 1 19 | flowerbed[i] = 1 20 | if n == 0: 21 | return True 22 | 23 | return False 24 | -------------------------------------------------------------------------------- /leetcode75/62. Unique Paths/Solution.cs: -------------------------------------------------------------------------------- 1 | // Not space optimized 2 | public class Solution { 3 | public int UniquePaths(int m, int n) { 4 | 5 | int[][] grid = new int[m][]; 6 | 7 | for (int i = 0; i < m; ++i) { 8 | grid[i] = new int[n]; 9 | grid[i][0] = 1; //init first col 10 | } 11 | 12 | for (int j = 0; j < n; j++) { 13 | grid[0][j] = 1; //init first row 14 | } 15 | 16 | for (int i = 1; i < m; ++i) { 17 | for (int j = 1; j < n; ++j) { 18 | grid[i][j] = grid[i-1][j] + grid[i][j-1]; 19 | } 20 | } 21 | 22 | return grid[m-1][n-1]; 23 | } 24 | } 25 | 26 | // Space optimized 27 | 28 | public class Solution { 29 | public int UniquePaths(int m, int n) { 30 | 31 | int[] dp = new int[n]; 32 | 33 | for (int j = 0; j < n; j++) { 34 | dp[j] = 1; 35 | } 36 | 37 | for (int i = 1; i < m; ++i) { 38 | for (int j = 1; j < n; ++j) { 39 | dp[j] += dp[j-1]; 40 | } 41 | } 42 | 43 | return dp[n-1]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /leetcode75/62. Unique Paths/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | There is a robot on an m x n grid. The robot is initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time. 4 | 5 | Given the two integers m and n, return the number of possible unique paths that the robot can take to reach the bottom-right corner. 6 | 7 | The test cases are generated so that the answer will be less than or equal to 2 * 109. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | 14 | Input: m = 3, n = 7 15 | Output: 28 16 | Example 2: 17 | 18 | Input: m = 3, n = 2 19 | Output: 3 20 | Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner: 21 | 1. Right -> Down -> Down 22 | 2. Down -> Down -> Right 23 | 3. Down -> Right -> Down 24 | 25 | 26 | Constraints: 27 | 28 | 1 <= m, n <= 100 29 | 30 | # Thought process 31 | 32 | Let's say P[i][j] are the number of unique paths to reach cell[i][j]. 33 | Then from the problem definition: P[i][j] = P[i-1][j] + P[i][j-1] = paths from up + path from right 34 | 35 | The dp approach implementing according to the formula takes O(m*n) space. 36 | 37 | Observing that at each step we only use the previous row the space can be optimized to O(n). 38 | 39 | When we move to a new row (in the outer loop), the values in dp already represent the number of paths from the top for each column. 40 | 41 | # Complexity 42 | 43 | Time complexity is O(m*n) 44 | Space complexity is O(m*n) if not optimized or O(n) if optimized 45 | -------------------------------------------------------------------------------- /leetcode75/714. Best Time to Buy and Sell Stock with Transaction Fee/Solution.cs: -------------------------------------------------------------------------------- 1 | // Top down 2 | public class Solution { 3 | 4 | private Dictionary<(int,bool), int> cache; 5 | 6 | public int MaxProfit(int[] prices, int fee) { 7 | cache = new Dictionary<(int,bool), int>(); 8 | return MaxProfit(prices, fee, 0, true); 9 | } 10 | 11 | private int MaxProfit(int[] prices, int fee, int day, bool canBuy) { 12 | 13 | if (day == prices.Length) 14 | return 0; 15 | 16 | if (cache.ContainsKey((day,canBuy))) 17 | return cache[(day,canBuy)]; 18 | 19 | int result; 20 | if (canBuy) { 21 | int skipDay = MaxProfit(prices, fee, day+1, true); 22 | int buy = MaxProfit(prices, fee, day+1, false) - prices[day]; 23 | result = Math.Max(skipDay, buy); 24 | } 25 | else { 26 | int skipDay = MaxProfit(prices, fee, day+1, false); 27 | int sell = MaxProfit(prices, fee, day+1, true) + prices[day] - fee; 28 | result = Math.Max(skipDay, sell); 29 | } 30 | cache[(day,canBuy)] = result; 31 | return result; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /leetcode75/714. Best Time to Buy and Sell Stock with Transaction Fee/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an array prices where prices[i] is the price of a given stock on the ith day, and an integer fee representing a transaction fee. 4 | 5 | Find the maximum profit you can achieve. You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. 6 | 7 | Note: 8 | 9 | You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again). 10 | The transaction fee is only charged once for each stock purchase and sale. 11 | 12 | 13 | Example 1: 14 | 15 | Input: prices = [1,3,2,8,4,9], fee = 2 16 | Output: 8 17 | Explanation: The maximum profit can be achieved by: 18 | - Buying at prices[0] = 1 19 | - Selling at prices[3] = 8 20 | - Buying at prices[4] = 4 21 | - Selling at prices[5] = 9 22 | The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8. 23 | Example 2: 24 | 25 | Input: prices = [1,3,7,5,10,3], fee = 3 26 | Output: 6 27 | 28 | 29 | Constraints: 30 | 31 | 1 <= prices.length <= 5 * 104 32 | 1 <= prices[i] < 5 * 104 33 | 0 <= fee < 5 * 104 34 | 35 | # Thought Process 36 | 37 | This is a dynamic programming problem: we need to try all the possible combinations of buying and selling stocks and subproblems overlap. 38 | 39 | At any given day, we can be in one of two states: 40 | a) Holding a stock 41 | b) Not holding a stock 42 | 43 | If we're holding a stock, we can: 44 | a) Continue holding it 45 | b) Sell it (and pay the transaction fee) 46 | If we're not holding a stock, we can: 47 | a) Continue not holding 48 | b) Buy a stock 49 | 50 | The top-down recursive solution uses the following structure: 51 | 52 | - index (current day) 53 | - canBuy (boolean indicating if we can buy a stock) 54 | 55 | The bottom-up solution uses the following recurrence: 56 | 57 | hold[i] = max(hold[i-1], notHold[i-1] - prices[i]) 58 | notHold[i] = max(notHold[i-1], hold[i-1] + prices[i] - fee) 59 | 60 | where 61 | 62 | hold[i]: Maximum profit on day i if we're holding a stock 63 | notHold[i]: Maximum profit on day i if we're not holding a stock 64 | 65 | # Complexity 66 | 67 | O(N) time 68 | -------------------------------------------------------------------------------- /leetcode75/72. Edit Distance/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | private string _word1; 4 | private string _word2; 5 | private Dictionary<(int, int), int> _cache; 6 | 7 | public int MinDistance(string word1, string word2) { 8 | _word1 = word1; 9 | _word2 = word2; 10 | _cache = new Dictionary<(int, int), int>(); 11 | return CalculateMinDistance(0, 0); 12 | } 13 | 14 | private int CalculateMinDistance(int i, int j) { 15 | 16 | if (i == _word1.Length) { 17 | return _word2.Length-j; 18 | } 19 | 20 | if (j == _word2.Length) { 21 | return _word1.Length-i; 22 | } 23 | 24 | if (_cache.TryGetValue((i,j), out int cachedResult)) { 25 | return cachedResult; 26 | } 27 | 28 | int result; 29 | if (_word1[i] == _word2[j]) { 30 | result = CalculateMinDistance(i+1, j+1); 31 | } 32 | else { 33 | int insert = CalculateMinDistance(i, j+1); 34 | int delete = CalculateMinDistance(i+1, j); 35 | int replace = CalculateMinDistance(i+1, j+1); 36 | result = 1 + Math.Min(replace, Math.Min(insert, delete)); 37 | } 38 | _cache[(i,j)] = result; 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /leetcode75/72. Edit Distance/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2. 4 | 5 | You have the following three operations permitted on a word: 6 | 7 | Insert a character 8 | Delete a character 9 | Replace a character 10 | 11 | 12 | Example 1: 13 | 14 | Input: word1 = "horse", word2 = "ros" 15 | Output: 3 16 | Explanation: 17 | horse -> rorse (replace 'h' with 'r') 18 | rorse -> rose (remove 'r') 19 | rose -> ros (remove 'e') 20 | Example 2: 21 | 22 | Input: word1 = "intention", word2 = "execution" 23 | Output: 5 24 | Explanation: 25 | intention -> inention (remove 't') 26 | inention -> enention (replace 'i' with 'e') 27 | enention -> exention (replace 'n' with 'x') 28 | exention -> exection (replace 'n' with 'c') 29 | exection -> execution (insert 'u') 30 | 31 | 32 | Constraints: 33 | 34 | 0 <= word1.length, word2.length <= 500 35 | word1 and word2 consist of lowercase English letters. 36 | -------------------------------------------------------------------------------- /leetcode75/739. Daily Temperatures/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int[] DailyTemperatures(int[] temperatures) { 3 | 4 | int n = temperatures.Length; 5 | var s = new Stack(); 6 | var result = new int[n]; 7 | 8 | for(int i = n-1; i >= 0; i--) { 9 | 10 | while(s.Count > 0 && temperatures[s.Peek()] <= temperatures[i]) { 11 | s.Pop(); 12 | } 13 | 14 | if (s.Count > 0) { 15 | result[i] = s.Peek() - i; 16 | } 17 | s.Push(i); 18 | } 19 | return result; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /leetcode75/739. Daily Temperatures/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given an array of integers temperatures represents the daily temperatures, return an array answer such that answer[i] is the number of days you have to wait after the ith day to get a warmer temperature. If there is no future day for which this is possible, keep answer[i] == 0 instead. 4 | 5 | Example 1: 6 | 7 | Input: temperatures = [73,74,75,71,69,72,76,73] 8 | Output: [1,1,4,2,1,1,0,0] 9 | Example 2: 10 | 11 | Input: temperatures = [30,40,50,60] 12 | Output: [1,1,1,0] 13 | Example 3: 14 | 15 | Input: temperatures = [30,60,90] 16 | Output: [1,1,0] 17 | 18 | 19 | Constraints: 20 | 21 | 1 <= temperatures.length <= 105 22 | 30 <= temperatures[i] <= 100 23 | 24 | # Thought Process 25 | 26 | The brute force is: for each day, look at all future days until a warmer day is found. This would be O(n^2) time complexity, which is inefficient for large inputs. 27 | 28 | Notice that we're looking for the "next greater element" for each temperature. The only way to know this in advance is to iterate backwards. 29 | 30 | If we iterate backward the problem becomes can be the current temperature a warmer day for some previous day? 31 | 32 | The current temperature can always be a possible warmer day for the previous one. But all the temperatures we have already seen that are <= than the current one cannot anymore and can be discarded. 33 | 34 | A stack containing monotonically increasing temperatures is the perfect data structure for this task. 35 | 36 | 37 | # Complexity 38 | 39 | O(N) time to iterate the temperatures and O(N) space for the stack 40 | -------------------------------------------------------------------------------- /leetcode75/746. Min Cost Climbing Stairs/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MinCostClimbingStairs(int[] cost) { 3 | int previousCost = 0, currentCost = 0; 4 | 5 | for (int i = 2; i <= cost.Length; i++) { 6 | int nextCost = Math.Min(cost[i - 1] + currentCost, cost[i - 2] + previousCost); 7 | previousCost = currentCost; 8 | currentCost = nextCost; 9 | } 10 | 11 | return currentCost; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /leetcode75/746. Min Cost Climbing Stairs/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps. 4 | 5 | You can either start from the step with index 0, or the step with index 1. 6 | 7 | Return the minimum cost to reach the top of the floor. 8 | 9 | 10 | 11 | Example 1: 12 | 13 | Input: cost = [10,15,20] 14 | Output: 15 15 | Explanation: You will start at index 1. 16 | - Pay 15 and climb two steps to reach the top. 17 | The total cost is 15. 18 | Example 2: 19 | 20 | Input: cost = [1,100,1,1,1,100,1,1,100,1] 21 | Output: 6 22 | Explanation: You will start at index 0. 23 | - Pay 1 and climb two steps to reach index 2. 24 | - Pay 1 and climb two steps to reach index 4. 25 | - Pay 1 and climb two steps to reach index 6. 26 | - Pay 1 and climb one step to reach index 7. 27 | - Pay 1 and climb two steps to reach index 9. 28 | - Pay 1 and climb one step to reach the top. 29 | The total cost is 6. 30 | 31 | 32 | Constraints: 33 | 34 | 2 <= cost.length <= 1000 35 | 0 <= cost[i] <= 999 36 | 37 | # Thought Process 38 | 39 | Classic dynamic programming problem where the min_cost[i] = Min(min_cost[i-1] + cost[i-1], min_cost[i-2] + cost[i-2]) 40 | 41 | # Complexity 42 | 43 | Time Complexity: O(n), where n is the length of the cost array. 44 | Space Complexity: O(1), as it only use a constant amount of extra space. 45 | -------------------------------------------------------------------------------- /leetcode75/790. Domino and Tromino Tiling/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int NumTilings(int n) { 3 | const int MOD = 1_000_000_007; 4 | 5 | // handle base case scenarios 6 | if (n <= 2) { 7 | return n; 8 | } 9 | 10 | // f[k]: number of ways to "fully cover a board" of width k 11 | long[] f = new long[n + 1]; 12 | // p[k]: number of ways to "partially cover a board" of width k 13 | long[] p = new long[n + 1]; 14 | 15 | // initialize f and p with results for the base case scenarios 16 | f[1] = 1L; 17 | f[2] = 2L; 18 | p[2] = 1L; 19 | 20 | for (int k = 3; k < n + 1; ++k) { 21 | f[k] = (f[k - 1] + f[k - 2] + 2 * p[k - 1]) % MOD; 22 | p[k] = (p[k - 1] + f[k - 2]) % MOD; 23 | } 24 | 25 | return (int)(f[n]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /leetcode75/790. Domino and Tromino Tiling/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You have two types of tiles: a 2 x 1 domino shape and a tromino shape. You may rotate these shapes. 4 | 5 | 6 | Given an integer n, return the number of ways to tile an 2 x n board. Since the answer may be very large, return it modulo 109 + 7. 7 | 8 | In a tiling, every square must be covered by a tile. Two tilings are different if and only if there are two 4-directionally adjacent cells on the board such that exactly one of the tilings has both squares occupied by a tile. 9 | 10 | 11 | 12 | Example 1: 13 | 14 | 15 | Input: n = 3 16 | Output: 5 17 | Explanation: The five different ways are show above. 18 | Example 2: 19 | 20 | Input: n = 1 21 | Output: 1 22 | 23 | 24 | Constraints: 25 | 26 | 1 <= n <= 1000 27 | 28 | # Thought process 29 | 30 | # Complexity 31 | -------------------------------------------------------------------------------- /leetcode75/875. Koko Eating Bananas/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public int MinEatingSpeed(int[] piles, int h) { 3 | 4 | int left = 1, right = piles.Max(); 5 | 6 | while (left < right) { 7 | int mid = left + (right - left) / 2; 8 | if (CanEatAll(piles, h, mid)) { 9 | right = mid; 10 | } 11 | else { 12 | left = mid+1; 13 | } 14 | } 15 | return left; 16 | } 17 | 18 | private bool CanEatAll(int[] piles, int h, int k) { 19 | int timeNeeded = 0; 20 | foreach (int pile in piles) { 21 | timeNeeded += (pile - 1) / k + 1; // Equivalent to (int) Math.Ceiling(pile / (double)k) 22 | if (timeNeeded > h) return false; 23 | } 24 | return true; 25 | } 26 | } 27 | 28 | //n,n,n,n,n,n,y,y,y,y,y,y,y 29 | -------------------------------------------------------------------------------- /leetcode75/875. Koko Eating Bananas/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Koko loves to eat bananas. There are n piles of bananas, the ith pile has piles[i] bananas. The guards have gone and will come back in h hours. 4 | 5 | Koko can decide her bananas-per-hour eating speed of k. Each hour, she chooses some pile of bananas and eats k bananas from that pile. If the pile has less than k bananas, she eats all of them instead and will not eat any more bananas during this hour. 6 | 7 | Koko likes to eat slowly but still wants to finish eating all the bananas before the guards return. 8 | 9 | Return the minimum integer k such that she can eat all the bananas within h hours. 10 | 11 | 12 | 13 | Example 1: 14 | 15 | Input: piles = [3,6,7,11], h = 8 16 | Output: 4 17 | Example 2: 18 | 19 | Input: piles = [30,11,23,4,20], h = 5 20 | Output: 30 21 | Example 3: 22 | 23 | Input: piles = [30,11,23,4,20], h = 6 24 | Output: 23 25 | 26 | 27 | Constraints: 28 | 29 | 1 <= piles.length <= 104 30 | piles.length <= h <= 109 31 | 1 <= piles[i] <= 109 32 | 33 | # Thought Process 34 | 35 | Brute force is to check all possible eating rates from 1 to Max number and find the first one allowing to eat all piles within the time. 36 | 37 | But this is unnecessary because we can use binary search. 38 | 39 | If we try to answer the question "can we eat all the piles with a given rate k?" we will get the following answera: 40 | 41 | No, No, No, ...., No, Yes, Yes, ..., Yes for all possible k from 1 to max 42 | 43 | We can use binary search to find the first yes. 44 | 45 | # Complexity 46 | 47 | - O(Nlog(M)) time, where M is the max number of bananas per pile 48 | - O(1) space 49 | -------------------------------------------------------------------------------- /leetcode75/901. Online Stock Span/Solution.cs: -------------------------------------------------------------------------------- 1 | public class StockSpanner { 2 | private Stack<(int Price, int Day)> _stack; 3 | private int _day; 4 | 5 | public StockSpanner() { 6 | _stack = new Stack<(int, int)>(); 7 | _day = 0; 8 | } 9 | 10 | public int Next(int price) { 11 | _day++; 12 | 13 | while (_stack.Count > 0 && _stack.Peek().Price <= price) { 14 | _stack.Pop(); 15 | } 16 | 17 | int span = _stack.Count > 0 ? _day - _stack.Peek().Day : _day; 18 | _stack.Push((Price: price, Day: _day)); 19 | 20 | return span; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /leetcode75/994. Rotting Oranges/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | 3 | static readonly (int dx, int dy)[] directions={(1,0), (0,1),(-1,0),(0,-1)}; 4 | 5 | public int OrangesRotting(int[][] grid) { 6 | int rows = grid.Length; 7 | int cols = grid[0].Length; 8 | int fresh = 0, rotten = 0; 9 | var rottenQueue = new Queue<(int row, int col, int time)>(); 10 | 11 | for (int i = 0; i < rows; ++i) { 12 | for (int j = 0; j < cols; ++j) { 13 | if (grid[i][j] == 1) 14 | fresh++; 15 | else if (grid[i][j] == 2) 16 | rottenQueue.Enqueue((i,j, 0)); 17 | } 18 | } 19 | 20 | if (fresh == 0) 21 | return 0; 22 | 23 | while (rottenQueue.Count > 0) { 24 | 25 | var (row, col, time) = rottenQueue.Dequeue(); 26 | 27 | foreach (var (dx,dy) in directions) { 28 | int nextRow = row+dx, nextCol = col+dy; 29 | if (IsInsideGrid(nextRow, nextCol, rows, cols) && 30 | grid[nextRow][nextCol] == 1) { 31 | grid[nextRow][nextCol] = 2; 32 | rottenQueue.Enqueue((nextRow,nextCol,time+1)); 33 | fresh--; 34 | 35 | if (fresh == 0) 36 | return time+1; 37 | } 38 | } 39 | } 40 | return -1; 41 | } 42 | 43 | private bool IsInsideGrid(int x, int y, int sizeX, int sizeY){ 44 | return x >= 0 && x < sizeX && y >= 0 && y < sizeY; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /leetcode75/994. Rotting Oranges/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | You are given an m x n grid where each cell can have one of three values: 4 | 5 | 0 representing an empty cell, 6 | 1 representing a fresh orange, or 7 | 2 representing a rotten orange. 8 | Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten. 9 | 10 | Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1. 11 | 12 | 13 | 14 | Example 1: 15 | 16 | 17 | Input: grid = [[2,1,1],[1,1,0],[0,1,1]] 18 | Output: 4 19 | Example 2: 20 | 21 | Input: grid = [[2,1,1],[0,1,1],[1,0,1]] 22 | Output: -1 23 | Explanation: The orange in the bottom left corner (row 2, column 0) is never rotten, because rotting only happens 4-directionally. 24 | Example 3: 25 | 26 | Input: grid = [[0,2]] 27 | Output: 0 28 | Explanation: Since there are already no fresh oranges at minute 0, the answer is just 0. 29 | 30 | 31 | Constraints: 32 | 33 | m == grid.length 34 | n == grid[i].length 35 | 1 <= m, n <= 10 36 | grid[i][j] is 0, 1, or 2. 37 | 38 | # Thought Process 39 | 40 | The grid is an unweighted graph and minimum time meansminimum steps -> BFS. 41 | 42 | # Complexity 43 | 44 | O(Rows*Cols) time and space 45 | -------------------------------------------------------------------------------- /stack/20. Valid Parentheses/Solution.cs: -------------------------------------------------------------------------------- 1 | public class Solution { 2 | public bool IsValid(string s) { 3 | 4 | if (s.Length % 2 != 0) 5 | return false; 6 | 7 | var bracketsMap = new Dictionary { 8 | {')', '('}, 9 | {']', '['}, 10 | {'}', '{'} 11 | }; 12 | 13 | var stack = new Stack(); 14 | 15 | foreach (char c in s) { 16 | if (bracketsMap.ContainsKey(c)) { 17 | // Closing bracket 18 | if (stack.Count == 0 || stack.Pop() != bracketsMap[c]) 19 | return false; 20 | } else { 21 | // Opening bracket 22 | stack.Push(c); 23 | } 24 | } 25 | 26 | return stack.Count == 0; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /stack/20. Valid Parentheses/notes.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. 4 | 5 | An input string is valid if: 6 | 7 | Open brackets must be closed by the same type of brackets. 8 | Open brackets must be closed in the correct order. 9 | Every close bracket has a corresponding open bracket of the same type. 10 | 11 | 12 | Example 1: 13 | 14 | Input: s = "()" 15 | 16 | Output: true 17 | 18 | Example 2: 19 | 20 | Input: s = "()[]{}" 21 | 22 | Output: true 23 | 24 | Example 3: 25 | 26 | Input: s = "(]" 27 | 28 | Output: false 29 | 30 | Example 4: 31 | 32 | Input: s = "([])" 33 | 34 | Output: true 35 | 36 | 37 | 38 | Constraints: 39 | 40 | 1 <= s.length <= 104 41 | s consists of parentheses only '()[]{}'. 42 | 43 | # Thought Process 44 | 45 | You need to iterate over the input and check that for every closed bracket there is a correspondin open bracket. 46 | 47 | A stack is the ideal data structure for that. 48 | Insert open bracket into the stack and when a closed bracket comes, check if the open bracket at the top of the stack is of the same type. 49 | If at the end the stack is empty the expression is valid. 50 | 51 | # Complexity 52 | 53 | O(N) time and space 54 | -------------------------------------------------------------------------------- /templates/arrays-and-strings/template.cs: -------------------------------------------------------------------------------- 1 | // Two pointers: one input, opposite ends 2 | 3 | public int fn(int[] arr) { 4 | int left = 0; 5 | int right = arr.length - 1; 6 | int ans = 0; 7 | 8 | while (left < right) { 9 | // do some logic here with left and right 10 | if (CONDITION) { 11 | left++; 12 | } else { 13 | right--; 14 | } 15 | } 16 | 17 | return ans; 18 | } 19 | 20 | // Two pointers: two inputs, exhaust both 21 | 22 | public int fn(int[] arr1, int[] arr2) { 23 | int i = 0, j = 0, ans = 0; 24 | 25 | while (i < arr1.length && j < arr2.length) { 26 | // do some logic here 27 | if (CONDITION) { 28 | i++; 29 | } else { 30 | j++; 31 | } 32 | } 33 | 34 | while (i < arr1.length) { 35 | // do logic 36 | i++; 37 | } 38 | 39 | while (j < arr2.length) { 40 | // do logic 41 | j++; 42 | } 43 | 44 | return ans; 45 | } 46 | 47 | // Sliding window 48 | 49 | public int fn(int[] arr) { 50 | int left = 0, ans = 0, curr = 0; 51 | 52 | for (int right = 0; right < arr.length; right++) { 53 | // do logic here to add arr[right] to curr 54 | 55 | while (WINDOW_CONDITION_BROKEN) { 56 | // remove arr[left] from curr 57 | left++; 58 | } 59 | 60 | // update ans 61 | } 62 | 63 | return ans; 64 | } 65 | 66 | // Build a prefix sum 67 | 68 | public int[] fn(int[] arr) { 69 | int[] prefix = new int[arr.length]; 70 | prefix[0] = arr[0]; 71 | 72 | for (int i = 1; i < arr.length; i++) { 73 | prefix[i] = prefix[i - 1] + arr[i]; 74 | } 75 | 76 | return prefix; 77 | } 78 | 79 | // Efficient string building 80 | 81 | public String fn(char[] arr) { 82 | StringBuilder sb = new StringBuilder(); 83 | for (char c: arr) { 84 | sb.append(c); 85 | } 86 | 87 | return sb.toString(); 88 | } 89 | -------------------------------------------------------------------------------- /templates/arrays-and-strings/template.py: -------------------------------------------------------------------------------- 1 | # Two pointers: one input, opposite ends 2 | 3 | def fn(arr): 4 | left = ans = 0 5 | right = len(arr) - 1 6 | 7 | while left < right: 8 | # do some logic here with left and right 9 | if CONDITION: 10 | left += 1 11 | else: 12 | right -= 1 13 | 14 | return ans 15 | 16 | 17 | # Two pointers: two inputs, exhaust both 18 | 19 | def fn(arr1, arr2): 20 | i = j = ans = 0 21 | 22 | while i < len(arr1) and j < len(arr2): 23 | # do some logic here 24 | if CONDITION: 25 | i += 1 26 | else: 27 | j += 1 28 | 29 | while i < len(arr1): 30 | # do logic 31 | i += 1 32 | 33 | while j < len(arr2): 34 | # do logic 35 | j += 1 36 | 37 | return ans 38 | 39 | 40 | # Sliding window 41 | 42 | def fn(arr): 43 | left = ans = curr = 0 44 | 45 | for right in range(len(arr)): 46 | # do logic here to add arr[right] to curr 47 | 48 | while WINDOW_CONDITION_BROKEN: 49 | # remove arr[left] from curr 50 | left += 1 51 | 52 | # update ans 53 | 54 | return ans 55 | 56 | # Build a prefix sum 57 | 58 | def fn(arr): 59 | prefix = [arr[0]] 60 | for i in range(1, len(arr)): 61 | prefix.append(prefix[-1] + arr[i]) 62 | 63 | return prefix 64 | 65 | # Efficient string building 66 | 67 | # arr is a list of characters 68 | def fn(arr): 69 | ans = [] 70 | for c in arr: 71 | ans.append(c) 72 | 73 | return "".join(ans) 74 | --------------------------------------------------------------------------------