0% found this document useful (0 votes)
35 views

Merged

The document contains a collection of coding problems and their solutions, primarily focused on data structures and algorithms. Each problem includes a description, example inputs and outputs, constraints, and a Java implementation. Topics covered include duplicate detection, anagrams, two-sum problems, grouping anagrams, frequent elements, product arrays, Sudoku validation, string encoding/decoding, and finding longest consecutive sequences.

Uploaded by

raghu d.r
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views

Merged

The document contains a collection of coding problems and their solutions, primarily focused on data structures and algorithms. Each problem includes a description, example inputs and outputs, constraints, and a Java implementation. Topics covered include duplicate detection, anagrams, two-sum problems, grouping anagrams, frequent elements, product arrays, Sudoku validation, string encoding/decoding, and finding longest consecutive sequences.

Uploaded by

raghu d.r
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 440

217.

Contains Duplicate
Easy

Given an integer array nums, return true if any value appears at least twice in the array, and return false if every element is distinct.

Example 1:

Input: nums = [1,2,3,1]


Output: true

Example 2:

Input: nums = [1,2,3,4]


Output: false

Example 3:

Input: nums = [1,1,1,3,3,4,3,2,4,2]


Output: true

Constraints:

1 <= nums.length <= 105


-109 <= nums[i] <= 109

class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int num : nums) {
if(!set.add(num))
return true;
}
return false;
}
}

242. Valid Anagram


Easy

Given two strings s and t, return true if t is an anagram of s, and false otherwise.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

Example 1:

Input: s = "anagram", t = "nagaram"


Output: true

Example 2:

Input: s = "rat", t = "car"


Output: false

Constraints:

1 <= s.length, t.length <= 5 * 104


s and t consist of lowercase English letters.
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length())
return false;

int[] chars = new int[26];

for(int i = 0; i < s.length(); i++) {


chars[s.charAt(i)-'a']++;
chars[t.charAt(i)-'a']--;
}

for(int n : chars) {
if(n != 0)
return false;
}

return true;
}
}

Follow up: What if the inputs contain Unicode characters? How would you adapt your
solution to such a case?
We can be used a map, or a array of size equal to the characterset size of unicode.

1. Two Sum
Easy

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

You can return the answer in any order.

Example 1:

Input: nums = [2,7,11,15], target = 9


Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6


Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6


Output: [0,1]

Constraints:

2 <= nums.length <= 104


-109 <= nums[i] <= 109
-109 <= target <= 109
Only one valid answer exists.

Follow-up: Can you come up with an algorithm that is less than O(n2) time complexity?
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
Map<Integer, Integer> map = new HashMap<>();

for(int i = 0; i < n; i++) {


int remaining = target-nums[i];
if(map.containsKey(remaining)) {
return new int[] {map.get(remaining), i};
}
map.put(nums[i], i);
}
return new int[]{-1, -1};
}
}

49. Group Anagrams


Medium

Given an array of strings strs, group the anagrams together. You can return the answer in any order.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

Example 1:

Input: strs = ["eat","tea","tan","ate","nat","bat"]


Output: [["bat"],["nat","tan"],["ate","eat","tea"]]

Example 2:

Input: strs = [""]


Output: [[""]]

Example 3:

Input: strs = ["a"]


Output: [["a"]]

Constraints:

1 <= strs.length <= 104


0 <= strs[i].length <= 100
strs[i] consists of lowercase English letters.
class Solution {
// 1
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> res = new ArrayList<>();
Map<String, List<String>> mp = new HashMap<>();
for (String str : strs) {
char[] c = str.toCharArray();
Arrays.sort(c);
String r = new String(c);
if (!mp.containsKey(r))
mp.put(r, new ArrayList<String>());
mp.get(r).add(str);
}
for (String key : mp.keySet()) {
res.add(mp.get(key));
}

return res;
}

// 2
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> res = new ArrayList<>();
Map<String, List<String>> mp = new HashMap<>();
for (String str : strs) {
char[] hash = new char[26];
for(char ch : str.toCharArray())
hash[ch-'a']++;
String r = new String(hash);
if (!mp.containsKey(r))
mp.put(r, new ArrayList<String>());
mp.get(r).add(str);
}
for (String key : mp.keySet()) {
res.add(mp.get(key));
}

return res;
}

347. Top K Frequent Elements


Medium

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.

Example 1:

Input: nums = [1,1,1,2,2,3], k = 2


Output: [1,2]

Example 2:

Input: nums = [1], k = 1


Output: [1]

Constraints:

1 <= nums.length <= 105


k is in the range [1, the number of unique elements in the array].
It is guaranteed that the answer is unique.
Follow up: Your algorithm's time complexity must be better than O(n log n), where n is the
array's size.

class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> mp = new HashMap<>();
int[] res = new int[k];
for(int num : nums) {
mp.put(num, mp.getOrDefault(num,0)+1);
}
PriorityQueue<Pair> pq = new PriorityQueue<>((a,b)->b.val-a.val);
for(int key : mp.keySet())
pq.offer(new Pair(key, mp.get(key)));
for(int i = 0; i < k; i++)
res[i] = pq.poll().key;

return res;
}
}

class Pair {
int key;
int val;

Pair(int k, int v) {
key = k;
val = v;
}
}

238. Product of Array Except Self


Medium

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].

The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.

You must write an algorithm that runs in O(n) time and without using the division operation.

Example 1:

Input: nums = [1,2,3,4]


Output: [24,12,8,6]

Example 2:

Input: nums = [-1,1,0,-3,3]


Output: [0,0,9,0,0]

Constraints:

2 <= nums.length <= 105


-30 <= nums[i] <= 30
The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.

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.)
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] res = new int[n];
int pre = 1;
res[0] = 1;
for(int i = 0; i < n-1; i++) {
pre *= nums[i];
res[i+1] = pre;
}
int post = 1;
for(int i = n-1; i > 0; i--) {
post *= nums[i];
res[i-1] *= post;
}
return res;
}
}

36. Valid Sudoku


Medium

Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

Each row must contain the digits 1-9 without repetition.


Each column must contain the digits 1-9 without repetition.
Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition.

Note:

A Sudoku board (partially filled) could be valid but is not necessarily solvable.
Only the filled cells need to be validated according to the mentioned rules.

Example 1:

Input: board =
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true

Example 2:

Input: board =
[["8","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: false

Explanation: Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8's in the top left 3x3 sub-box, it is invalid.

Constraints:
board.length == 9
board[i].length == 9
board[i][j] is a digit 1-9 or '.'.

// My Solution
class Solution {
public boolean isValidSudoku(char[][] board) {

for(int i = 0; i < 9; i++) {


for(int j = 0; j < 9; j++) {
char c = board[i][j];
if(isDigit(c) && !isValid(c, i, j, board)) {
return false;
}
}
}

return true;
}

private boolean isDigit(char c) {


if(c-'0' > 9 || c-'0' < 0)
return false;
return true;
}

private boolean isValid(char c, int i, int j, char[][] board) {


if(isValidRow(c, i, j, board) && isValidCol(c, i, j, board) && isValidGrid(c, i, j, board))
return true;
return false;
}

private boolean isValidRow(char c, int row, int col, char[][] board) {


for(int i = 0; i < 9; i++) {
if(board[row][i] == c && i != col)
return false;
}
return true;
}

private boolean isValidCol(char c, int row, int col, char[][] board) {


for(int i = 0; i < 9; i++) {
if(board[i][col] == c && i != row)
return false;
}
return true;
}

private boolean isValidGrid(char c, int row, int col, char[][] board) {


int initialRow = 3*(row/3), initialCol = 3*(col/3);

for(int i = initialRow; i < initialRow+3; i++) {


for(int j = initialCol; j < initialCol+3; j++) {
if(board[i][j] == c && i != row && j != col)
return false;
}
}
return true;
}
}
// Using Set
public boolean isValidSudoku(char[][] board) {
Set seen = new HashSet();
for (int i=0; i<9; ++i) {
for (int j=0; j<9; ++j) {
char number = board[i][j];
if (number != '.')
if (!seen.add(number + " in row " + i) ||
!seen.add(number + " in column " + j) ||
!seen.add(number + " in block " + i/3 + "-" + j/3))
return false;
}
}
return true;
}

659 · Encode and Decode Strings


Description Design an algorithm to encode a list of strings to a string. The encoded string is then sent over the network and is decoded back to the original list of strings.

Please implement encode and decode

Example1

Input: ["lint","code","love","you"]
Output: ["lint","code","love","you"]
Explanation:
One possible encode method is: "lint:;code:;love:;you"

Example2

Input: ["we", "say", ":", "yes"]


Output: ["we", "say", ":", "yes"]
Explanation:
One possible encode method is: "we:;say:;:::;yes"
public class Solution {
/*
* @param strs: a list of strings
* @return: encodes a list of strings to a single string.
*/
public String encode(List<String> strs) {

StringBuilder sb = new StringBuilder();


char seperator = '`';
for(String str : strs) {
sb.append(str.length());
sb.append(seperator);
sb.append(str);
}
return sb.toString();
}

/*
* @param str: A string
* @return: dcodes a single string to a list of strings
*/
public List<String> decode(String str) {

List<String> strs = new ArrayList<>();


int i = 0;
while(i < str.length()) {
int j = i;
while(str.charAt(j) != '`')
j++;
int len = Integer.parseInt(str.substring(i, j));
strs.add(str.substring(j+1, j+1+len));
i = j + 1 + len;
}
return strs;
}
}

128. Longest Consecutive Sequence


Medium

Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence.

You must write an algorithm that runs in O(n) time.

Example 1:

Input: nums = [100,4,200,1,3,2]


Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.

Example 2:

Input: nums = [0,3,7,2,5,8,4,6,0,1]


Output: 9

Constraints:

0 <= nums.length <= 105

-109 <= nums[i] <= 109


class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet<>();
int result = 0;
for(int num : nums)
set.add(num);
int count = 0;
for(int num : nums) {
if(!set.contains(num-1)) {
while(set.contains(num++))
count++;
result = Math.max(result, count);
count = 0;
}
}
return result;
}
}

Resources
Youtube (https://youtu.be/shs0KM3wKv8)
Leetcode Discussion (https://leetcode.com/discuss/general-discussion/1068545/HASH-TABLE-and-MAP-POWERFUL-GUIDE-!!!)

125. Valid Palindrome


Easy

A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward.
Alphanumeric characters include letters and numbers.

Given a string s, return true if it is a palindrome, or false otherwise.

Example 1:

Input: s = "A man, a plan, a canal: Panama"


Output: true
Explanation: "amanaplanacanalpanama" is a palindrome.

Example 2:

Input: s = "race a car"


Output: false
Explanation: "raceacar" is not a palindrome.

Example 3:

Input: s = " "


Output: true
Explanation: s is an empty string "" after removing non-alphanumeric characters.
Since an empty string reads the same forward and backward, it is a palindrome.

Constraints:

1 <= s.length <= 2 * 105


s consists only of printable ASCII characters.
class Solution {
public boolean isPalindrome(String s) {
int i = 0, j = s.length()-1;
while(i < j) {
while(i < j && !Character.isLetterOrDigit(s.charAt(i)))
i++;
while(i < j && !Character.isLetterOrDigit(s.charAt(j)))
j--;
if(Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j)))
return false;
i++;
j--;
}

return true;
}
}

167. Two Sum II - Input Array Is Sorted


Medium

Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number. Let these two
numbers be numbers[index1] and numbers[index2] where 1 <= index1 < index2 <= numbers.length.

Return the indices of the two numbers, index1 and index2, added by one as an integer array [index1, index2] of length 2.

The tests are generated such that there is exactly one solution. You may not use the same element twice.

Your solution must use only constant extra space.

Example 1:

Input: numbers = [2,7,11,15], target = 9


Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore, index1 = 1, index2 = 2. We return [1, 2].

Example 2:

Input: numbers = [2,3,4], target = 6


Output: [1,3]
Explanation: The sum of 2 and 4 is 6. Therefore index1 = 1, index2 = 3. We return [1, 3].

Example 3:

Input: numbers = [-1,0], target = -1


Output: [1,2]
Explanation: The sum of -1 and 0 is -1. Therefore index1 = 1, index2 = 2. We return [1, 2].

Constraints:

2 <= numbers.length <= 3 * 104


-1000 <= numbers[i] <= 1000
numbers is sorted in non-decreasing order.
-1000 <= target <= 1000
The tests are generated such that there is exactly one solution.
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0, right = numbers.length-1;

while(left < right) {


int sum = numbers[left] + numbers[right];
if(sum == target) {
return new int[]{left+1, right+1};
} else if(sum < target)
left++;
else
right--;
}

return new int[]{-1, -1};


}
}

15. 3Sum
Medium

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

Notice that the solution set must not contain duplicate triplets.

Example 1:

Input: nums = [-1,0,1,2,-1,-4]


Output: [[-1,-1,2],[-1,0,1]]

Example 2:

Input: nums = []
Output: []

Example 3:

Input: nums = [0]


Output: []

Constraints:

0 <= nums.length <= 3000

-105 <= nums[i] <= 105


class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length-2; i++) {
if(i > 0 && nums[i] == nums[i-1])
continue;
search(i, nums, result);
}
return result;
}

private void search(int index, int[] nums, List<List<Integer>> result) {


int left = index+1, right = nums.length-1;
while(left < right) {
int sum = nums[index] + nums[left] + nums[right];
if(sum == 0) {
result.add(Arrays.asList(nums[index], nums[left], nums[right]));
left++;
right--;
while(left < right && nums[left]==nums[left-1])
left++;
while(left < right && nums[right]==nums[right+1])
right--;
} else if(sum < 0) {
left++;
} else {
right--;
}
}
}
}

11. Container With Most Water


Medium

You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]).

Find two lines that together with the x-axis form a container, such that the container contains the most water.

Return the maximum amount of water a container can store.

Notice that you may not slant the container.

Example 1:

Input: height = [1,8,6,2,5,4,8,3,7]


Output: 49
Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section

Example 2:

Input: height = [1,1]


Output: 1

Constraints:

n == height.length
2 <= n <= 105
0 <= height[i] <= 104
class Solution {
public int maxArea(int[] height) {
int area = 0, maxArea = 0;
int left = 0, right = height.length-1;

while(left < right) {


area = Math.min(height[left], height[right])*(right-left);
maxArea = Math.max(maxArea, area);
if(height[left] < height[right]) {
left++;
} else {
right--;
}
}

return maxArea;
}
}

42. Trapping Rain Water


(https://leetcode.com/problems/trapping-rain-
water/)
Hard

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.

Example 1:

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]


Output: 6
Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1].
In this case, 6 units of rain water (blue section) are being trapped.

Example 2:

Input: height = [4,2,0,3,2,5]


Output: 9

Constraints:

n == height.length

1 <= n <= 2 * 104

0 <= height[i] <= 105

Approach
First Approach:

- leftMax array to keep the max element on the left of the index
- rightMax array to keep the max element on the right of the index
- at each index take the min(leftMax, rightMax) - heigh[index], if it's greater than 0 add it to ans
- Time : O(N)
- Space: O(N)

Second Approach:
- instead of keep arrays to store the leftmax & rightMax, we can use two variables
- if leftMax <= rightMax
- try to update leftMax
- if min(leftMax, rightMax) - heigh[index] > 0, add it to ans
- move the left pointer ahead
- else
- same as above just replacing the left's by right
- decrement the right pointer

Solution
class Solution {
public int trap(int[] height) {
int left = 0, right = height.length-1;
int lMax = height[0], rMax = height[right];
int ans = 0;
while(left <= right) {
if(lMax <= rMax) {
lMax = Math.max(lMax, height[left]);
int val = Math.min(lMax, rMax)-height[left];
ans += val < 0 ? 0 : val;
left++;
} else {
rMax = Math.max(rMax, height[right]);
int val = Math.min(lMax, rMax)-height[right];
ans += val < 0 ? 0 : val;
right--;
}
}
return ans;
}
}

Complexity Analysis
- Time Complexity: O(N) -> go through each elements of the height array
- Space Complexity: O(1)

Resources
Youtube (https://youtu.be/-gjxg6Pln50)
Leetcode Discussion (https://leetcode.com/discuss/study-guide/1688903/Solved-all-two-pointers-problems-in-100-days.)

121. Best Time to Buy and Sell Stock


Easy

You are given an array prices where prices[i] is the price of a given stock on the ith day.

You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.

Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.

Example 1:
Input: prices = [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell.

Example 2:

Input: prices = [7,6,4,3,1]


Output: 0
Explanation: In this case, no transactions are done and the max profit = 0.

Constraints:

1 <= prices.length <= 105


0 <= prices[i] <= 104

class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if(n == 1) return 0;
int maxP = 0;
int l = 0, r = 1;

while(r < n) {
if(prices[l] < prices[r]) {
int profit = prices[r] - prices[l];
maxP = Math.max(maxP, profit);
} else
l = r;
r++;
}
return maxP;
}
}

3. Longest Substring Without Repeating


Characters
Medium

Given a string s, find the length of the longest substring without repeating characters.

Example 1:

Input: s = "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.

Example 2:

Input: s = "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.

Example 3:

Input: s = "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Notice that the answer must be a substring, "pwke" is a subsequence and not a substring.
Constraints:

0 <= s.length <= 5 * 104


s consists of English letters, digits, symbols and spaces.

class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> mp = new HashMap<>();
int maxLen = 0, winStart = 0;

for(int winEnd = 0; winEnd < s.length(); winEnd++) {


char ch = s.charAt(winEnd);
if(mp.containsKey(ch)) {
winStart = Math.max(winStart, mp.get(ch)+1);
}
mp.put(ch, winEnd);
maxLen = Math.max(winEnd - winStart + 1, maxLen);
}
return maxLen;
}
}

424. Longest Repeating Character


Replacement
Medium

You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most
k times.

Return the length of the longest substring containing the same letter you can get after performing the above operations.

Example 1:

Input: s = "ABAB", k = 2
Output: 4
Explanation: Replace the two 'A's with two 'B's or vice versa.

Example 2:

Input: s = "AABABBA", k = 1
Output: 4
Explanation: Replace the one 'A' in the middle with 'B' and form "AABBBBA".
The substring "BBBB" has the longest repeating letters, which is 4.

Constraints:

1 <= s.length <= 105


s consists of only uppercase English letters.
0 <= k <= s.length

Notes
- use a map to store the frequency
- maxRepeatingCharCount = max(maxRepeatingCharCount, mp.get(char))
- slide the left pointer until the equation satifies: windowEnd - windowStart + 1 - maxRepeatingCharCount > k
- maxLen = max(maxLen, windowEnd - windowStart + 1)

Solution:
class Solution {
public int characterReplacement(String s, int k) {
int windowStart = 0, maxLength = 0, mostRepeatingCharCount = 0;

HashMap<Character, Integer> charFreqMap = new HashMap();

for(int windowEnd = 0; windowEnd < s.length(); windowEnd++) {


char right = s.charAt(windowEnd);
charFreqMap.put(right, charFreqMap.getOrDefault(right, 0)+1);
mostRepeatingCharCount = Math.max(mostRepeatingCharCount, charFreqMap.get(right));

while(windowEnd-windowStart+1 - mostRepeatingCharCount > k) {


char left = s.charAt(windowStart);
charFreqMap.put(left, charFreqMap.get(left)-1);
if(charFreqMap.get(left) == 0)
charFreqMap.remove(left);
windowStart++;
}
maxLength = Math.max(maxLength, windowEnd-windowStart+1);
}

return maxLength;
}
}

567. Permutation in String


Medium

Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise.

In other words, return true if one of s1's permutations is the substring of s2.

Example 1:

Input: s1 = "ab", s2 = "eidbaooo"


Output: true
Explanation: s2 contains one permutation of s1 ("ba").

Example 2:

Input: s1 = "ab", s2 = "eidboaoo"


Output: false

Constraints:

1 <= s1.length, s2.length <= 104


s1 and s2 consist of lowercase English letters.

Approach
- 2 array of size 26 to keep count of chars present in both the strings[s1 and substring of s2]
- match keeps the number of characters matches in both s1 and substring of s2
- if matches = 26, we have a permutation in that window
- on each window, we check for conditions [ for both the chars(at left and at right) ]:
- increment s2Map[rightCharInd]++ and decrement s2Map[leftCharInd]--
- if the char's count are same increment the matches
- if the char's count differ by 1
- right: s1Map[rightCharInd] + 1 == s2Map[rightCharInd],
- left: s1Map[leftCharInd] - 1 == s2Map[leftCharInd] decrement the matches
Solution
class Solution {
public boolean checkInclusion(String s1, String s2) {
if(s1.length() > s2.length())
return false;
char[] s1Map = new char[26];
char[] s2Map = new char[26];

for(int i = 0; i < s1.length(); i++) {


s1Map[s1.charAt(i)-'a']++;
s2Map[s2.charAt(i)-'a']++;
}

int matches = 0;
for(int i = 0; i < 26; i++) {
if(s1Map[i] == s2Map[i])
matches++;
}
int windowStart = 0;
for(int windowEnd = s1.length(); windowEnd < s2.length(); windowEnd++) {
if(matches == 26) return true;
int rightCharInd = s2.charAt(windowEnd)-'a';
s2Map[rightCharInd]++;
if(s1Map[rightCharInd] == s2Map[rightCharInd])
matches++;
else if(s1Map[rightCharInd] + 1 == s2Map[rightCharInd])
matches--;

int leftCharInd = s2.charAt(windowStart)-'a';


s2Map[leftCharInd]--;
if(s1Map[leftCharInd] == s2Map[leftCharInd])
matches++;
else if(s1Map[leftCharInd] - 1 == s2Map[leftCharInd])
matches--;

windowStart++;
}

return matches == 26;


}
}

Complexity Analysis:
Time Complexity: O(s2.length())
Space Complexity: O(26) ~ O(1)

76. Minimum Window Substring


(https://leetcode.com/problems/minimum-
window-substring/)
Hard

Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If
there is no such substring, return the empty string "".
The testcases will be generated such that the answer is unique.

A substring is a contiguous sequence of characters within the string.

Example 1:

Input: s = "ADOBECODEBANC", t = "ABC"


Output: "BANC"
Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t.

Example 2:

Input: s = "a", t = "a"


Output: "a"
Explanation: The entire string s is the minimum window.

Example 3:

Input: s = "a", t = "aa"


Output: ""
Explanation: Both 'a's from t must be included in the window.
Since the largest window of s only has one 'a', return empty string.

Constraints:

m == s.length
n == t.length
1 <= m, n <= 105
s and t consist of uppercase and lowercase English letters.

Approach
- Try to increase the window untill we have all the characters of the pattern
- Once we get the pattern, we update the result if length is less than earlier
- Shrink the window while the window have all the required characters & update the result

Solution
Approach 1
class Solution {
public String minWindow(String s, String t) {
int winS = 0, winE = 0;
String ans = "";
Map<Character, Integer> tMp = new HashMap<>();
Map<Character, Integer> wMp = new HashMap<>();
for(char c : t.toCharArray()) {
tMp.put(c, tMp.getOrDefault(c, 0)+1);
}
while(winS < s.length() && winE < s.length()) {
char c = s.charAt(winE);
wMp.put(c, wMp.getOrDefault(c, 0)+1);
while(winS <= winE && satisfy(wMp, tMp)) {
if(ans == "")
ans = s.substring(winS, winE+1);
ans = (winE-winS+1) < ans.length()?s.substring(winS, winE+1):ans;
wMp.put(s.charAt(winS), wMp.get(s.charAt(winS))-1);
if(wMp.get(s.charAt(winS)) == 0)
wMp.remove(s.charAt(winS));
winS++;
}
winE++;
}
return ans;
}

private boolean satisfy(Map<Character, Integer> wMp, Map<Character, Integer> tMp) {


for(char c : tMp.keySet()) {
if(!wMp.containsKey(c) || wMp.get(c) < tMp.get(c))
return false;
}
return true;
}
}

Approach 2 (Optimized)
class Solution {
public String minWindow(String str, String pattern) {
int windowStart = 0, minLen = Integer.MAX_VALUE, matched = 0;
Map<Character, Integer> charFreqMap = new HashMap<>();
String res = "";
for (char ch : pattern.toCharArray())
charFreqMap.put(ch, charFreqMap.getOrDefault(ch, 0) + 1);

for (int windowEnd = 0; windowEnd < str.length(); windowEnd++) {


char right = str.charAt(windowEnd);

if (charFreqMap.containsKey(right)) {
charFreqMap.put(right, charFreqMap.get(right) - 1);
if (charFreqMap.get(right) == 0)
matched++;
}

while (matched == charFreqMap.size()) {


if (minLen > windowEnd - windowStart + 1) {
minLen = windowEnd - windowStart + 1;
res = str.substring(windowStart, windowEnd + 1);
}

char left = str.charAt(windowStart);


if (charFreqMap.containsKey(left)) {
if (charFreqMap.get(left) == 0)
matched--;
charFreqMap.put(left, charFreqMap.get(left) + 1);
}
windowStart++;
}
}
return res;
}
}

Complexity Analysis
Approach 1:
- Time Complexity: O(N*K), N: length of str, K: length of pattern
- Space Complexity: O(K), K: length of pattern
Approach 2:
- Time Complexity: O(N), N: length of str
- Space Complexity: O(K), K: length of pattern

239. Sliding Window Maximum


(https://leetcode.com/problems/sliding-
window-maximum/)
Hard

You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the
window. Each time the sliding window moves right by one position.

Return the max sliding window.

Example 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

Example 2:

Input: nums = [1], k = 1


Output: [1]

Constraints:

1 <= nums.length <= 105

-104 <= nums[i] <= 104


1 <= k <= nums.length

Approach
- Use Deque, to keep the elements
- we'll keep the elemenet in decreasing order
- while the element at the first of the queue, i.e the index of the nums,
if it's out of the window, keep removing the element
- while the element at the last of the queue, i.e the index,
if it's less than equal to new element of the nums, keep removing the element
- insert the index of new element of nums
- if wE greater than k-1, then add the first element (index of the element in nums)
of the queue to res

Solution
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
int wS = 0, s = 0;
ArrayDeque<Integer> q = new ArrayDeque<>();

for (int wE = 0; wE < nums.length; wE++) {


// while the element at the first of the queue, i.e the index,
// if it's out of the window, keep removing the element
while(!q.isEmpty() && q.peekFirst() <= wE-k)
q.pollFirst();

// while the element at the last of the queue, i.e the index,
// if it's less than equal to new element of the nums,
// keep removing the element
while(!q.isEmpty() && nums[q.peekLast()] <= nums[wE])
q.pollLast();

// insert the index of new element of nums


q.offerLast(wE);

// if wE greater than k-1, then add the first element


// (index of the element in nums) of the queue to res
if(wE >= k-1)
res[s++] = nums[q.peekFirst()];
}
return res;
}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(k), where k < n

Resources
Leetcode - template (https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-%27substring%27-problems)
Leetcode - Overview & Question bank (https://leetcode.com/discuss/study-guide/1773891/Sliding-Window-Technique-and-Question-Bank)
Youtube Playlist - Hindi (https://youtube.com/playlist?list=PL_z_8CaSLPWeM8BDJmIYDaoQ5zuwyxnfj)
Youtube Playlist - Neetcode (https://youtube.com/playlist?list=PLot-Xpze53leOBgcVsJBEGrHPd_7x_koV)

20. Valid Parentheses


Easy

Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

An input string is valid if:

Open brackets must be closed by the same type of brackets.


Open brackets must be closed in the correct order.

Example 1:

Input: s = "()"
Output: true

Example 2:
Input: s = "()[]{}"
Output: true

Example 3:

Input: s = "(]"
Output: false

Constraints:

1 <= s.length <= 104


s consists of parentheses only '()[]'.

class Solution {
public boolean isValid(String s) {
Stack<Character> st = new Stack<>();

for(int i = 0; i < s.length(); i++) {


char c = s.charAt(i);
if(c == '(' || c == '{' || c == '[')
st.push(c);
else if(c == ')' && !st.empty()) {
char ch = st.pop();
if(ch != '(')
return false;
} else if(c == '}' && !st.empty()) {
char ch = st.pop();
if(ch != '{')
return false;
} else if(c == ']' && !st.empty()) {
char ch = st.pop();
if(ch != '[')
return false;
} else
return false;
}

return st.empty();
}
}

155. Min Stack


Easy

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

Implement the MinStack class:

MinStack() initializes the stack object.


void push(int val) pushes the element val onto the stack.
void pop() removes the element on the top of the stack.
int top() gets the top element of the stack.
int getMin() retrieves the minimum element in the stack.

Example 1:
Input
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

Output
[null,null,null,null,-3,null,0,-2]

Explanation
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); // return -3
minStack.pop();
minStack.top(); // return 0
minStack.getMin(); // return -2

Constraints:

-231 <= val <= 231 - 1


Methods pop, top and getMin operations will always be called on non-empty stacks.
At most 3 * 104 calls will be made to push, pop, top, and getMin.
class Pair {
int val;
int min;

Pair(int v, int m) {
val = v;
min = m;
}

void setVal(int v) {
val = v;
}

void setMin(int m) {
min = m;
}
}
class MinStack {
Stack<Pair> st;
public MinStack() {
st = new Stack<>();
}

public void push(int val) {


if(st.empty())
st.push(new Pair(val, val));
else {
int min = Math.min(st.peek().min, val);
st.push(new Pair(val,min));
}
}

public void pop() {


st.pop();
}

public int top() {


return st.peek().val;
}

public int getMin() {


return st.peek().min;
}
}

/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/

150. Evaluate Reverse Polish Notation


Medium

Evaluate the value of an arithmetic expression in Reverse Polish Notation.

Valid operators are +, -, *, and /. Each operand may be an integer or another expression.

Note that division between two integers should truncate toward zero.

It is guaranteed that the given RPN expression is always valid. That means the expression would always evaluate to a result, and there will not be any division by zero operation.
Example 1:

Input: tokens = ["2","1","+","3","*"]


Output: 9
Explanation: ((2 + 1) * 3) = 9

Example 2:

Input: tokens = ["4","13","5","/","+"]


Output: 6
Explanation: (4 + (13 / 5)) = 6

Example 3:

Input: tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]


Output: 22
Explanation: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

Constraints:

1 <= tokens.length <= 104


tokens[i] is either an operator: "+", "-", "*", or "/", or an integer in the range [-200, 200].

Solution
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> st = new Stack<>();
for(String s : tokens) {
if(s.equals("+")) {
st.push(st.pop()+st.pop());
} else if(s.equals("-")) {
int n2 = st.pop();
int n1 = st.pop();
st.push(n1-n2);
} else if(s.equals("*")) {
st.push(st.pop()*st.pop());
} else if(s.equals("/")) {
int n2 = st.pop();
int n1 = st.pop();
st.push(n1/n2);
} else {
st.push(Integer.parseInt(s));
}
}
return st.peek();
}
}

Comlexity Analysis
Time Complexity: O(N) -> traversing through all the tokens
Space Complexity: O(N) -> for the stack
22. Generate Parentheses
Medium

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

Feels like mismatch for the pattern satck

Example 1:

Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]

Example 2:

Input: n = 1
Output: ["()"]

Constraints:

1 <= n <= 8

Approach
- Backtracking
- add '(' always and add ')' only if count('(') > count(')')
- add the string into the result when count('(') > count(')') && count('(') == n
- base case: count('(') > n || count(')') > n

Solution
class Solution {
public List<String> generateParenthesis(int n) {
List<String> ans = new ArrayList<>();
generate(0, n, 0, 0, ans, "");
return ans;
}

private void generate(int index, int n, int lCount, int rCount, List<String> ans, String op) {
if(lCount > n || rCount > n)
return;
if(lCount == rCount && lCount == n) {
ans.add(op);
}
if(lCount > rCount) {
String op1 = op + ")";
generate(index+1, n, lCount, rCount+1, ans, op1);
}
String op2 = op + "(";
generate(index+1, n, lCount+1, rCount, ans, op2);
}
}

739. Daily Temperatures


Medium
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.

Example 1:

Input: temperatures = [73,74,75,71,69,72,76,73]


Output: [1,1,4,2,1,1,0,0]

Example 2:

Input: temperatures = [30,40,50,60]


Output: [1,1,1,0]

Example 3:

Input: temperatures = [30,60,90]


Output: [1,1,0]

Constraints:

1 <= temperatures.length <= 105


30 <= temperatures[i] <= 100

Approach
- Use a stack to keep temperatures and indices
- We'll check while peek element is less the current temperature
- We'll pop and res[popped Index] = i-popped Index;
- push each element and index to the stack

TODO: O(1) extra-space


Solution
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
Stack<int[]> st = new Stack<>();
int[] res = new int[n];

for(int i = 0; i < n; i++) {


while(!st.empty() && st.peek()[0] < temperatures[i]) {
int[] temp = st.pop();
res[temp[1]] = i-temp[1];
}
st.push(new int[]{temperatures[i], i});
}

return res;
}
}

Complexity Analysis
- Time Complexity: O(N)
At first glance, it may look like the time complexity of this algorithm should be O(N^2),
because there is a nested while loop inside the for loop. However, each element can only be added to the stack once,
which means the stack is limited to N pops. Every iteration of the while loop uses 1 pop,
which means the while loop will not iterate more than N times in total, across all iterations of the for loop.
An easier way to think about this is that in the worst case, every element will be pushed and popped once.
This gives a time complexity of O(2*N) = O(N).
- Space Complexity: O(N)
If the input was non-increasing, then no element would ever be popped from the stack,
and the stack would grow to a size of N elements at the end.

853. Car Fleet


Medium

There are n cars going to the same destination along a one-lane road. The destination is target miles away.

You are given two integer array position and speed, both of length n, where position[i] is the position of the ith car and speed[i] is the speed of the ith car (in miles per hour).

A car can never pass another car ahead of it, but it can catch up to it and drive bumper to bumper at the same speed. The faster car will slow down to match the slower car's speed.
The distance between these two cars is ignored (i.e., they are assumed to have the same position).

A car fleet is some non-empty set of cars driving at the same position and same speed. Note that a single car is also a car fleet.

If a car catches up to a car fleet right at the destination point, it will still be considered as one car fleet.

Return the number of car fleets that will arrive at the destination.

Example 1:

Input: target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]


Output: 3
Explanation:
The cars starting at 10 (speed 2) and 8 (speed 4) become a fleet, meeting each other at 12.
The car starting at 0 does not catch up to any other car, so it is a fleet by itself.
The cars starting at 5 (speed 1) and 3 (speed 3) become a fleet, meeting each other at 6. The fleet moves at speed 1 until it reaches
Note that no other cars meet these fleets before the destination, so the answer is 3.

Example 2:

Input: target = 10, position = [3], speed = [3]


Output: 1
Explanation: There is only one car, hence there is only one fleet.

Example 3:

Input: target = 100, position = [0,2,4], speed = [4,2,1]


Output: 1
Explanation:
The cars starting at 0 (speed 4) and 2 (speed 2) become a fleet, meeting each other at 4. The fleet moves at speed 2.
Then, the fleet (speed 2) and the car starting at 4 (speed 1) become one fleet, meeting each other at 6. The fleet moves at speed 1 u

Constraints:

n == position.length == speed.length

1 <= n <= 105


0 < target <= 106
0 <= position[i] < target
All the values of position are unique.
0 < speed[i] <= 106

Approach
Solution
class Solution {
// 1
public int carFleet(int target, int[] position, int[] speed) {
int n = position.length, res = 0;
double[][] cars = new double[n][2];

for(int i = 0; i < n; i++) {


cars[i] = new double[] {position[i], (double)(target-position[i])/speed[i]};
}
Arrays.sort(cars, (a, b) -> Double.compare(a[0], b[0]));
double curr = 0;
for(int i = n-1; i >= 0; i--) {
if(cars[i][1] > curr) {
curr = cars[i][1];
res++;
}
}
return res;
}
// 2
public int carFleet(int target, int[] pos, int[] speed) {
Map<Integer, Double> m = new TreeMap<>(Collections.reverseOrder());
for (int i = 0; i < pos.length; ++i)
m.put(pos[i], (double)(target - pos[i]) / speed[i]);
int res = 0; double cur = 0;
for (double time : m.values()) {
if (time > cur) {
cur = time;
res++;
}
}
return res;
}
}

Complexity Analysis
- Time Complexity:O(NlogN) -> sorting
- Space Complexity: O(N)

84. Largest Rectangle in Histogram


(https://leetcode.com/problems/largest-
rectangle-in-histogram/)
Hard

Given an array of integers heights representing the histogram's bar height where the width of each bar is 1, return the area of the largest rectangle in the histogram.

Example 1:

Input: heights = [2,1,5,6,2,3]


Output: 10
Explanation: The above is a histogram where width of each bar is 1.
The largest rectangle is shown in the red area, which has an area = 10 units.
Example 2:

Input: heights = [2,4]


Output: 4

Constraints:

1 <= heights.length <= 105


0 <= heights[i] <= 104

Approach
- Monotonic Stack
- store index and height in the stack
- while the top element is greater in height than the new element,
- keep poping the top element and compare the area with maxArea
- start will have the index of the poped element(new element can be extended towards the left)
- push start and height into the stack
- while stack is not empty,
- keep poping and compare the area with maxArea
- return maxArea

Solution
class Solution {
public int largestRectangleArea(int[] heights) {
int maxArea = 0;
Stack<int[]> st = new Stack<>();
for(int i = 0; i < heights.length; i++) {
int start = i;
while(!st.isEmpty() && st.peek()[1] > heights[i]) {
int[] pair = st.pop();
maxArea = Math.max(maxArea, pair[1] * (i-pair[0]));
start = pair[0];
}
st.push(new int[]{start, heights[i]});
}

while(!st.isEmpty()) {
int[] pair = st.pop();
maxArea = Math.max(maxArea, pair[1] * (heights.length-pair[0]));
}

return maxArea;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

Resources
Articles
Geeksforgeeks (https://www.geeksforgeeks.org/stack-data-structure/)
A-comprehensive-guide-and-template-for-monotonic-stack-based-problems (https://leetcode.com/discuss/study-guide/2347639/A-comprehensive-guide-and-template-for-
monotonic-stack-based-problems)
Videos
Playlist (https://youtube.com/playlist?list=PL6Zs6LgrJj3vWOf01wMHiTy9IFufptfG3)
Playlist - Hindi (https://youtube.com/playlist?list=PLDzeHZWIZsTrhXYYtx4z8-u8zA-DzuVsj)

704. Binary Search


Easy

Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise,
return -1.

You must write an algorithm with O(log n) runtime complexity.

Example 1:

Input: nums = [-1,0,3,5,9,12], target = 9


Output: 4
Explanation: 9 exists in nums and its index is 4

Example 2:

Input: nums = [-1,0,3,5,9,12], target = 2


Output: -1
Explanation: 2 does not exist in nums so return -1

Constraints:

1 <= nums.length <= 104


-104 < nums[i], target < 104
All the integers in nums are unique.
nums is sorted in ascending order.

class Solution {
public int search(int[] nums, int target) {
int l = 0, r = nums.length-1;

while(l <= r) {
int mid = l + (r - l)/2;

if(nums[mid] == target)
return mid;
else if(nums[mid] < target)
l = mid + 1;
else
r = mid - 1;
}

return -1;
}
}

74. Search a 2D Matrix


Medium

Write an efficient algorithm that searches for a value target in an m x n integer matrix matrix. This matrix has the following properties:

Integers in each row are sorted from left to right. The first integer of each row is greater than the last integer of the previous row.

Example 1:
Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
Output: true

Example 2:

Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13


Output: false

Constraints:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104

Approach
1st:
- keep two pointer one for row, other for column
- initialize tem to point to the last element of the 1st row
- if the element is equal to target then return true
- else if element is < target then move the row pointer downward
- else shift the column pointer to left

2nd:
[It would work only when the element at 1st index of next row should be greater then last element of the previous row]
- left pointer at 0, right at n*m-1 (at last element of the matrix)
- do the binarysearch
- target element can be find using
- rowIndex = mid / number of elements in each row
- colIndex = mid % number of elements in each row

Solution
class Solution {
// worst case go through O(n+m) elements
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int row = 0, col = n-1;
while(row >= 0 && row < m && col >= 0 && col < n) {
int val = matrix[row][col];
if(val == target)
return true;
else if(val < target)
row++;
else
col--;
}
return false;
}

// worst case it would go through O(log(n*m)) elements


public boolean searchMatrix1(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int left = 0, right = m*n-1;
while(left <= right) {
int mid = left + (right-left)/2;
int val = matrix[mid/n][mid%n];
if(val == target)
return true;
else if(val < target)
left = mid+1;
else
right = mid-1;
}
return false;
}
}

Complexity Analysis
- Time Complexity:
- 1st approach: O(n+m)
- 2nd approach: O(log(n*m))
- Space Coplexity: O(1) for both approaches

875. Koko Eating Bananas


Medium

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.

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.

Koko likes to eat slowly but still wants to finish eating all the bananas before the guards return.

Return the minimum integer k such that she can eat all the bananas within h hours.

Example 1:

Input: piles = [3,6,7,11], h = 8


Output: 4

Example 2:
Input: piles = [30,11,23,4,20], h = 5
Output: 30

Example 3:

Input: piles = [30,11,23,4,20], h = 6


Output: 23

Constraints:

1 <= piles.length <= 104

piles.length <= h <= 109


1 <= piles[i] <= 109

Approach
- Binary search on range (1, max(pile))
- while the condition satisfies go towards left half
- else go towards right half

Solution
class Solution {
public int minEatingSpeed(int[] piles, int h) {
int left = 1, right = piles[0];
for(int pile : piles) {
if(pile < left)
left = pile;
if(pile > right)
right = pile;
}
int result = right;
while(left <= right) {
int mid = left + (right-left)/2;
if(isSatisfy(piles, mid, h)) {
result = Math.min(result, mid);
right = mid-1;
} else {
left = mid+1;
}
}
return result;
}

private boolean isSatisfy(int[] piles, int mid, int h) {


int count = 0;
for(int pile : piles) {
count += (pile/mid);
if(pile%mid != 0)
count++;
}
return count <= h;
}
}

Complexity Analysis
- Time Complexity: O(n*logm), m: max(piles)
- Space Complexity: O(1)
33. Search in Rotated Sorted Array
Medium

There is an integer array nums sorted in ascending order (with distinct values).

Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1],
nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2].

Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums.

You must write an algorithm with O(log n) runtime complexity.

Example 1:

Input: nums = [4,5,6,7,0,1,2], target = 0


Output: 4

Example 2:

Input: nums = [4,5,6,7,0,1,2], target = 3


Output: -1

Example 3:

Input: nums = [1], target = 0


Output: -1

Constraints:

1 <= nums.length <= 5000

-104 <= nums[i] <= 104


All values of nums are unique.
nums is an ascending array that is possibly rotated.

-104 <= target <= 104

Approach
Modified Binary Search

Solution
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length-1;

while(left <= right) {


int mid = left + (right - left) / 2;

if(nums[mid] == target)
return mid;
// left sorted portion
if(nums[left] <= nums[mid]) {
if(target < nums[left] || target > nums[mid])
left = mid+1;
else
right = mid-1;
}
// right sorted portion
else {
if(target < nums[mid] || target > nums[right])
right = mid-1;
else
left = mid+1;
}
}
return -1;
}
}

Complexity Analysis
- Time Complexity: O(logN)
- Space Complexity: O(1)

153. Find Minimum in Rotated Sorted Array


Medium

Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become:

[4,5,6,7,0,1,2] if it was rotated 4 times.


[0,1,2,4,5,6,7] if it was rotated 7 times.

Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]].

Given the sorted rotated array nums of unique elements, return the minimum element of this array.

You must write an algorithm that runs in O(log n) time.

Example 1:

Input: nums = [3,4,5,1,2]


Output: 1
Explanation: The original array was [1,2,3,4,5] rotated 3 times.

Example 2:

Input: nums = [4,5,6,7,0,1,2]


Output: 0
Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times.

Example 3:
Input: nums = [11,13,15,17]
Output: 11
Explanation: The original array was [11,13,15,17] and it was rotated 4 times.

Constraints:

n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
All the integers of nums are unique.
nums is sorted and rotated between 1 and n times.

Approach
Modified Binary Search

Solution
class Solution {
public int findMin(int[] nums) {
int n = nums.length;
int start = 0, end = n-1;
int res = 0;
while(start <= end) {
int mid = start + (end - start)/2;
int prev = (mid-1+n)%n, next = (mid+1)%n;
if(nums[mid]<nums[prev] && nums[mid]<nums[next])
return nums[mid];
else if(nums[end] <= nums[mid])
start = mid+1;
else
end = mid-1;
}
return nums[res];
}
}

Complexity Analysis
- Time Complexity: O(logN)
- Space Complexity: O(1)

981. Time Based Key-Value Store


Medium

Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key's value at a certain timestamp.

Implement the TimeMap class:

TimeMap() Initializes the object of the data structure.


void set(String key, String value, int timestamp) Stores the key key with the value value at the given time timestamp.
String get(String key, int timestamp) Returns a value such that set was called previously, with timestamp_prev <= timestamp. If there are multiple such values, it
returns the value associated with the largest timestamp_prev. If there are no values, it returns "".

Example 1:
Input
["TimeMap", "set", "get", "get", "set", "get", "get"]
[[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]]
Output
[null, null, "bar", "bar", null, "bar2", "bar2"]

Explanation
TimeMap timeMap = new TimeMap();
timeMap.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1.
timeMap.get("foo", 1); // return "bar"
timeMap.get("foo", 3); // return "bar", since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the
timeMap.set("foo", "bar2", 4); // store the key "foo" and value "bar2" along with timestamp = 4.
timeMap.get("foo", 4); // return "bar2"
timeMap.get("foo", 5); // return "bar2"

Constraints:

1 <= key.length, value.length <= 100


key and value consist of lowercase English letters and digits.
1 <= timestamp <= 107
All the timestamps timestamp of set are strictly increasing.
At most 2 * 105 calls will be made to set and get.

Approach
Solution
class TimeMap {
class Pair {
int timestamp;
String value;

Pair(String value, int timestamp) {


this.value = value;
this.timestamp = timestamp;
}
}

Map<String, List<Pair>> mp;

public TimeMap() {
mp = new HashMap<>();
}

public void set(String key, String value, int timestamp) {


if (!mp.containsKey(key)) {
mp.put(key, new ArrayList<>());
}
mp.get(key).add(new Pair(value, timestamp));
}

public String get(String key, int timestamp) {


String ans = "";
if (!mp.containsKey(key)) {
return ans;
}
List<Pair> list = mp.get(key);

int index = binarSearch(list, timestamp);


if (index == -1) {
return ans;
}

ans = list.get(index).value;
return ans;
}

private int binarSearch(List<TimeMap.Pair> list, int timestamp) {


int l = 0;
int r = list.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (list.get(mid).timestamp == timestamp) {
return mid;
} else if (list.get(mid).timestamp < timestamp) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return r;
}

/**
* Your TimeMap object will be instantiated and called as such:
* TimeMap obj = new TimeMap();
* obj.set(key,value,timestamp);
* String param_2 = obj.get(key,timestamp);
*/
public class TimeMap {

private Map<String,TreeMap<Integer,String>> map;

/** Initialize your data structure here. */


public TimeMap() {
map = new HashMap<>();
}

public void set(String key, String value, int timestamp) {


if(!map.containsKey(key)) {
map.put(key,new TreeMap<>());
}
map.get(key).put(timestamp,value);
}

public String get(String key, int timestamp) {


TreeMap<Integer,String> treeMap = map.get(key);
if(treeMap==null) {
return "";
}
Integer floor = treeMap.floorKey(timestamp);
if(floor==null) {
return "";
}
return treeMap.get(floor);
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

4. Median of Two Sorted Arrays


(https://leetcode.com/problems/median-of-
two-sorted-arrays/)
Hard

Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.

The overall run time complexity should be O(log (m+n)).

Example 1:

Input: nums1 = [1,3], nums2 = [2]


Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.

Example 2:

Input: nums1 = [1,2], nums2 = [3,4]


Output: 2.50000
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.

Constraints:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000

-106 <= nums1[i], nums2[i] <= 106

Approach
Solution
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if(nums1.length > nums2.length)
return findMedianSortedArrays(nums2, nums1);
int total = nums1.length + nums2.length;
int half = (total + 1) / 2;
int l = 0, r = nums1.length;
double result = 0d;
while(l <= r) {
int i = l + (r - l) / 2;
int j = half - i;
int nums1L = i > 0 ? nums1[i-1]:Integer.MIN_VALUE;
int nums1R = i < nums1.length ? nums1[i]:Integer.MAX_VALUE;
int nums2L = j > 0 ? nums2[j-1]:Integer.MIN_VALUE;
int nums2R = j < nums2.length ? nums2[j]:Integer.MAX_VALUE;

if(nums1L <= nums2R && nums2L <= nums1R) {


if(total % 2 == 0) {
return (Math.max(nums1L, nums2L) + Math.min(nums1R, nums2R)) / 2.0;
} else {
return Math.max(nums1L, nums2L);
}
} else if(nums1L > nums2R) {
r = i - 1;
} else {
l = i + 1;
}
}
return result;
}
}

Complexity Analysis
- Time Complexity: O(log(m+n))
- Space Complexity: O(1)

Resources
Articles:
Binary-Search-101-The-Ultimate-Binary-Search-Handbook (https://leetcode.com/problems/binary-search/discuss/423162/Binary-Search-101-The-Ultimate-Binary-Search-
Handbook)
Python-Clear-explanation-Powerful-Ultimate-Binary-Search-Template.-Solved-many-problems. (https://leetcode.com/problems/koko-eating-bananas/discuss/769702/Python-
Clear-explanation-Powerful-Ultimate-Binary-Search-Template.-Solved-many-problems.)
JavaC%2B%2BPython-Binary-Search (https://leetcode.com/problems/koko-eating-bananas/discuss/152324/JavaC%2B%2BPython-Binary-Search)
Binary-Search-for-Beginners-Problems-or-Patterns-or-Sample-solutions (https://leetcode.com/discuss/study-guide/691825/Binary-Search-for-Beginners-Problems-or-Patterns-
or-Sample-solutions)
5-variations-of-Binary-search-(A-Self-Note) (https://leetcode.com/discuss/study-guide/1322500/5-variations-of-Binary-search-(A-Self-Note))

Videos:
Binary Search Introduction (https://youtu.be/P3YID7liBug)
Neetcode Playlist (https://youtube.com/playlist?list=PLot-Xpze53leNZQd0iINpD-MAhMOMzWvO)

206. Reverse Linked List


Given the head of a singly linked list, reverse the list, and return the reversed list.

Example 1:

Input: head = [1,2,3,4,5]


Output: [5,4,3,2,1]

Example 2:

Input: head = [1,2]


Output: [2,1]

Example 3:

Input: head = []
Output: []

Constraints:

The number of nodes in the list is the range [0, 5000].


-5000 <= Node.val <= 5000

Follow up: A linked list can be reversed either iteratively or recursively. Could you
implement both?

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode curr = head, next = head, prev = null;

while(curr != null) {
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}

return prev;
}
}

TODO: Recursive solution


21. Merge Two Sorted Lists
Easy

You are given the heads of two sorted linked lists list1 and list2.

Merge the two lists in a one sorted list. The list should be made by splicing together the nodes of the first two lists.

Return the head of the merged linked list.

Example 1:
Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]

Example 2:
Input: list1 = [], list2 = []
Output: []

Example 3:
Input: list1 = [], list2 = [0]
Output: [0]

Constraints:

The number of nodes in both lists is in the range [0, 50]. -100 <= Node.val <= 100 Both list1 and list2 are sorted in non-decreasing order.

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(0);
ListNode result = dummy;
while(list1 != null && list2 != null) {
if(list1.val < list2.val) {
dummy.next = list1;
dummy = dummy.next;
list1 = list1.next;
} else {
dummy.next = list2;
dummy = dummy.next;
list2 = list2.next;
}
}

if(list1 != null) {
dummy.next = list1;
}

if(list2 != null) {
dummy.next = list2;
}

return result.next;
}
}
143. Reorder List
Medium

You are given the head of a singly linked-list. The list can be represented as:

L0 → L1 → … → Ln - 1 → Ln Reorder the list to be on the following form:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … You may not modify the values in the list's nodes. Only nodes themselves may be changed.

Example 1:

Input: head = [1,2,3,4]


Output: [1,4,2,3]

Example 2:

Input: head = [1,2,3,4,5]


Output: [1,5,2,4,3]

Constraints:

The number of nodes in the list is in the range [1, 5 * 104].


1 <= Node.val <= 1000

Approach
- Reverse the other half(mid to end)
- 2 pointers one from the head other from the reverse head of other list
- use temp variable to keep the next pointers of both the list
- 1st list's next points to 2nd list & 2nd list's next point to the temp variable containing the next of the 1st list
- move both the pointers

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public void reorderList(ListNode head) {
ListNode slow = head, fast = head.next;

while(fast != null && fast.next != null) {


slow = slow.next;
fast = fast.next.next;
}
ListNode first = head, second = reverse(slow.next);
slow.next = null;

while(second != null) {
ListNode tmp1 = first.next, tmp2 = second.next;
first.next = second;
second.next = tmp1;
first = tmp1;
second = tmp2;
}

private ListNode reverse(ListNode head) {


ListNode curr = head, prev = null, next = null;

while(curr != null) {
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}

Complexity Analysis
- Time Complexity: O(2*N) ~ O(N)
- Space Complexity: O(1)

19. Remove Nth Node From End of List


Medium

Given the head of a linked list, remove the nth node from the end of the list and return its head.

Example 1:

Input: head = [1,2,3,4,5], n = 2


Output: [1,2,3,5]

Example 2:
Input: head = [1], n = 1
Output: []

Example 3:

Input: head = [1,2], n = 1


Output: [1]

Constraints:

The number of nodes in the list is sz.

1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz

Follow up: Could you do this in one pass?

Approach
- 2 pointers approach
- dummy head for so that removing head would be easier
- fast pointer would be ahead by n nodes
- slow and fast will start moving until fast reach the last element
- slow's next node would be the one we need to remove

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode slow = dummyHead, fast = dummyHead;

while(n > 0) {
fast = fast.next;
n--;
}

while(fast != null && fast.next != null) {


slow = slow.next;
fast = fast.next;
}

slow.next = slow.next.next;

return dummyHead.next;
}
}

Complexity Analysis
- Time Complexity: O(N) , one pass only
- Space Complexity: O(1)

138. Copy List with Random Pointer


Medium

A linked list of length n is given such that each node contains an additional random pointer, which could point to any node in the list, or null.

Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has its value set to the value of its corresponding original node.
Both the next and random pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state.
None of the pointers in the new list should point to nodes in the original list.

For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding two nodes x and y in the copied list, x.random --> y.

Return the head of the copied linked list.

The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:

val: an integer representing Node.val


random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any
node.

Your code will only be given the head of the original linked list.

Example 1:

Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]


Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]

Example 2:

Input: head = [[1,1],[2,1]]


Output: [[1,1],[2,1]]

Example 3:

Input: head = [[3,null],[3,0],[3,null]]


Output: [[3,null],[3,0],[3,null]]

Constraints:

0 <= n <= 1000


-104 <= Node.val <= 104
Node.random is null or is pointing to some node in the linked list.

Approach
- Step 1: Duplicate each node such that old1->new1->old2->new2 ...
- Step 2: Random pointer of new = Random pointer of old's next
- Step 3: Seperate the the nodes to form old1->old2.. & new1->new2..

Solution
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;

public Node(int val) {


this.val = val;
this.next = null;
this.random = null;
}
}
*/

class Solution {
public Node copyRandomList(Node head) {
if(head == null)
return null;

// Step 1: Duplicate each node such that old1->new1->old2->new2 ...


Node curr = head, next = null;
while(curr != null) {
next = curr.next;
Node newNode = new Node(curr.val);
curr.next = newNode;
newNode.next = next;
curr = next;
}

// Step 2: Random pointer of new = Random pointer of old's next


curr = head;
Node nextNode = head.next;
while(nextNode != null) {
nextNode.random = curr.random == null ? null : curr.random.next;
if(nextNode.next == null)
break;
nextNode = nextNode.next.next;
curr = curr.next.next;
}

// Step 3: Seperate the the nodes to form old1->old2.. & new1->new2..


Node p = head, c = head.next, n = null;
Node newListHead = c;
while(p != null) {
n = c.next;
p.next = n;
if (n == null)
break;
c.next = n.next;
p = p.next;
c = c.next;
}

return newListHead;
}
}

Complexity Analysis
- Time Complexity: O(N+2*N+N) ~ O(N)
- Space Complexity: O(1), no extra memory is used (memory used is for new list only)
2. Add Two Numbers
Medium

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two
numbers and return the sum as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example 1:

Input: l1 = [2,4,3], l2 = [5,6,4]


Output: [7,0,8]
Explanation: 342 + 465 = 807.

Example 2:

Input: l1 = [0], l2 = [0]


Output: [0]

Example 3:

Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]


Output: [8,9,9,9,0,0,0,1]

Constraints:

The number of nodes in each linked list is in the range [1, 100].
0 <= Node.val <= 9
It is guaranteed that the list represents a number that does not have leading zeros.

Approach
- Three pointers, one for each given list and 3rd one for resultant list
- untill both the pointers pointing to given lists are null or carry > 0
- add new node to the resultant list

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode curr1 = l1, curr2 = l2, curr3 = res;
int carry = 0;
while(curr1 != null || curr2 != null || carry > 0) {
int v1 = curr1 == null ? 0 : curr1.val;
int v2 = curr2 == null ? 0 : curr2.val;
int sum = v1 + v2 + carry;
carry = sum / 10;
curr3.next = new ListNode(sum % 10);
curr3 = curr3.next;
curr1 = curr1 == null ? curr1 : curr1.next;
curr2 = curr2 == null ? curr2 : curr2.next;
}

return res.next;
}
}

Complexity Analysis
- Time Complexity: O(max(len(list1, list2))
- Space Complexity: O(max(len(list1, list2))

141. Linked List Cycle


Easy

Given head, the head of a linked list, determine if the linked list has a cycle in it.

There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the
node that tail's next pointer is connected to. Note that pos is not passed as a parameter.

Return true if there is a cycle in the linked list. Otherwise, return false.

Example 1:

Input: head = [3,2,0,-4], pos = 1


Output: true
Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).

Example 2:

Input: head = [1,2], pos = 0


Output: true
Explanation: There is a cycle in the linked list, where the tail connects to the 0th node.

Example 3:
Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.

Constraints:

The number of the nodes in the list is in the range [0, 104].
-105 <= Node.val <= 105
pos is -1 or a valid index in the linked-list.

Follow up: Can you solve it using O(1) (i.e. constant) memory?

Approach
- slow and fast pointer
- move fast twice that of slow
- break if fast reach null or fast becomes equal to slow
- if slow == fast, thenn has a cycle otherwise no cycle

Solution
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null)
return false;
ListNode slow = head, fast = head.next;

while(fast != null && slow != fast) {


slow = slow.next;
fast = fast.next;
if(fast != null)
fast = fast.next;
}
return slow == fast;
}
}

Complexity Analysis
- Time Complexity: O(N), N : numbers of nodes in the list
- Space Complexity: O(1)

287. Find the Duplicate Number


Medium

Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive.
There is only one repeated number in nums, return this repeated number.

You must solve the problem without modifying the array nums and uses only constant extra space.

Example 1:

Input: nums = [1,3,4,2,2]


Output: 2

Example 2:

Input: nums = [3,1,3,4,2]


Output: 3

Constraints:

1 <= n <= 105


nums.length == n + 1
1 <= nums[i] <= n
All the integers in nums appear only once except for precisely one integer which appears two or more times.

Follow up:
How can we prove that at least one duplicate number must exist in nums?
Can you solve the problem in linear runtime complexity?

Approach
- slow and fast pointer, similar to cycle in link list

Solution
class Solution {
public int findDuplicate(int[] nums) {

int slow = nums[0], fast = nums[0];


do {
slow = nums[slow];
fast = nums[nums[fast]];
} while(slow != fast);

slow = nums[0];

while(slow != fast) {
slow = nums[slow];
fast = nums[fast];
}

return slow;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(1)

146. LRU Cache


Medium
Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

Implement the LRUCache class:

LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
int get(int key) Return the value of the key if the key exists, otherwise return -1.
void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity
from this operation, evict the least recently used key.
The functions get and put must each run in O(1) average time complexity.

Example 1:

Input
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]

Explanation
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1); // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2); // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1); // return -1 (not found)
lRUCache.get(3); // return 3
lRUCache.get(4); // return 4

Constraints:

1 <= capacity <= 3000


0 <= key <= 104
0 <= value <= 105
At most 2 * 105 calls will be made to get and put.

Approach
- Doublely linkedlist, it allows O(1) deletion of a given node
- Map to keep the key mapped to a node, getting the value for a key at O(1) time
- Keeping head and tail of the linked list
- add at head and remove from tail(if size exceeds)
- get method:
- if map doesn't contain the key return -1
- else get the node, remove it, add it (it'll add it to the head of the list), return the value

- put method:
- if map already contains the key, remove the node
- size of map = size, then remove the node at tail
- insert the new node

- insert method: (helper method)


- add the node to the next of the head and update the pointers
- add the key and node to the map

- remove method: (helper method)


- remove the node using the pointers
- remove the key and node from the map

Solution
/*
* Hepler class
* Node to create doublely linked list
*/
class Node {
int key;
int val;
Node prev;
Node next;
Node(int key, int val) {
this.key = key;
this.val = val;
}
}

class LRUCache {
Node head = new Node(0, 0);
Node tail = new Node(0, 0);
Map<Integer, Node> mp;
int size;

public LRUCache(int capacity) {


head.next = tail;
tail.prev = head;
mp = new HashMap<>();
size = capacity;
}

public int get(int key) {


if(mp.containsKey(key)) {
Node node = mp.get(key);
remove(node);
insert(node);
return node.val;
} else
return -1;
}

public void put(int key, int value) {


if(mp.containsKey(key)) {
Node node = mp.get(key);
remove(node);
}
if(mp.size() == size)
remove(tail.prev);
insert(new Node(key, value));
}

private void remove(Node node) {


mp.remove(node.key);
node.prev.next = node.next;
node.next.prev = node.prev;
}

private void insert(Node node) {


Node headNext = head.next;
head.next = node;
node.prev = head;
node.next = headNext;
headNext.prev = node;
mp.put(node.key, node);
}
}

/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/

Complexity Analysis
- Time Complexity: (on average)
- get() : O(1)
- put() : O(1)

- Space Complexity:
- ~ O(N) for map and doublely linkedlist (over all)
- get() : O(1)
- put() : O(1)

23. Merge k Sorted Lists


Hard

You are given an array of k linked-lists lists, each linked-list is sorted in ascending order.

Merge all the linked-lists into one sorted linked-list and return it.

Example 1:

Input: lists = [[1,4,5],[1,3,4],[2,6]]


Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
1->4->5,
1->3->4,
2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6

Example 2:

Input: lists = []
Output: []

Example 3:

Input: lists = [[]]


Output: []

Constraints:

k == lists.length
0 <= k <= 104
0 <= lists[i].length <= 500
-104 <= lists[i][j] <= 104
lists[i] is sorted in ascending order.

The sum of lists[i].length will not exceed 104.

Approach
- Create a function to merge two sorted lists
- Use it to merge all the given lists
Sub approach 1:
- we can merge two lists then use the merge list as new list and merge it with next given list
Sub approach 2:
- we can merge all the lists as a group of 2 lists untill we're left with 1 final list

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
int k = lists.length;
if(k == 0)
return null;
int i = 0;
while(i+1<k) {
ListNode l1 = lists[i], l2 = lists[i+1];
lists[i+1] = merge2LL(l1, l2);
i++;
}
return lists[k-1];
}

public ListNode merge2LL(ListNode l1, ListNode l2) {


ListNode dummyHead = new ListNode(0);
ListNode tmp = dummyHead;
while(l1 != null && l2 != null) {
if(l1.val < l2.val) {
tmp.next = l1;
ListNode nextL1 = l1.next;
l1.next = l2;
l1 = nextL1;
tmp = tmp.next;
} else {
tmp.next = l2;
ListNode nextL2 = l2.next;
l2.next = l1;
l2 = nextL2;
tmp = tmp.next;
}
}

if(l1 == null)
tmp.next = l2;
if(l2 == null)
tmp.next = l1;
return dummyHead.next;
}
}

Optimized
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
return partion(lists, 0, lists.length - 1);
}

public ListNode partion(ListNode[] lists, int s, int e) {


if (s == e) return lists[s];
if (s < e) {
int q = (s + e) / 2;
ListNode l1 = partion(lists, s, q);
ListNode l2 = partion(lists, q + 1, e);
return merge2LL(l1, l2);
} else return null;
}

public ListNode merge2LL(ListNode l1, ListNode l2) {


ListNode dummyHead = new ListNode(0);
ListNode tmp = dummyHead;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
tmp.next = l1;
ListNode nextL1 = l1.next;
l1.next = l2;
l1 = nextL1;
tmp = tmp.next;
} else {
tmp.next = l2;
ListNode nextL2 = l2.next;
l2.next = l1;
l2 = nextL2;
tmp = tmp.next;
}
}

if (l1 == null) tmp.next = l2;


if (l2 == null) tmp.next = l1;
return dummyHead.next;
}
}

Complexity Analysis
- Time complexity : O(Nlogk) where k is the number of linked lists.
We can merge two sorted linked list in O(n) time where nn is the total number of nodes in two lists.
Sum up the merge process and we can get: O(Nlogk)
- Space complexity : O(logk) for recursive call stack
We can merge two sorted linked lists in O(1) space.
25. Reverse Nodes in k-Group
(https://leetcode.com/problems/reverse-
nodes-in-k-group/)
Hard

Given the head of a linked list, reverse the nodes of the list k at a time, and return the modified list.

k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes, in the end, should remain as it is.

You may not alter the values in the list's nodes, only nodes themselves may be changed.

Example 1:

Input: head = [1,2,3,4,5], k = 2


Output: [2,1,4,3,5]

Example 2:

Input: head = [1,2,3,4,5], k = 3


Output: [3,2,1,4,5]

Constraints:

The number of nodes in the list is n.


1 <= k <= n <= 5000
0 <= Node.val <= 1000

Follow-up: Can you solve the problem in O(1) extra memory space?

Approach
- Reusing reverse linkedlist function
- dummy root, it's next point to head of given linkedlist
- use two pointer curr and prev
- storing firstNode of every group
- index to find whether we have the complete group or not, as it determines whether we have reverse the that group or not
- if complete group is there prev.next points to the newly reversed list and
the prev points to firstNode(prev group, which is the last node of the prev group)
- else prev.next points to firstNode

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {

ListNode root = new ListNode(0, head); // dummy head


ListNode curr = head, prev = root;

while(curr != null) {
ListNode tail = curr; // keep track of the 1st element of each group
int listIndex = 0;

while(curr != null && listIndex < k) {


curr = curr.next;
listIndex++;
}
// listIndex != k means we have a group less than k size
if(listIndex != k)
prev.next = tail;
// less than k size so simply pointing prev to the
// first element of the group
else {
// reverse the group
prev.next = reverse(tail, k);
// prev will move to the first element(now the last) of the group
// so that next of it would have the reverse of the group
prev = tail;
}
}
return root.next;
}

private ListNode reverse(ListNode head, int k) {


ListNode curr = head, prev = null, next = null;

while(curr != null && k > 0) {


k--;
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
head = prev;
return head;
}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(1)

Resources
226. Invert Binary Tree
(https://leetcode.com/problems/invert-binary-
tree/)
Given the root of a binary tree, invert the tree, and return its root.

Example 1:
Input: root = [4,2,7,1,3,6,9]
Output: [4,7,2,9,6,3,1]

Example 2:
Input: root = [2,1,3]
Output: [2,3,1]

Example 3:
Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 100].


-100 <= Node.val <= 100

Approach
- Recursion
- store one pointer to right or left subtree
- swap left with right and make call recursively

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null)
return null;
TreeNode tmp = root.right;
root.right = invertTree(root.left);
root.left = invertTree(tmp);

return root;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(H), atmost height of tree would be the number of recursive call stack at a given point of time

104. Maximum Depth of Binary Tree


Given the root of a binary tree, return its maximum depth.

A binary tree's maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

Example 1:
Input: root = [3,9,20,null,null,15,7]
Output: 3

Example 2:
Input: root = [1,null,2]
Output: 2

Constraints:

The number of nodes in the tree is in the range [0, 104].


-100 <= Node.val <= 100

Approach
- Recursion
- if node = null return 0
- return 1 + max(leftHeight, rightHeight)

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null)
return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(H), height of the tree

543. Diameter of Binary Tree


Easy

Given the root of a binary tree, return the length of the diameter of the tree.

The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

The length of a path between two nodes is represented by the number of edges between them.

Example 1:

Input: root = [1,2,3,4,5]


Output: 3
Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3].

Example 2:

Input: root = [1,2]


Output: 1

Constraints:

The number of nodes in the tree is in the range [1, 104].


-100 <= Node.val <= 100
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int maxDiameter;
public int diameterOfBinaryTree(TreeNode root) {
maxDiameter = 0;
height(root);
return maxDiameter;
}

public int height(TreeNode root) {


if(root == null)
return -1;
int left = height(root.left);
int right = height(root.right);
int h = 1 + Math.max(left, right);
maxDiameter = Math.max(maxDiameter, left + right + 2);
return h;
}
}

110. Balanced Binary Tree


Easy

Given a binary tree, determine if it is height-balanced.

For this problem, a height-balanced binary tree is defined as:

a binary tree in which the left and right subtrees of every node differ in height by no more than 1.

Example 1:

Input: root = [3,9,20,null,null,15,7]


Output: true

Example 2:

Input: root = [1,2,2,3,3,null,null,4,4]


Output: false

Example 3:

Input: root = []
Output: true

Constraints:

The number of nodes in the tree is in the range [0, 5000].


-104 <= Node.val <= 104
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null)
return true;
int res = helper(root);
return res == -1?false:true;
}

private int helper(TreeNode root) {


if(root == null)
return 0;
int leftH = helper(root.left);
if(leftH == -1)
return -1;
int rightH = helper(root.right);
if(rightH == -1)
return -1;
return Math.abs(leftH - rightH)>1?-1:1+Math.max(leftH, rightH);
}
}

100. Same Tree


Easy

Given the roots of two binary trees p and q, write a function to check if they are the same or not.

Two binary trees are considered the same if they are structurally identical, and the nodes have the same value.

Example 1:

Input: p = [1,2,3], q = [1,2,3]


Output: true

Example 2:

Input: p = [1,2], q = [1,null,2]


Output: false

Example 3:

Input: p = [1,2,1], q = [1,1,2]


Output: false

Constraints:

The number of nodes in both trees is in the range [0, 100].


-104 <= Node.val <= 104
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q == null) return true;
if(p == null || q == null) return false;
return (p.val == q.val) && (isSameTree(p.left, q.left)) && (isSameTree(p.right,q.right));
}
}

572. Subtree of Another Tree


Easy

Given the roots of two binary trees root and subRoot, return true if there is a subtree of root with the same structure and node values of subRoot and false otherwise.

A subtree of a binary tree tree is a tree that consists of a node in tree and all of this node's descendants. The tree tree could also be considered as a subtree of itself.

Example 1:

Input: root = [3,4,5,1,2], subRoot = [4,1,2]


Output: true

Example 2:

Input: root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]


Output: false

Constraints:

The number of nodes in the root tree is in the range [1, 2000].
The number of nodes in the subRoot tree is in the range [1, 1000].
-104 <= root.val <= 104
-104 <= subRoot.val <= 104
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null && subRoot == null)
return true;
if(root == null || subRoot == null)
return false;

Queue<TreeNode> q = new LinkedList<>();


q.offer(root);

while(!q.isEmpty()) {
TreeNode node = q.poll();
if(node.val == subRoot.val) {
if(isSameTree(node, subRoot))
return true;
}
if(node.left != null)
q.offer(node.left);
if(node.right != null)
q.offer(node.right);
}

return false;
}

private boolean isSameTree(TreeNode p, TreeNode q) {


if(p == null && q == null)
return true;
if(p == null || q == null)
return false;
return (p.val == q.val) && (isSameTree(p.left, q.left)) && (isSameTree(p.right, q.right));
}
}

235. Lowest Common Ancestor of a Binary


Search Tree
Easy

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants
(where we allow a node to be a descendant of itself).”

Example 1:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.

Example 2:

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4


Output: 2
Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

Example 3: Input: root = [2,1], p = 2, q = 1 Output: 2

Constraints:

The number of nodes in the tree is in the range [2, 105].

-109 <= Node.val <= 109


All Node.val are unique.
p != q
p and q will exist in the BST.

Approach
- Recursion
- if root's val < p's val & q's val then reduce the problem to right subtree of root
- else if root's val > p's val & q's val then reduce the problem to left subtree of root
- else return root (if p's val or q's val equal to root's val or for p & q lies in different subtree of the root)

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/

class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null)
return null;
int val = root.val;
if(val < p.val && val < q.val)
return lowestCommonAncestor(root.right, p, q);
else if(val > p.val && val > q.val)
return lowestCommonAncestor(root.left, p, q);
return root;
}
}

Complexity Analysis
- Time Complexity: O(logN)
- Space Complexity: O(logN) recursive calls

102. Binary Tree Level Order Traversal


Medium

Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

Example 1:

Input: root = [3,9,20,null,null,15,7]


Output: [[3],[9,20],[15,7]]

Example 2:

Input: root = [1]


Output: [[1]]

Example 3:

Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 2000].


-1000 <= Node.val <= 1000

Approach
- Use Queue to insert the nodes
- for each level we'll poll node and check if left or right of it is not null then add left or right of it to the queue

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if(root == null)
return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);

while(!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currLevel = new ArrayList<>(levelSize);

for(int i = 0; i < levelSize; i++) {


TreeNode currNode = queue.poll();
currLevel.add(currNode.val);
if(currNode.left != null)
queue.offer(currNode.left);
if(currNode.right != null)
queue.offer(currNode.right);
}

result.add(currLevel);
}
return result;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N), max number of nodes in a level

199. Binary Tree Right Side View


Medium

Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.

Example 1:

Input: root = [1,2,3,null,5,null,4]


Output: [1,3,4]

Example 2:
Input: root = [1,null,3]
Output: [1,3]

Example 3:

Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 100].


-100 <= Node.val <= 100

Approach
- BFS
- Queue
- add the last element of each level into the result list

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null)
return res;

Queue<TreeNode> queue = new LinkedList<>();


queue.offer(root);

while(!queue.isEmpty()) {
int levelSize = queue.size();
for(int i = 0; i < levelSize; i++) {
TreeNode currNode = queue.poll();
if(currNode.left != null)
queue.offer(currNode.left);
if(currNode.right != null)
queue.offer(currNode.right);
if(i == levelSize-1)
res.add(currNode.val);
}
}

return res;
}
}
Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

1448. Count Good Nodes in Binary Tree


(https://leetcode.com/problems/count-good-
nodes-in-binary-tree/)
Medium

Share Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X.

Return the number of good nodes in the binary tree.

Example 1:

Input: root = [3,1,4,3,null,1,5]


Output: 4
Explanation: Nodes in blue are good.
Root Node (3) is always a good node.
Node 4 -> (3,4) is the maximum value in the path starting from the root.
Node 5 -> (3,4,5) is the maximum value in the path
Node 3 -> (3,1,3) is the maximum value in the path.

Example 2:

Input: root = [3,3,null,4,2]


Output: 3
Explanation: Node 2 -> (3, 3, 2) is not good, because "3" is higher than it.

Example 3:

Input: root = [1]


Output: 1
Explanation: Root is considered as good.

Constraints:

The number of nodes in the binary tree is in the range [1, 10^5].
Each node's value is between [-104, 104].

Approach
- check if the node is null
- if node.val >= previously send max, res will be one
- res += left Subtree call with updated max
- res += right Subtree call with updated max

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int goodNodes(TreeNode root) {
return goodNodes(root, Integer.MIN_VALUE);
}

public int goodNodes(TreeNode root, int max) {


if (root == null) return 0;
int res = root.val >= max ? 1 : 0;
res += goodNodes(root.left, Math.max(max, root.val));
res += goodNodes(root.right, Math.max(max, root.val));
return res;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(height)

98. Validate Binary Search Tree


Medium

Given the root of a binary tree, determine if it is a valid binary search tree (BST).

A valid BST is defined as follows:

The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys greater than the node's key. Both the left
and right subtrees must also be binary search trees.

Example 1:

Input: root = [2,1,3]


Output: true

Example 2:
Input: root = [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.

Constraints:

The number of nodes in the tree is in the range [1, 104].


-231 <= Node.val <= 231 - 1

Approach
- Recursion
- for each node's val we check whether it's in the range (min, max)
- if it fails we return false
- recursively call left and right subtree
- start with (-infinity, +infinity)
- left child -> (min, node.val)
- right child -> (node.val, max)

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isValidBST(TreeNode root) {
Long min = Long.MIN_VALUE;
Long max = Long.MAX_VALUE;

return helper(root, min, max);

private boolean helper(TreeNode root, Long min, Long max) {


if(root == null)
return true;
Long val = (long) root.val;
if(val <= min || val >= max)
return false;
return helper(root.left, min, val) && helper(root.right, val, max);
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(H), H : height of tree, recursive calls
230. Kth Smallest Element in a BST
Medium

Given the root of a binary search tree, and an integer k, return the kth smallest value (1-indexed) of all the values of the nodes in the tree.

Example 1:

Input: root = [3,1,4,null,2], k = 1


Output: 1

Example 2:

Input: root = [5,3,6,2,4,null,null,1], k = 3


Output: 3

Constraints:

The number of nodes in the tree is n.


1 <= k <= n <= 104
0 <= Node.val <= 104

Follow up: If the BST is modified often (i.e., we can do insert and delete operations) and
you need to find the kth smallest frequently, how would you optimize?

Approach
- Recursion
- inorder traversal gives nodes in sorted order of a BST
- ans[] -> store the answer and k in the second index
- decrement ans[1] until it become 0
- when it become zero, that node is the ans

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int kthSmallest(TreeNode root, int k) {
int[] ans = new int[2];
ans[1] = k;
inorder(root, ans);
return ans[0];
}

private void inorder(TreeNode root, int[] ans) {


if(root == null)
return;
inorder(root.left, ans);
ans[1]--;
if(ans[1] == 0)
ans[0] = root.val;
inorder(root.right, ans);
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

105. Construct Binary Tree from Preorder


and Inorder Traversal
(https://leetcode.com/problems/construct-
binary-tree-from-preorder-and-inorder-
traversal/)
Medium

Given two integer arrays preorder and inorder where preorder is the preorder traversal of a binary tree and inorder is the inorder traversal of the same tree, construct and return the
binary tree.

Example 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

Example 2:

Input: preorder = [-1], inorder = [-1]


Output: [-1]

Constraints:

1 <= preorder.length <= 3000


inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder and inorder consist of unique values.
Each value of inorder also appears in preorder.
preorder is guaranteed to be the preorder traversal of the tree.
inorder is guaranteed to be the inorder traversal of the tree.

Approach
- start and end index
- inorderMap: mapping of inorder array with the index
- if start > end the return null
- root -> node with the preorder val with the preorder index
- root.left -> recursive call, in range (start, inorderMap.get(val)-1)
- root.right -> recursive call, in range (inorderMap.get(val)+1, end)

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int preInd;
public TreeNode buildTree(int[] preorder, int[] inorder) {
preInd = 0;
int n = inorder.length;
int startIndex = 0, endIndex = n-1;
Map<Integer, Integer> inorderMap = new HashMap<>();
for(int i = 0; i < n; i++)
inorderMap.put(inorder[i], i);
return helper(preorder, inorder, startIndex, endIndex, inorderMap);
}

public TreeNode helper(int[] preorder, int[] inorder, int startIndex, int endIndex, Map<Integer, Integer> inorderMap) {
if(startIndex > endIndex)
return null;
int val = preorder[preInd++];
TreeNode root = new TreeNode(val);
root.left = helper(preorder, inorder, startIndex, inorderMap.get(val)-1, inorderMap);
root.right = helper(preorder, inorder, inorderMap.get(val)+1, endIndex, inorderMap);
return root;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

124. Binary Tree Maximum Path Sum


(https://leetcode.com/problems/binary-tree-
maximum-path-sum/)
Hard

A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most
once. Note that the path does not need to pass through the root.

The path sum of a path is the sum of the node's values in the path.

Given the root of a binary tree, return the maximum path sum of any non-empty path.

Example 1:
Input: root = [1,2,3]
Output: 6
Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.

Example 2:

Input: root = [-10,9,20,null,null,15,7]


Output: 42
Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.

Constraints:

The number of nodes in the tree is in the range [1, 3 * 104].


-1000 <= Node.val <= 1000

Approach
- compare max at each node
- left call, right call
- compare max with node's val+left val, node's val+right val, node's val+left val+right val
- return max of node's val, node's val+left val, node's val+right val

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int max;
public int maxPathSum(TreeNode root) {
max = -1001;
helper(root);
return max;
}

private int helper(TreeNode root) {


if(root == null)
return 0;
int val = root.val;
max = Math.max(max, val);
int lVal = helper(root.left);
int rVal = helper(root.right);
max = Math.max(max, Math.max(rVal+val, Math.max(lVal+val, lVal+rVal+val)));

return Math.max(rVal+val, Math.max(lVal+val, val));


}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(logn), recursive call stacks

297. Serialize and Deserialize Binary Tree (https://leetcode.com/problems/serialize-and-deserialize-binary-tree/)

Hard

Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network
connection link to be reconstructed later in the same or another computer environment.

Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a
binary tree can be serialized to a string and this string can be deserialized to the original tree structure.

Clarification: The input/output format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with
different approaches yourself.

Example 1:

Input: root = [1,2,3,null,null,4,5]


Output: [1,2,3,null,null,4,5]

Example 2:

Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 104].


-1000 <= Node.val <= 1000

Approach

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {

// Encodes a tree to a single string.


public String serialize(TreeNode root) {
StringBuilder result = new StringBuilder();
serialHelper(root, result);
return result.toString();
}

private void serialHelper(TreeNode root, StringBuilder result) {


if (root == null) {
return;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode curr = q.poll();
if (curr == null) {
result.append(",");
continue;
}
result.append(curr.val + ",");
q.offer(curr.left);
q.offer(curr.right);
}
}

// Decodes your encoded data to tree.


public TreeNode deserialize(String data) {
String[] nodes = data.split(",");

if (nodes[0] == "") return null;


return deserialHelper(nodes);
}

private TreeNode deserialHelper(String[] data) {


String val = data[0];
TreeNode root = new TreeNode(Integer.parseInt(val));
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
for (int i = 1; i < data.length; i += 2) {
TreeNode curr = q.poll();
if (!data[i].equals("")) {
curr.left = new TreeNode(Integer.parseInt(data[i]));
q.offer(curr.left);
}
if (i + 1 < data.length && !data[i + 1].equals("")) {
curr.right = new TreeNode(Integer.parseInt(data[i + 1]));
q.offer(curr.right);
}
}

return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

Tree
Patterns
BFS
DFS (Preorder, Inorder & Postorder)

BFS
Here are the steps of our algorithm:

1. Start by pushing the root node to the queue.


2. Keep iterating until the queue is empty.
3. In each iteration, first count the elements in the queue (let’s call it levelSize). We will have these many nodes in the current level.
4. Next, remove levelSize nodes from the queue and push their value in an array to represent the current level.
5. After removing each node from the queue, insert both of its children into the queue.
6. If the queue is not empty, repeat from step 3 for the next level.

Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

Example 1:

Input: root = [3,9,20,null,null,15,7]


Output: [[3],[9,20],[15,7]]

Example 2:

Input: root = [1]


Output: [[1]]

Example 3:
Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 2000].


-1000 <= Node.val <= 1000

import java.util.*;

class TreeNode {
int val;
TreeNode left;
TreeNode right;

TreeNode(int x) {
val = x;
}
};

class Main {
public static List<List<Integer>> traverse(TreeNode root) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if (root == null)
return result;

Queue<TreeNode> queue = new LinkedList<>();


queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>(levelSize);
for (int i = 0; i < levelSize; i++) {
TreeNode currentNode = queue.poll();
// add the node to the current level
currentLevel.add(currentNode.val);
// insert the children of current node in the queue
if (currentNode.left != null)
queue.offer(currentNode.left);
if (currentNode.right != null)
queue.offer(currentNode.right);
}
result.add(currentLevel);
}

return result;
}

public static void main(String[] args) {


TreeNode root = new TreeNode(12);
root.left = new TreeNode(7);
root.right = new TreeNode(1);
root.left.left = new TreeNode(9);
root.right.left = new TreeNode(10);
root.right.right = new TreeNode(5);
List<List<Integer>> result = Main.traverse(root);
System.out.println("Level order traversal: " + result);
}
}

Time complexity

The time complexity of the above algorithm is O(N), where ‘N’ is the total number of nodes in the tree. This is due to the fact that we traverse each node once.

Space complexity
The space complexity of the above algorithm will be O(N) as we need to return a list containing the level order traversal. We will also need O(N) space for the queue. Since we can
have a maximum of N/2 nodes at any level (this could happen only at the lowest level), therefore we will need O(N) space to store them in the queue.

DFS

Inorder(tree)

1. Traverse the left subtree, i.e., call Inorder(left-subtree)


2. Visit the root.
3. Traverse the right subtree, i.e., call Inorder(right-subtree)

import java.util.*;

class TreeNode {
int val;
TreeNode left;
TreeNode right;

TreeNode(int x) {
val = x;
}
};

class Main {
public static void traverse(TreeNode root) {

if (root == null)
return;
traverse(root.left);
System.out.println(root.val);
traverse(root.right);
}

public static void main(String[] args) {


TreeNode root = new TreeNode(12);
root.left = new TreeNode(7);
root.right = new TreeNode(1);
root.left.left = new TreeNode(9);
root.right.left = new TreeNode(10);
root.right.right = new TreeNode(5);
Main.traverse(root);
}
}

Time complexity

The time complexity of the above algorithm is O(N), where ‘N’ is the total number of nodes in the tree. This is due to the fact that we traverse each node once.

Space complexity

The space complexity of the above algorithm will be O(H) where H is the height of the tree, needed for recursive call stacks.
208. Implement Trie (Prefix Tree)
(https://leetcode.com/problems/implement-
trie-prefix-tree/)
Medium

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.

Implement the Trie class:

Trie() Initializes the trie object.


void insert(String word) Inserts the string word into the trie.
boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise.
boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.

Example 1:

Input
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output
[null, null, true, false, true, null, true]

Explanation
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // return True
trie.search("app"); // return False
trie.startsWith("app"); // return True
trie.insert("app");
trie.search("app"); // return True

Constraints:

1 <= word.length, prefix.length <= 2000


word and prefix consist only of lowercase English letters.

At most 3 * 104 calls in total will be made to insert, search, and startsWith.

Approach
26 links, end marker
[ , , ...26]
/ . . . 26 \ /..26..\

Solution
class Trie {
TrieNode root;
public Trie() {
root = new TrieNode();
}

public void insert(String word) {


TrieNode curr = root;
for(int i = 0; i < word.length(); i++) {
int ind = word.charAt(i)-'a';

if(curr.node[ind] != null) {
curr = curr.node[ind];
} else {
curr.node[ind] = new TrieNode();
curr = curr.node[ind];
}
}
curr.end = true;
}

public boolean search(String word) {


TrieNode curr = root;
for(int i = 0; i < word.length(); i++) {
int ind = word.charAt(i)-'a';
if(curr.node[ind] != null)
curr = curr.node[ind];
else
return false;
}
return curr.end == true;
}

public boolean startsWith(String prefix) {


TrieNode curr = root;
for(int i = 0; i < prefix.length(); i++) {
int ind = prefix.charAt(i)-'a';
if(curr.node[ind] != null)
curr = curr.node[ind];
else
return false;
}
return true;
}
}

class TrieNode {
TrieNode[] node = new TrieNode[26];
boolean end;
}

/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/

Complexity Analysis
- Time Complexity:
- insert: O(m), where m is the key length.
- search: O(m)
- startsWith: O(m)
- Space Complexity:
- insert: O(m)
- search: O(1)
- startsWith: O(1)

211. Design Add and Search Words Data


Structure ()
Medium

Design a data structure that supports adding new words and finding if a string matches any previously added string.

Implement the WordDictionary class:

WordDictionary() Initializes the object.


void addWord(word) Adds word to the data structure, it can be matched later.
bool search(word) Returns true if there is any string in the data structure that matches word or false otherwise. word may contain dots '.' where dots can be matched with
any letter.

Example:

Input
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
Output
[null,null,null,null,false,true,true,true]

Explanation
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // return False
wordDictionary.search("bad"); // return True
wordDictionary.search(".ad"); // return True
wordDictionary.search("b.."); // return True

Constraints:

1 <= word.length <= 25


word in addWord consists of lowercase English letters.
word in search consist of '.' or lowercase English letters.
There will be at most 3 dots in word for search queries.
At most 104 calls will be made to addWord and search.

Approach
- Use Trie

Solution
class WordDictionary {
TrieNode root;

public WordDictionary() {
root = new TrieNode();
}

public void addWord(String word) {


TrieNode curr = root;
for (int i = 0; i < word.length(); i++) {
int ind = word.charAt(i) - 'a';

if (curr.node[ind] != null) {
curr = curr.node[ind];
} else {
curr.node[ind] = new TrieNode();
curr = curr.node[ind];
}
}
curr.end = true;
}

public boolean search(String word) {


return trivialSearch(word, root);
}

private boolean trivialSearch(String word, TrieNode root) {


TrieNode curr = root;
if (word.length() == 0)
return curr.end;
boolean ans = false;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
if (ch == '.') {
for (int j = 0; j < 26; j++) {
if (curr.node[j] != null) {
ans = trivialSearch(word.substring(i + 1), curr.node[j]);
if (ans)
return true;
}
}
return false;
} else {
int ind = ch - 'a';
if (curr.node[ind] != null)
curr = curr.node[ind];
else
return false;
}
}
return curr.end;
}

class TrieNode {
TrieNode[] node = new TrieNode[26];
boolean end;
}
}

/**
* Your WordDictionary object will be instantiated and called as such:
* WordDictionary obj = new WordDictionary();
* obj.addWord(word);
* boolean param_2 = obj.search(word);
*/
Complexity Analysis
- Time Complexity:
- Space Complexity:

Resources
703. Kth Largest Element in a Stream
Easy

Design a class to find the kth largest element in a stream. Note that it is the kth largest element in the sorted order, not the kth distinct element.

Implement KthLargest class:

KthLargest(int k, int[] nums) Initializes the object with the integer k and the stream of integers nums.
int add(int val) Appends the integer val to the stream and returns the element representing the kth largest element in the stream.

Example 1:

Input
["KthLargest", "add", "add", "add", "add", "add"]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
Output
[null, 4, 5, 5, 8, 8]

Explanation

KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);


kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8
kthLargest.add(4); // return 8

Constraints:

1 <= k <= 104


0 <= nums.length <= 104
-104 <= nums[i] <= 104
-104 <= val <= 104
At most 104 calls will be made to add.
It is guaranteed that there will be at least k elements in the array when you search for the kth element.
class KthLargest {
PriorityQueue<Integer> pq;
static int k;
public KthLargest(int k, int[] nums) {
this.k = k;
pq = new PriorityQueue<>();
for(int num: nums)
pq.offer(num);

while(pq.size() > k)
pq.poll();
}

public int add(int val) {


pq.offer(val);
if(pq.size() > k)
pq.poll();

return pq.peek();
}
}

/**
* Your KthLargest object will be instantiated and called as such:
* KthLargest obj = new KthLargest(k, nums);
* int param_1 = obj.add(val);
*/

1046. Last Stone Weight


Easy

You are given an array of integers stones where stones[i] is the weight of the ith stone.

We are playing a game with the stones. On each turn, we choose the heaviest two stones and smash them together. Suppose the heaviest two stones have weights x and y with x <=
y. The result of this smash is:

If x == y, both stones are destroyed, and If x != y, the stone of weight x is destroyed, and the stone of weight y has new weight y - x. At the end of the game, there is at most one stone
left.

Return the smallest possible weight of the left stone. If there are no stones left, return 0.

Example 1:

Input: stones = [2,7,4,1,8,1]


Output: 1
Explanation:
We combine 7 and 8 to get 1 so the array converts to [2,4,1,1,1] then,
we combine 2 and 4 to get 2 so the array converts to [2,1,1,1] then,
we combine 2 and 1 to get 1 so the array converts to [1,1,1] then,
we combine 1 and 1 to get 0 so the array converts to [1] then that's the value of the last stone.

Example 2:

Input: stones = [1]


Output: 1

Constraints:

1 <= stones.length <= 30


1 <= stones[i] <= 1000
class Solution {
public int lastStoneWeight(int[] stones) {
PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> b-a); // max heap, Collections.reverseOrder()

for(int stone : stones)


pq.offer(stone);

while(pq.size() > 1) {
int x = pq.poll();
int y = pq.poll();
if(x != y) {
pq.offer(x-y); // abs not rqd as x would always be greater than equal to y
}
}

return pq.isEmpty()?0:pq.peek();
}
}

973. K Closest Points to Origin


Medium

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).

The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2).

You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in).

Example 1:

Input: points = [[1,3],[-2,2]], k = 1


Output: [[-2,2]]
Explanation:
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]].

Example 2:

Input: points = [[3,3],[5,-1],[-2,4]], k = 2


Output: [[3,3],[-2,4]]
Explanation: The answer [[-2,4],[3,3]] would also be accepted.

Constraints:

1 <= k <= points.length <= 4


-104 < xi, yi < 4

Approach
- Have a Max PriorityQueue based on euclidean distance
- add each point to PQ, if size > k then poll
- remaining points inside the PQ is the answer

Solution
class Solution {
public int[][] kClosest(int[][] points, int k) {
int[][] res = new int[k][2];
PriorityQueue<Point> pq = new PriorityQueue<Point>((a, b) -> new Double(b.dist).compareTo(new Double(a.dist)));

for(int[] point: points) {


pq.offer(new Point(point[0], point[1]));
if(pq.size() > k)
pq.poll();
}
int ind = 0;
while(!pq.isEmpty()) {
Point p = pq.poll();
res[ind][0] = p.x;
res[ind][1] = p.y;
ind++;
}
return res;
}
}

class Point {
int x;
int y;

double dist;

Point(int x, int y) {
this.x = x;
this.y = y;
dist = Math.pow(x*x + y*y, 0.5);
}
}

class Solution {
public int[][] kClosest(int[][] points, int k) {
int[][] res = new int[k][2];
PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> (b[0]*b[0] + b[1]*b[1]) - (a[0]*a[0] + a[1]*a[1]));

for(int[] point: points) {


pq.offer(point);
if(pq.size() > k)
pq.poll();
}
int ind = 0;
while(!pq.isEmpty()) {
int[] p = pq.poll();
res[ind][0] = p[0];
res[ind][1] = p[1];
ind++;
}
return res;
}
}

Complexity Analysis
- Time Complexity: O(nlogk) -> Adding to/removing from the heap (or priority queue)
only takes O(\log k) time when the size of the heap is capped at k elements.
- Space Complexity: O(k) -> The heap (or priority queue) will contain at most kk elements.

TODO: Quick Select, BinarySearch solution of this problem


215. Kth Largest Element in an Array
Medium

Given an integer array nums and an integer k, return the kth largest element in the array.

Note that it is the kth largest element in the sorted order, not the kth distinct element.

Example 1:

Input: nums = [3,2,1,5,6,4], k = 2


Output: 5

Example 2:

Input: nums = [3,2,3,1,2,4,5,5,6], k = 4


Output: 4

Constraints:

1 <= k <= nums.length <= 104


-104 <= nums[i] <= 104

Approach
- Min PriorityQueue
- if size becomes > k, poll
- after going through the array, we'll have the kth largest element as the min element in the PQ

Solution
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<>((a,b)->a-b);
for(int num : nums) {
pq.offer(num);
if(pq.size() > k)
pq.poll();
}
return pq.peek();
}
}

Complexity Analysis
- Time Complexity: O(nlogk)
- Space Complexity: O(k)

TODO
Quick Select

621. Task Scheduler


(https://leetcode.com/problems/task-
scheduler/)
Medium

Given a characters array tasks, representing the tasks a CPU needs to do, where each letter represents a different task. Tasks could be done in any order. Each task is done in one
unit of time. For each unit of time, the CPU could complete either one task or just be idle.

However, there is a non-negative integer n that represents the cooldown period between two same tasks (the same letter in the array), that is that there must be at least n units of
time between any two same tasks.

Return the least number of units of times that the CPU will take to finish all the given tasks.

Example 1:

Input: tasks = ["A","A","A","B","B","B"], n = 2


Output: 8
Explanation:
A -> B -> idle -> A -> B -> idle -> A -> B
There is at least 2 units of time between any two same tasks.

Example 2:

Input: tasks = ["A","A","A","B","B","B"], n = 0


Output: 6
Explanation: On this case any permutation of size 6 would work since n = 0.
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
And so on.

Example 3:

Input: tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2


Output: 16
Explanation:
One possible solution is
A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> idle -> idle -> A -> idle -> idle -> A

Constraints:

1 <= task.length <= 104


tasks[i] is upper-case English letter.
The integer n is in the range [0, 100].

Approach

Solution
class Solution {
public int leastInterval(char[] tasks, int n) {
int[] freq = new int[26];
int max = 0; // keep the max value
int maxCount = 0; // number of tasks with the max value

for(char task : tasks) {


freq[task-'A']++;
if(freq[task-'A'] == max) {
maxCount++;
} else if(freq[task-'A'] > max) {
max = freq[task-'A'];
maxCount = 1;
}
}

// number of blank sequence


int blankSeq = max - 1;
// number of blanks in each sequence
int blankSeqLen = n - (maxCount - 1);
// total empty slots
int emptySlots = blankSeq * blankSeqLen;
// non-max tasks count
int availableSlots = tasks.length - (maxCount * max);
// total amount of idle tiem
int idleTime = Math.max(0, emptySlots - availableSlots);

return tasks.length + idleTime;


}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(26)

295. Find Median from Data Stream


Hard

The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value and the median is the mean of the two middle values.

For example, for arr = [2,3,4], the median is 3. For example, for arr = [2,3], the median is (2 + 3) / 2 = 2.5. Implement the MedianFinder class:

MedianFinder() initializes the MedianFinder object. void addNum(int num) adds the integer num from the data stream to the data structure. double findMedian() returns the median of
all elements so far. Answers within 10-5 of the actual answer will be accepted.

Example 1:
Input
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
Output
[null, null, null, 1.5, null, 2.0]

Explanation
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1, 2]
medianFinder.findMedian(); // return 1.5 (i.e., (1 + 2) / 2)
medianFinder.addNum(3); // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

Constraints:

-105 <= num <= 105


There will be at least one element in the data structure before calling findMedian.
At most 5 * 104 calls will be made to addNum and findMedian.

Follow up:

If all integer numbers from the stream are in the range [0, 100], how would you optimize your solution?
If 99% of all integer numbers from the stream are in the range [0, 100], how would you optimize your solution?

Approach
- Use two heaps, one max heap(smallerList), other min heap(largerList)
- if smallList is not empty and first element is less than equal to num then add num to largeList
- else add num to smallList
- balance the size of both the heaps
- so that smallList never become more than 1 + size of largeList
- largeList's size always remain less or equal to the size of smallList

Solution
class MedianFinder {
int size;
PriorityQueue<Integer> smallList;
PriorityQueue<Integer> largeList;
public MedianFinder() {
smallList = new PriorityQueue<>(Collections.reverseOrder()); // maxHeap
largeList = new PriorityQueue<>(); // minHeap
}

public void addNum(int num) {

if(!smallList.isEmpty() && smallList.peek() <= num)


largeList.offer(num);
else
smallList.offer(num);
// size balance
if(smallList.size() > largeList.size()+1) {
int tmp = smallList.poll();
largeList.offer(tmp);
} else if(smallList.size() < largeList.size()) {
int tmp = largeList.poll();
smallList.offer(tmp);
}
size++;
}

public double findMedian() {

if(size % 2 != 0) {
return (double) smallList.peek();
} else {
return (double) (smallList.peek() + largeList.peek())/2.0;
}
}
}

/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/

Complexity Analysis
- Time Complexity:
- add operation: O(logN)
- find median operation: O(1)
- Space Complexity:
- add operation: O(1)
- find median operation: O(1)

Resources
78. Subsets
(https://leetcode.com/problems/subsets/)
Medium
Given an integer array nums of unique elements, return all possible subsets (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

Input: nums = [1,2,3]


Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

Example 2:

Input: nums = [0]


Output: [[],[0]]

Constraints:

1 <= nums.length <= 10


-10 <= nums[i] <= 10
All the numbers of nums are unique.

Approach
Backtracking:
- Either select the element or not
- recursive call
tmp.add(nums[index]);
backtrack(nums, index+1, tmp, ans);
tmp.remove(tmp.size()-1);
backtrack(nums, index+1, tmp, ans);

Solution
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<Integer> tmp = new ArrayList<>();
List<List<Integer>> ans = new ArrayList<>();
backtrack(nums, 0, tmp, ans);
return ans;
}

private void backtrack(int[] nums, int index, List<Integer> tmp, List<List<Integer>> ans) {
if(index > nums.length)
return;
if(index == nums.length) {
ans.add(new ArrayList<>(tmp));
return;
}
tmp.add(nums[index]);
backtrack(nums, index+1, tmp, ans);
tmp.remove(tmp.size()-1);
backtrack(nums, index+1, tmp, ans);
}
}

Complexity Analysis
- Time Complexity: O(2^N)
- Space Complexity: O(N)
39. Combination Sum
(https://leetcode.com/problems/combination-
sum/)
Medium

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.

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.

It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input.

Example 1:

Input: candidates = [2,3,6,7], target = 7


Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.

Example 2:

Input: candidates = [2,3,5], target = 8


Output: [[2,2,2,2],[2,3,3],[3,5]]

Example 3:

Input: candidates = [2], target = 1


Output: []

Constraints:

1 <= candidates.length <= 30


1 <= candidates[i] <= 200
All elements of candidates are distinct.
1 <= target <= 500

Approach
Backtrack:
- We can choose any of the element present in the array(n choices)
- In subsets, we have only 2 choices
- Looping through elements to create a n way recursive call
for(int i = index; i < nums.length; i++) {
tmp.add(nums[i]);
backtrack(nums, i, tmp, ans, target - nums[i]);
tmp.remove(tmp.size()-1);
}

Solution
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
backtrack(candidates, 0, new ArrayList<>(), res, target);

return res;
}

private void backtrack(int[] nums, int index, List<Integer> tmp, List<List<Integer>> ans, int target) {
if(index >= nums.length || target < 0)
return;
if(target == 0) {
ans.add(new ArrayList<>(tmp));
return;
}

for(int i = index; i < nums.length; i++) {


tmp.add(nums[i]);
backtrack(nums, i, tmp, ans, target - nums[i]);
tmp.remove(tmp.size()-1);
}
}
}

Complexity Analysis
- Time Complexity: O(N^N)
- Space Complexity: O(N)

46. Permutations
(https://leetcode.com/problems/permutations/)
Medium

Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.

Example 1:

Input: nums = [1,2,3]


Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

Example 2:

Input: nums = [0,1]


Output: [[0,1],[1,0]]

Example 3:

Input: nums = [1]


Output: [[1]]

Constraints:

1 <= nums.length <= 6


-10 <= nums[i] <= 10
All the integers of nums are unique.

Approach
- we have n choices initially, after each selection we can't take that element again
- take a mark array to keep track of what elements fron the nums array which are already select in the current path
for(int i = 0; i < nums.length; i++) {
if(mark[i] == false) {
mark[i] = true;
tmp.add(nums[i]);
backtrack(nums, mark, tmp, ans);
tmp.remove(tmp.size()-1);
mark[i] = false;
}
}

Solution
class Solution {
public List<List<Integer>> permute(int[] nums) {
boolean[] mark = new boolean[nums.length];
List<List<Integer>> ans = new ArrayList<>();
backtrack(nums, mark, new ArrayList<>(), ans);
return ans;
}

private void backtrack(int[] nums, boolean[] mark, List<Integer> tmp, List<List<Integer>> ans) {
if(tmp.size() == nums.length) {
ans.add(new ArrayList<>(tmp));
return;
}

for(int i = 0; i < nums.length; i++) {


if(mark[i] == false) {
mark[i] = true;
tmp.add(nums[i]);
backtrack(nums, mark, tmp, ans);
tmp.remove(tmp.size()-1);
mark[i] = false;
}
}
}
}

Complexity Analysis
- Time Complexity: O(N^N)
- Space Complexity: O(N)

90. Subsets II
(https://leetcode.com/problems/subsets-ii/)
Medium

Given an integer array nums that may contain duplicates, return all possible subsets (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

Input: nums = [1,2,2]


Output: [[],[1],[1,2],[1,2,2],[2],[2,2]]
Example 2:

Input: nums = [0]


Output: [[],[0]]

Constraints:

1 <= nums.length <= 10


-10 <= nums[i] <= 10

Approach
Ref: 1.Subsets (https://github.com/dipjul/NeetCode-150/blob/13aea2145a56c73ef5e1b8ea0b5cbe94745415e9/10.%20Backtracking/1.Subsets.md)

Same as Subsets
- to skip duplicates, before making the 2nd backtracking call we check the current and next element
- while those are equal we keep skipping to the next index

Solution
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(nums);
backtrack(nums, 0, new ArrayList<>(), ans);
return ans;
}

private void backtrack(int[] nums, int index, List<Integer> tmp, List<List<Integer>> ans) {
if(index > nums.length)
return;
if(index == nums.length) {
ans.add(new ArrayList<>(tmp));
return;
}
tmp.add(nums[index]);
backtrack(nums, index+1, tmp, ans);
tmp.remove(tmp.size()-1);
while(index+1 < nums.length && nums[index] == nums[index+1])
index++;
backtrack(nums, index+1, tmp, ans);
}
}

Complexity Analysis
- Time Complexity: O(2^N)
- Space Complexity: O(N)

40. Combination Sum II


(https://leetcode.com/problems/combination-
sum-ii/)
Medium

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.
Each number in candidates may only be used once in the combination.

Note: The solution set must not contain duplicate combinations.

Example 1:

Input: candidates = [10,1,2,7,6,1,5], target = 8


Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

Example 2:

Input: candidates = [2,5,2,1,2], target = 5


Output:
[
[1,2,2],
[5]
]

Constraints:

1 <= candidates.length <= 100


1 <= candidates[i] <= 50
1 <= target <= 30

Approach
Ref: 2.Combination Sum (https://github.com/dipjul/NeetCode-150/blob/76226b171098f898f8263acd34b2d3236d30f471/10.%20Backtracking/2.CombinationSum.md)

- We can't contain duplicates, so we can't use the same numbers twice


- Before making the 2nd recursive call, we check the current and next element
- while those are same we keep skipping them

Solution
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(candidates);
backtrack(candidates, 0, target, new ArrayList<>(), ans);
return ans;
}

private void backtrack(int[] nums, int index, int target, List<Integer> tmp, List<List<Integer>> ans) {
if(target == 0) {
ans.add(new ArrayList<>(tmp));
return;
}
if(index >= nums.length || target < 0)
return;

tmp.add(nums[index]);
backtrack(nums, index+1, target-nums[index], tmp, ans);
tmp.remove(tmp.size()-1);
while(index+1 < nums.length && nums[index] == nums[index+1])
index++;
backtrack(nums, index+1, target, tmp, ans);
}
}

Complexity Analysis
- Time Complexity: O(2^N)
- Space Complexity: O(N)

79. Word Search


(https://leetcode.com/problems/word-search/)
Medium

Given an m x n grid of characters board and a string word, return true if word exists in the grid.

The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than
once.

Example 1:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"


Output: true

Example 2:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"


Output: true

Example 3:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"


Output: false

Constraints:

m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board and word consists of only lowercase and uppercase English letters.

Follow up: Could you use search pruning to make your solution faster with a larger board?

Approach
Backtracking:
- 4 choices top, down, left & right moves
- mark the element we are accessing
- if we match the word, we get the ans

Solution
class Solution {
public boolean exist(char[][] board, String word) {
boolean[] res = new boolean[1];
boolean[][] mark = new boolean[board.length][board[0].length];
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
if(board[i][j] == word.charAt(0)) {
backtrack(board, word, mark, i, j, res);
if(res[0] == true)
return true;
}
}
}
return false;
}

private void backtrack(char[][] board, String word, boolean[][] mark, int i, int j, boolean[] res) {
if (word.length() == 0) {
res[0] = true;
return;
}
if (i >= board.length || j >= board[0].length || i < 0 || j < 0 || mark[i][j])
return;

if (word.charAt(0) == board[i][j]) {
mark[i][j] = true;
if (i >= 0) {
backtrack(board, word.substring(1), mark, i - 1, j, res);
}
if (j >= 0) {
backtrack(board, word.substring(1), mark, i, j - 1, res);
}
if (i < board.length) {
backtrack(board, word.substring(1), mark, i + 1, j, res);
}
if (j < board[0].length) {
backtrack(board, word.substring(1), mark, i, j + 1, res);
}
mark[i][j] = false;
}
}
}
// little optimized
class Solution {
boolean[][] visited;
public boolean exist(char[][] board, String word) {
int rows = board.length;
int cols = board[0].length;
visited = new boolean[rows][cols];

for(int i = 0; i < rows; i++) {


for(int j = 0; j < cols; j++) {
if(word.charAt(0) == board[i][j] && search(i, j, 0, word, board))
return true;
}
}
return false;
}

public boolean search(int i, int j, int index, String word, char[][] board) {
if(index == word.length())
return true;

if(i < 0 || i >= board.length || j < 0 || j >= board[0].length || word.charAt(index) != board[i][j] || visited[i][j]) {
return false;
}

visited[i][j] = true;

if(
search(i+1, j, index+1, word, board) ||
search(i-1, j, index+1, word, board) ||
search(i, j+1, index+1, word, board) ||
search(i, j-1, index+1, word, board)
) {
return true;
}

visited[i][j] = false;
return false;
}
}

Complexity Analysis
- Time Complexity: O(4^N)
- Space Complexity: O(N)

131. Palindrome Partitioning


(https://leetcode.com/problems/palindrome-
partitioning/)
Medium

Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s.

A palindrome string is a string that reads the same backward as forward.

Example 1:
Input: s = "aab"
Output: [["a","a","b"],["aa","b"]]

Example 2:

Input: s = "a"
Output: [["a"]]

Constraints:

1 <= s.length <= 16


s contains only lowercase English letters.

Approach
- backtracking
- go through all possible combination of substring
- if the substring is palindrome add it to the tmp result and backtrack
- if substring becomes empty then add tmp result to the result

Solution
class Solution {
public List<List<String>> partition(String s) {
List<List<String>> result = new ArrayList();
helper(s, new ArrayList<String>(), result);
return result;
}

public void helper(String s, List<String> step, List<List<String>> result) {


if(s == null || s.length() == 0) {
result.add(new ArrayList<>(step));
return;
}

for(int i = 1; i <= s.length(); i++) {


String temp = s.substring(0, i);
if(isPalindrome(temp)){
step.add(temp);
helper(s.substring(i, s.length()), step, result);
step.remove(step.size()-1);
}
}
return;
}

private boolean isPalindrome(String s) {


int start = 0, end = s.length()-1;
while(start <= end) {
if(s.charAt(start++) != s.charAt(end--))
return false;
}
return true;
}
}

Complexity Analysis
- Time Complexity: O(N*2^N)
- Space Complexity: O(N)

17. Letter Combinations of a Phone Number


(https://leetcode.com/problems/letter-
combinations-of-a-phone-number/)
Medium

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.

A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

Example 1:

Input: digits = "23"


Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]

Example 2:

Input: digits = ""


Output: []

Example 3:

Input: digits = "2"


Output: ["a","b","c"]

Constraints:

0 <= digits.length <= 4


digits[i] is a digit in the range ['2', '9'].

Approach
Backtrack:
char chs[] = mp.get(str.charAt(i));
for(int j = 0; j < chs.length; j++) {
recursive(str, i+1, s+chs[j], res); - stmt
}
stmt is equivalent to:
String s = s+chs[j];
recursive(str, i+1, s, res);
s = s.substring(0,s.length()-1);

Solution
class Solution {
Map<Character, char[]> mp;
public List<String> letterCombinations(String digits) {
if(digits.equals(""))
return new ArrayList<>();
mp = new HashMap<>();
mp.put('2', new char[]{'a', 'b', 'c'});
mp.put('3', new char[]{'d', 'e', 'f'});
mp.put('4', new char[]{'g', 'h', 'i'});
mp.put('5', new char[]{'j', 'k', 'l'});
mp.put('6', new char[]{'m', 'n', 'o'});
mp.put('7', new char[]{'p', 'q', 'r', 's'});
mp.put('8', new char[]{'t', 'u', 'v'});
mp.put('9', new char[]{'w', 'x', 'y', 'z'});
List<String> res = new ArrayList<>();
recursive(digits, 0, "", res);
return res;
}

private void recursive(String str, int i, String s, List<String> res) {


if(i > str.length())
return;
if(i == str.length()) {
res.add(s);
return;
}
char chs[] = mp.get(str.charAt(i));
for(int j = 0; j < chs.length; j++) {
recursive(str, i+1, s+chs[j], res);
}

}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

51. N-Queens
(https://leetcode.com/problems/n-queens/)
Hard

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.

Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively.

Example 1:

Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above

Example 2:

Input: n = 1
Output: [["Q"]]
Constraints:

1 <= n <= 9

Approach
- Backtracking
- Used 3 boolean arrays to mark row, left diagonal and rightdiagonal
- r-c could be -ve so we add the n-1 to it
- for each column,
- we go through each row and check whether we can place the queen or not
- if possible then we place the column index at the row[]
- recursive call to next column

Solution
// Do not try this at home
class Solution {
int[] row;
boolean[] rw, ld, rd;
public List<List<String>> solveNQueens(int n) {
row = new int[n];
rw = new boolean[n];
ld = new boolean[2*n-1];
rd = new boolean[2*n-1];
List<List<String>> ans = new ArrayList<>();
backtrack(0, n, ans);
return ans;
}

private void backtrack(int c, int n, List<List<String>> ans) {


if(c == n) {
ans.add(printer(row, n));
}
for(int r = 0; r < n; r++) {
if(!rw[r] && !ld[r - c + n - 1] && !rd[r + c]) {
rw[r] = ld[r - c + n - 1] = rd[r + c] = true;
row[c] = r;
backtrack(c+1,n,ans);
rw[r] = ld[r - c + n - 1] = rd[r + c] = false;
}
}
}

private List<String> printer(int[] row, int n) {


List<String> res = new ArrayList<>();
for(int i = 0; i < n; i++) {
StringBuilder sb = new StringBuilder(n);
for(int j = 0; j < n; j++) {
if(j == row[i])
sb.append('Q');
else
sb.append('.');
}
res.add(sb.toString());
}
return res;
}
}

Complexity Analysis
- Time Complexity: O(n!)
- Space Complexity: O(n^2), building the result

Resources
200. Number of Islands
(https://leetcode.com/problems/number-of-
islands/)
Medium

Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands.

An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

Input: grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1

Example 2:

Input: grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 3

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] is '0' or '1'.

Approach
- DFS
- number of time DFS called is the ans

Soluiton
class Solution {
public int numIslands(char[][] grid) {
boolean[][] mark = new boolean[grid.length][grid[0].length];
int res = 0;
for(int i = 0; i < grid.length; i++) {
for(int j = 0; j < grid[0].length; j++) {
if(!mark[i][j] && grid[i][j] == '1') {
res++;
dfs(grid, i, j, mark);
}
}
}
return res;
}

private void dfs(char[][] grid, int i, int j, boolean[][] mark) {


if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || mark[i][j] || grid[i][j] == '0')
return;
mark[i][j] = true;
dfs(grid, i+1, j, mark);
dfs(grid, i, j-1, mark);
dfs(grid, i-1, j, mark);
dfs(grid, i, j+1, mark);
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n*m)

133. Clone Graph


(https://leetcode.com/problems/clone-graph/)
Medium

Given a reference of a node in a connected undirected graph.

Return a deep copy (clone) of the graph.

Each node in the graph contains a value (int) and a list (List[Node]) of its neighbors.

class Node {
public int val;
public List<Node> neighbors;
}

Test case format:

For simplicity, each node's value is the same as the node's index (1-indexed). For example, the first node with val == 1, the second node with val == 2, and so on. The graph is
represented in the test case using an adjacency list.

An adjacency list is a collection of unordered lists used to represent a finite graph. Each list describes the set of neighbors of a node in the graph.

The given node will always be the first node with val = 1. You must return the copy of the given node as a reference to the cloned graph.

Example 1:
Input: adjList = [[2,4],[1,3],[2,4],[1,3]]
Output: [[2,4],[1,3],[2,4],[1,3]]
Explanation: There are 4 nodes in the graph.
1st node (val = 1)'s neighbors are 2nd node (val = 2) and 4th node (val = 4).
2nd node (val = 2)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).
3rd node (val = 3)'s neighbors are 2nd node (val = 2) and 4th node (val = 4).
4th node (val = 4)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).

Example 2:

Input: adjList = [[]]


Output: [[]]
Explanation: Note that the input contains one empty list. The graph consists of only one node with val = 1 and it does not have any n

Example 3:

Input: adjList = []
Output: []
Explanation: This an empty graph, it does not have any nodes.

Constraints:

The number of nodes in the graph is in the range [0, 100].


1 <= Node.val <= 100
Node.val is unique for each node.
There are no repeated edges and no self-loops in the graph.
The Graph is connected and all nodes can be visited starting from the given node.

Approach

Solution
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> neighbors;
public Node() {
val = 0;
neighbors = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
neighbors = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _neighbors) {
val = _val;
neighbors = _neighbors;
}
}
*/

class Solution {

public Node cloneGraph(Node node) {


if (node == null) return null;
Map<Integer, Node> mp = new HashMap<>();
Queue<Node> q = new LinkedList<>();
Set<Node> set = new HashSet<>();
q.offer(node);

while (!q.isEmpty()) {
Node curr = q.poll();
set.add(curr);

List<Node> nei = curr.neighbors;


ArrayList<Node> newNei = new ArrayList<>();
for (Node n : nei) {
if (!set.contains(n)) q.offer(n);
if (!mp.containsKey(n.val)) {
Node newN = new Node(n.val);
mp.put(newN.val, newN);
newNei.add(newN);
} else {
Node newN = mp.get(n.val);
newNei.add(newN);
}
}
if (!mp.containsKey(curr.val)) {
Node newNode = new Node(curr.val, newNei);
mp.put(curr.val, newNode);
} else {
Node newNode = mp.get(curr.val);
newNode.neighbors = newNei;
mp.put(curr.val, newNode);
}
}

return mp.get(node.val);
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n+m)

695. Max Area of Island


(https://leetcode.com/problems/max-area-of-
island/)
Medium

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.

The area of an island is the number of cells with a value 1 in the island.

Return the maximum area of an island in grid. If there is no island, return 0.

Example 1:

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]]
Output: 6
Explanation: The answer is not 11, because the island must be connected 4-directionally.

Example 2:

Input: grid = [[0,0,0,0,0,0,0,0]]


Output: 0

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j] is either 0 or 1.

Approach
- DFS
- count number of dfs call inside

Solution
class Solution {
public int maxAreaOfIsland(int[][] grid) {
boolean[][] mark = new boolean[grid.length][grid[0].length];
int res = 0;

for (int i = 0; i < grid.length; i++) {


for (int j = 0; j < grid[0].length; j++) {
if (!mark[i][j] && grid[i][j] == 1) {
res = Math.max(res, dfs(grid, i, j, mark));
}
}
}
return res;
}

private int dfs(int[][] grid, int i, int j, boolean[][] mark) {


if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || mark[i][j] || grid[i][j] == 0) {
return 0;
}
mark[i][j] = true;
return (1 + dfs(grid, i + 1, j, mark) +
dfs(grid, i, j - 1, mark) +
dfs(grid, i - 1, j, mark) +
dfs(grid, i, j + 1, mark));
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n*m)

417. Pacific Atlantic Water Flow ()


Medium

There is an m x n rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island's left and top edges, and the Atlantic Ocean
touches the island's right and bottom edges.

The island is partitioned into a grid of square cells. You are given an m x n integer matrix heights where heights[r][c] represents the height above sea level of the cell at coordinate (r,
c).

The island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell's height is less than or equal to the current
cell's height. Water can flow from any cell adjacent to an ocean into the ocean.

Return a 2D list of grid coordinates result where result[i] = [ri, ci] denotes that rain water can flow from cell (ri, ci) to both the Pacific and Atlantic oceans.

Example 1:

Input: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]


Output: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]

Example 2:

Input: heights = [[2,1],[1,2]]


Output: [[0,0],[0,1],[1,0],[1,1]]

Constraints:

m == heights.length
n == heights[r].length
1 <= m, n <= 200
0 <= heights[r][c] <= 105

Approach
- dfs: start from all the boundary along with respective set for pacific and atlantic
- intersection of both set

Solution
class Solution {

public List<List<Integer>> pacificAtlantic(int[][] heights) {


List<List<Integer>> res = new ArrayList<>();
Set<String> pacific = new HashSet<>();
Set<String> atlantic = new HashSet<>();
int rows = heights.length, cols = heights[0].length;
for (int i = 0; i < cols; i++) {
dfs(heights, 0, i, pacific, heights[0][i]);
dfs(heights, rows-1, i, atlantic, heights[rows-1][i]);
}
for (int i = 0; i < rows; i++) {
dfs(heights, i, 0, pacific, heights[i][0]);
dfs(heights, i, cols-1, atlantic, heights[i][cols-1]);
}
pacific.retainAll(atlantic);
for (String s : pacific) {
String[] arr = s.split(",");
List<Integer> a = new ArrayList<>();
a.add(Integer.parseInt(arr[0]));
a.add(Integer.parseInt(arr[1]));
res.add(a);
}
return res;
}

private void dfs(int[][] grid, int i, int j, Set<String> visited, int prev) {
if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] < prev || visited.contains(i + "," + j)) return;

visited.add(i + "," + j);


dfs(grid, i, j - 1, visited, grid[i][j]);
dfs(grid, i, j + 1, visited, grid[i][j]);
dfs(grid, i - 1, j, visited, grid[i][j]);
dfs(grid, i + 1, j, visited, grid[i][j]);
}
}

Complexity Analysis
- Time Complexity: O(m*n)
- Space Complexity: O(m*n)

130. Surrounded Regions


(https://leetcode.com/problems/surrounded-
regions/)
Medium

Given an m x n matrix board containing 'X' and 'O', capture all regions that are 4-directionally surrounded by 'X'.

A region is captured by flipping all 'O's into 'X's in that surrounded region.

Example 1:

Input: board = [
["X","X","X","X"],
["X","O","O","X"],
["X","X","O","X"],
["X","O","X","X"]]
Output: [
["X","X","X","X"],
["X","X","X","X"],
["X","X","X","X"],
["X","O","X","X"]]
Explanation: Surrounded regions should not be on the border, which means that any 'O' on the border of the board are not flipped to

Example 2:

Input: board = [["X"]]


Output: [["X"]]

Constraints:

m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j] is 'X' or 'O'.

Approach
- start dfs from all the boundaries
- mark all the cells that can be reach from the dfs as 1
- all those which were not marked 1 and in give board is "0", set them "X"

Solution
class Solution {
public void solve(char[][] board) {
int[][] mark = new int[board.length][board[0].length];
for (int j = 0; j < board[0].length; j++) {
if (board[0][j] == 'O')
dfs(board, 0, j, mark);
}
for (int j = 0; j < board[0].length; j++) {
if (board[board.length - 1][j] == 'O')
dfs(board, board.length - 1, j, mark);
}
for (int j = 0; j < board.length; j++) {
if (board[j][0] == 'O')
dfs(board, j, 0, mark);
}
for (int j = 0; j < board.length; j++) {
if (board[j][board[0].length - 1] == 'O')
dfs(board, j, board[0].length - 1, mark);
}
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
if(mark[i][j] == 0 && board[i][j] == 'O')
board[i][j] = 'X';
}
}
}

private void dfs(char[][] grid, int i, int j, int[][] mark) {


if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || mark[i][j] == 1 || grid[i][j] == 'X')
return;
mark[i][j] = 1;
dfs(grid, i, j - 1, mark);
dfs(grid, i, j + 1, mark);
dfs(grid, i - 1, j, mark);
dfs(grid, i + 1, j, mark);
}
}

Complexity Analysis
- Time Complexity: O(m*n)
- Space Complexity: O(m*n)

994. Rotting Oranges


(https://leetcode.com/problems/rotting-
oranges/)
Medium

You are given an m x n grid where each cell can have one of three values:

0 representing an empty cell,


1 representing a fresh orange, or
2 representing a rotten orange.

Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten.

Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1.
Example 1:

Input: grid = [[2,1,1],[1,1,0],[0,1,1]]


Output: 4

Example 2:

Input: grid = [[2,1,1],[0,1,1],[1,0,1]]


Output: -1
Explanation: The orange in the bottom left corner (row 2, column 0) is never rotten, because rotting only happens 4-directionally.

Example 3:

Input: grid = [[0,2]]


Output: 0
Explanation: Since there are already no fresh oranges at minute 0, the answer is just 0.

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 10
grid[i][j] is 0, 1, or 2.

Approaches
- Implementation of Topological Sort
- Insert all the rotten tomatoes inside the queue as sources
- have an fresh tomato count
- while sources queue not empty,
- we pull one pos at a time and check it's neighbour if there is any fresh tomato
- if we found fresh tomato, we make it rotten and add to our sources queue, decrement the fresh count
- for each run of while loop,
- we check if the sources is not empty, increment the time

Solution
class Solution {
public int orangesRotting(int[][] grid) {
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1} };
Queue<int[]> q = new LinkedList<>();
int n = grid.length, m = grid[0].length;
int countFresh = 0;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(grid[i][j] == 1)
countFresh++;
if(grid[i][j] == 2)
q.offer(new int[]{i, j});
}
}

if(countFresh == 0) return 0;
int time = 0;
while(!q.isEmpty()) {
int size = q.size();
for(int i = 0; i < size; i++) {
int[] currPos = q.poll();
int currI = currPos[0], currJ = currPos[1];
for(int[] dir : dirs) {
if(currI+dir[0] < 0 || currJ+dir[1] < 0 || currI+dir[0] >= n || currJ+dir[1] >= m || grid[currI+dir[0]][currJ+dir
continue;
if(grid[currI+dir[0]][currJ+dir[1]] == 1) {
grid[currI+dir[0]][currJ+dir[1]] = 2;
q.offer(new int[]{currI+dir[0], currJ+dir[1]});
countFresh--;
}
}

}
if(!q.isEmpty())
time++;

}
return countFresh!=0?-1:time;
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n*m)

663 · Walls and Gates


Medium

Description You are given a m x n 2D grid initialized with these three possible values.

-1 : A wall or an obstacle.

0 : A gate.

INF - Infinity means an empty room. We use the value 2^31 - 1 = 2147483647 to represent INF as you may assume that the distance to a gate is less than 2147483647.

Fill each empty room with the distance to its nearest gate. If it is impossible to reach a Gate, that room should remain filled with INF

Example 1
Input:
[
[2147483647,-1,0,2147483647],
[2147483647,2147483647,2147483647,-1],
[2147483647,-1,2147483647,-1],
[0,-1,2147483647,2147483647]]
Output:
[
[3,-1,0,1],
[2,2,1,-1],
[1,-1,2,-1],
[0,-1,3,4]]

Explanation:
the 2D grid is:
INF -1 0 INF
INF INF INF -1
INF -1 INF -1
0 -1 INF INF
the answer is:
3 -1 0 1
2 2 1 -1
1 -1 2 -1
0 -1 3 4

Example 2

Input:
[
[0,-1],
[2147483647,2147483647]]
Output:
[
[0,-1],
[1,2]]

Tags

Breadth First Search/BFS

Company

Facebook
Google

Approach
- BFS or Topological Sort
- Start from the grid having 0 (add them to a queue)
- while queue is not empty:
- if neighbour of current position is INF, change it to 1+val coming from the queue and add it to queue
- otherwise take min of neighour's value and 1+val

Solution
public class Solution {
/**
* @param rooms: m x n 2D grid
* @return: nothing
*/
public void wallsAndGates(int[][] grid) {
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1} };
Queue<int[]> q = new LinkedList<>();
int n = grid.length, m = grid[0].length;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(grid[i][j] == 0)
q.offer(new int[]{i, j});
}
}

while (!q.isEmpty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
int[] currPos = q.poll();
int currI = currPos[0], currJ = currPos[1];
int val = grid[currI][currJ];
for (int[] dir : dirs) {
if (currI + dir[0] < 0 || currJ + dir[1] < 0 || currI + dir[0] >= n || currJ + dir[1] >= m
|| grid[currI + dir[0]][currJ + dir[1]] == -1)
continue;
else if (grid[currI + dir[0]][currJ + dir[1]] == 2147483647) {
grid[currI + dir[0]][currJ + dir[1]] = 1 + val;
q.offer(new int[] { currI + dir[0], currJ + dir[1] });
} else {
grid[currI + dir[0]][currJ + dir[1]] = Math.min(grid[currI + dir[0]][currJ + dir[1]], 1 + val);
}
}

}
}
}
}

Complexity Analysis
- Time Complexity: O(k*n*m)
- Space Complexity: O(n*m)

207. Course Schedule


(https://leetcode.com/problems/course-
schedule/)
Medium

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you
must take course bi first if you want to take course ai.

For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1. Return true if you can finish all courses. Otherwise, return false.

Example 1:
Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.

Example 2:

Input: numCourses = 2, prerequisites = [[1,0],[0,1]]


Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible

Constraints:

1 <= numCourses <= 2000


0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
All the pairs prerequisites[i] are unique.

Approach
Topological Sort
- Have look a the solution for understanding the Topological sort algorithm

Solution
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// List<Integer> result = new ArrayList<>();
int result = 0;

// 1. Initialize the graph


Map<Integer, List<Integer>> graph = new HashMap<>();
Map<Integer, Integer> inDegree = new HashMap<>();

for(int i = 0; i < numCourses; i++) {


graph.put(i, new ArrayList<>());
inDegree.put(i, 0);
}

// 2. Build the graph


for(int i = 0; i < prerequisites.length; i++) {
int child = prerequisites[i][0], parent = prerequisites[i][1];
graph.get(parent).add(child);
inDegree.put(child, inDegree.get(child)+1);
}

// 3. Add all the sources(i.e, vertices with in-degree 0) to a queue


Queue<Integer> sources = new LinkedList<>();
for(Map.Entry<Integer, Integer> entry: inDegree.entrySet())
if(entry.getValue() == 0)
sources.offer(entry.getKey());

// 4. For each source, add it to the result, subtract 1 from all of it's children's in-degree
// & add if any child has in-degree 0, add it to sources queue
while(!sources.isEmpty()) {
int vertex = sources.poll();
result++;
for(int child:graph.get(vertex)) {
inDegree.put(child, inDegree.get(child)-1);
if(inDegree.get(child) == 0)
sources.offer(child);
}
}

// 5. If size of result equal to numCourses then return true else return false
return result == numCourses;
}
}

Complexity Analysis
- Time Complexity: O(m*n)
- Space Complexity: O(m*n)

210. Course Schedule II


(https://leetcode.com/problems/course-
schedule-ii/)
Medium

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you
must take course bi first if you want to take course ai.
For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1. Return the ordering of courses you should take to finish all courses. If there are many valid
answers, return any of them. If it is impossible to finish all courses, return an empty array.

Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]


Output: [0,1]
Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order

Example 2:

Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]


Output: [0,2,1,3]
Explanation: There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 a
So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3].

Example 3:

Input: numCourses = 1, prerequisites = []


Output: [0]

Constraints:

1 <= numCourses <= 2000


0 <= prerequisites.length <= numCourses * (numCourses - 1)
prerequisites[i].length == 2
0 <= ai, bi < numCourses
ai != bi
All the pairs [ai, bi] are distinct.

Aprroach
Ref: 8. Course Schedule (https://github.com/dipjul/NeetCode-150/blob/1db1597fe0d82d4741ecd5ee3600aea518824bb1/11.%20Graphs/8.CourseSchedule.md)

Topologicalo Sort

Solution
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
List<Integer> result = new ArrayList<>();

// 1. Initialize the graph


Map<Integer, List<Integer>> graph = new HashMap<>();
Map<Integer, Integer> inDegree = new HashMap<>();

for(int i = 0; i < numCourses; i++) {


graph.put(i, new ArrayList<>());
inDegree.put(i, 0);
}

// 2. Build the graph


for(int i = 0; i < prerequisites.length; i++) {
int child = prerequisites[i][0], parent = prerequisites[i][1];
graph.get(parent).add(child);
inDegree.put(child, inDegree.get(child)+1);
}

// 3. Add all the sources(i.e, vertices with in-degree 0) to a queue


Queue<Integer> sources = new LinkedList<>();
for(Map.Entry<Integer, Integer> entry: inDegree.entrySet())
if(entry.getValue() == 0)
sources.offer(entry.getKey());

// 4. For each source, add it to the result, subtract 1 from all of it's children's in-degree
// & add if any child has in-degree 0, add it to sources queue
while(!sources.isEmpty()) {
int vertex = sources.poll();
result.add(vertex);
for(int child:graph.get(vertex)) {
inDegree.put(child, inDegree.get(child)-1);
if(inDegree.get(child) == 0)
sources.offer(child);
}
}

if(result.size() != numCourses)
return new int[]{};
return result.stream().mapToInt(i->i).toArray();
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n*m)

684. Redundant Connection


(https://leetcode.com/problems/redundant-
connection/)
Medium

In this problem, a tree is an undirected graph that is connected and has no cycles.
You are given a graph that started as a tree with n nodes labeled from 1 to n, with one additional edge added. The added edge has two different vertices chosen from 1 to n, and was
not an edge that already existed. The graph is represented as an array edges of length n where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the graph.

Return an edge that can be removed so that the resulting graph is a tree of n nodes. If there are multiple answers, return the answer that occurs last in the input.

Example 1:

Input: edges = [[1,2],[1,3],[2,3]]


Output: [2,3]

Example 2:

Input: edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]


Output: [1,4]

Constraints:

n == edges.length
3 <= n <= 1000
edges[i].length == 2
1 <= ai < bi <= edges.length
ai != bi
There are no repeated edges.
The given graph is connected.

Approach
Ref: Union Find (https://github.com/dipjul/NeetCode-150/blob/e7002953ae531e571f4d148f591a265dda256d7d/Algorithms/1.UnionFind.md)

- Using UnionFind we'll check whether the nodes of the given edge are already connected without using the current edge
- if already connect which means this edge is reduntant
- else union the nodes of the current edge

Solution
class Solution {
int[] parent;
int[] rank;
public int[] findRedundantConnection(int[][] edges) {
int n = edges.length;
init(n);
int[] res = new int[2];
for(int[] edge:edges) {
if(!union(edge[0], edge[1]))
res = new int[]{ edge[0], edge[1] };
}
return res;
}

private void init(int n) {


parent = new int[n+1];
rank = new int[n+1];
for(int i = 1; i <= n; i++) {
parent[i] = i;
rank[i] = 1;
}
}

private int find(int val) {


while(val != parent[val]) {
parent[val] = parent[parent[val]];
val = parent[val];
}
return parent[val];
}

private boolean union(int x, int y) {


int p1 = find(x);
int p2 = find(y);
if(p1 == p2)
return false;
if(rank[p1] > rank[p2]) {
parent[p2] = p1;
rank[p1] += rank[p2];
} else {
parent[p1] = p2;
rank[p2] += rank[p1];
}
return true;
}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(n)

323. Number of Connected Components in


an Undirected Graph
(https://leetcode.com/problems/number-of-
connected-components-in-an-undirected-
graph/)
Medium

Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to find the number of connected components in an undirected
graph.

Example 1:

Input: n = 5 and edges = [[0, 1], [1, 2], [3, 4]]

0 3
| |
1 --- 2 4

Output: 2

Example 2:

Input: n = 5 and edges = [[0, 1], [1, 2], [2, 3], [3, 4]]

0 4
| |
1 --- 2 --- 3

Output: 1

Note: You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Approach
1. DFS
- Number of times dfs called is equal to the number of components
2. Union Find
- Take number of nodes as the number of components initially
- if the nodes of an edge are not already connected then decrease the count and union the nodes

Solution
// 1. DFS
class Solution {
public int countComponents(int n, int[][] edges) {
HashMap<Integer, List<Integer>> graph = new HashMap<Integer, List<Integer>>();
boolean[] visited = new boolean[n];

int count = 0;
// Step - 1 Build the graph
for(int i = 0; i < n; i++) {
graph.put(i, new ArrayList<Integer>());
}

for (int i = 0; i < edges.length; i++){


// Make Undirected Graph
graph.get(edges[i][0]).add(edges[i][1]);
graph.get(edges[i][1]).add(edges[i][0]);
}

// Step -2 run algorithm


for (int i = 0; i < n; i++) {
if(!visited[i]) {
count++;
dfs(i, graph, visited);
}
}
return count;

private void dfs(int at, HashMap<Integer, List<Integer>> graph, boolean[] visited) {


visited[at] = true;
for(Integer child: graph.get(at)) {
if(!visited[child]) {
dfs(child, graph, visited);
}
}
}
}
// 2. Union Find
class Solution {
public int countComponents(int n, int[][] edges) {
UnionFind uf = new UnionFind(n);
int count = n;
for(int[] edge: edges) {
if(uf.isConnected(edge[0], edge[1]))
continue;
count--;
uf.union(edge[0], edge[1]);
}
return count;
}
}
class UnionFind {
int[] root;
int[] rank;

UnionFind(int size) {
root = new int[size];
rank = new int[size];

for(int i = 0; i < size; i++) {


root[i] = i;
rank[i] = 0;
}
}

public int find(int x) {


if(x == root[x])
return x;
return root[x] = find(root[x]);
}

public void union(int x, int y) {


int rootX = find(x);
int rootY = find(y);

if(rootX != rootY) {
if(rank[rootX] > rank[rootY])
root[rootY] = rootX;
else if(rank[rootX] < rank[rootY])
root[rootX] = rootY;
else {
root[rootY] = rootX;
rank[rootX]++;
}
}
}

public boolean isConnected(int x, int y) {


return find(x) == find(y);
}
}

Complexity Analysis
- Time Complexity:
- DFS: O(n)
- Union Find: O(n)
- Space Complexity:
- DFS: O(n+m)
- Union Find: O(n)
178 · Graph Valid Tree
(https://www.lintcode.com/problem/178/)
Medium

Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.

You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Example 1:

Input: n = 5 edges = [[0, 1], [0, 2], [0, 3], [1, 4]]
Output: true.

Example 2:

Input: n = 5 edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]]
Output: false.

Approach
- check if number of edges is equal to n-1 or not
- if not equal return false;
- do dfs and put it in a set, size of set should be equal to number of nodes

Solution
public class Solution {
/**
* @param n: An integer
* @param edges: a list of undirected edges
* @return: true if it's a valid tree, or false
*/
public boolean validTree(int n, int[][] edges) {
// write your code here
if(edges.length != n-1)
return false;
if(n == 0 || n == 1)
return true;
Set<Integer> mark = new HashSet<>();
Map<Integer, List<Integer>> graph = new HashMap<>();

for(int i = 0; i < edges.length; i++) {


int n1 = edges[i][0];
int n2 = edges[i][1];
List<Integer> arr1 = graph.getOrDefault(n1, new ArrayList<>());
arr1.add(n2);
List<Integer> arr2 = graph.getOrDefault(n2, new ArrayList<>());
arr2.add(n1);
graph.put(n1, arr1);
graph.put(n2, arr2);
}
dfs(graph, 0, mark);
return mark.size() == n;
}

private void dfs(Map<Integer, List<Integer>> graph, int i, Set<Integer> mark) {


mark.add(i);
for(int node : graph.get(i)) {
if(!mark.contains(node))
dfs(graph, node, mark);
}
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n+m)

127. Word Ladder


(https://leetcode.com/problems/word-ladder/)
Hard

A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:

Every adjacent pair of words differs by a single letter.


Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList.
sk == endWord

Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence from beginWord to endWord, or 0 if no
such sequence exists.

Example 1:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is 5 words long.

Example 2:

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]


Output: 0
Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.

Constraints:

1 <= beginWord.length <= 10


endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord, endWord, and wordList[i] consist of lowercase English letters.
beginWord != endWord
All the words in wordList are unique.

Approach
- Add the begin word to the wordList
- Create a graph such that neighbours of each node is differ by one character
- eg. fog would be neighbour of cog
- if endWord not present in the graph return 0
- How to find if two words are neighbour?
- Loop through the two words, whenever it differs increment the count
- if count == 1 return true else false
- Start bfs search from the beginWord untill we reach the endWord
- Keep count of distance from beginWord, return distance

Solution
class Solution {

public int ladderLength(String beginWord, String endWord, List<String> wordList) {


Map<String, List<String>> graph = new HashMap<>();
wordList.add(0, beginWord);
for (int i = 0; i < wordList.size(); i++) {
for (int j = i + 1; j < wordList.size(); j++) {
String s1 = wordList.get(i);
String s2 = wordList.get(j);
if (differByOne(s1, s2)) {
List<String> arr1 = graph.getOrDefault(s1, new ArrayList<>());
arr1.add(s2);
List<String> arr2 = graph.getOrDefault(s2, new ArrayList<>());
arr2.add(s1);
graph.put(s1, arr1);
graph.put(s2, arr2);
}
}
}

if (!graph.containsKey(endWord)) return 0;

Queue<String> q = new LinkedList<>();

q.offer(beginWord);

if (q.size() == 0) return 0;
Set<String> visited = new HashSet<>();
int pathSize = 0;
while (!q.isEmpty()) {
int size = q.size();
pathSize++;
for (int i = 0; i < size; i++) {
String s1 = q.poll();
visited.add(s1);
if (s1.equals(endWord)) return pathSize;
for (String s : graph.get(s1)) {
if (!visited.contains(s)) q.offer(s);
}
}
}

return 0;
}

private boolean differByOne(String word1, String word2) {


if (word1.length() != word2.length()) return false;
int n = word1.length();
int count = 0;
for (int i = 0; i < n; i++) {
if (word1.charAt(i) != word2.charAt(i)) {
count++;
if (count > 1) return false;
}
}
return count == 1;
}
}

Complexity Analysis
- Time Complexity: O(n*n)
- Space Complexity: O(n+m)

Resources
1584. Min Cost to Connect All Points
(https://leetcode.com/problems/min-cost-to-
connect-all-points/)
Medium

You are given an array points representing integer coordinates of some points on a 2D-plane, where points[i] = [xi, yi].

The cost of connecting two points [xi, yi] and [xj, yj] is the manhattan distance between them: |xi - xj| + |yi - yj|, where |val| denotes the absolute value of val.

Return the minimum cost to make all points connected. All points are connected if there is exactly one simple path between any two points.

Example 1:

Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]]


Output: 20
Explanation:

We can connect the points as shown above to get the minimum cost of 20.
Notice that there is a unique path between every pair of points.

Example 2:

Input: points = [[3,12],[-2,5],[-4,1]]


Output: 18

Constraints:

1 <= points.length <= 1000 -106 <= xi, yi <= 106 All pairs (xi, yi) are distinct.

Approach
- MST
- PRIMS Algorithm

Solution
class Solution {
class Edge {
int[] x;
int[] y;
int cost;
Edge(int[] x, int[] y) {
this.x = x;
this.y = y;
this.cost = Math.abs(x[0]-y[0])+Math.abs(x[1]-y[1]);
}
}

public int minCostConnectPoints(int[][] points) {


// MST
// prims
int cost = 0;
Set<int[]> visited = new HashSet<>(); // to store the visited vertices
int numOfVertices = points.length;
PriorityQueue<Edge> q = new PriorityQueue<>((a, b)->a.cost-b.cost); // to store the edges based on cost
visited.add(points[0]);
Queue<int[]> source = new LinkedList<>(); // sources to determine which node to relax
source.add(points[0]);
while(visited.size() != numOfVertices) { // till all nodes are visited or n-1 edges are added
int[] src = source.poll();
putEdges(src, visited, points, q); // put hte edges to the queue
while(!q.isEmpty()) {
Edge edge = q.poll(); // get the best edge
if (!detectCycle(src, edge.y, visited)) { // if cycle is not form after adding the edge
cost += edge.cost;
visited.add(edge.y);
source.add(edge.y);
break; // so that it doesn't look to add the other edges right away
}
}
}
return cost;
}

private void putEdges(int[] point, Set<int[]> set, int[][] points, PriorityQueue<Edge> q) {


for(int[] pnt : points) {
if(pnt != point && !set.contains(pnt))
q.offer(new Edge(point, pnt));
}
}

// to detect cycle
private boolean detectCycle(int[] a, int[] b, Set<int[]> set) {
return set.contains(a) && set.contains(b);
}
}

Complexity Analysis
- Time Complexity: O(V*E)
- Space Complexity: O(V+E)

743. Network Delay Time


(https://leetcode.com/problems/network-
delay-time/)
Medium

You are given a network of n nodes, labeled from 1 to n. You are also given times, a list of travel times as directed edges times[i] = (ui, vi, wi), where ui is the source node, vi is the
target node, and wi is the time it takes for a signal to travel from source to target.

We will send a signal from a given node k. Return the minimum time it takes for all the n nodes to receive the signal. If it is impossible for all the n nodes to receive the signal, return
-1.

Example 1:

Input: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2


Output: 2

Example 2:

Input: times = [[1,2,1]], n = 2, k = 1


Output: 1

Example 3:

Input: times = [[1,2,1]], n = 2, k = 2


Output: -1

Constraints:

1 <= k <= n <= 100


1 <= times.length <= 6000
times[i].length == 3
1 <= ui, vi <= n
ui != vi
0 <= wi <= 100
All the pairs (ui, vi) are unique. (i.e., no multiple edges.)

Approach

Solution
public class Solution {

public int networkDelayTime(int[][] times, int n, int k) {


Map<Integer, List<int[]>> graph = new HashMap<>();
for (int[] time : times) {
List<int[]> neighbours = graph.getOrDefault(time[0], new ArrayList<>());
neighbours.add(new int[] { time[1], time[2] });
graph.put(time[0], neighbours);
}
int[] cost = new int[n + 1];
for (int i = 1; i <= n; i++) cost[i] = 100005;
cost[k] = 0;
Queue<Integer> q = new LinkedList<>();
q.offer(k);
while (!q.isEmpty()) {
int vertex = q.poll();
if (!graph.containsKey(vertex)) continue;

List<int[]> neighbours = graph.get(vertex);


for (int[] nei : neighbours) {
int newCost = cost[vertex] + nei[1];
if (newCost < cost[nei[0]]) {
cost[nei[0]] = newCost;
q.offer(nei[0]);
}
}
}
int ans = -1;
for (int i = 1; i <= n; i++) {
if (cost[i] >= 100005) return -1;
if (cost[i] > ans) ans = cost[i];
}
return ans;
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

787. Cheapest Flights Within K Stops


(https://leetcode.com/problems/cheapest-
flights-within-k-stops/)
Medium

There are n cities connected by some number of flights. You are given an array flights where flights[i] = [fromi, toi, pricei] indicates that there is a flight from city fromi to city toi with
cost pricei.

You are also given three integers src, dst, and k, return the cheapest price from src to dst with at most k stops. If there is no such route, return -1.

Example 1:
Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1
Output: 700
Explanation:
The graph is shown above.
The optimal path with at most 1 stop from city 0 to 3 is marked in red and has cost 100 + 600 = 700.
Note that the path through cities [0,1,2,3] is cheaper but is invalid because it uses 2 stops.

Example 2:

Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1


Output: 200
Explanation:
The graph is shown above.
The optimal path with at most 1 stop from city 0 to 2 is marked in red and has cost 100 + 100 = 200.

Example 3:

Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0


Output: 500
Explanation:
The graph is shown above.
The optimal path with no stops from city 0 to 2 is marked in red and has cost 500.

Constraints:

1 <= n <= 100


0 <= flights.length <= (n * (n - 1) / 2)
flights[i].length == 3
0 <= fromi, toi < n
fromi != toi
1 <= pricei <= 104
There will not be any multiple flights between two cities.
0 <= src, dst, k < n
src != dst

Approach
Ref: Bellman Ford (https://github.com/dipjul/NeetCode-
150/blob/9a8121cc3db395bc2b180b56c88524c678b72d03/Algorithms/4.Bellman-ford.md)

- Bellman ford algorithm


- It runs for n * E time, n : number of nodes & E : edges
- here modified to run for k+1 times

Solution
class Solution {

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
int[] cost = new int[n];
Arrays.fill(cost, Integer.MAX_VALUE);
int[] tmp = new int[n];
Arrays.fill(tmp, Integer.MAX_VALUE);
cost[src] = 0;
tmp[src] = 0;
while(k >= 0) {
for(int[] flight : flights) {
if(cost[flight[0]] != Integer.MAX_VALUE) {
int newCost = cost[flight[0]]+flight[2];
if(newCost < tmp[flight[1]])
tmp[flight[1]] = newCost;
}
}
cost = Arrays.copyOfRange(tmp, 0, n);
k--;
}
return cost[dst] == Integer.MAX_VALUE?-1:cost[dst];
}
}

Complexity Analysis
- Time Complexity: O(k*E), E size of flights array
- Space Complexity: O(n)

Resources
70. Climbing Stairs
(https://leetcode.com/problems/climbing-
stairs/)
Easy

You are climbing a staircase. It takes n steps to reach the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Example 1:

Input: n = 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

Example 2:
Input: n = 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

Constraints:

1 <= n <= 45

Approach
- Problem effectively becomes fibonacci

Solution
class Solution {
public int climbStairs(int n) {
int one = 1, two = 1;
for(int i = 2; i <= n; i++) {
int tmp = one;
one = one + two;
two = tmp;
}
return one;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(1)

746. Min Cost Climbing Stairs


(https://leetcode.com/problems/min-cost-
climbing-stairs/)
Easy

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.

You can either start from the step with index 0, or the step with index 1.

Return the minimum cost to reach the top of the floor.

Example 1:

Input: cost = [10,15,20]


Output: 15
Explanation: You will start at index 1.
- Pay 15 and climb two steps to reach the top.
The total cost is 15.

Example 2:
Input: cost = [1,100,1,1,1,100,1,1,100,1]
Output: 6
Explanation: You will start at index 0.
- Pay 1 and climb two steps to reach index 2.
- Pay 1 and climb two steps to reach index 4.
- Pay 1 and climb two steps to reach index 6.
- Pay 1 and climb one step to reach index 7.
- Pay 1 and climb two steps to reach index 9.
- Pay 1 and climb one step to reach the top.
The total cost is 6.

Constraints:

2 <= cost.length <= 1000


0 <= cost[i] <= 999

Approach
- Start from the end of the array
- Recursive formula:
- cost[i] = cost[i] + min(cost[i+1], cost[i+2])
- return min(cost[o], cost[1])

Solution
class Solution {
public int minCostClimbingStairs(int[] cost) {

for(int i = cost.length-3; i >= 0; i--) {


cost[i] += Math.min(cost[i+1], cost[i+2]);
}

return Math.min(cost[0], cost[1]);


}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity O(1)

198. House Robber


(https://leetcode.com/problems/house-
robber/)
Medium

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.

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.

Example 1:
Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: nums = [2,7,9,3,1]


Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.

Constraints:

1 <= nums.length <= 100


0 <= nums[i] <= 400

Approach
Pattern:
for loop
dp[i] = max(dp[i-1], nums[i]+dp[i-2]);
- either take the amount till previous element or else add current element to the amount till pre previous elements

Solution
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n == 1)
return nums[0];
int[] dp = new int[n];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < n; i++) {
dp[i] = Math.max(dp[i-1], nums[i]+dp[i-2]);
}

return dp[n-1];
}

// optimized
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n == 1)
return nums[0];
int prev2 = nums[0];
int prev1 = Math.max(nums[0], nums[1]);

for(int i = 2; i < n; i++) {


int temp = prev1;
prev1 = Math.max(prev1, nums[i]+prev2);
prev2 = temp;
}
return prev1;
}
}
Complexity Analysis
1st solution:
- Time Complexity: O(N)
- Space Complexity: O(N)
2nd solution:
- Time Complexity: O(N)
- Space Complexity: O(1)

213. House Robber II


(https://leetcode.com/problems/house-
robber-ii/)
Medium

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That
means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system connected, and it will automatically contact the police if two adjacent houses
were broken into on the same night.

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.

Example 1:

Input: nums = [2,3,2]


Output: 3
Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2), because they are adjacent houses.

Example 2:

Input: nums = [1,2,3,1]


Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 3:

Input: nums = [1,2,3]


Output: 3

Constraints:

1 <= nums.length <= 100


0 <= nums[i] <= 1000

Approach
Reference: 3. House Robber (https://github.com/dipjul/NeetCode-150/blob/d95d91ca2c7e0e25f421e3959ce0c0b62d9ba5a0/13.%201-
D%20Dynamic%20Programming/3.HouseRobber.md)

- Reuse house robber, 1st time for [0, n-1] & 2nd time for [1, n]
- Return max out of them

Solution
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n == 1)
return nums[0];
if(n == 2)
return Math.max(nums[0], nums[1]);
int prev2 = nums[0];
int prev1 = Math.max(nums[0], nums[1]);

for(int i = 2; i < n-1; i++) {


int temp = prev1;
prev1 = Math.max(prev1, nums[i]+prev2);
prev2 = temp;
}
int ans1 = prev1;
prev2 = nums[1];
prev1 = Math.max(nums[1], nums[2]);

for(int i = 3; i < n; i++) {


int temp = prev1;
prev1 = Math.max(prev1, nums[i]+prev2);
prev2 = temp;
}
int ans2 = prev1;
return Math.max(ans1, ans2);
}
}

Complexity Analysis
- Time Complexity: O(2 * N) ~ O(N)
- Space Complexity: O(1)

5. Longest Palindromic Substring


Medium

Given a string s, return the longest palindromic substring in s.

Example 1:

Input: s = "babad"
Output: "bab"
Explanation: "aba" is also a valid answer.

Example 2:

Input: s = "cbbd"
Output: "bb"

Constraints:

1 <= s.length <= 1000


s consist of only digits and English letters.

Approach
Approach 1:
- for each index, try to find odd length & even length palindromes
- odd: start the inner loop from the same index
- even: start the inner loop one pointer on i and other at i+1
- expand towards the left and right of i while the chars are same
- keep checking the len, and store the start and end
- substring(start,end+1), end+1 because the substring method takes end index one more than the original end index

Approach 2:
- Manacher Algorithm

Solution
class Solution {
public String longestPalindrome(String s) {
int resLen = 0, start = 0, end = 0;

if(s == null || s.length() == 0)


return "";

for(int i = 0; i < s.length(); i++) {

// odd length palindromes


int left = i, right = i;
while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
if(right-left+1 > resLen) {
start = left;
end = right;
resLen = right-left+1;
}
left--;
right++;
}

// even length palindromes


left = i;
right = i+1;
while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
if(right-left+1 > resLen) {
start = left;
end = right;
resLen = right-left+1;
}
left--;
right++;
}

return s.substring(start, end+1);


}
}
Manacher Algorithm

class Solution {
public String longestPalindrome(String s) {
StringBuilder sb = new StringBuilder();
// appending chars before, inbetween and end to make the string
// such that we can use the manacher odd algorithm
sb.append("$");
for(int i = 0; i < s.length(); i++) {
sb.append("#");
sb.append(s.charAt(i));
}
sb.append("#");
sb.append("&");

int[] p = manacher_odd(sb.toString());
int center = 0, resLen = 0;
for(int i = 0; i < p.length; i++) {
if(p[i] > resLen) {
center = i;
resLen = p[i];
}
}

return s.substring((center-resLen)/2, (center+resLen)/2);


}

private int[] manacher_odd(String s) {


int[] p = new int[s.length()]; // max palindromes length centered at the index
int center = 0; // center is to keep the new center
int rightBoundary = 0; // rightBoundary to keep the boundary

for(int i = 1; i < s.length()-1; i++) {


// find the mirror [..mirror...center...i..]
int mirror = 2*center-i;

// if i is inside the boundary then only we can assign the value


if(i < rightBoundary)
p[i] = Math.min(p[mirror], rightBoundary-i);

// while the chars are matching keep incrementing p[i]


// which interns result into match the next chars to the left and right
while(s.charAt(i-(p[i]+1)) == s.charAt(i+(p[i]+1)))
p[i]++;

// if the palindrome with centered i, exceeds the right boundary


// then we need to update the right boundary and the new center
if(i + p[i] > rightBoundary) {
center = i;
rightBoundary = i + p[i];
}
}
return p;
}
}

Complexity Analysis
- Time Complexity:
- Approach 1: O(N*N)
- Approach 2: O(N)
- Space Complexity:
- Approach 1: O(1)
- Approach 2: O(N)

647. Palindromic Substrings


(https://leetcode.com/problems/palindromic-
substrings/)
Medium

Given a string s, return the number of palindromic substrings in it.

A string is a palindrome when it reads the same backward as forward.

A substring is a contiguous sequence of characters within the string.

Example 1:

Input: s = "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".

Example 2:

Input: s = "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".

Constraints:

1 <= s.length <= 1000


s consists of lowercase English letters.

Approach
Reference: 5.LongestPalindromicSubstring (https://github.com/dipjul/NeetCode-150/blob/7961198c07e09a081fec9fbcc445e315bab042a7/13.%201-
D%20Dynamic%20Programming/5.LongestPalindromicSubstring.md)

Approach 1:
- expand around every element as a center
- ood length (i,i)
- even length (i,i+1)
- increment for each match

Approach 2:
- Manacher Algorithm
- every element in p[i]
- (p[i]+1)/2

Solution
class Solution {
public int countSubStrings(String s) {
if (s.length() < 2) {
return s.length();
}
int result = 0;
for (int i = 0; i < s.length(); i++) {
// Odd Length
int left = i, right = i;
while (left >=0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
result++;
left -=1;
right +=1;
}
// Even Length
left = i;
right = i + 1;
while (left >=0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
result++;
left -=1;
right +=1;
}
}
return result;
}
}
class Solution {
public int countSubstrings(String s) {
StringBuilder sb = new StringBuilder();
sb.append("$");
for(int i = 0; i < s.length(); i++) {
sb.append("#");
sb.append(s.charAt(i));
}
sb.append("#");
sb.append("@");
int[] p = manacher_odd(sb.toString());
int ans = 0;
for(int i = 0; i < p.length; i++)
ans += (p[i]+1)/2;
return ans;
}

private int[] manacher_odd(String s) {


int[] p = new int[s.length()];
int center = 0, rightBoundary = 0;

for(int i = 1; i < s.length()-1; i++) {

int mirror = 2*center-i;


if(i < rightBoundary)
p[i] = Math.min(p[mirror], rightBoundary-i);

while(s.charAt(i-(p[i]+1)) == s.charAt(i+(p[i]+1)))
p[i]++;

if(i + p[i] > rightBoundary) {


center = i;
rightBoundary = i + p[i];
}
}

return p;
}
}

Complexity Analysis
- Time Complexity:
- Approach 1: O(N*N)
- Approach 2: O(N)
- Space Complexity:
- Approach 1: O(1)
- Approach 2: O(N)

91. Decode Ways


(https://leetcode.com/problems/decode-
ways/)
Medium

A message containing letters from A-Z can be encoded into numbers using the following mapping:

'A' -> "1"


'B' -> "2"

...

'Z' -> "26"

To decode an encoded message, all the digits must be grouped then mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example,
"11106" can be mapped into:

"AAJF" with the grouping (1 1 10 6)

"KJF" with the grouping (11 10 6)

Note that the grouping (1 11 06) is invalid because "06" cannot be mapped into 'F' since "6" is different from "06".

Given a string s containing only digits, return the number of ways to decode it.

The test cases are generated so that the answer fits in a 32-bit integer.

Example 1:

Input: s = "12"
Output: 2
Explanation: "12" could be decoded as "AB" (1 2) or "L" (12).

Example 2:

Input: s = "226"
Output: 3
Explanation: "226" could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

Example 3:

Input: s = "06"
Output: 0
Explanation: "06" cannot be mapped to "F" because of the leading zero ("6" is different from "06").

Constraints:

1 <= s.length <= 100


s contains only digits and may contain leading zero(s).

Approach
- Approach
- take an dp array of size 1 greater than len(s)
- at the last index we'll keep 1
- for every char, if char not equal of 0
- dp[i] += dp[i+1]
- if s(i,i+2) is < 26
- dp[i] += dp[i+2]
- at last we'll have the ans at dp[0]

- Optimize
- pattern seems like fibonacci
- we can replace the array with three variables
- curr will have dp[i]
- prev will have dp[i+1]
- prev2 will have dp[i+2]

Solution
class Solution {

public int numDecodings(String s) {


int n = s.length();
int[] dp = new int[n+1];
dp[n] = 1;

for(int i = n-1; i >= 0; i--) {


char ch = s.charAt(i);
if(ch != '0') {
dp[i] += dp[i+1];
if(i < n-1 && Integer.valueOf(s.substring(i,i+2)) <= 26)
dp[i] += dp[i+2];
}
}
return dp[0];
}
// Optimized
public int numDecodings(String s) {
int n = s.length();

int prev = 1, prev2 = 0, curr = 0;

for(int i = n-1; i >= 0; i--) {


char ch = s.charAt(i);
curr = 0;
if(ch != '0') {
curr += prev;
if(i < n-1 && ((ch-'0' == 1) || (ch-'0' <= 2 && s.charAt(i+1)-'0' <= 6)))
curr += prev2;
}
int tmp = prev;
prev = curr;
prev2 = tmp;
}
return curr;
}
}

Complexity Analysis
- Time Complexity:
- Approach 1 & Approach 2: O(N)
- Space Complexity:
- Approach 1: O(N), dp array of n size
- Approach 2: O(1), 3 variables

322. Coin Change


(https://leetcode.com/problems/coin-change/)
Medium

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

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.

You may assume that you have an infinite number of each kind of coin.

Example 1:
Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3


Output: -1

Example 3:

Input: coins = [1], amount = 0


Output: 0

Constraints:

1 <= coins.length <= 12

1 <= coins[i] <= 231 - 1


0 <= amount <= 104

Approach
- we'll have dp array of size amount+1
- we'll initiaze the array with large value
- dp[0] would be 0
- for in range(1, amount)
- for each coin
- dp[i] = min(dp[i], 1 + dp[i-coin])
- dp[amount] will either have the ans or else it'll have the default value

Solution
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}

Complexity Analysis
- Time Complex: O(N*K), N: amount+1, K = coins.length
- Space complexity: O(N), N: amount+1

152. Maximum Product Subarray


(https://leetcode.com/problems/maximum-
product-subarray/)
Medium

Given an integer array nums, find a contiguous non-empty subarray within the array that has the largest product, and return the product.

The test cases are generated so that the answer will fit in a 32-bit integer.

A subarray is a contiguous subsequence of the array.

Example 1:

Input: nums = [2,3,-2,4]


Output: 6
Explanation: [2,3] has the largest product 6.

Example 2:

Input: nums = [-2,0,-1]


Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.

Constraints:

1 <= nums.length <= 2 * 104


-10 <= nums[i] <= 10
The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.

Approach
- two variable to keep max & min at that index
- result, initially stores the max element present in the array
- if we encounter 0, min and max set to 1
- else
- min = min(min*arr[i],max*arr[i],arr[i])
- max = max(prevMin*arr[i], max*arr[i], arr[i])
- result = max(max, result)

Solution
class Solution {
public int maxProduct(int[] nums) {
int n = nums.length;
int min = nums[0], max = nums[0];
int result = nums[0];

for(int i = 1; i < n; i++)


result = Math.max(result, nums[i]);

for(int i = 1; i < n; i++) {


if(nums[i] == 0) {
min = 1;
max = 1;
} else {
int tmp1 = min;
min = Math.min(min*nums[i], Math.min(max*nums[i], nums[i]));
max = Math.max(tmp1*nums[i], Math.max(max*nums[i], nums[i]));
result = Math.max(result, max);
}
}

return result;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(1)

139. Word Break


(https://leetcode.com/problems/word-break/)
Medium

Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.

Note that the same word in the dictionary may be reused multiple times in the segmentation.

Example 1:

Input: s = "leetcode", wordDict = ["leet","code"]


Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".

Example 2:

Input: s = "applepenapple", wordDict = ["apple","pen"]


Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.

Example 3:

Input: s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]


Output: false

Constraints:

1 <= s.length <= 300


1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s and wordDict[i] consist of only lowercase English letters.
All the strings of wordDict are unique.

Approach
Decision tree of Backtracking

Cache

Bottom up

- start from last index


- if index + len of word from dict is less than eqaul to n and substring of s from index to index + len(word) is equal to word
- dp[index] = dp[index + len(word)]
- if dp[index] is true move to next index
- dp[0] would have the result

Solution
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
int n = s.length();
boolean dp[] = new boolean[n+1];
dp[n] = true;

for(int i = n-1; i >= 0; i--) {


for(String w : wordDict) {
if(i + w.length() <= n && w.equals(s.substring(i, i+w.length())))
dp[i] = dp[i + w.length()];

if(dp[i])
break;
}
}
return dp[0];
}
}

Complexity Analysis
- Time Complexity: O(N*len(wordInDict))
- Space Complexity: O(N)

300. Longest Increasing Subsequence


(https://leetcode.com/problems/longest-
increasing-subsequence/)
Medium

Given an integer array nums, return the length of the longest strictly increasing subsequence.

A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements. For example, [3,6,2,7] is a
subsequence of the array [0,3,1,6,2,2,7].

Example 1:

Input: nums = [10,9,2,5,3,7,101,18]


Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.

Example 2:

Input: nums = [0,1,0,3,2,3]


Output: 4

Example 3:

Input: nums = [7,7,7,7,7,7,7]


Output: 1

Constraints:

1 <= nums.length <= 2500


-104 <= nums[i] <= 104
Follow up: Can you come up with an algorithm that runs in O(n log(n)) time complexity?

Approach
- Starting from 2nd last element, lis[] will store 1 for all the elements
- compare it with the next element, if the next element is less than current element
- add max of lis[curr], 1+lis[next]

Solution
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
int[] lis = new int[n];
Arrays.fill(lis, 1);
lis[n-1] = 1;
int res = 1;
for(int i = n-2; i >= 0; i--) {
for(int j = i+1; j < n; j++) {
if(nums[i] < nums[j])
lis[i] = Math.max(lis[i], 1+lis[j]);
}
res = Math.max(res, lis[i]);
}
return res;
}
}

Complexity Analysis
- Time Complexity: O(n^2)
- Space Complexity: O(n)

416. Partition Equal Subset Sum


(https://leetcode.com/problems/partition-
equal-subset-sum/)
Medium

Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Example 1:

Input: nums = [1,5,11,5]


Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:

Input: nums = [1,2,3,5]


Output: false
Explanation: The array cannot be partitioned into equal sum subsets.
Constraints:

1 <= nums.length <= 200


1 <= nums[i] <= 100

Approach
- sum of all the elements, if it's odd return false
- otherwise, find whther there is a subset such that it's equal to sum/2

Solution
class Solution {
Boolean[][] dp;
public boolean canPartition(int[] nums) {
int sum = 0;
for(int num : nums) {
sum += num;
}
if(sum%2 != 0)
return false;
dp = new Boolean[nums.length][sum/2+1];
return subsetSum(nums, 0, sum/2);
}

// DP
private boolean subsetSum(int[] nums, int ind, int sum) {
if(ind >= nums.length || sum < 0)
return false;
if(sum == 0)
return true;
if(dp[ind][sum] != null)
return dp[ind][sum];
dp[ind][sum] = subsetSum(nums, ind+1, sum-nums[ind]) || subsetSum(nums, ind+1, sum);
return dp[ind][sum];
}

// Recursion
private boolean subsetSum2(int[] nums, int ind, int sum) {
if(ind >= nums.length)
return false;
if(sum == 0)
return true;
return subsetSum(nums, ind+1, sum-nums[ind]) || subsetSum(nums, ind+1, sum);
}
}

Complexity Analysis
- Time Complexity: O(N*Sum)
- Space Complexity: O(N*Sum)

Resources
62. Unique Paths
(https://leetcode.com/problems/unique-
paths/)
Medium

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.

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.

The test cases are generated so that the answer will be less than or equal to 2 * 109.

Example 1:

Input: m = 3, n = 7
Output: 28

Example 2:

Input: m = 3, n = 2
Output: 3
Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Down -> Down
2. Down -> Down -> Right
3. Down -> Right -> Down

Constraints:

1 <= m, n <= 100

Approach
- Last row and last column would have only one way
- others:
- dp[i][j] = dp[i+1][j]+dp[i][j+1];
- dp[0][0] would have the result

Solution
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
// last column
for(int i = 0; i < m; i++) {
dp[i][n-1] = 1;
}
// last row
for(int i = 0; i < n; i++) {
dp[m-1][i] = 1;
}

for(int i = m-2; i >= 0; i--) {


for(int j = n-2; j >= 0; j--) {
dp[i][j] = dp[i+1][j]+dp[i][j+1];
}
}
return dp[0][0];
}
}

Complexity Analysis
- Time Complexity: O(m*n)
- Space Complexity: O(m*n)

1143. Longest Common Subsequence


(https://leetcode.com/problems/longest-
common-subsequence/)
Medium

Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.

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.

For example, "ace" is a subsequence of "abcde". A common subsequence of two strings is a subsequence that is common to both strings.

Example 1:

Input: text1 = "abcde", text2 = "ace"


Output: 3
Explanation: The longest common subsequence is "ace" and its length is 3.

Example 2:

Input: text1 = "abc", text2 = "abc"


Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.

Example 3:

Input: text1 = "abc", text2 = "def"


Output: 0
Explanation: There is no such common subsequence, so the result is 0.

Constraints:

1 <= text1.length, text2.length <= 1000


text1 and text2 consist of only lowercase English characters.

Approach
- Classic DP problem
- create a table, chars of one string as row and other as column
- fill out the value
- Formula that would appear
- if chars are equal: dp[i][j] = 1 + dp[i-1][j-1], (diagonally prev element)
- else: dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]), max(top element, left element)

Solution
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int n1 = text1.length(), n2 = text2.length();
int[][] dp = new int[n1+1][n2+1];

for(int i = 1; i <= n1; i++) {


char c1 = text1.charAt(i-1);
for(int j = 1; j <= n2; j++) {
char c2 = text2.charAt(j-1);
if(c1 == c2) {
dp[i][j] = 1 + dp[i-1][j-1];
} else {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[n1][n2];
}
}

Complexity Analysis
- Time Compolexity: O(N*M), N : length of string 1, M : length of string 2
- Space Complexity: O(N*M)

309. Best Time to Buy and Sell Stock with


Cooldown
(https://leetcode.com/problems/best-time-to-
buy-and-sell-stock-with-cooldown/)
Medium

You are given an array prices where prices[i] is the price of a given stock on the ith day.

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:

After you sell your stock, you cannot buy stock on the next day (i.e., cooldown one day). Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the
stock before you buy again).

Example 1:

Input: prices = [1,2,3,0,2]


Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]

Example 2:

Input: prices = [1]


Output: 0

Constraints:

1 <= prices.length <= 5000


0 <= prices[i] <= 1000
Approach

- From the above options diagram


- we can have two states:
- buying
- selling
- for buying we can either sell or cooldown
- for selling state we must give cooldown before next buying

Solution
class Solution {
public int maxProfit(int[] prices) {
boolean buy = false;
Map<String, Integer> mp = new HashMap<>();
return helper(prices, 0, mp, true);
}

private int helper(int[] prices, int index, Map<String, Integer> mp, boolean buying) {
if(index >= prices.length)
return 0;
if(mp.containsKey("("+index+","+buying+")"))
return mp.get("("+index+","+buying +")");
// in both the cases we have the cooldown
int cooldown = helper(prices, index+1, mp, buying);
if(buying) {
int buy = helper(prices, index+1, mp, !buying) - prices[index];
mp.put("("+index+","+buying +")", Math.max(cooldown, buy));
} else {
// we can't buy in next index so we pass the index+2
int sell = helper(prices, index+2, mp, !buying) + prices[index];
mp.put("("+index+","+buying +")", Math.max(cooldown, sell));
}
return mp.get("("+index+","+buying +")");
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

518. Coin Change 2


(https://leetcode.com/problems/coin-change-
2/)
Medium

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0.

You may assume that you have an infinite number of each kind of coin.

The answer is guaranteed to fit into a signed 32-bit integer.

Example 1:

Input: amount = 5, coins = [1,2,5]


Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

Example 2:

Input: amount = 3, coins = [2]


Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.

Example 3:

Input: amount = 10, coins = [10]


Output: 1

Constraints:

1 <= coins.length <= 300


1 <= coins[i] <= 5000
All the values of coins are unique.
0 <= amount <= 5000

Approach
- Make a table and go through a example
- generic recursive formula:
- dp[i][j] = dp[i-1][j] + dp[i][j-coin]
- take care of edge cases

Solution
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int dp[][] = new int[n][amount+1];

for(int i = 0; i < n; i++)


dp[i][0] = 1;

for(int i = 0; i < n; i++) {


for(int j = 1; j <= amount; j++) {
int val = j-coins[i];
if(i > 0) {
if(val >= 0)
dp[i][j] = dp[i-1][j] + dp[i][val];
else
dp[i][j] = dp[i-1][j];
}
else {
if(val >= 0)
dp[i][j] = dp[i][val];
else
dp[i][j] = 0;
}
}
}

return dp[n-1][amount];
}

// Consise
public int change(int amount, int[] coins) {
int[][] dp = new int[coins.length+1][amount+1];
dp[0][0] = 1;

for (int i = 1; i <= coins.length; i++) {


dp[i][0] = 1;
for (int j = 1; j <= amount; j++) {
dp[i][j] = dp[i-1][j] + (j >= coins[i-1] ? dp[i][j-coins[i-1]] : 0);
}
}
return dp[coins.length][amount];
}

// Optimal
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i-coin];
}
}
return dp[amount];
}
}

Complexity Analysis
- Time Complexity: O(Amount*No. of coins)
- Space Complexity:
- 2D: O(Amount*No. of coins)
- Optimal: O(Amount+1)
329. Longest Increasing Path in a Matrix
(https://leetcode.com/problems/longest-
increasing-path-in-a-matrix/)
Hard

Given an m x n integers matrix, return the length of the longest increasing path in matrix.

From each cell, you can either move in four directions: left, right, up, or down. You may not move diagonally or move outside the boundary (i.e., wrap-around is not allowed).

Example 1:

Input: matrix = [[9,9,4],[6,6,8],[2,1,1]]


Output: 4
Explanation: The longest increasing path is [1, 2, 6, 9].

Example 2:

Input: matrix = [[3,4,5],[3,2,6],[2,2,1]]


Output: 4
Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.

Example 3:

Input: matrix = [[1]]


Output: 1

Constraints:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 200
0 <= matrix[i][j] <= 231 - 1

Approach
- DFS/backtracking with DP
- Start from each element in the matrix
- Find the longest increasing path we can make from that position
- by moving towards the 4 directions
- Store the result in a table
- Reuse the value

Solution
class Solution {

public int longestIncreasingPath(int[][] matrix) {


int[][] cache = new int[matrix.length][matrix[0].length];
int res = 1;
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[0].length; j++)
res = Math.max(res, backtrack(matrix, i, j, -1, cache));
}
return res;
}

private int backtrack(int[][] matrix, int i, int j, int prev, int[][] cache) {
if(i < 0 || j < 0 || i >= matrix.length || j >= matrix[0].length || prev >= matrix[i][j])
return 0;

if(cache[i][j] != 0)
return cache[i][j];

int max = 1;

max = Math.max(max, 1 + backtrack(matrix, i-1, j, matrix[i][j], cache));


max = Math.max(max, 1 + backtrack(matrix, i, j-1, matrix[i][j], cache));
max = Math.max(max, 1 + backtrack(matrix, i+1, j, matrix[i][j], cache));
max = Math.max(max, 1 + backtrack(matrix, i, j+1, matrix[i][j], cache));

cache[i][j] = max;
return max;
}
}

Complexity Analysis
- Time Complexity: dfs: O(n*m), we'll go through each position, after that it'll return from any position in O(1)
- Space Complexity: O(n*m)

72. Edit Distance


(https://leetcode.com/problems/edit-
distance/)
Hard

Share Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2.

You have the following three operations permitted on a word:

Insert a character
Delete a character
Replace a character

Example 1:

Input: word1 = "horse", word2 = "ros"


Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')
Example 2:

Input: word1 = "intention", word2 = "execution"


Output: 5
Explanation:
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')

Constraints:

0 <= word1.length, word2.length <= 500


word1 and word2 consist of lowercase English letters.

Aproach
- make a table with one word as row and other as column,
- append same char at the start of the both words to represent that both are empty string then 0 operation rqd to convert from word
- generic recursive equation:
- dp[i][j] = dp[i-1][j-1], if char are same
- dp[i][j] = 1 + min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]), i.e. 1 + min(left, top, diagonally prev)
- take care of base cases

Solution
class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length(), n = word2.length();
int dp[][] = new int[n+1][m+1];
for(int i = 1; i <= n; i++) {
dp[i][0] = i;
}

for(int i = 1; i <= m; i++) {


dp[0][i] = i;
}

for(int i = 1; i <= n; i++) {


char ch1 = word2.charAt(i-1);
for(int j = 1; j <= m; j++) {
char ch2 = word1.charAt(j-1);

if(ch1 == ch2)
dp[i][j] = dp[i-1][j-1];
else
dp[i][j] = 1 + Math.min(dp[i-1][j], Math.min(dp[i-1][j-1], dp[i][j-1]));
}
}

return dp[n][m];
}
}

Complexity Analysis
- Time Complexity: O(len(word1)*len(word2))
- Space Complexity: O(len(word1)*len(word2))
Resources
53. Maximum Subarray
Easy

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

A subarray is a contiguous part of an array.

Example 1:

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]


Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Example 2:

Input: nums = [1]


Output: 1

Example 3:

Input: nums = [5,4,-1,7,8]


Output: 23

Constraints:

1 <= nums.length <= 105


-104 <= nums[i] <= 104

Follow up: If you have figured out the O(n) solution, try coding another solution using the
divide and conquer approach, which is more subtle.

class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;

int total = 0;
int result = nums[0];

for(int num : nums) {


if(total < 0)
total = 0;
total = total+num;
result = Math.max(result, total);

return result;
}
}

55. Jump Game


(https://leetcode.com/problems/jump-game/)
Medium
You are given an integer array nums. You are initially positioned at the array's first index, and each element in the array represents your maximum jump length at that position.

Return true if you can reach the last index, or false otherwise.

Example 1:

Input: nums = [2,3,1,1,4]


Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: nums = [3,2,1,0,4]


Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the l

Constraints:

1 <= nums.length <= 104


0 <= nums[i] <= 105

Aprroach
- one variable(for eg. ans) is keeping upto which index can we move from the current index
- if ans value is less than the current index, which means we can't move to the current index
- at every index we check if you can better the ans,
which means if we can move to a higher index than the current index stored in ans
- if ans is sotring the value more than equal to the length of the given array

Solution
class Solution {
public boolean canJump(int[] nums) {
int ans = 0, n = nums.length;
for(int i = 0; i < n-1; i++) {
if(ans < i)
return false;
if(ans < i+nums[i])
ans = i+nums[i];
if(ans >= n-1)
return true;
}
return ans >= n-1;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(1)

45. Jump Game II


(https://leetcode.com/problems/jump-game-
ii/)
Medium

Given an array of non-negative integers nums, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Your goal is to reach the last index in the minimum number of jumps.

You can assume that you can always reach the last index.

Example 1:

Input: nums = [2,3,1,1,4]


Output: 2
Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index

Example 2:

Input: nums = [2,3,0,1,4]


Output: 2

Constraints:

1 <= nums.length <= 104


0 <= nums[i] <= 1000

Approach
- dp array initialized to large value, later on it will store the min jumps required to reach last index from each index
- we start from end, second last index, try to see if we can move to the last index
- for each index you look for the min step we need to reach last index
- Pattern:
- for each nums's index(i):
- for range(0,num) (j):
- dp[i] = min(dp[i], 1 + dp[i+j]

TODO : Optimization

Solution
class Solution {
public int jump(int[] nums) {
int n = nums.length;
int dp[] = new int[n];
Arrays.fill(dp, 10000);
dp[n - 1] = 0;

for (int i = n - 2; i >= 0; i--) {


for (int j = nums[i]; j >= 0; j--) {
if (i + j < n)
dp[i] = Math.min(dp[i], 1 + dp[i + j]);
}
}
return dp[0];
}
}

Complexity Analysis
- Time Complexity: O(k*N)
- Space Complexity: O(N)

134. Gas Station


(https://leetcode.com/problems/gas-station/)
Medium

There are n gas stations along a circular route, where the amount of gas at the ith station is gas[i].

You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from the ith station to its next (i + 1)th station. You begin the journey with an empty tank at one of the gas
stations.

Given two integer arrays gas and cost, return the starting gas station's index if you can travel around the circuit once in the clockwise direction, otherwise return -1. If there exists a
solution, it is guaranteed to be unique

Example 1:

Input: gas = [1,2,3,4,5], cost = [3,4,5,1,2]


Output: 3
Explanation:
Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 4. Your tank = 4 - 1 + 5 = 8
Travel to station 0. Your tank = 8 - 2 + 1 = 7
Travel to station 1. Your tank = 7 - 3 + 2 = 6
Travel to station 2. Your tank = 6 - 4 + 3 = 5
Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3.
Therefore, return 3 as the starting index.

Example 2:

Input: gas = [2,3,4], cost = [3,4,3]


Output: -1
Explanation:
You can't start at station 0 or 1, as there is not enough gas to travel to the next station.
Let's start at station 2 and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 0. Your tank = 4 - 3 + 2 = 3
Travel to station 1. Your tank = 3 - 3 + 3 = 3
You cannot travel back to station 2, as it requires 4 unit of gas but you only have 3.
Therefore, you can't travel around the circuit once no matter where you start.

Constraints:

n == gas.length == cost.length
1 <= n <= 105
0 <= gas[i], cost[i] <= 104

Approach
- Trivial:
- we'll store the difference of gas[i]-cost[i]
- for all the +ve differences we do the process

TODO:Optimization

Solution
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int ind = 0, sum = 0;
int[] diff = new int[gas.length];
PriorityQueue<Pair> pq = new PriorityQueue<>((a,b)->b.value-a.value);
for (int i = 0; i < gas.length; i++) {
diff[i] = gas[i] - cost[i];
sum += diff[i];
if(diff[i]>=0)
pq.offer(new Pair(diff[i], i));
}
if (sum < 0)
return -1;

while(!pq.isEmpty()) {
Pair p = pq.poll();
if (p.value >= 0) {
int pathSum = 0;
ind = p.index;
int j = p.index;
do {
pathSum += diff[j];
if (pathSum < 0)
break;
j = (j + 1) % gas.length;
} while(j != p.index);

if (pathSum >= 0)
break;
}
}

return ind;
}
}

class Pair {
int value;
int index;

Pair(int v, int i) {
value = v;
index = i;
}
}

Complexity Analysis
- Time Complexity: O(k*N), k is number of elements in diff array that are 0 or +ve
- Space Complexity: O(N)

846. Hand of Straights


(https://leetcode.com/problems/hand-of-
straights/)
Medium

Alice has some number of cards and she wants to rearrange the cards into groups so that each group is of size groupSize, and consists of groupSize consecutive cards.
Given an integer array hand where hand[i] is the value written on the ith card and an integer groupSize, return true if she can rearrange the cards, or false otherwise.

Example 1:

Input: hand = [1,2,3,6,2,3,4,7,8], groupSize = 3


Output: true
Explanation: Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8]

Example 2:

Input: hand = [1,2,3,4,5], groupSize = 4


Output: false
Explanation: Alice's hand can not be rearranged into groups of 4.

Constraints:

1 <= hand.length <= 104


0 <= hand[i] <= 109
1 <= groupSize <= hand.length

Note: This question is the same as 1296: https://leetcode.com/problems/divide-array-in-sets-of-k-consecutive-numbers/ (https://leetcode.com/problems/divide-array-in-sets-of-


k-consecutive-numbers/)

Approach
// TODO

Solution
class Solution {
public boolean isNStraightHand(int[] hand, int groupSize) {
if(hand.length % groupSize != 0)
return false;

Map<Integer, Integer> mp = new HashMap<>();


PriorityQueue<Integer> pq = new PriorityQueue<>((a,b)->a-b);

for(int i = 0; i < hand.length; i++) {


if(!mp.containsKey(hand[i])) {
pq.offer(hand[i]);
mp.put(hand[i], 1);
} else {
mp.put(hand[i], mp.get(hand[i])+1);
}
}

while(!pq.isEmpty()) {
int min = pq.peek();
int sz = 0;
while(sz < groupSize) {
if(!mp.containsKey(min))
return false;
mp.put(min, mp.get(min)-1);
if(mp.get(min) == 0) {
mp.remove(min);
int val = pq.poll();
if(val != min)
return false;
}
min++;
sz++;
}

}
return true;
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

678. Valid Parenthesis String


(https://leetcode.com/problems/valid-
parenthesis-string/)
Medium

Given a string s containing only three types of characters: '(', ')' and '*', return true if s is valid.

The following rules define a valid string:

Any left parenthesis '(' must have a corresponding right parenthesis ')'. Any right parenthesis ')' must have a corresponding left parenthesis '('. Left parenthesis '(' must go before the
corresponding right parenthesis ')'. '*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string "".
Example 1:

Input: s = "()"
Output: true

Example 2:

Input: s = "(*)"
Output: true

Example 3:

Input: s = "(*))"
Output: true

Constraints:

1 <= s.length <= 100 s[i] is '(', ')' or '*'.

Approach
Approach 1:
- backtrack
- count+1 if '('
- count-1 if ')'
- For '*':
- 3 calls: count+1, count-1, count+0
- dp (index, count) = true/false

Approach 2: greedy

Solution
class Solution {
Boolean[][] dp;

public boolean checkValidString(String s) {


dp = new Boolean[s.length()][s.length()];
return backtrack(s, 0, 0);
}

private boolean backtrack(String s, int index, int count) {


if (count < 0 || (index >= s.length() && count > 0))
return false;
if (index >= s.length() && count == 0)
return true;
if (dp[index][count] != null)
return dp[index][count];
if (s.charAt(index) == '*') {
dp[index][count] = (backtrack(s, index + 1, count + 1) ||
backtrack(s, index + 1, count - 1) ||
backtrack(s, index + 1, count));
} else if (s.charAt(index) == '(') {
dp[index][count] = backtrack(s, index + 1, count + 1);
} else {
dp[index][count] = backtrack(s, index + 1, count - 1);
}
return dp[index][count];
}
}
public class ValidString {
public static boolean isValid(String str) {
int leftMin = 0, leftMax = 0;
for (char ch: str.toCharArray()) {
if (ch == '(') {
leftMin++;
leftMax++;
}
else if (ch == ')') {
leftMin--;
leftMax--;
}
else {
leftMin--;
leftMax++;
}
if (leftMax < 0) { // Means we encounter ) as a starting character or # of occurrences of ) is > (
return false;
}
if (leftMin < 0) {
leftMin = 0;
}
}
return leftMin == 0;
}
}

Complexity Analysis
- Time Complexity:
- Approach 1: O(n^3)
- Approach 2: O(n)
- Space Complexity:
- Approach 1: O(n)
- Approach 2: O(1)

Resources
57. Insert Interval
(https://leetcode.com/problems/insert-
interval/)
Medium

You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending
order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval.

Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if
necessary).

Return intervals after the insertion.

Example 1:

Input: intervals = [[1,3],[6,9]], newInterval = [2,5]


Output: [[1,5],[6,9]]
Example 2:

Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]


Output: [[1,2],[3,10],[12,16]]
Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

Constraints:

0 <= intervals.length <= 104


intervals[i].length == 2
0 <= starti <= endi <= 105
intervals is sorted by starti in ascending order.
newInterval.length == 2
0 <= start <= end <= 105

Approach
- Step 1: while interval's end is lesser than new interval's start, keep adding it to result
- After step 1, we'll be at the position where new interval will start(either overlap or as the initial interval or as the last inter
- Step 2: while intervals's start is <= new interval's end
- Keep taking the min start & max end and assign into the new interval(overlapping)
- After step 2, we'll have the merged new interval, add it to result
- Step 3: while intervals not finished processing
- keep adding them to the result

Solution
class Solution {

public int[][] insert(int[][] intervals, int[] newInterval) {


if (intervals.length < 1) return new int[][] { newInterval };

List<int[]> mergedList = new ArrayList<>();


int index = 0;
while (index < intervals.length && intervals[index][1] < newInterval[0]) mergedList.add(intervals[index++]);

while (index < intervals.length && intervals[index][0] <= newInterval[1]) {


newInterval[0] = Math.min(newInterval[0], intervals[index][0]);
newInterval[1] = Math.max(newInterval[1], intervals[index][1]);
index++;
}
mergedList.add(newInterval);

while (index < intervals.length) mergedList.add(intervals[index++]);

return mergedList.toArray(new int[mergedList.size()][]);


}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(n)

56. Merge Intervals


(https://leetcode.com/problems/merge-
intervals/)
Medium

Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.

Example 1:

Input: intervals = [[1,3],[2,6],[8,10],[15,18]]


Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].

Example 2:

Input: intervals = [[1,4],[4,5]]


Output: [[1,5]]
Explanation: Intervals [1,4] and [4,5] are considered overlapping.

Constraints:

1 <= intervals.length <= 104


intervals[i].length == 2
0 <= starti <= endi <= 104

Approach
- Sort the intervals by start time
- Use arr to store the merged list
- Start by inserting the first element
- looping through all the other intervals
- check if last interval inserted into arr overlapped with current interval
- if yes then create a new interval and push to arr
- else insert the current interval to arr

Solution
class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> arr = new ArrayList<>();
arr.add(intervals[0]);
int start = intervals[0][0];
int end = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
int[] curr = intervals[i];
int[] prev = arr.get(arr.size() - 1);
if (curr[0] <= prev[1]) {
arr.remove(arr.size() - 1);
start = Math.min(curr[0], prev[0]);
end = Math.max(curr[1], prev[1]);
arr.add(new int[]{start, end});
} else {
arr.add(curr);
}
}
return arr.toArray(new int[arr.size()][]);
}
}

Complexity Analysis
- Time Complexity: O(nlogn)
- Space Complexity: O(n), for arr

435. Non-overlapping Intervals


(https://leetcode.com/problems/non-
overlapping-intervals/)
Medium

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.

Example 1:

Input: intervals = [[1,2],[2,3],[3,4],[1,3]]


Output: 1
Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping.

Example 2:

Input: intervals = [[1,2],[1,2],[1,2]]


Output: 2
Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping.

Example 3:

Input: intervals = [[1,2],[2,3]]


Output: 0
Explanation: You don't need to remove any of the intervals since they're already non-overlapping.

Constraints:

1 <= intervals.length <= 105


intervals[i].length == 2
-5 * 104 <= starti < endi <= 5 * 104

Approach
- Sort the intervals by end time
- store the first interval (we're storing it as prev)
- loop through the intervals starting the 2nd interval
- if current interval overlap with prev the increment the count
- else assign the current interval to the prev

Solution
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if(intervals.length <= 1)
return 0;
Arrays.sort(intervals, (a,b)->a[1]-b[1]);

int count = 0;
int[] prev = intervals[0];
for(int i = 1; i < intervals.length; i++) {
int[] curr = intervals[i];
if(prev[1] > curr[0])
count++;
else
prev = intervals[i];
}

return count;
}
}

Complexity Analysis
- Time Complexity: O(nlogn)
- Space Complexity: O(1)

920 · Meeting Rooms [LintCode]


Easy

Description Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), determine if a person could attend all meetings.

Wechat reply the【Video】get the free video lessons , the latest frequent Interview questions , etc. (wechat id :jiuzhang15)

(0,8),(8,10) is not conflict at 8

Example 1

Input: intervals = [(0,30),(5,10),(15,20)]


Output: false
Explanation:
(0,30), (5,10) and (0,30),(15,20) will conflict

Example 2

Input: intervals = [(5,8),(9,15)]


Output: true
Explanation:
Two times will not conflict
/**
* Definition of Interval:
* public classs Interval {
* int start, end;
* Interval(int start, int end) {
* this.start = start;
* this.end = end;
* }
* }
*/

public class Solution {


/**
* @param intervals: an array of meeting time intervals
* @return: if a person could attend all meetings
*/
public boolean canAttendMeetings(List<Interval> intervals) {

if(intervals.size() == 0 || intervals.size() == 1)
return true;
Collections.sort(intervals, (a,b)->a.end-b.end);
Interval next = intervals.get(intervals.size()-1);
for(int i = intervals.size()-2; i >= 0; i--) {
Interval current = intervals.get(i);

if(current.end > next.start && current.end <= next.end)


return false;
next = current;
}
return true;
}
}

919 · Meeting Rooms II ()


Medium

Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), find the minimum number of conference rooms required.)

(0,8),(8,10) is not conflict at 8

Example 1

Input: intervals = [(0,30),(5,10),(15,20)]


Output: 2
Explanation:
We need two meeting rooms
room1: (0,30)
room2: (5,10),(15,20)

Example2

Input: intervals = [(2,7)]


Output: 1
Explanation:
Only need one meeting room

Approach
Approach 1:
- Create a array of size, largest element - smallest element + 1
- for each interval:
- increment all the index between [start, end)
- keep tracking the max element in that array, that's the answer

Approach 2(Better):
- Sort starting time and ending seperatedly
- Two pointers one from traversing through the start[] and the other through the end[]
- keep track of max count(it would have the answer)
- if pointer pointing at start < pointer pointing at end
- increment the count & move the start pointer forward
- else
- decrement the count & move the end pointer forward

Solution
/**
* Definition of Interval:
* public class Interval {
* int start, end;
* Interval(int start, int end) {
* this.start = start;
* this.end = end;
* }
* }
*/

public class Solution {


/**
* @param intervals: an array of meeting time intervals
* @return: the minimum number of conference rooms required
*/
public int minMeetingRooms(List<Interval> intervals) {
// Write your code here
if(intervals == null || intervals.size() == 0)
return 0;
int rooms = 0;
int max = intervals.get(0).end;
int min = intervals.get(0).start;

for(int i = 1; i < intervals.size(); i++) {


max = Math.max(max, intervals.get(i).end);
min = Math.min(min, intervals.get(i).start);
}
int[] arr = new int[max-min+1];
for(Interval interval: intervals) {
int s = interval.start-min;
int e = interval.end-min;
for(int i = s; i < e; i++) {
arr[i]++;
rooms = Math.max(rooms, arr[i]);
}
}
return rooms;
}
}
// Better Solution
public class Solution {
public int minMeetingRooms(List<Interval> intervals) {
// Check for the base case. If there are no intervals, return 0
if (intervals.size() == 0) {
return 0;
}

Integer[] start = new Integer[intervals.size()];


Integer[] end = new Integer[intervals.size()];

for (int i = 0; i < intervals.size(); i++) {


start[i] = intervals.get(i).start;
end[i] = intervals.get(i).end;
}

// Sort the intervals by end time


Arrays.sort(end);

// Sort the intervals by start time


Arrays.sort(start);

// The two pointers in the algorithm: e_ptr and s_ptr.


int startPointer = 0, endPointer = 0;

// Variables to keep track of maximum number of rooms used.


int usedRooms = 0, count = 0;

// Iterate over intervals.


while (startPointer < intervals.size()) {

// If there is a meeting that has started before the time


// the meeting at `end_pointer` starts means overlapping
if (start[startPointer] < end[endPointer]) {
count += 1;
startPointer += 1;
}
// a room is free now
else {
count -= 1;
endPointer += 1;
}
usedRooms = Math.max(usedRooms, count);
}

return usedRooms;
}
}

Complexity Analysis
- Time Complexity:
- Better approach: O(nlogn)
- Space Complexity:
- Better approach: O(n)

Resources
48. Rotate Image
Medium

You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise).

You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.

Example 1:

Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]


Output: [[7,4,1],[8,5,2],[9,6,3]]

Example 2:

Input: matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]


Output: [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

Constraints:

n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000

Approach
- Roatate = Transpose + Reverse

Solution
class Solution {
public void rotate(int[][] matrix) {
transpose(matrix);
for(int[] nums : matrix) {
reverse(nums);
}
}

private void transpose(int[][] matrix) {


for(int i = 0; i < matrix.length; i++) {
for(int j = i + 1; j < matrix.length; j++) {
int tmp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = tmp;
}
}
}

private void reverse(int[] nums) {


for(int i = 0, j = nums.length-1; i < j; i++, j--) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
}

Complexity Analysis
- Time Complexity: O(n), n : number of elements in the matrix
- Space Complexity: O(1)
54. Spiral Matrix
Medium

Given an m x n matrix, return all elements of the matrix in spiral order.

Example 1:

Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]


Output: [1,2,3,6,9,8,7,4,5]

Example 2:

Input: matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]


Output: [1,2,3,4,8,12,11,10,9,5,6,7]

Constraints:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100

Approach

Solution
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<>();
int m = matrix.length - 1;
int n = matrix[0].length - 1;
int sr = 0, sc = 0;
int i = sr, j = sc;
while (sc <= n || sr <= m) {

// 1st row
while (j <= n) {
res.add(matrix[i][j]);
j++;
}
sr++;
j = n;
i = sr;

// condition
if (i > m || j > n) {
break;
}
// last colunm
while (i <= m) {
res.add(matrix[i][j]);
i++;
}
n--;
i = m;
j = n;

if (i > m || j > n) {
break;
}

// last row
while (j >= sc) {
res.add(matrix[i][j]);
j--;
}
m--;
j = sc;
i = m;

if (i > m || j > n) {
break;
}
// 1st column
while (i >= sr) {
res.add(matrix[i][j]);
i--;
}
sc++;
i = sr;
j = sc;

if (i > m || j > n) {
break;
}
}
return res;
}
}
Complexity Analysis
- Time Complexity: O(n), n : number of elements in the matrix
- Space Complexity: O(1)

73. Set Matrix Zeroes


Medium

Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0's.

You must do it in place.

Example 1:

Input: matrix = [[1,1,1],[1,0,1],[1,1,1]]


Output: [[1,0,1],[0,0,0],[1,0,1]]

Example 2:

Input: matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]


Output: [[0,0,0,0],[0,4,5,0],[0,3,1,0]]

Constraints:

m == matrix.length
n == matrix[0].length
1 <= m, n <= 200
-231 <= matrix[i][j] <= 231 - 1

Follow up:

A straightforward solution using O(mn) space is probably a bad idea.


A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?

Approach
- Use the first row and column to keep track of zeroes
- Use one variable for the 0th row or 0 column depending upon our choice

Solution
class Solution {
public void setZeroes(int[][] matrix) {
int col0 = 1;
int n = matrix.length, m = matrix[0].length;
// first column
for(int i = 0; i < n; i++) {
if(matrix[i][0] == 0)
col0 = 0;
}
// first row
for(int i = 0; i < m; i++) {
if(matrix[0][i] == 0)
matrix[0][0] = 0;
}
// starting from (1,1)
for(int i = 1; i < n; i++) {
for(int j = 1; j < m; j++) {
if(matrix[i][j] == 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}

for(int i = 1; i < n; i++) {


for(int j = 1; j < m; j++) {
if(matrix[i][0] == 0 || matrix[0][j] == 0)
matrix[i][j] = 0;
}
}

if(matrix[0][0] == 0) {
for(int i = 1; i < m; i++)
matrix[0][i] = 0;
}

if(col0 == 0) {
for(int i = 0; i < n; i++)
matrix[i][0] = 0;
}
}
}

Complexity Analysis
- Time Complexity: O(N), N : numbero of elements in the matrix
- Space Complexity: O(1)

202. Happy Number


Easy

Write an algorithm to determine if a number n is happy.

A happy number is a number defined by the following process:

Starting with any positive integer, replace the number by the sum of the squares of its digits.
Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.
Those numbers for which this process ends in 1 are happy.

Return true if n is a happy number, and false if not.


Example 1:

Input: n = 19
Output: true
Explanation:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

Example 2:

Input: n = 2
Output: false

Constraints:

1 <= n <= 231 - 1

class Solution {
public boolean isHappy(int n) {
int slow = n, fast = n;
do {
slow = sum(slow);
fast = sum(sum(fast));
} while(slow != fast);
return slow == 1;

private int sum(int n) {


int sum = 0;
while(n > 0) {
int digit = n % 10;
sum += (digit*digit);
n /= 10;
}
return sum;
}
}

66. Plus One


Easy

You are given a large integer represented as an integer array digits, where each digits[i] is the ith digit of the integer. The digits are ordered from most significant to least significant in
left-to-right order. The large integer does not contain any leading 0's.

Increment the large integer by one and return the resulting array of digits.

Example 1:

Input: digits = [1,2,3]


Output: [1,2,4]
Explanation: The array represents the integer 123.
Incrementing by one gives 123 + 1 = 124.
Thus, the result should be [1,2,4].

Example 2:
Input: digits = [4,3,2,1]
Output: [4,3,2,2]
Explanation: The array represents the integer 4321.
Incrementing by one gives 4321 + 1 = 4322.
Thus, the result should be [4,3,2,2].

Example 3:

Input: digits = [9]


Output: [1,0]
Explanation: The array represents the integer 9.
Incrementing by one gives 9 + 1 = 10.
Thus, the result should be [1,0].

Constraints:

1 <= digits.length <= 100


0 <= digits[i] <= 9
digits does not contain any leading 0's.

class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length;

for(int i = n-1; i >= 0; i--) {


if(digits[i] < 9) {
digits[i]++;
return digits;
}
digits[i] = 0;
}
int[] newDigits = new int[n+1];
newDigits[0] = 1;
return newDigits;

}
}

50. Pow(x, n)
(https://leetcode.com/problems/powx-n/)
Medium

Implement pow(x, n), which calculates x raised to the power n (i.e., xn).

Example 1:

Input: x = 2.00000, n = 10
Output: 1024.00000

Example 2:

Input: x = 2.10000, n = 3
Output: 9.26100

Example 3:

Input: x = 2.00000, n = -2
Output: 0.25000
Explanation: 2-2 = 1/22 = 1/4 = 0.25
Constraints:

-100.0 < x < 100.0


-231 <= n <= 231-1
-104 <= xn <= 104

Approach
- Recursiom:
- we can divide the power by 2 and same value can be reused
- For non-negative:
- for even: pow(x, n) = pow(x, n/2)*pow(x,n/2)
- for odd: - for even: pow(x, n) = x*pow(x, n/2)*pow(x,n/2)
- For negative:
- for even: pow(x, n) = pow(x, n/2)*pow(x,n/2)
- for odd: - for even: pow(x, n) = 1/x*pow(x, n/2)*pow(x,n/2)

Solution
class Solution {
public double myPow(double x, int n) {
if(n < 0)
return myPowNeg(x, n);
return myPowPos(x, n);
}

private double myPowNeg(double x, int n) {


if(x == 1 || n == 0)
return 1;
if(n == -1)
return 1/x;
double ans = myPow(x, n/2);
ans *= ans;
if(n%2 != 0)
return ans/x;
return ans;
}

private double myPowPos(double x, int n) {


if(x == 1 || n == 0)
return 1;
double ans = myPow(x, n/2);
ans *= ans;
if(n%2 != 0)
return ans*x;
return ans;
}
}

Complexity Analysis
- Time Complexity: O(logn)
- Space Complexity: O(logn)

43. Multiply Strings


(https://leetcode.com/problems/multiply-
strings/)
Medium

Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string.

Note: You must not use any built-in BigInteger library or convert the inputs to integer directly.

Example 1:

Input: num1 = "2", num2 = "3"


Output: "6"

Example 2:

Input: num1 = "123", num2 = "456"


Output: "56088"

Constraints:

1 <= num1.length, num2.length <= 200


num1 and num2 consist of digits only.
Both num1 and num2 do not contain any leading zero, except the number 0 itself.

Approach
- Reverse the number strings
- StringBuilder(mutable string) of 400 size, as num1 & num2 of 200 length at max
- calculate the multiplication and store overwrite it at the i+j position
- after each inner loop, we insert the carry at i+j position, j would be 1 position more the the original length of the second number
- reverse the string
- remove all the leading 0s
- return 0 if result string size is zero or else return thr result string

Solution
class Solution {
public String multiply(String num1, String num2) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 400; i++)
sb.append(0);
num1 = reverse(num1);
num2 = reverse(num2);
if(num1.length() > num2.length()) {
String tmp = num1;
num1 = num2;
num2 = tmp;
}
int carry = 0;
int i = 0, j = 0;
for (i = 0; i < num2.length(); i++) {
carry = 0;
for (j = 0; j < num1.length(); j++) {
int a = num2.charAt(i)-'0';
int b = num1.charAt(j)-'0';
int n = a * b + carry;
int prev = sb.charAt(i+j)-'0';
int sum = (prev + n) % 10;
carry = (n+prev) / 10;
sum +='0';
sb.setCharAt(i + j, (char) sum);
}
sb.setCharAt(i+j, (char) (carry+'0'));
}

sb.setCharAt(i+j-1, (char) (carry+'0'));


sb.reverse();
int ind = 0;
while(sb.length() > 0 && sb.charAt(ind) == '0')
sb.deleteCharAt(ind);
return sb.length() == 0?"0":sb.toString();
}

private String reverse(String s) {


StringBuilder sb = new StringBuilder(s);
sb.reverse();
return sb.toString();
}
}

Complexity Analysis
- Time Compexity: O(n*m)
- Space Complexity: O(400)

Resources
136. Single Number
Easy

Given a non-empty array of integers nums, every element appears twice except for one. Find that single one.

You must implement a solution with a linear runtime complexity and use only constant extra space.

Example 1:
Input: nums = [2,2,1]
Output: 1

Example 2:

Input: nums = [4,1,2,1,2]


Output: 4

Example 3:

Input: nums = [1]


Output: 1

Constraints:

1 <= nums.length <= 3 * 104


-3 * 104 <= nums[i] <= 3 * 104
Each element in the array appears twice except for one element which appears only once.

class Solution {
public int singleNumber(int[] nums) {
int res = 0;

for(int num : nums) {


res ^= num;
}

return res;
}
}

191. Number of 1 Bits


Easy

Write a function that takes an unsigned integer and returns the number of '1' bits it has (also known as the Hamming weight).

Note:

Note that in some languages, such as Java, there is no unsigned integer type. In this case, the input will be given as a signed integer type. It should not affect your implementation, as
the integer's internal binary representation is the same, whether it is signed or unsigned. In Java, the compiler represents the signed integers using 2's complement notation.
Therefore, in Example 3, the input represents the signed integer. -3.

Example 1:

Input: n = 00000000000000000000000000001011
Output: 3
Explanation: The input binary string 00000000000000000000000000001011 has a total of three '1' bits.

Example 2:

Input: n = 00000000000000000000000010000000
Output: 1
Explanation: The input binary string 00000000000000000000000010000000 has a total of one '1' bit.

Example 3:

Input: n = 11111111111111111111111111111101
Output: 31
Explanation: The input binary string 11111111111111111111111111111101 has a total of thirty one '1' bits.

Constraints:
The input must be a binary string of length 32.

Follow up: If this function is called many times, how would you optimize it?

public class Solution {


// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count = 0;

while(n != 0) {
n = n & (n-1);
count++;
}

return count;
}
}

338. Counting Bits


Easy

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.

Example 1:

Input: n = 2
Output: [0,1,1]
Explanation:
0 --> 0
1 --> 1
2 --> 10

Example 2:

Input: n = 5
Output: [0,1,1,2,1,2]
Explanation:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101

Constraints:

0 <= n <= 105

Follow up:
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?
Can you do it without using any built-in function (i.e., like __builtin_popcount in C++)?
class Solution {
public int[] countBits(int n) {
int[] dp = new int[n+1];

int offset = 1;

for(int i = 1; i < n+1; i++) {


if(offset * 2 == i)
offset = i;
dp[i] = 1 + dp[i-offset];
}

return dp;
}
}

class Solution {
public int[] countBits(int n) {
int[] dp = new int[n+1];

for(int i = 1; i < n+1; i++) {

dp[i] = dp[i/2] + i % 2;
}

return dp;
}
}

190. Reverse Bits


Easy

Reverse bits of a given 32 bits unsigned integer.

Note:

Note that in some languages, such as Java, there is no unsigned integer type. In this case, both input and output will be given as a signed integer type. They should not affect
your implementation, as the integer's internal binary representation is the same, whether it is signed or unsigned. In Java, the compiler represents the signed integers using 2's
complement notation. Therefore, in Example 2 above, the input represents the signed integer -3 and the output represents the signed integer -1073741825.

Example 1:

Input: n = 00000010100101000001111010011100
Output: 964176192 (00111001011110000010100101000000)
Explanation: The input binary string 00000010100101000001111010011100 represents the unsigned integer 43261596, so return 964176192 w

Example 2:

Input: n = 11111111111111111111111111111101
Output: 3221225471 (10111111111111111111111111111111)
Explanation: The input binary string 11111111111111111111111111111101 represents the unsigned integer 4294967293, so return 322122547

Constraints:

The input must be a binary string of length 32

Follow up: If this function is called many times, how would you optimize it?
public class Solution {
// you need treat n as an unsigned value
public int reverseBits(int n) {
int res = 0;
for(int i = 0; i < 32; i++) {
int bit = (n >> i) & 1; // check whether the bit is set or not [right shift each bit & 000..1] -> gives the value present
res = res | (bit << (31 - i)); // put the bit at 31 - i th position i.e, reverse
}
return res;
}
}

268. Missing Number


Easy

Given an array nums containing n distinct numbers in the range [0, n], return the only number in the range that is missing from the array.

Example 1:

Input: nums = [3,0,1]


Output: 2
Explanation: n = 3 since there are 3 numbers, so all numbers are in the range [0,3]. 2 is the missing number in the range since it do

Example 2:

Input: nums = [0,1]


Output: 2
Explanation: n = 2 since there are 2 numbers, so all numbers are in the range [0,2]. 2 is the missing number in the range since it do

Example 3:

Input: nums = [9,6,4,2,3,5,7,0,1]


Output: 8
Explanation: n = 9 since there are 9 numbers, so all numbers are in the range [0,9]. 8 is the missing number in the range since it do

Constraints:

n == nums.length
1 <= n <= 104
0 <= nums[i] <= n
All the numbers of nums are unique.

Follow up: Could you implement a solution using only O(1) extra space complexity and
O(n) runtime complexity?

class Solution {
public int missingNumber(int[] nums) {
int n = nums.length;
int sum = n * (n + 1) / 2;

for(int i = 0; i < n; i++) {


sum -= nums[i];
}
return sum;
}
}
371. Sum of Two Integers
(https://leetcode.com/problems/sum-of-two-
integers/)
Medium

Given two integers a and b, return the sum of the two integers without using the operators + and -.

Example 1:

Input: a = 1, b = 2
Output: 3

Example 2:

Input: a = 2, b = 3
Output: 5

Constraints:

-1000 <= a, b <= 1000

Approach

Solution

Complexity Analysis
- Time Complexity:
- Space Complexity:

Resources
class UnionFind {
int[] root;
int[] rank;

UnionFind(int size) {
root = new int[size];
rank = new int[size];

for(int i = 0; i < size; i++) {


root[i] = i;
rank[i] = 0;
}
}

public int find(int x) {


if(x == root[x])
return x;
return root[x] = find(root[x]);
}

public void union(int x, int y) {


int rootX = find(x);
int rootY = find(y);

if(rootX != rootY) {
if(rank[rootX] > rank[rootY])
root[rootY] = rootX;
else if(rank[rootX] < rank[rootY])
root[rootX] = rootY;
else {
root[rootY] = rootX;
rank[rootX]++;
}
}
}

public boolean isConnected(int x, int y) {


return find(x) == find(y);
}
}

2290. Minimum Obstacle Removal to Reach


Corner
(https://leetcode.com/problems/minimum-
obstacle-removal-to-reach-corner/)
Hard

You are given a 0-indexed 2D integer array grid of size m x n. Each cell has one of two values:

0 represents an empty cell,


1 represents an obstacle that may be removed.
You can move up, down, left, or right from and to an empty cell.

Return the minimum number of obstacles to remove so you can move from the upper left corner (0, 0) to the lower right corner (m - 1, n - 1).

Example 1:
Input: grid = [[0,1,1],[1,1,0],[1,1,0]]
Output: 2
Explanation: We can remove the obstacles at (0, 1) and (0, 2) to create a path from (0, 0) to (2, 2).
It can be shown that we need to remove at least 2 obstacles, so we return 2.
Note that there may be other ways to remove 2 obstacles to create a path.

Example 2:

Input: grid = [[0,1,0,0,0],[0,1,0,1,0],[0,0,0,1,0]]


Output: 0
Explanation: We can move from (0, 0) to (2, 4) without removing any obstacles, so we return 0.

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 105
2 <= m * n <= 105
grid[i][j] is either 0 or 1.
grid[0][0] == grid[m - 1][n - 1] == 0

Approach
- Node class to store distance, row number, column number, done processing
- Map to store the key and the node for that key
- priority queue based on distance
- add the source to pq, with distance 0
- while pq not empty, poll a node
- if node is not done
- mark it done
- check all it's neighbour's distance with the node.dsitance+grid[neighbour's row][neighbour's col]
- update the distance of neighbour if it's greater than above calculated distance
- and add the neighbour into the pq

Solution
class Solution {
class Node {
int dist;
int i;
int j;
boolean done;
Node(int d, int ii, int jj, boolean done) {
dist = d;
i = ii;
j = jj;
done = done;
}
}

public int minimumObstacles(int[][] grid) {


int[][] dirs = {{0,-1},{-1,0},{0,1},{1,0}};
int n = grid.length, m = grid[0].length;
PriorityQueue<Node> pq = new PriorityQueue<>((a, b)->a.dist-b.dist);
Map<String, Node> mp = new HashMap<>();
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
String key = ""+i+","+j;
Node node = null;
if(i == 0 && j == 0)
node = new Node(0,i,j,false);
else
node = new Node(100001,i,j,false);
mp.put(key, node);
}
}

pq.offer(mp.get("0,0"));
while(!pq.isEmpty()) {
Node node = pq.poll();
if(node.done == false) {
node.done = true;
int i = node.i;
int j = node.j;
for(int[] dir:dirs) {
int new_i = i+dir[0];
int new_j = j+dir[1];
if(new_i >= 0 && new_i < n && new_j >= 0 && new_j < m) {
String neiK = ""+new_i+","+new_j;
Node nei = mp.get(neiK);
int cost = node.dist+grid[new_i][new_j];
if(cost < nei.dist) {
nei.dist = cost;
pq.offer(mp.get(neiK));
}
}
}
}

}
return mp.get(""+(n-1)+","+(m-1)).dist;
}
}

Complexity Analysis
- Time Complexity: O(n*m log (n*m))
- Space Complexity: O(n*m)
BFS on Graph
• Undiscovered – the vertex is in its initial, virgin state.

• Discovered – the vertex has been found, but we have not yet checked out all its incident edges.

• Processed – the vertex after we have visited all of its incident edges

public class Graph {


public Map<Integer, List<int[]>> adjList;

public int nvertices;

public int nedges;

public int[] degree;

public boolean directed;

public Graph(int v) {
degree = new int[v];
nvertices = v;
nedges = 0;
adjList = new HashMap<>();
for(int i = 0; i < v; i++) {
adjList.put(i, new ArrayList<>());
}
}

public Graph(int v, boolean directed) {


this(v);
if(directed)
this.directed = true;
}

public void insertEdge(int x, int y, int weight, boolean directed) {


List<int[]> list = adjList.get(x);
int[] tmp = new int[2];
tmp[0] = y;
tmp[1] = weight;
list.add(tmp);
adjList.put(x, list);
if(!directed) {
List<int[]> list2 = adjList.get(y);
int[] tmp2 = new int[2];
tmp2[0] = x;
tmp2[1] = weight;
list2.add(tmp2);
adjList.put(y, list2);
}
}

public void insertEdge(int x, int y, boolean directed) {


insertEdge(x, y, 0, directed);
}
}
public class BfsGraph {
boolean[] processed;
boolean[] discovered;
int[] parent;

Graph g;

public BfsGraph(Graph g) {
this.g = g;
processed = new boolean[g.nvertices];
discovered = new boolean[g.nvertices];
parent = new int[g.nvertices];
for(int i = 0; i < g.nvertices; i++)
parent[i] = -1;
}

public void bfs(int start) {

Queue<Integer> q = new LinkedList<>();


int v;
int y;

q.offer(start);
discovered[start] = true;

while (!q.isEmpty()) {
v = q.poll();
// process vertex early - v
System.out.println(v);
processed[v] = true;
List<int[]> neighbors = g.adjList.get(v);
for(int[] neighbor : neighbors) {
y = neighbor[0];
if(!processed[y] || g.directed) {
// process edge(v, y)
System.out.println(v+"-"+y);
}
if(!discovered[y]) {
q.offer(y);
discovered[y] = true;
parent[y] = v;
}
}
// process vertex late - v
}
}

public void processEdge(int x, int y) {


g.nedges++;
}

public void findPath(int start, int end) {


if(start == end || (end == -1))
System.out.println(start);
else {
findPath(start, parent[end]);
System.out.println(end);
}
}
}
Bellman Ford Algorithm for Single Source
Shortest Path
In graph theory, the Bellman-Ford (BF) algorithm is a Single Source Shortest Path (SSSP) algorithm. This means it can find the shortest path from one node to any other
node.However, BF is not ideal for most SSSP problems because it has a time complexity of O(EV). It is better to use Dijkstra’s algorithm which is much faster. It is on the order of
Θ((E+V)log(V)) when using a binary heap priority queue.

However, Dijkstra’s algorithm can fail when the graph has negative edge weights. This is when BF becomes really handy because it can be used to detect negative cycles and
determine where they occur.Finding negative cycles can be useful in many types of applications. One particularly neat application arises in finance when performing an
arbitragebetween two or more markets.

Approach
public class BellmanFordEdgeList {

// A directed edge
public static class Edge {
double cost;
int from, to;

public Edge(int from, int to, double cost) {


this.to = to;
this.from = from;
this.cost = cost;
}
}

/**
* An implementation of the Bellman-Ford algorithm. The algorithm finds the shortest path between
* a starting node and all other nodes in the graph. The algorithm also detects negative cycles.
* If a node is part of a negative cycle then the minimum cost for that node is set to
* Double.NEGATIVE_INFINITY.
*
* @param edges - An edge list containing directed edges forming the graph
* @param V - The number of vertices in the graph.
* @param start - The id of the starting node
*/
public static double[] bellmanFord(Edge[] edges, int V, int start) {

double[] dist = new double[V];


java.util.Arrays.fill(dist, Double.POSITIVE_INFINITY);
dist[start] = 0;

// Only in the worst case does it take V-1 iterations for the Bellman-Ford
// algorithm to complete. Another stopping condition is when we're unable to
// relax an edge, this means we have reached the optimal solution early.
boolean relaxedAnEdge = true;

// For each vertex, apply relaxation for all the edges


for (int v = 0; v < V - 1 && relaxedAnEdge; v++) {
relaxedAnEdge = false;
for (Edge edge : edges) {
if (dist[edge.from] + edge.cost < dist[edge.to]) {
dist[edge.to] = dist[edge.from] + edge.cost;
relaxedAnEdge = true;
}
}
}

// Run algorithm a second time to detect which nodes are part


// of a negative cycle. A negative cycle has occurred if we
// can find a better path beyond the optimal solution.
relaxedAnEdge = true;
for (int v = 0; v < V - 1 && relaxedAnEdge; v++) {
relaxedAnEdge = false;
for (Edge edge : edges) {
if (dist[edge.from] + edge.cost < dist[edge.to]) {
dist[edge.to] = Double.NEGATIVE_INFINITY;
relaxedAnEdge = true;
}
}
}

// Return the array containing the shortest distance to every node


return dist;
}
}
Complexity Analysis
- Time Complexity: O(V*E)
- Space Complexity: O(V)

KMP String matching algorithm


28. Implement strStr()
Easy

Implement strStr().

Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Clarification:

What should we return when needle is an empty string? This is a great question to ask during an interview.

For the purpose of this problem, we will return 0 when needle is an empty string. This is consistent to C's strstr() and Java's indexOf().

Example 1:

Input: haystack = "hello", needle = "ll" Output: 2 Example 2:

Input: haystack = "aaaaa", needle = "bba" Output: -1

Constraints:

1 <= haystack.length, needle.length <= 104


haystack and needle consist of only lowercase English characters.

Approach
- build pattern:
- we build the pattern for the substring
- it would be use in matching the pattern
- how:
- initialize two pointers, starting from 0(j) and 1(i)
- while i < len(substring)
- if s[i] == s[j] then set pattern[i] = j and increment both pointers, (which means we found a suffix that matches a prefix)
- else if j > 0 then set j to pattern[j-1]+1, (j-1 holds the last index where it had a successful match)
- else increment i
- matching pattern
- similar to build pattern
- how:
- initialize two pointers, starting from 0(j) points to sunstring and 0(i) points to string
- while i + len(substring) - j <= len(string)
- if s[i] == s[j]
- if j = len(substring)-1
- we found the match
- increment both pointers,
- else if j > 0 then set j to pattern[j-1]+1, (j-1 holds the last index where it had a successful match)
- else increment i

Solution
class Solution {
public int strStr(String haystack, String needle) {
int[] pattern = new int[needle.length()];
Arrays.fill(pattern, -1);
buildPattern(needle, pattern);
return matchIndex(haystack, needle, pattern);
}

private void buildPattern(String str, int[] pattern) {


int j = 0;
int i = 1;
while(i < str.length()) {
if(str.charAt(i) == str.charAt(j)) {
pattern[i] = j;
i++;
j++;
} else if(j > 0) {
j = pattern[j-1] + 1;
} else {
i++;
}
}
}

private int matchIndex(String str, String substr, int[] pattern) {


int j = 0, i = 0;

while(i + substr.length() - j <= str.length()) {


if(str.charAt(i) == substr.charAt(j)) {
if(j == substr.length()-1)
return i-j;
i++;
j++;
} else if(j > 0) {
j = pattern[j-1] + 1;
} else {
i++;
}
}
return -1;
}
}

Complexity Analysis
- Time Complexity: O(n+m), n - length of string, m - length of substring
- Space Complexity: O(m), to build the pattern array

Segment Tree
Introduction
Approach
Implementation
class SegmentTree { // the segment tree is stored like a heap array
private int[] st, A;
private int n;
private int left(int p) {
return p << 1;
} // same as binary heap operations
private int right(int p) {
return (p << 1) + 1;
}

private void build(int p, int L, int R) {


if (L == R) // as L == R, either one is fine
st[p] = L; // store the index
else { // recursively compute the values
build(left(p), L, (L + R) / 2);
build(right(p), (L + R) / 2 + 1, R);
int p1 = st[left(p)], p2 = st[right(p)];
st[p] = (A[p1] <= A[p2]) ? p1 : p2;
}
}

private int rmq(int p, int L, int R, int i, int j) { // O(log n)


if (i > R || j < L) return -1; // current segment outside query range
if (L >= i && R <= j) return st[p]; // inside query range

// compute the min position in the left and right part of the interval
int p1 = rmq(left(p), L, (L + R) / 2, i, j);
int p2 = rmq(right(p), (L + R) / 2 + 1, R, i, j);

if (p1 == -1) return p2; // if we try to access segment outside query


if (p2 == -1) return p1; // same as above
return (A[p1] <= A[p2]) ? p1 : p2;
} // as as in build routine

private int update_point(int p, int L, int R, int idx, int new_value) {


// this update code is still preliminary, i == j
// must be able to update range in the future!
int i = idx, j = idx;

// if the current interval does not intersect


// the update interval, return this st node value!
if (i > R || j < L)
return st[p];

// if the current interval is included in the update range,


// update that st[node]
if (L == i && R == j) {
A[i] = new_value; // update the underlying array
return st[p] = L; // this index
}

// compute the minimum position in the


// left and right part of the interval
int p1, p2;
p1 = update_point(left(p), L, (L + R) / 2, idx, new_value);
p2 = update_point(right(p), (L + R) / 2 + 1, R, idx, new_value);

// return the position where the overall minimum is


return st[p] = (A[p1] <= A[p2]) ? p1 : p2;
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

Fenwick Tree
Introduction
Approach
Implementation
class FenwickTree {
int[] ft, nums;

private int LSOne(int n) {


return (n & (-n));
}

public FenwickTree(int n, int[] nums) {


ft = new int[n + 1];
this.nums = nums;
build(nums);
}

private void build(int[] nums) {


for (int j = 0; j < nums.length; j++) {
for (int i = j + 1; i <= nums.length; i += LSOne(i)) {
ft[i] += nums[j];
}
}
}

private int rsq(int i) {


int sum = 0;
for (; i > 0; i -= LSOne(i))
sum += ft[i];
return sum;
}

public int sumRange(int i, int j) {


return rsq(j + 1) - rsq(i);
}

public void update(int ind, int val) {


int tmp = nums[ind];
nums[ind] = val;
ind++;
for (; ind < ft.length; ind += LSOne(ind))
ft[ind] += (val - tmp);
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:
Fenwick Tree
Introduction
Approach
Implementation
public class FenwickTree2D {
int[][] ft;

public FenwickTree2D(int[][] arr) {


int n = arr.length, m = arr[0].length;
ft = new int[n + 1][m + 1];
build(arr);
}

private int LSOne(int n) {


return (n & (-n));
}
private void build(int[][] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
update(i + 1, j + 1, arr[i][j]);
}
}
}

public void update(int row, int col, int value) {


for (int i = row; i < ft.length; i += LSOne(i)) {
for (int j = col; j < ft[0].length; j += LSOne(j)) {
ft[i][j] += value;
}
}
}

private int querySum(int row, int col) {


int sum = 0;
for (int i = row; i > 0; i -= LSOne(i)) {
for (int j = col; j > 0; j -= LSOne(j)) {
sum += ft[i][j];
}
}
return sum;
}

public int rangeSum(int x1, int y1, int x2, int y2) {
// Inclusion & Exclusion principle
return querySum(x2, y2) - querySum(x1 - 1, y2) - querySum(x2, y1 - 1) + querySum(x1 - 1, y1 - 1);
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:
NeetCode-150
1. Arrays & Hashing Resources
(https://github.com/dipjul/NeetCode-
150/blob/main/01.%20Arrays%20&%20Hashing/)
1. Contains Duplicate Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/01.%20ContainsDuplicate.md)
2. Valid Anagram Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/02.ValidAnagram.md)
3. Two Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/03.TwoSum.md)
4. Group Anagrams Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/04.GroupAnagrams.md)
5. Top K Frequent Elements Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/05.TopKElements.md)
6. Product of Array Except Self Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/06.ProductOfArrayExceptSelf.md)
7. Valid Sudoku Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/07.ValidSodoku.md)
8. Encode And Decode Strings Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/08.EncodeAndDecodeStrings.md)
9. Longest Consecutive Sequence Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/09.LongestConsecutiveSequence.md)

2. Two Pointers Resources (https://github.com/dipjul/NeetCode-


150/blob/main/02.%20Two%20Pointer/)
1. Valid Palindrome Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/01.ValidPalindrome.md)
2. Two Sum II Input Array Is Sorted Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/02.TwoSumII.md)
3. 3Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/03.3Sum.md)
4. Container With Most Water Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/04.ContainerWithMostWater.md)
5. Trapping Rain Water Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/05.TrappingRainWater.md)

3. Sliding Window Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/03.%20Sliding%20Window/)
1. Best Time to Buy And Sell Stock Solution (https://github.com/dipjul/NeetCode-150/blob/main/03.%20Sliding%20Window/01.BestTimeToBuy&SellStock.md)
2. Longest Substring Without Repeating Characters Solution (https://github.com/dipjul/NeetCode-
150/blob/main/03.%20Sliding%20Window/02.LongestSubstringWithoutRepeatingCharacters.md)
3. Longest Repeating Character Replacement Solution (https://github.com/dipjul/NeetCode-
150/blob/main/03.%20Sliding%20Window/03.LongestRepeatingCharacterReplacement.md)
4. Permutation In String Solution (https://github.com/dipjul/NeetCode-150/blob/main/03.%20Sliding%20Window/04.PermutationInString.md)
5. Minimum Window Substring Solution (https://github.com/dipjul/NeetCode-150/blob/main/03.%20Sliding%20Window/05.MinimumWindowSubstring.md)
6. Sliding Window Maximum Solution (https://github.com/dipjul/NeetCode-150/blob/main/03.%20Sliding%20Window/06.SlidingWindowMaximum.md)

4. Stack Resources (https://github.com/dipjul/NeetCode-


150/blob/main/04.%20Stack/)
1. Valid Parentheses Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/01.ValidParentheses.md)
2. Min Stack Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/02.MinStack.md)
3. Evaluate Reverse Polish Notation Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/03.EvaluateReversePolishNotation.md)
4. Generate Parentheses Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/04.GenerateParentheses.md)
5. Daily Temperatures Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/05.DailyTemperatures.md)
6. Car Fleet Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/06.CarFleet.md)
7. Largest Rectangle In Histogram Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/07.LargestRectangleInHistogram.md)
5. Binary Search Resources
(https://github.com/dipjul/NeetCode-
150/blob/main/05.%20Binary%20Search/)
1. Binary Search Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/01.BinarySearch.md)
2. Search a 2D Matrix Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/02.SearchIn2DMatrix.md)
3. Koko Eating Bananas Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/03.KokoEatingBananas.md)
4. Search In Rotated Sorted Array Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/04.SearchInRotatedSortedArray.md)
5. Find Minimum In Rotated Sorted Array Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/05.FindMinimumInRotatedSortedArray.md)
6. Time Based Key Value Store Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/06.TimeBasedKeyValueStore.md)
7. Median of Two Sorted Arrays Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/07.MedianOfTwoSortedArrays.md)

6. Lined List Resources (https://github.com/dipjul/NeetCode-


150/blob/main/06.%20LinkedList/)
1. Reverse Linked List Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/01.ReverseLinkedList.md)
2. Merge Two Sorted Lists Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/02.MergeTwoLinkedList.md)
3. Reorder List Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/03.ReorderList.md)
4. Remove Nth Node From End of List Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/04.RemoveNthNodeFromEndOfTheList.md)
5. Copy List With Random Pointer Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/05.CopyListWithRandomPointer.md)
6. Add Two Numbers Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/06.AddTwoNumbers.md)
7. Linked List Cycle Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/07.LinkedListCycle.md)
8. Find The Duplicate Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/08.FindDuplicateNumber.md)
9. LRU Cache Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/09.LRUCache.md)
10. Merge K Sorted Lists Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/10.MergeKSortedLists.md)
11. Reverse Nodes In K Group Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/11.ReverseNodesInK-Group.md)

7. Trees Resources (https://github.com/dipjul/NeetCode-


150/blob/main/07.%20Tree/)
1. Invert Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/01.InvertBinaryTree.md)
2. Maximum Depth of Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/02.MaxDepthOfBinaryTree.md)
3. Diameter of Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/03.DiameterOfABinaryTree.md)
4. Balanced Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/04.BalancedBinaryTree.md)
5. Same Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/05.SameTree.md)
6. Subtree of Another Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/06.SubtreeOfAnotherTree.md)
7. Lowest Common Ancestor of a Binary Search Tree Solution (https://github.com/dipjul/NeetCode-
150/blob/main/07.%20Tree/07.LowsetCommonAncestorOfABinarySearchTree.md)
8. Binary Tree Level Order Traversal Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/08.BinaryTreeLevelOrderTraversal.md)
9. Binary Tree Right Side View Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/09.BinaryTreeRightSideView.md)
10. Count Good Nodes In Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/10.CountGoodNodesInBinaryTree.md)
11. Validate Binary Search Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/11.ValidateBinarySearchTree.md)
12. Kth Smallest Element In a Bst Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/12.KthSmallestElementInABST.md)
13. Construct Binary Tree From Preorder And Inorder Traversal Solution (https://github.com/dipjul/NeetCode-
150/blob/main/07.%20Tree/13.ConstructBinaryTreeFromPreorderAndInorderTraversal.md)
14. Binary Tree Maximum Path Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/14.BinaryTreeMaximumPathSum.md)
15. Serialize And Deserialize Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/15.SerializeAndDeserializeBinaryTree.md)

8. Tries Resources (https://github.com/dipjul/NeetCode-


150/blob/main/08.%20Trie/)
1. Implement Trie Prefix Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/08.%20Trie/01.ImplementTrie.md)
2. Design Add And Search Words Data Structure Solution (https://github.com/dipjul/NeetCode-150/blob/main/08.%20Trie/02.DesignAddandSearchWordDataStructure.md)
3. Word Search II Solution ()
9. Heap / PriorityQueue Resources
(https://github.com/dipjul/NeetCode-
150/blob/main/09.%20Heap%20or%20PriorityQueue/)
1. Kth Largest Element In a Stream Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/01.kthLargestElementInAStream.md)
2. Last Stone Weight Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/02.LastStoneWeight.md)
3. K Closest Points to Origin Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/03.KClosestPointsToOrigin.md)
4. Kth Largest Element In An Array Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/04.KthLargestElementInAnArray.md)
5. Task Scheduler Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/05.TaskScheduler.md)
6. Design Twitter
7. Find Median From Data Stream Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/07.FindMedianFromDataStream.md)

10. Backtracking Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/10.%20Backtracking/)
1. Subsets Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/01.Subsets.md)
2. Combination Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/02.CombinationSum.md)
3. Permutations Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/03.Permutations.md)
4. Subsets II Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/04.SubsetsII.md)
5. Combination Sum II Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/05.CombinationSumII.md)
6. Word Search Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/06.WordSearch.md)
7. Palindrome Partitioning Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/07.PalindromePartitioning.md)
8. Letter Combinations of a Phone Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/08.LetterCombinationsOfAPhone.md)
9. N Queens Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/09.N-Queens.md)

11. Graphs Resources (https://github.com/dipjul/NeetCode-


150/blob/main/11.%20Graphs/)
1. Number of Islands Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/01.NumberOfIslands.md)
2. Clone Graph Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/02.CloneGraph.md)
3. Max Area of Island Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/03.MaxAreaOfIsland.md)
4. Pacific Atlantic Water Flow Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/04.PacificAtlanticWaterFlow.md)
5. Surrounded Regions Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/05.SurroundedRegions.md)
6. Rotting Oranges Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/06.RottenOranges.md)
7. Walls And Gates Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/07.WallsAndGates.md)
8. Course Schedule Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/08.CourseSchedule.md)
9. Course Schedule II Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/09.CourseScheduleII.md)
10. Redundant Connection Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/10.RedundantConnection.md)
11. Number of Connected Components In An Undirected Graph Solution (https://github.com/dipjul/NeetCode-
150/blob/main/11.%20Graphs/11.NumberOfConnectedComponentsInAnUndirectedGraph.md)
12. Graph Valid Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/12.GraphValidTree.md)
13. Word Ladder Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/13.WordLadder.md)

12. Advanced Graphs Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/12.%20Advanced%20Graphs/)
1. Reconstruct Itinerary Solution ()
2. Min Cost to Connect All Points Solution (https://github.com/dipjul/NeetCode-150/blob/main/12.%20Advanced%20Graphs/02.MinCostToConnectAllPoints.md)
3. Network Delay Time Solution (https://github.com/dipjul/NeetCode-150/blob/main/12.%20Advanced%20Graphs/03.NetworkDelayTime.md)
4. Swim In Rising Water Solution ()
5. Alien Dictionary Solution ()
6. Cheapest Flights Within K Stops Solution (https://github.com/dipjul/NeetCode-150/blob/main/12.%20Advanced%20Graphs/06.CheapestFlightsWithinKStops.md)
13. 1-D Dynamic Programming Resources
(https://github.com/dipjul/NeetCode-150/blob/main/13.%201-
D%20Dynamic%20Programming/)
1. Climbing Stairs Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/01.ClimbingStairs.md)
2. Min Cost Climbing Stairs Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/02.MinCostClimbingStair.md)
3. House Robber Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/03.HouseRobber.md)
4. House Robber II Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/04.HouseRobberII.md)
5. Longest Palindromic Substring Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/05.LongestPalindromicSubstring.md)
6. Palindromic Substrings Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/06.PalindromicSubstrings.md)
7. Decode Ways Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/07.DecodeWays.md)
8. Coin Change Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/08.CoinChange.md)
9. Maximum Product Subarray Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/09.MaxProductSubArray.md)
10. Word Break Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/10.WordBreak.md)
11. Longest Increasing Subsequence Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-
D%20Dynamic%20Programming/11.LongestIncreasingSubsequence.md)
12. Partition Equal Subset Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/12.PartitionEqualSubsetSum.md)

14. 2-D Dynamic Programming Resources


(https://github.com/dipjul/NeetCode-150/blob/main/14.%202-
D%20Dynamic%20Programming/)
1. Unique Paths Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-D%20Dynamic%20Programming/01.UniquePaths.md)
2. Longest Common Subsequence Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-
D%20Dynamic%20Programming/02.LongestCommonSubsequence.md)
3. Best Time to Buy And Sell Stock With Cooldown Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-
D%20Dynamic%20Programming/03.BestTimeToBuyAndSellStockWithCooldown.md)
4. Coin Change II Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-D%20Dynamic%20Programming/04.CoinChange2.md)
5. Target Sum
6. Interleaving String
7. Longest Increasing Path In a Matrix Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-
D%20Dynamic%20Programming/07.LongestIncreasingPathInAMatrix.md)
8. Distinct Subsequences Solution ()
9. Edit Distance Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-D%20Dynamic%20Programming/09.EditDistance.md)
10. Burst Balloons
11. Regular Expression Matching

15. Greedy Resources (https://github.com/dipjul/NeetCode-


150/blob/main/15.%20Greedy/)
1. Maximum Subarray Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/01.MaximumSubarray.md)
2. Jump Game Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/02.Jump.md)
3. Jump Game II Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/03.JumpII.md)
4. Gas Station Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/04.GasStation.md)
5. Hand of Straights Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/05.HandOfStraights.md)
6. Merge Triplets to Form Target Triplet Solution ()
7. Partition Labels Solution ()
8. Valid Parenthesis String Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/08.ValidParenthesisString.md)

16. Intervals Resources (https://github.com/dipjul/NeetCode-


150/blob/main/16.%20Intervals/)
1. Insert Interval Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/01.InsertInterval.md)
2. Merge Intervals Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/02.MergeInterval.md)
3. Non Overlapping Intervals Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/03.Non-OverlappingIntervals.md)
4. Meeting Rooms Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/04.MeetingRooms.md)
5. Meeting Rooms II Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/05.MeetingRoomsII.md)
6. Minimum Interval to Include Each Query

17. Math & Geometry Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/17.%20Math%20&%20Geometry/)
1. Rotate Image Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/01.RotateImage.md)
2. Spiral Matrix Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/02.SpiralMatrix.md)
3. Set Matrix Zeroes Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/03.SetMatrixZeroes.md)
4. Happy Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/04.HappyNumber.md)
5. Plus One Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/05.PlusOne.md)
6. Pow(x, n) Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/06.Pow(x,n).md)
7. Multiply Strings Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/07.MultiplyStrings.md)
8. Detect Squares

18. Bit Manipulation Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/18.%20Bit%20Manipulation/)
1. Single Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/01.SingleNumber.md)
2. Number of 1 Bits Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/02.NumberOf1Bits.md)
3. Counting Bits Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/03.CountingBits.md)
4. Reverse Bits Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/04.ReverseBits.md)
5. Missing Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/05.MissingNumber.md)
6. Sum of Two Integers Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/06.SumOfTwoIntegers.md)
7. Reverse Integer

19. Misc-DSA (Not from Neetcode)


1. Union Find (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/01.UnionFind.md)
2. Dijkstra's Algorithm (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/02.Dijkstra%27s%20Algorithm.md)
3. BFS on Graph (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/03.BFS-Graph.md)
4. Bellman Ford (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/04.Bellman-ford.md)
5. KMP Algorithm (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/05.KMP-Algorithm.md)
6. Segment Tree (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/06.SegmentTree.md)
7. Fenwick Tree (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/07.FenwickTree.md)
8. Fenwick Tree 2D (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/08.FenwickTree2D.md)

217. Contains Duplicate


Easy

Given an integer array nums, return true if any value appears at least twice in the array, and return false if every element is distinct.

Example 1:

Input: nums = [1,2,3,1]


Output: true

Example 2:

Input: nums = [1,2,3,4]


Output: false

Example 3:
Input: nums = [1,1,1,3,3,4,3,2,4,2]
Output: true

Constraints:

1 <= nums.length <= 105


-109 <= nums[i] <= 109

class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int num : nums) {
if(!set.add(num))
return true;
}
return false;
}
}

242. Valid Anagram


Easy

Given two strings s and t, return true if t is an anagram of s, and false otherwise.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

Example 1:

Input: s = "anagram", t = "nagaram"


Output: true

Example 2:

Input: s = "rat", t = "car"


Output: false

Constraints:

1 <= s.length, t.length <= 5 * 104


s and t consist of lowercase English letters.

class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length())
return false;

int[] chars = new int[26];

for(int i = 0; i < s.length(); i++) {


chars[s.charAt(i)-'a']++;
chars[t.charAt(i)-'a']--;
}

for(int n : chars) {
if(n != 0)
return false;
}

return true;
}
}
Follow up: What if the inputs contain Unicode characters? How would you adapt your
solution to such a case?
We can be used a map, or a array of size equal to the characterset size of unicode.

1. Two Sum
Easy

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

You can return the answer in any order.

Example 1:

Input: nums = [2,7,11,15], target = 9


Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6


Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6


Output: [0,1]

Constraints:

2 <= nums.length <= 104


-109 <= nums[i] <= 109
-109 <= target <= 109
Only one valid answer exists.

Follow-up: Can you come up with an algorithm that is less than O(n2) time complexity?

class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
Map<Integer, Integer> map = new HashMap<>();

for(int i = 0; i < n; i++) {


int remaining = target-nums[i];
if(map.containsKey(remaining)) {
return new int[] {map.get(remaining), i};
}
map.put(nums[i], i);
}
return new int[]{-1, -1};
}
}

49. Group Anagrams


Medium

Given an array of strings strs, group the anagrams together. You can return the answer in any order.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.
Example 1:

Input: strs = ["eat","tea","tan","ate","nat","bat"]


Output: [["bat"],["nat","tan"],["ate","eat","tea"]]

Example 2:

Input: strs = [""]


Output: [[""]]

Example 3:

Input: strs = ["a"]


Output: [["a"]]

Constraints:

1 <= strs.length <= 104


0 <= strs[i].length <= 100
strs[i] consists of lowercase English letters.

class Solution {
// 1
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> res = new ArrayList<>();
Map<String, List<String>> mp = new HashMap<>();
for (String str : strs) {
char[] c = str.toCharArray();
Arrays.sort(c);
String r = new String(c);
if (!mp.containsKey(r))
mp.put(r, new ArrayList<String>());
mp.get(r).add(str);
}
for (String key : mp.keySet()) {
res.add(mp.get(key));
}

return res;
}

// 2
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> res = new ArrayList<>();
Map<String, List<String>> mp = new HashMap<>();
for (String str : strs) {
char[] hash = new char[26];
for(char ch : str.toCharArray())
hash[ch-'a']++;
String r = new String(hash);
if (!mp.containsKey(r))
mp.put(r, new ArrayList<String>());
mp.get(r).add(str);
}
for (String key : mp.keySet()) {
res.add(mp.get(key));
}

return res;
}

}
347. Top K Frequent Elements
Medium

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.

Example 1:

Input: nums = [1,1,1,2,2,3], k = 2


Output: [1,2]

Example 2:

Input: nums = [1], k = 1


Output: [1]

Constraints:

1 <= nums.length <= 105


k is in the range [1, the number of unique elements in the array].
It is guaranteed that the answer is unique.

Follow up: Your algorithm's time complexity must be better than O(n log n), where n is the
array's size.

class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> mp = new HashMap<>();
int[] res = new int[k];
for(int num : nums) {
mp.put(num, mp.getOrDefault(num,0)+1);
}
PriorityQueue<Pair> pq = new PriorityQueue<>((a,b)->b.val-a.val);
for(int key : mp.keySet())
pq.offer(new Pair(key, mp.get(key)));
for(int i = 0; i < k; i++)
res[i] = pq.poll().key;

return res;
}
}

class Pair {
int key;
int val;

Pair(int k, int v) {
key = k;
val = v;
}
}

238. Product of Array Except Self


Medium

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].

The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.

You must write an algorithm that runs in O(n) time and without using the division operation.
Example 1:

Input: nums = [1,2,3,4]


Output: [24,12,8,6]

Example 2:

Input: nums = [-1,1,0,-3,3]


Output: [0,0,9,0,0]

Constraints:

2 <= nums.length <= 105


-30 <= nums[i] <= 30
The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.

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.)

class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] res = new int[n];
int pre = 1;
res[0] = 1;
for(int i = 0; i < n-1; i++) {
pre *= nums[i];
res[i+1] = pre;
}
int post = 1;
for(int i = n-1; i > 0; i--) {
post *= nums[i];
res[i-1] *= post;
}
return res;
}
}

36. Valid Sudoku


Medium

Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

Each row must contain the digits 1-9 without repetition.


Each column must contain the digits 1-9 without repetition.
Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition.

Note:

A Sudoku board (partially filled) could be valid but is not necessarily solvable.
Only the filled cells need to be validated according to the mentioned rules.

Example 1:
Input: board =
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true

Example 2:

Input: board =
[["8","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: false

Explanation: Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8's in the top left 3x3 sub-box, it is invalid.

Constraints:

board.length == 9
board[i].length == 9
board[i][j] is a digit 1-9 or '.'.
// My Solution
class Solution {
public boolean isValidSudoku(char[][] board) {

for(int i = 0; i < 9; i++) {


for(int j = 0; j < 9; j++) {
char c = board[i][j];
if(isDigit(c) && !isValid(c, i, j, board)) {
return false;
}
}
}

return true;
}

private boolean isDigit(char c) {


if(c-'0' > 9 || c-'0' < 0)
return false;
return true;
}

private boolean isValid(char c, int i, int j, char[][] board) {


if(isValidRow(c, i, j, board) && isValidCol(c, i, j, board) && isValidGrid(c, i, j, board))
return true;
return false;
}

private boolean isValidRow(char c, int row, int col, char[][] board) {


for(int i = 0; i < 9; i++) {
if(board[row][i] == c && i != col)
return false;
}
return true;
}

private boolean isValidCol(char c, int row, int col, char[][] board) {


for(int i = 0; i < 9; i++) {
if(board[i][col] == c && i != row)
return false;
}
return true;
}

private boolean isValidGrid(char c, int row, int col, char[][] board) {


int initialRow = 3*(row/3), initialCol = 3*(col/3);

for(int i = initialRow; i < initialRow+3; i++) {


for(int j = initialCol; j < initialCol+3; j++) {
if(board[i][j] == c && i != row && j != col)
return false;
}
}
return true;
}
}
// Using Set
public boolean isValidSudoku(char[][] board) {
Set seen = new HashSet();
for (int i=0; i<9; ++i) {
for (int j=0; j<9; ++j) {
char number = board[i][j];
if (number != '.')
if (!seen.add(number + " in row " + i) ||
!seen.add(number + " in column " + j) ||
!seen.add(number + " in block " + i/3 + "-" + j/3))
return false;
}
}
return true;
}

659 · Encode and Decode Strings


Description Design an algorithm to encode a list of strings to a string. The encoded string is then sent over the network and is decoded back to the original list of strings.

Please implement encode and decode

Example1

Input: ["lint","code","love","you"]
Output: ["lint","code","love","you"]
Explanation:
One possible encode method is: "lint:;code:;love:;you"

Example2

Input: ["we", "say", ":", "yes"]


Output: ["we", "say", ":", "yes"]
Explanation:
One possible encode method is: "we:;say:;:::;yes"
public class Solution {
/*
* @param strs: a list of strings
* @return: encodes a list of strings to a single string.
*/
public String encode(List<String> strs) {

StringBuilder sb = new StringBuilder();


char seperator = '`';
for(String str : strs) {
sb.append(str.length());
sb.append(seperator);
sb.append(str);
}
return sb.toString();
}

/*
* @param str: A string
* @return: dcodes a single string to a list of strings
*/
public List<String> decode(String str) {

List<String> strs = new ArrayList<>();


int i = 0;
while(i < str.length()) {
int j = i;
while(str.charAt(j) != '`')
j++;
int len = Integer.parseInt(str.substring(i, j));
strs.add(str.substring(j+1, j+1+len));
i = j + 1 + len;
}
return strs;
}
}

128. Longest Consecutive Sequence


Medium

Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence.

You must write an algorithm that runs in O(n) time.

Example 1:

Input: nums = [100,4,200,1,3,2]


Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.

Example 2:

Input: nums = [0,3,7,2,5,8,4,6,0,1]


Output: 9

Constraints:

0 <= nums.length <= 105


-109 <= nums[i] <= 109
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet<>();
int result = 0;
for(int num : nums)
set.add(num);
int count = 0;
for(int num : nums) {
if(!set.contains(num-1)) {
while(set.contains(num++))
count++;
result = Math.max(result, count);
count = 0;
}
}
return result;
}
}

Resources
Youtube (https://youtu.be/shs0KM3wKv8)
Leetcode Discussion (https://leetcode.com/discuss/general-discussion/1068545/HASH-TABLE-and-MAP-POWERFUL-GUIDE-!!!)

125. Valid Palindrome


Easy

A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward.
Alphanumeric characters include letters and numbers.

Given a string s, return true if it is a palindrome, or false otherwise.

Example 1:

Input: s = "A man, a plan, a canal: Panama"


Output: true
Explanation: "amanaplanacanalpanama" is a palindrome.

Example 2:

Input: s = "race a car"


Output: false
Explanation: "raceacar" is not a palindrome.

Example 3:

Input: s = " "


Output: true
Explanation: s is an empty string "" after removing non-alphanumeric characters.
Since an empty string reads the same forward and backward, it is a palindrome.

Constraints:

1 <= s.length <= 2 * 105


s consists only of printable ASCII characters.
class Solution {
public boolean isPalindrome(String s) {
int i = 0, j = s.length()-1;
while(i < j) {
while(i < j && !Character.isLetterOrDigit(s.charAt(i)))
i++;
while(i < j && !Character.isLetterOrDigit(s.charAt(j)))
j--;
if(Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j)))
return false;
i++;
j--;
}

return true;
}
}

167. Two Sum II - Input Array Is Sorted


Medium

Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number. Let these two
numbers be numbers[index1] and numbers[index2] where 1 <= index1 < index2 <= numbers.length.

Return the indices of the two numbers, index1 and index2, added by one as an integer array [index1, index2] of length 2.

The tests are generated such that there is exactly one solution. You may not use the same element twice.

Your solution must use only constant extra space.

Example 1:

Input: numbers = [2,7,11,15], target = 9


Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore, index1 = 1, index2 = 2. We return [1, 2].

Example 2:

Input: numbers = [2,3,4], target = 6


Output: [1,3]
Explanation: The sum of 2 and 4 is 6. Therefore index1 = 1, index2 = 3. We return [1, 3].

Example 3:

Input: numbers = [-1,0], target = -1


Output: [1,2]
Explanation: The sum of -1 and 0 is -1. Therefore index1 = 1, index2 = 2. We return [1, 2].

Constraints:

2 <= numbers.length <= 3 * 104


-1000 <= numbers[i] <= 1000
numbers is sorted in non-decreasing order.
-1000 <= target <= 1000
The tests are generated such that there is exactly one solution.
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0, right = numbers.length-1;

while(left < right) {


int sum = numbers[left] + numbers[right];
if(sum == target) {
return new int[]{left+1, right+1};
} else if(sum < target)
left++;
else
right--;
}

return new int[]{-1, -1};


}
}

15. 3Sum
Medium

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

Notice that the solution set must not contain duplicate triplets.

Example 1:

Input: nums = [-1,0,1,2,-1,-4]


Output: [[-1,-1,2],[-1,0,1]]

Example 2:

Input: nums = []
Output: []

Example 3:

Input: nums = [0]


Output: []

Constraints:

0 <= nums.length <= 3000


-105 <= nums[i] <= 105
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length-2; i++) {
if(i > 0 && nums[i] == nums[i-1])
continue;
search(i, nums, result);
}
return result;
}

private void search(int index, int[] nums, List<List<Integer>> result) {


int left = index+1, right = nums.length-1;
while(left < right) {
int sum = nums[index] + nums[left] + nums[right];
if(sum == 0) {
result.add(Arrays.asList(nums[index], nums[left], nums[right]));
left++;
right--;
while(left < right && nums[left]==nums[left-1])
left++;
while(left < right && nums[right]==nums[right+1])
right--;
} else if(sum < 0) {
left++;
} else {
right--;
}
}
}
}

11. Container With Most Water


Medium

You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]).

Find two lines that together with the x-axis form a container, such that the container contains the most water.

Return the maximum amount of water a container can store.

Notice that you may not slant the container.

Example 1:

Input: height = [1,8,6,2,5,4,8,3,7]


Output: 49
Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section

Example 2:

Input: height = [1,1]


Output: 1

Constraints:

n == height.length
2 <= n <= 105
0 <= height[i] <= 104
class Solution {
public int maxArea(int[] height) {
int area = 0, maxArea = 0;
int left = 0, right = height.length-1;

while(left < right) {


area = Math.min(height[left], height[right])*(right-left);
maxArea = Math.max(maxArea, area);
if(height[left] < height[right]) {
left++;
} else {
right--;
}
}

return maxArea;
}
}

42. Trapping Rain Water


(https://leetcode.com/problems/trapping-rain-
water/)
Hard

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.

Example 1:

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]


Output: 6
Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1].
In this case, 6 units of rain water (blue section) are being trapped.

Example 2:

Input: height = [4,2,0,3,2,5]


Output: 9

Constraints:

n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105

Approach
First Approach:

- leftMax array to keep the max element on the left of the index
- rightMax array to keep the max element on the right of the index
- at each index take the min(leftMax, rightMax) - heigh[index], if it's greater than 0 add it to ans
- Time : O(N)
- Space: O(N)

Second Approach:
- instead of keep arrays to store the leftmax & rightMax, we can use two variables
- if leftMax <= rightMax
- try to update leftMax
- if min(leftMax, rightMax) - heigh[index] > 0, add it to ans
- move the left pointer ahead
- else
- same as above just replacing the left's by right
- decrement the right pointer

Solution
class Solution {
public int trap(int[] height) {
int left = 0, right = height.length-1;
int lMax = height[0], rMax = height[right];
int ans = 0;
while(left <= right) {
if(lMax <= rMax) {
lMax = Math.max(lMax, height[left]);
int val = Math.min(lMax, rMax)-height[left];
ans += val < 0 ? 0 : val;
left++;
} else {
rMax = Math.max(rMax, height[right]);
int val = Math.min(lMax, rMax)-height[right];
ans += val < 0 ? 0 : val;
right--;
}
}
return ans;
}
}

Complexity Analysis
- Time Complexity: O(N) -> go through each elements of the height array
- Space Complexity: O(1)

Resources
Youtube (https://youtu.be/-gjxg6Pln50)
Leetcode Discussion (https://leetcode.com/discuss/study-guide/1688903/Solved-all-two-pointers-problems-in-100-days.)

121. Best Time to Buy and Sell Stock


Easy

You are given an array prices where prices[i] is the price of a given stock on the ith day.

You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.

Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.

Example 1:
Input: prices = [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell.

Example 2:

Input: prices = [7,6,4,3,1]


Output: 0
Explanation: In this case, no transactions are done and the max profit = 0.

Constraints:

1 <= prices.length <= 105


0 <= prices[i] <= 104

class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if(n == 1) return 0;
int maxP = 0;
int l = 0, r = 1;

while(r < n) {
if(prices[l] < prices[r]) {
int profit = prices[r] - prices[l];
maxP = Math.max(maxP, profit);
} else
l = r;
r++;
}
return maxP;
}
}

3. Longest Substring Without Repeating


Characters
Medium

Given a string s, find the length of the longest substring without repeating characters.

Example 1:

Input: s = "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.

Example 2:

Input: s = "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.

Example 3:

Input: s = "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Notice that the answer must be a substring, "pwke" is a subsequence and not a substring.
Constraints:

0 <= s.length <= 5 * 104


s consists of English letters, digits, symbols and spaces.

class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> mp = new HashMap<>();
int maxLen = 0, winStart = 0;

for(int winEnd = 0; winEnd < s.length(); winEnd++) {


char ch = s.charAt(winEnd);
if(mp.containsKey(ch)) {
winStart = Math.max(winStart, mp.get(ch)+1);
}
mp.put(ch, winEnd);
maxLen = Math.max(winEnd - winStart + 1, maxLen);
}
return maxLen;
}
}

424. Longest Repeating Character


Replacement
Medium

You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most
k times.

Return the length of the longest substring containing the same letter you can get after performing the above operations.

Example 1:

Input: s = "ABAB", k = 2
Output: 4
Explanation: Replace the two 'A's with two 'B's or vice versa.

Example 2:

Input: s = "AABABBA", k = 1
Output: 4
Explanation: Replace the one 'A' in the middle with 'B' and form "AABBBBA".
The substring "BBBB" has the longest repeating letters, which is 4.

Constraints:

1 <= s.length <= 105


s consists of only uppercase English letters.
0 <= k <= s.length

Notes
- use a map to store the frequency
- maxRepeatingCharCount = max(maxRepeatingCharCount, mp.get(char))
- slide the left pointer until the equation satifies: windowEnd - windowStart + 1 - maxRepeatingCharCount > k
- maxLen = max(maxLen, windowEnd - windowStart + 1)

Solution:
class Solution {
public int characterReplacement(String s, int k) {
int windowStart = 0, maxLength = 0, mostRepeatingCharCount = 0;

HashMap<Character, Integer> charFreqMap = new HashMap();

for(int windowEnd = 0; windowEnd < s.length(); windowEnd++) {


char right = s.charAt(windowEnd);
charFreqMap.put(right, charFreqMap.getOrDefault(right, 0)+1);
mostRepeatingCharCount = Math.max(mostRepeatingCharCount, charFreqMap.get(right));

while(windowEnd-windowStart+1 - mostRepeatingCharCount > k) {


char left = s.charAt(windowStart);
charFreqMap.put(left, charFreqMap.get(left)-1);
if(charFreqMap.get(left) == 0)
charFreqMap.remove(left);
windowStart++;
}
maxLength = Math.max(maxLength, windowEnd-windowStart+1);
}

return maxLength;
}
}

567. Permutation in String


Medium

Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise.

In other words, return true if one of s1's permutations is the substring of s2.

Example 1:

Input: s1 = "ab", s2 = "eidbaooo"


Output: true
Explanation: s2 contains one permutation of s1 ("ba").

Example 2:

Input: s1 = "ab", s2 = "eidboaoo"


Output: false

Constraints:

1 <= s1.length, s2.length <= 104


s1 and s2 consist of lowercase English letters.

Approach
- 2 array of size 26 to keep count of chars present in both the strings[s1 and substring of s2]
- match keeps the number of characters matches in both s1 and substring of s2
- if matches = 26, we have a permutation in that window
- on each window, we check for conditions [ for both the chars(at left and at right) ]:
- increment s2Map[rightCharInd]++ and decrement s2Map[leftCharInd]--
- if the char's count are same increment the matches
- if the char's count differ by 1
- right: s1Map[rightCharInd] + 1 == s2Map[rightCharInd],
- left: s1Map[leftCharInd] - 1 == s2Map[leftCharInd] decrement the matches
Solution
class Solution {
public boolean checkInclusion(String s1, String s2) {
if(s1.length() > s2.length())
return false;
char[] s1Map = new char[26];
char[] s2Map = new char[26];

for(int i = 0; i < s1.length(); i++) {


s1Map[s1.charAt(i)-'a']++;
s2Map[s2.charAt(i)-'a']++;
}

int matches = 0;
for(int i = 0; i < 26; i++) {
if(s1Map[i] == s2Map[i])
matches++;
}
int windowStart = 0;
for(int windowEnd = s1.length(); windowEnd < s2.length(); windowEnd++) {
if(matches == 26) return true;
int rightCharInd = s2.charAt(windowEnd)-'a';
s2Map[rightCharInd]++;
if(s1Map[rightCharInd] == s2Map[rightCharInd])
matches++;
else if(s1Map[rightCharInd] + 1 == s2Map[rightCharInd])
matches--;

int leftCharInd = s2.charAt(windowStart)-'a';


s2Map[leftCharInd]--;
if(s1Map[leftCharInd] == s2Map[leftCharInd])
matches++;
else if(s1Map[leftCharInd] - 1 == s2Map[leftCharInd])
matches--;

windowStart++;
}

return matches == 26;


}
}

Complexity Analysis:
Time Complexity: O(s2.length())
Space Complexity: O(26) ~ O(1)

76. Minimum Window Substring


(https://leetcode.com/problems/minimum-
window-substring/)
Hard

Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If
there is no such substring, return the empty string "".
The testcases will be generated such that the answer is unique.

A substring is a contiguous sequence of characters within the string.

Example 1:

Input: s = "ADOBECODEBANC", t = "ABC"


Output: "BANC"
Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t.

Example 2:

Input: s = "a", t = "a"


Output: "a"
Explanation: The entire string s is the minimum window.

Example 3:

Input: s = "a", t = "aa"


Output: ""
Explanation: Both 'a's from t must be included in the window.
Since the largest window of s only has one 'a', return empty string.

Constraints:

m == s.length
n == t.length
1 <= m, n <= 105
s and t consist of uppercase and lowercase English letters.

Approach
- Try to increase the window untill we have all the characters of the pattern
- Once we get the pattern, we update the result if length is less than earlier
- Shrink the window while the window have all the required characters & update the result

Solution
Approach 1
class Solution {
public String minWindow(String s, String t) {
int winS = 0, winE = 0;
String ans = "";
Map<Character, Integer> tMp = new HashMap<>();
Map<Character, Integer> wMp = new HashMap<>();
for(char c : t.toCharArray()) {
tMp.put(c, tMp.getOrDefault(c, 0)+1);
}
while(winS < s.length() && winE < s.length()) {
char c = s.charAt(winE);
wMp.put(c, wMp.getOrDefault(c, 0)+1);
while(winS <= winE && satisfy(wMp, tMp)) {
if(ans == "")
ans = s.substring(winS, winE+1);
ans = (winE-winS+1) < ans.length()?s.substring(winS, winE+1):ans;
wMp.put(s.charAt(winS), wMp.get(s.charAt(winS))-1);
if(wMp.get(s.charAt(winS)) == 0)
wMp.remove(s.charAt(winS));
winS++;
}
winE++;
}
return ans;
}

private boolean satisfy(Map<Character, Integer> wMp, Map<Character, Integer> tMp) {


for(char c : tMp.keySet()) {
if(!wMp.containsKey(c) || wMp.get(c) < tMp.get(c))
return false;
}
return true;
}
}

Approach 2 (Optimized)
class Solution {
public String minWindow(String str, String pattern) {
int windowStart = 0, minLen = Integer.MAX_VALUE, matched = 0;
Map<Character, Integer> charFreqMap = new HashMap<>();
String res = "";
for (char ch : pattern.toCharArray())
charFreqMap.put(ch, charFreqMap.getOrDefault(ch, 0) + 1);

for (int windowEnd = 0; windowEnd < str.length(); windowEnd++) {


char right = str.charAt(windowEnd);

if (charFreqMap.containsKey(right)) {
charFreqMap.put(right, charFreqMap.get(right) - 1);
if (charFreqMap.get(right) == 0)
matched++;
}

while (matched == charFreqMap.size()) {


if (minLen > windowEnd - windowStart + 1) {
minLen = windowEnd - windowStart + 1;
res = str.substring(windowStart, windowEnd + 1);
}

char left = str.charAt(windowStart);


if (charFreqMap.containsKey(left)) {
if (charFreqMap.get(left) == 0)
matched--;
charFreqMap.put(left, charFreqMap.get(left) + 1);
}
windowStart++;
}
}
return res;
}
}

Complexity Analysis
Approach 1:
- Time Complexity: O(N*K), N: length of str, K: length of pattern
- Space Complexity: O(K), K: length of pattern
Approach 2:
- Time Complexity: O(N), N: length of str
- Space Complexity: O(K), K: length of pattern

239. Sliding Window Maximum


(https://leetcode.com/problems/sliding-
window-maximum/)
Hard

You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the
window. Each time the sliding window moves right by one position.

Return the max sliding window.

Example 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

Example 2:

Input: nums = [1], k = 1


Output: [1]

Constraints:

1 <= nums.length <= 105


-104 <= nums[i] <= 104
1 <= k <= nums.length

Approach
- Use Deque, to keep the elements
- we'll keep the elemenet in decreasing order
- while the element at the first of the queue, i.e the index of the nums,
if it's out of the window, keep removing the element
- while the element at the last of the queue, i.e the index,
if it's less than equal to new element of the nums, keep removing the element
- insert the index of new element of nums
- if wE greater than k-1, then add the first element (index of the element in nums)
of the queue to res

Solution
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
int wS = 0, s = 0;
ArrayDeque<Integer> q = new ArrayDeque<>();

for (int wE = 0; wE < nums.length; wE++) {


// while the element at the first of the queue, i.e the index,
// if it's out of the window, keep removing the element
while(!q.isEmpty() && q.peekFirst() <= wE-k)
q.pollFirst();

// while the element at the last of the queue, i.e the index,
// if it's less than equal to new element of the nums,
// keep removing the element
while(!q.isEmpty() && nums[q.peekLast()] <= nums[wE])
q.pollLast();

// insert the index of new element of nums


q.offerLast(wE);

// if wE greater than k-1, then add the first element


// (index of the element in nums) of the queue to res
if(wE >= k-1)
res[s++] = nums[q.peekFirst()];
}
return res;
}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(k), where k < n

Resources
Leetcode - template (https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-%27substring%27-problems)
Leetcode - Overview & Question bank (https://leetcode.com/discuss/study-guide/1773891/Sliding-Window-Technique-and-Question-Bank)
Youtube Playlist - Hindi (https://youtube.com/playlist?list=PL_z_8CaSLPWeM8BDJmIYDaoQ5zuwyxnfj)
Youtube Playlist - Neetcode (https://youtube.com/playlist?list=PLot-Xpze53leOBgcVsJBEGrHPd_7x_koV)

20. Valid Parentheses


Easy

Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

An input string is valid if:

Open brackets must be closed by the same type of brackets.


Open brackets must be closed in the correct order.

Example 1:

Input: s = "()"
Output: true

Example 2:
Input: s = "()[]{}"
Output: true

Example 3:

Input: s = "(]"
Output: false

Constraints:

1 <= s.length <= 104


s consists of parentheses only '()[]'.

class Solution {
public boolean isValid(String s) {
Stack<Character> st = new Stack<>();

for(int i = 0; i < s.length(); i++) {


char c = s.charAt(i);
if(c == '(' || c == '{' || c == '[')
st.push(c);
else if(c == ')' && !st.empty()) {
char ch = st.pop();
if(ch != '(')
return false;
} else if(c == '}' && !st.empty()) {
char ch = st.pop();
if(ch != '{')
return false;
} else if(c == ']' && !st.empty()) {
char ch = st.pop();
if(ch != '[')
return false;
} else
return false;
}

return st.empty();
}
}

155. Min Stack


Easy

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

Implement the MinStack class:

MinStack() initializes the stack object.


void push(int val) pushes the element val onto the stack.
void pop() removes the element on the top of the stack.
int top() gets the top element of the stack.
int getMin() retrieves the minimum element in the stack.

Example 1:
Input
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

Output
[null,null,null,null,-3,null,0,-2]

Explanation
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); // return -3
minStack.pop();
minStack.top(); // return 0
minStack.getMin(); // return -2

Constraints:

-231 <= val <= 231 - 1


Methods pop, top and getMin operations will always be called on non-empty stacks.
At most 3 * 104 calls will be made to push, pop, top, and getMin.
class Pair {
int val;
int min;

Pair(int v, int m) {
val = v;
min = m;
}

void setVal(int v) {
val = v;
}

void setMin(int m) {
min = m;
}
}
class MinStack {
Stack<Pair> st;
public MinStack() {
st = new Stack<>();
}

public void push(int val) {


if(st.empty())
st.push(new Pair(val, val));
else {
int min = Math.min(st.peek().min, val);
st.push(new Pair(val,min));
}
}

public void pop() {


st.pop();
}

public int top() {


return st.peek().val;
}

public int getMin() {


return st.peek().min;
}
}

/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/

150. Evaluate Reverse Polish Notation


Medium

Evaluate the value of an arithmetic expression in Reverse Polish Notation.

Valid operators are +, -, *, and /. Each operand may be an integer or another expression.

Note that division between two integers should truncate toward zero.

It is guaranteed that the given RPN expression is always valid. That means the expression would always evaluate to a result, and there will not be any division by zero operation.
Example 1:

Input: tokens = ["2","1","+","3","*"]


Output: 9
Explanation: ((2 + 1) * 3) = 9

Example 2:

Input: tokens = ["4","13","5","/","+"]


Output: 6
Explanation: (4 + (13 / 5)) = 6

Example 3:

Input: tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]


Output: 22
Explanation: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

Constraints:

1 <= tokens.length <= 104


tokens[i] is either an operator: "+", "-", "*", or "/", or an integer in the range [-200, 200].

Solution
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> st = new Stack<>();
for(String s : tokens) {
if(s.equals("+")) {
st.push(st.pop()+st.pop());
} else if(s.equals("-")) {
int n2 = st.pop();
int n1 = st.pop();
st.push(n1-n2);
} else if(s.equals("*")) {
st.push(st.pop()*st.pop());
} else if(s.equals("/")) {
int n2 = st.pop();
int n1 = st.pop();
st.push(n1/n2);
} else {
st.push(Integer.parseInt(s));
}
}
return st.peek();
}
}

Comlexity Analysis
Time Complexity: O(N) -> traversing through all the tokens
Space Complexity: O(N) -> for the stack
22. Generate Parentheses
Medium

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

Feels like mismatch for the pattern satck

Example 1:

Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]

Example 2:

Input: n = 1
Output: ["()"]

Constraints:

1 <= n <= 8

Approach
- Backtracking
- add '(' always and add ')' only if count('(') > count(')')
- add the string into the result when count('(') > count(')') && count('(') == n
- base case: count('(') > n || count(')') > n

Solution
class Solution {
public List<String> generateParenthesis(int n) {
List<String> ans = new ArrayList<>();
generate(0, n, 0, 0, ans, "");
return ans;
}

private void generate(int index, int n, int lCount, int rCount, List<String> ans, String op) {
if(lCount > n || rCount > n)
return;
if(lCount == rCount && lCount == n) {
ans.add(op);
}
if(lCount > rCount) {
String op1 = op + ")";
generate(index+1, n, lCount, rCount+1, ans, op1);
}
String op2 = op + "(";
generate(index+1, n, lCount+1, rCount, ans, op2);
}
}

739. Daily Temperatures


Medium
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.

Example 1:

Input: temperatures = [73,74,75,71,69,72,76,73]


Output: [1,1,4,2,1,1,0,0]

Example 2:

Input: temperatures = [30,40,50,60]


Output: [1,1,1,0]

Example 3:

Input: temperatures = [30,60,90]


Output: [1,1,0]

Constraints:

1 <= temperatures.length <= 105


30 <= temperatures[i] <= 100

Approach
- Use a stack to keep temperatures and indices
- We'll check while peek element is less the current temperature
- We'll pop and res[popped Index] = i-popped Index;
- push each element and index to the stack

TODO: O(1) extra-space


Solution
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
Stack<int[]> st = new Stack<>();
int[] res = new int[n];

for(int i = 0; i < n; i++) {


while(!st.empty() && st.peek()[0] < temperatures[i]) {
int[] temp = st.pop();
res[temp[1]] = i-temp[1];
}
st.push(new int[]{temperatures[i], i});
}

return res;
}
}

Complexity Analysis
- Time Complexity: O(N)
At first glance, it may look like the time complexity of this algorithm should be O(N^2),
because there is a nested while loop inside the for loop. However, each element can only be added to the stack once,
which means the stack is limited to N pops. Every iteration of the while loop uses 1 pop,
which means the while loop will not iterate more than N times in total, across all iterations of the for loop.
An easier way to think about this is that in the worst case, every element will be pushed and popped once.
This gives a time complexity of O(2*N) = O(N).
- Space Complexity: O(N)
If the input was non-increasing, then no element would ever be popped from the stack,
and the stack would grow to a size of N elements at the end.

853. Car Fleet


Medium

There are n cars going to the same destination along a one-lane road. The destination is target miles away.

You are given two integer array position and speed, both of length n, where position[i] is the position of the ith car and speed[i] is the speed of the ith car (in miles per hour).

A car can never pass another car ahead of it, but it can catch up to it and drive bumper to bumper at the same speed. The faster car will slow down to match the slower car's speed.
The distance between these two cars is ignored (i.e., they are assumed to have the same position).

A car fleet is some non-empty set of cars driving at the same position and same speed. Note that a single car is also a car fleet.

If a car catches up to a car fleet right at the destination point, it will still be considered as one car fleet.

Return the number of car fleets that will arrive at the destination.

Example 1:

Input: target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]


Output: 3
Explanation:
The cars starting at 10 (speed 2) and 8 (speed 4) become a fleet, meeting each other at 12.
The car starting at 0 does not catch up to any other car, so it is a fleet by itself.
The cars starting at 5 (speed 1) and 3 (speed 3) become a fleet, meeting each other at 6. The fleet moves at speed 1 until it reaches
Note that no other cars meet these fleets before the destination, so the answer is 3.

Example 2:

Input: target = 10, position = [3], speed = [3]


Output: 1
Explanation: There is only one car, hence there is only one fleet.

Example 3:

Input: target = 100, position = [0,2,4], speed = [4,2,1]


Output: 1
Explanation:
The cars starting at 0 (speed 4) and 2 (speed 2) become a fleet, meeting each other at 4. The fleet moves at speed 2.
Then, the fleet (speed 2) and the car starting at 4 (speed 1) become one fleet, meeting each other at 6. The fleet moves at speed 1 u

Constraints:

n == position.length == speed.length
1 <= n <= 105
0 < target <= 106
0 <= position[i] < target
All the values of position are unique.
0 < speed[i] <= 106

Approach
Solution
class Solution {
// 1
public int carFleet(int target, int[] position, int[] speed) {
int n = position.length, res = 0;
double[][] cars = new double[n][2];

for(int i = 0; i < n; i++) {


cars[i] = new double[] {position[i], (double)(target-position[i])/speed[i]};
}
Arrays.sort(cars, (a, b) -> Double.compare(a[0], b[0]));
double curr = 0;
for(int i = n-1; i >= 0; i--) {
if(cars[i][1] > curr) {
curr = cars[i][1];
res++;
}
}
return res;
}
// 2
public int carFleet(int target, int[] pos, int[] speed) {
Map<Integer, Double> m = new TreeMap<>(Collections.reverseOrder());
for (int i = 0; i < pos.length; ++i)
m.put(pos[i], (double)(target - pos[i]) / speed[i]);
int res = 0; double cur = 0;
for (double time : m.values()) {
if (time > cur) {
cur = time;
res++;
}
}
return res;
}
}

Complexity Analysis
- Time Complexity:O(NlogN) -> sorting
- Space Complexity: O(N)

84. Largest Rectangle in Histogram


(https://leetcode.com/problems/largest-
rectangle-in-histogram/)
Hard

Given an array of integers heights representing the histogram's bar height where the width of each bar is 1, return the area of the largest rectangle in the histogram.

Example 1:

Input: heights = [2,1,5,6,2,3]


Output: 10
Explanation: The above is a histogram where width of each bar is 1.
The largest rectangle is shown in the red area, which has an area = 10 units.
Example 2:

Input: heights = [2,4]


Output: 4

Constraints:

1 <= heights.length <= 105


0 <= heights[i] <= 104

Approach
- Monotonic Stack
- store index and height in the stack
- while the top element is greater in height than the new element,
- keep poping the top element and compare the area with maxArea
- start will have the index of the poped element(new element can be extended towards the left)
- push start and height into the stack
- while stack is not empty,
- keep poping and compare the area with maxArea
- return maxArea

Solution
class Solution {
public int largestRectangleArea(int[] heights) {
int maxArea = 0;
Stack<int[]> st = new Stack<>();
for(int i = 0; i < heights.length; i++) {
int start = i;
while(!st.isEmpty() && st.peek()[1] > heights[i]) {
int[] pair = st.pop();
maxArea = Math.max(maxArea, pair[1] * (i-pair[0]));
start = pair[0];
}
st.push(new int[]{start, heights[i]});
}

while(!st.isEmpty()) {
int[] pair = st.pop();
maxArea = Math.max(maxArea, pair[1] * (heights.length-pair[0]));
}

return maxArea;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

Resources
Articles
Geeksforgeeks (https://www.geeksforgeeks.org/stack-data-structure/)
A-comprehensive-guide-and-template-for-monotonic-stack-based-problems (https://leetcode.com/discuss/study-guide/2347639/A-comprehensive-guide-and-template-for-
monotonic-stack-based-problems)
Videos
Playlist (https://youtube.com/playlist?list=PL6Zs6LgrJj3vWOf01wMHiTy9IFufptfG3)
Playlist - Hindi (https://youtube.com/playlist?list=PLDzeHZWIZsTrhXYYtx4z8-u8zA-DzuVsj)

704. Binary Search


Easy

Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise,
return -1.

You must write an algorithm with O(log n) runtime complexity.

Example 1:

Input: nums = [-1,0,3,5,9,12], target = 9


Output: 4
Explanation: 9 exists in nums and its index is 4

Example 2:

Input: nums = [-1,0,3,5,9,12], target = 2


Output: -1
Explanation: 2 does not exist in nums so return -1

Constraints:

1 <= nums.length <= 104


-104 < nums[i], target < 104
All the integers in nums are unique.
nums is sorted in ascending order.

class Solution {
public int search(int[] nums, int target) {
int l = 0, r = nums.length-1;

while(l <= r) {
int mid = l + (r - l)/2;

if(nums[mid] == target)
return mid;
else if(nums[mid] < target)
l = mid + 1;
else
r = mid - 1;
}

return -1;
}
}

74. Search a 2D Matrix


Medium

Write an efficient algorithm that searches for a value target in an m x n integer matrix matrix. This matrix has the following properties:

Integers in each row are sorted from left to right. The first integer of each row is greater than the last integer of the previous row.

Example 1:
Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
Output: true

Example 2:

Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13


Output: false

Constraints:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104

Approach
1st:
- keep two pointer one for row, other for column
- initialize tem to point to the last element of the 1st row
- if the element is equal to target then return true
- else if element is < target then move the row pointer downward
- else shift the column pointer to left

2nd:
[It would work only when the element at 1st index of next row should be greater then last element of the previous row]
- left pointer at 0, right at n*m-1 (at last element of the matrix)
- do the binarysearch
- target element can be find using
- rowIndex = mid / number of elements in each row
- colIndex = mid % number of elements in each row

Solution
class Solution {
// worst case go through O(n+m) elements
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int row = 0, col = n-1;
while(row >= 0 && row < m && col >= 0 && col < n) {
int val = matrix[row][col];
if(val == target)
return true;
else if(val < target)
row++;
else
col--;
}
return false;
}

// worst case it would go through O(log(n*m)) elements


public boolean searchMatrix1(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int left = 0, right = m*n-1;
while(left <= right) {
int mid = left + (right-left)/2;
int val = matrix[mid/n][mid%n];
if(val == target)
return true;
else if(val < target)
left = mid+1;
else
right = mid-1;
}
return false;
}
}

Complexity Analysis
- Time Complexity:
- 1st approach: O(n+m)
- 2nd approach: O(log(n*m))
- Space Coplexity: O(1) for both approaches

875. Koko Eating Bananas


Medium

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.

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.

Koko likes to eat slowly but still wants to finish eating all the bananas before the guards return.

Return the minimum integer k such that she can eat all the bananas within h hours.

Example 1:

Input: piles = [3,6,7,11], h = 8


Output: 4

Example 2:
Input: piles = [30,11,23,4,20], h = 5
Output: 30

Example 3:

Input: piles = [30,11,23,4,20], h = 6


Output: 23

Constraints:

1 <= piles.length <= 104


piles.length <= h <= 109
1 <= piles[i] <= 109

Approach
- Binary search on range (1, max(pile))
- while the condition satisfies go towards left half
- else go towards right half

Solution
class Solution {
public int minEatingSpeed(int[] piles, int h) {
int left = 1, right = piles[0];
for(int pile : piles) {
if(pile < left)
left = pile;
if(pile > right)
right = pile;
}
int result = right;
while(left <= right) {
int mid = left + (right-left)/2;
if(isSatisfy(piles, mid, h)) {
result = Math.min(result, mid);
right = mid-1;
} else {
left = mid+1;
}
}
return result;
}

private boolean isSatisfy(int[] piles, int mid, int h) {


int count = 0;
for(int pile : piles) {
count += (pile/mid);
if(pile%mid != 0)
count++;
}
return count <= h;
}
}

Complexity Analysis
- Time Complexity: O(n*logm), m: max(piles)
- Space Complexity: O(1)
33. Search in Rotated Sorted Array
Medium

There is an integer array nums sorted in ascending order (with distinct values).

Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1],
nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2].

Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums.

You must write an algorithm with O(log n) runtime complexity.

Example 1:

Input: nums = [4,5,6,7,0,1,2], target = 0


Output: 4

Example 2:

Input: nums = [4,5,6,7,0,1,2], target = 3


Output: -1

Example 3:

Input: nums = [1], target = 0


Output: -1

Constraints:

1 <= nums.length <= 5000


-104 <= nums[i] <= 104
All values of nums are unique.
nums is an ascending array that is possibly rotated.
-104 <= target <= 104

Approach
Modified Binary Search

Solution
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length-1;

while(left <= right) {


int mid = left + (right - left) / 2;

if(nums[mid] == target)
return mid;
// left sorted portion
if(nums[left] <= nums[mid]) {
if(target < nums[left] || target > nums[mid])
left = mid+1;
else
right = mid-1;
}
// right sorted portion
else {
if(target < nums[mid] || target > nums[right])
right = mid-1;
else
left = mid+1;
}
}
return -1;
}
}

Complexity Analysis
- Time Complexity: O(logN)
- Space Complexity: O(1)

153. Find Minimum in Rotated Sorted Array


Medium

Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become:

[4,5,6,7,0,1,2] if it was rotated 4 times.


[0,1,2,4,5,6,7] if it was rotated 7 times.

Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]].

Given the sorted rotated array nums of unique elements, return the minimum element of this array.

You must write an algorithm that runs in O(log n) time.

Example 1:

Input: nums = [3,4,5,1,2]


Output: 1
Explanation: The original array was [1,2,3,4,5] rotated 3 times.

Example 2:

Input: nums = [4,5,6,7,0,1,2]


Output: 0
Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times.

Example 3:
Input: nums = [11,13,15,17]
Output: 11
Explanation: The original array was [11,13,15,17] and it was rotated 4 times.

Constraints:

n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
All the integers of nums are unique.
nums is sorted and rotated between 1 and n times.

Approach
Modified Binary Search

Solution
class Solution {
public int findMin(int[] nums) {
int n = nums.length;
int start = 0, end = n-1;
int res = 0;
while(start <= end) {
int mid = start + (end - start)/2;
int prev = (mid-1+n)%n, next = (mid+1)%n;
if(nums[mid]<nums[prev] && nums[mid]<nums[next])
return nums[mid];
else if(nums[end] <= nums[mid])
start = mid+1;
else
end = mid-1;
}
return nums[res];
}
}

Complexity Analysis
- Time Complexity: O(logN)
- Space Complexity: O(1)

981. Time Based Key-Value Store


Medium

Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key's value at a certain timestamp.

Implement the TimeMap class:

TimeMap() Initializes the object of the data structure.


void set(String key, String value, int timestamp) Stores the key key with the value value at the given time timestamp.
String get(String key, int timestamp) Returns a value such that set was called previously, with timestamp_prev <= timestamp. If there are multiple such values, it
returns the value associated with the largest timestamp_prev. If there are no values, it returns "".

Example 1:
Input
["TimeMap", "set", "get", "get", "set", "get", "get"]
[[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]]
Output
[null, null, "bar", "bar", null, "bar2", "bar2"]

Explanation
TimeMap timeMap = new TimeMap();
timeMap.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1.
timeMap.get("foo", 1); // return "bar"
timeMap.get("foo", 3); // return "bar", since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the
timeMap.set("foo", "bar2", 4); // store the key "foo" and value "bar2" along with timestamp = 4.
timeMap.get("foo", 4); // return "bar2"
timeMap.get("foo", 5); // return "bar2"

Constraints:

1 <= key.length, value.length <= 100


key and value consist of lowercase English letters and digits.
1 <= timestamp <= 107
All the timestamps timestamp of set are strictly increasing.
At most 2 * 105 calls will be made to set and get.

Approach
Solution
class TimeMap {
class Pair {
int timestamp;
String value;

Pair(String value, int timestamp) {


this.value = value;
this.timestamp = timestamp;
}
}

Map<String, List<Pair>> mp;

public TimeMap() {
mp = new HashMap<>();
}

public void set(String key, String value, int timestamp) {


if (!mp.containsKey(key)) {
mp.put(key, new ArrayList<>());
}
mp.get(key).add(new Pair(value, timestamp));
}

public String get(String key, int timestamp) {


String ans = "";
if (!mp.containsKey(key)) {
return ans;
}
List<Pair> list = mp.get(key);

int index = binarSearch(list, timestamp);


if (index == -1) {
return ans;
}

ans = list.get(index).value;
return ans;
}

private int binarSearch(List<TimeMap.Pair> list, int timestamp) {


int l = 0;
int r = list.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (list.get(mid).timestamp == timestamp) {
return mid;
} else if (list.get(mid).timestamp < timestamp) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return r;
}

/**
* Your TimeMap object will be instantiated and called as such:
* TimeMap obj = new TimeMap();
* obj.set(key,value,timestamp);
* String param_2 = obj.get(key,timestamp);
*/
public class TimeMap {

private Map<String,TreeMap<Integer,String>> map;

/** Initialize your data structure here. */


public TimeMap() {
map = new HashMap<>();
}

public void set(String key, String value, int timestamp) {


if(!map.containsKey(key)) {
map.put(key,new TreeMap<>());
}
map.get(key).put(timestamp,value);
}

public String get(String key, int timestamp) {


TreeMap<Integer,String> treeMap = map.get(key);
if(treeMap==null) {
return "";
}
Integer floor = treeMap.floorKey(timestamp);
if(floor==null) {
return "";
}
return treeMap.get(floor);
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

4. Median of Two Sorted Arrays


(https://leetcode.com/problems/median-of-
two-sorted-arrays/)
Hard

Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.

The overall run time complexity should be O(log (m+n)).

Example 1:

Input: nums1 = [1,3], nums2 = [2]


Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.

Example 2:

Input: nums1 = [1,2], nums2 = [3,4]


Output: 2.50000
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.

Constraints:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106

Approach
Solution
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if(nums1.length > nums2.length)
return findMedianSortedArrays(nums2, nums1);
int total = nums1.length + nums2.length;
int half = (total + 1) / 2;
int l = 0, r = nums1.length;
double result = 0d;
while(l <= r) {
int i = l + (r - l) / 2;
int j = half - i;
int nums1L = i > 0 ? nums1[i-1]:Integer.MIN_VALUE;
int nums1R = i < nums1.length ? nums1[i]:Integer.MAX_VALUE;
int nums2L = j > 0 ? nums2[j-1]:Integer.MIN_VALUE;
int nums2R = j < nums2.length ? nums2[j]:Integer.MAX_VALUE;

if(nums1L <= nums2R && nums2L <= nums1R) {


if(total % 2 == 0) {
return (Math.max(nums1L, nums2L) + Math.min(nums1R, nums2R)) / 2.0;
} else {
return Math.max(nums1L, nums2L);
}
} else if(nums1L > nums2R) {
r = i - 1;
} else {
l = i + 1;
}
}
return result;
}
}

Complexity Analysis
- Time Complexity: O(log(m+n))
- Space Complexity: O(1)

Resources
Articles:
Binary-Search-101-The-Ultimate-Binary-Search-Handbook (https://leetcode.com/problems/binary-search/discuss/423162/Binary-Search-101-The-Ultimate-Binary-Search-
Handbook)
Python-Clear-explanation-Powerful-Ultimate-Binary-Search-Template.-Solved-many-problems. (https://leetcode.com/problems/koko-eating-bananas/discuss/769702/Python-
Clear-explanation-Powerful-Ultimate-Binary-Search-Template.-Solved-many-problems.)
JavaC%2B%2BPython-Binary-Search (https://leetcode.com/problems/koko-eating-bananas/discuss/152324/JavaC%2B%2BPython-Binary-Search)
Binary-Search-for-Beginners-Problems-or-Patterns-or-Sample-solutions (https://leetcode.com/discuss/study-guide/691825/Binary-Search-for-Beginners-Problems-or-Patterns-
or-Sample-solutions)
5-variations-of-Binary-search-(A-Self-Note) (https://leetcode.com/discuss/study-guide/1322500/5-variations-of-Binary-search-(A-Self-Note))

Videos:
Binary Search Introduction (https://youtu.be/P3YID7liBug)
Neetcode Playlist (https://youtube.com/playlist?list=PLot-Xpze53leNZQd0iINpD-MAhMOMzWvO)

206. Reverse Linked List


Given the head of a singly linked list, reverse the list, and return the reversed list.

Example 1:

Input: head = [1,2,3,4,5]


Output: [5,4,3,2,1]

Example 2:

Input: head = [1,2]


Output: [2,1]

Example 3:

Input: head = []
Output: []

Constraints:

The number of nodes in the list is the range [0, 5000].


-5000 <= Node.val <= 5000

Follow up: A linked list can be reversed either iteratively or recursively. Could you
implement both?

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode curr = head, next = head, prev = null;

while(curr != null) {
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}

return prev;
}
}

TODO: Recursive solution


21. Merge Two Sorted Lists
Easy

You are given the heads of two sorted linked lists list1 and list2.

Merge the two lists in a one sorted list. The list should be made by splicing together the nodes of the first two lists.

Return the head of the merged linked list.

Example 1:
Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]

Example 2:
Input: list1 = [], list2 = []
Output: []

Example 3:
Input: list1 = [], list2 = [0]
Output: [0]

Constraints:

The number of nodes in both lists is in the range [0, 50]. -100 <= Node.val <= 100 Both list1 and list2 are sorted in non-decreasing order.

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(0);
ListNode result = dummy;
while(list1 != null && list2 != null) {
if(list1.val < list2.val) {
dummy.next = list1;
dummy = dummy.next;
list1 = list1.next;
} else {
dummy.next = list2;
dummy = dummy.next;
list2 = list2.next;
}
}

if(list1 != null) {
dummy.next = list1;
}

if(list2 != null) {
dummy.next = list2;
}

return result.next;
}
}
143. Reorder List
Medium

You are given the head of a singly linked-list. The list can be represented as:

L0 → L1 → … → Ln - 1 → Ln Reorder the list to be on the following form:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … You may not modify the values in the list's nodes. Only nodes themselves may be changed.

Example 1:

Input: head = [1,2,3,4]


Output: [1,4,2,3]

Example 2:

Input: head = [1,2,3,4,5]


Output: [1,5,2,4,3]

Constraints:

The number of nodes in the list is in the range [1, 5 * 104].


1 <= Node.val <= 1000

Approach
- Reverse the other half(mid to end)
- 2 pointers one from the head other from the reverse head of other list
- use temp variable to keep the next pointers of both the list
- 1st list's next points to 2nd list & 2nd list's next point to the temp variable containing the next of the 1st list
- move both the pointers

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public void reorderList(ListNode head) {
ListNode slow = head, fast = head.next;

while(fast != null && fast.next != null) {


slow = slow.next;
fast = fast.next.next;
}
ListNode first = head, second = reverse(slow.next);
slow.next = null;

while(second != null) {
ListNode tmp1 = first.next, tmp2 = second.next;
first.next = second;
second.next = tmp1;
first = tmp1;
second = tmp2;
}

private ListNode reverse(ListNode head) {


ListNode curr = head, prev = null, next = null;

while(curr != null) {
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}

Complexity Analysis
- Time Complexity: O(2*N) ~ O(N)
- Space Complexity: O(1)

19. Remove Nth Node From End of List


Medium

Given the head of a linked list, remove the nth node from the end of the list and return its head.

Example 1:

Input: head = [1,2,3,4,5], n = 2


Output: [1,2,3,5]

Example 2:
Input: head = [1], n = 1
Output: []

Example 3:

Input: head = [1,2], n = 1


Output: [1]

Constraints:

The number of nodes in the list is sz.

1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz

Follow up: Could you do this in one pass?

Approach
- 2 pointers approach
- dummy head for so that removing head would be easier
- fast pointer would be ahead by n nodes
- slow and fast will start moving until fast reach the last element
- slow's next node would be the one we need to remove

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode slow = dummyHead, fast = dummyHead;

while(n > 0) {
fast = fast.next;
n--;
}

while(fast != null && fast.next != null) {


slow = slow.next;
fast = fast.next;
}

slow.next = slow.next.next;

return dummyHead.next;
}
}

Complexity Analysis
- Time Complexity: O(N) , one pass only
- Space Complexity: O(1)

138. Copy List with Random Pointer


Medium

A linked list of length n is given such that each node contains an additional random pointer, which could point to any node in the list, or null.

Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has its value set to the value of its corresponding original node.
Both the next and random pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state.
None of the pointers in the new list should point to nodes in the original list.

For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding two nodes x and y in the copied list, x.random --> y.

Return the head of the copied linked list.

The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:

val: an integer representing Node.val


random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any
node.

Your code will only be given the head of the original linked list.

Example 1:

Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]


Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]

Example 2:

Input: head = [[1,1],[2,1]]


Output: [[1,1],[2,1]]

Example 3:

Input: head = [[3,null],[3,0],[3,null]]


Output: [[3,null],[3,0],[3,null]]

Constraints:

0 <= n <= 1000


-104 <= Node.val <= 104
Node.random is null or is pointing to some node in the linked list.

Approach
- Step 1: Duplicate each node such that old1->new1->old2->new2 ...
- Step 2: Random pointer of new = Random pointer of old's next
- Step 3: Seperate the the nodes to form old1->old2.. & new1->new2..

Solution
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;

public Node(int val) {


this.val = val;
this.next = null;
this.random = null;
}
}
*/

class Solution {
public Node copyRandomList(Node head) {
if(head == null)
return null;

// Step 1: Duplicate each node such that old1->new1->old2->new2 ...


Node curr = head, next = null;
while(curr != null) {
next = curr.next;
Node newNode = new Node(curr.val);
curr.next = newNode;
newNode.next = next;
curr = next;
}

// Step 2: Random pointer of new = Random pointer of old's next


curr = head;
Node nextNode = head.next;
while(nextNode != null) {
nextNode.random = curr.random == null ? null : curr.random.next;
if(nextNode.next == null)
break;
nextNode = nextNode.next.next;
curr = curr.next.next;
}

// Step 3: Seperate the the nodes to form old1->old2.. & new1->new2..


Node p = head, c = head.next, n = null;
Node newListHead = c;
while(p != null) {
n = c.next;
p.next = n;
if (n == null)
break;
c.next = n.next;
p = p.next;
c = c.next;
}

return newListHead;
}
}

Complexity Analysis
- Time Complexity: O(N+2*N+N) ~ O(N)
- Space Complexity: O(1), no extra memory is used (memory used is for new list only)
2. Add Two Numbers
Medium

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two
numbers and return the sum as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example 1:

Input: l1 = [2,4,3], l2 = [5,6,4]


Output: [7,0,8]
Explanation: 342 + 465 = 807.

Example 2:

Input: l1 = [0], l2 = [0]


Output: [0]

Example 3:

Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]


Output: [8,9,9,9,0,0,0,1]

Constraints:

The number of nodes in each linked list is in the range [1, 100].
0 <= Node.val <= 9
It is guaranteed that the list represents a number that does not have leading zeros.

Approach
- Three pointers, one for each given list and 3rd one for resultant list
- untill both the pointers pointing to given lists are null or carry > 0
- add new node to the resultant list

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode curr1 = l1, curr2 = l2, curr3 = res;
int carry = 0;
while(curr1 != null || curr2 != null || carry > 0) {
int v1 = curr1 == null ? 0 : curr1.val;
int v2 = curr2 == null ? 0 : curr2.val;
int sum = v1 + v2 + carry;
carry = sum / 10;
curr3.next = new ListNode(sum % 10);
curr3 = curr3.next;
curr1 = curr1 == null ? curr1 : curr1.next;
curr2 = curr2 == null ? curr2 : curr2.next;
}

return res.next;
}
}

Complexity Analysis
- Time Complexity: O(max(len(list1, list2))
- Space Complexity: O(max(len(list1, list2))

141. Linked List Cycle


Easy

Given head, the head of a linked list, determine if the linked list has a cycle in it.

There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the
node that tail's next pointer is connected to. Note that pos is not passed as a parameter.

Return true if there is a cycle in the linked list. Otherwise, return false.

Example 1:

Input: head = [3,2,0,-4], pos = 1


Output: true
Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).

Example 2:

Input: head = [1,2], pos = 0


Output: true
Explanation: There is a cycle in the linked list, where the tail connects to the 0th node.

Example 3:
Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.

Constraints:

The number of the nodes in the list is in the range [0, 104].
-105 <= Node.val <= 105
pos is -1 or a valid index in the linked-list.

Follow up: Can you solve it using O(1) (i.e. constant) memory?

Approach
- slow and fast pointer
- move fast twice that of slow
- break if fast reach null or fast becomes equal to slow
- if slow == fast, thenn has a cycle otherwise no cycle

Solution
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null)
return false;
ListNode slow = head, fast = head.next;

while(fast != null && slow != fast) {


slow = slow.next;
fast = fast.next;
if(fast != null)
fast = fast.next;
}
return slow == fast;
}
}

Complexity Analysis
- Time Complexity: O(N), N : numbers of nodes in the list
- Space Complexity: O(1)

287. Find the Duplicate Number


Medium

Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive.
There is only one repeated number in nums, return this repeated number.

You must solve the problem without modifying the array nums and uses only constant extra space.

Example 1:

Input: nums = [1,3,4,2,2]


Output: 2

Example 2:

Input: nums = [3,1,3,4,2]


Output: 3

Constraints:

1 <= n <= 105


nums.length == n + 1
1 <= nums[i] <= n
All the integers in nums appear only once except for precisely one integer which appears two or more times.

Follow up:
How can we prove that at least one duplicate number must exist in nums?
Can you solve the problem in linear runtime complexity?

Approach
- slow and fast pointer, similar to cycle in link list

Solution
class Solution {
public int findDuplicate(int[] nums) {

int slow = nums[0], fast = nums[0];


do {
slow = nums[slow];
fast = nums[nums[fast]];
} while(slow != fast);

slow = nums[0];

while(slow != fast) {
slow = nums[slow];
fast = nums[fast];
}

return slow;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(1)

146. LRU Cache


Medium
Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

Implement the LRUCache class:

LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
int get(int key) Return the value of the key if the key exists, otherwise return -1.
void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity
from this operation, evict the least recently used key.
The functions get and put must each run in O(1) average time complexity.

Example 1:

Input
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]

Explanation
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1); // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2); // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1); // return -1 (not found)
lRUCache.get(3); // return 3
lRUCache.get(4); // return 4

Constraints:

1 <= capacity <= 3000


0 <= key <= 104
0 <= value <= 105
At most 2 * 105 calls will be made to get and put.

Approach
- Doublely linkedlist, it allows O(1) deletion of a given node
- Map to keep the key mapped to a node, getting the value for a key at O(1) time
- Keeping head and tail of the linked list
- add at head and remove from tail(if size exceeds)
- get method:
- if map doesn't contain the key return -1
- else get the node, remove it, add it (it'll add it to the head of the list), return the value

- put method:
- if map already contains the key, remove the node
- size of map = size, then remove the node at tail
- insert the new node

- insert method: (helper method)


- add the node to the next of the head and update the pointers
- add the key and node to the map

- remove method: (helper method)


- remove the node using the pointers
- remove the key and node from the map

Solution
/*
* Hepler class
* Node to create doublely linked list
*/
class Node {
int key;
int val;
Node prev;
Node next;
Node(int key, int val) {
this.key = key;
this.val = val;
}
}

class LRUCache {
Node head = new Node(0, 0);
Node tail = new Node(0, 0);
Map<Integer, Node> mp;
int size;

public LRUCache(int capacity) {


head.next = tail;
tail.prev = head;
mp = new HashMap<>();
size = capacity;
}

public int get(int key) {


if(mp.containsKey(key)) {
Node node = mp.get(key);
remove(node);
insert(node);
return node.val;
} else
return -1;
}

public void put(int key, int value) {


if(mp.containsKey(key)) {
Node node = mp.get(key);
remove(node);
}
if(mp.size() == size)
remove(tail.prev);
insert(new Node(key, value));
}

private void remove(Node node) {


mp.remove(node.key);
node.prev.next = node.next;
node.next.prev = node.prev;
}

private void insert(Node node) {


Node headNext = head.next;
head.next = node;
node.prev = head;
node.next = headNext;
headNext.prev = node;
mp.put(node.key, node);
}
}

/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/

Complexity Analysis
- Time Complexity: (on average)
- get() : O(1)
- put() : O(1)

- Space Complexity:
- ~ O(N) for map and doublely linkedlist (over all)
- get() : O(1)
- put() : O(1)

23. Merge k Sorted Lists


Hard

You are given an array of k linked-lists lists, each linked-list is sorted in ascending order.

Merge all the linked-lists into one sorted linked-list and return it.

Example 1:

Input: lists = [[1,4,5],[1,3,4],[2,6]]


Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
1->4->5,
1->3->4,
2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6

Example 2:

Input: lists = []
Output: []

Example 3:

Input: lists = [[]]


Output: []

Constraints:

k == lists.length
0 <= k <= 104
0 <= lists[i].length <= 500
-104 <= lists[i][j] <= 104
lists[i] is sorted in ascending order.
The sum of lists[i].length will not exceed 104.

Approach
- Create a function to merge two sorted lists
- Use it to merge all the given lists
Sub approach 1:
- we can merge two lists then use the merge list as new list and merge it with next given list
Sub approach 2:
- we can merge all the lists as a group of 2 lists untill we're left with 1 final list

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
int k = lists.length;
if(k == 0)
return null;
int i = 0;
while(i+1<k) {
ListNode l1 = lists[i], l2 = lists[i+1];
lists[i+1] = merge2LL(l1, l2);
i++;
}
return lists[k-1];
}

public ListNode merge2LL(ListNode l1, ListNode l2) {


ListNode dummyHead = new ListNode(0);
ListNode tmp = dummyHead;
while(l1 != null && l2 != null) {
if(l1.val < l2.val) {
tmp.next = l1;
ListNode nextL1 = l1.next;
l1.next = l2;
l1 = nextL1;
tmp = tmp.next;
} else {
tmp.next = l2;
ListNode nextL2 = l2.next;
l2.next = l1;
l2 = nextL2;
tmp = tmp.next;
}
}

if(l1 == null)
tmp.next = l2;
if(l2 == null)
tmp.next = l1;
return dummyHead.next;
}
}

Optimized
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
return partion(lists, 0, lists.length - 1);
}

public ListNode partion(ListNode[] lists, int s, int e) {


if (s == e) return lists[s];
if (s < e) {
int q = (s + e) / 2;
ListNode l1 = partion(lists, s, q);
ListNode l2 = partion(lists, q + 1, e);
return merge2LL(l1, l2);
} else return null;
}

public ListNode merge2LL(ListNode l1, ListNode l2) {


ListNode dummyHead = new ListNode(0);
ListNode tmp = dummyHead;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
tmp.next = l1;
ListNode nextL1 = l1.next;
l1.next = l2;
l1 = nextL1;
tmp = tmp.next;
} else {
tmp.next = l2;
ListNode nextL2 = l2.next;
l2.next = l1;
l2 = nextL2;
tmp = tmp.next;
}
}

if (l1 == null) tmp.next = l2;


if (l2 == null) tmp.next = l1;
return dummyHead.next;
}
}

Complexity Analysis
- Time complexity : O(Nlogk) where k is the number of linked lists.
We can merge two sorted linked list in O(n) time where nn is the total number of nodes in two lists.
Sum up the merge process and we can get: O(Nlogk)
- Space complexity : O(logk) for recursive call stack
We can merge two sorted linked lists in O(1) space.
25. Reverse Nodes in k-Group
(https://leetcode.com/problems/reverse-
nodes-in-k-group/)
Hard

Given the head of a linked list, reverse the nodes of the list k at a time, and return the modified list.

k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes, in the end, should remain as it is.

You may not alter the values in the list's nodes, only nodes themselves may be changed.

Example 1:

Input: head = [1,2,3,4,5], k = 2


Output: [2,1,4,3,5]

Example 2:

Input: head = [1,2,3,4,5], k = 3


Output: [3,2,1,4,5]

Constraints:

The number of nodes in the list is n.


1 <= k <= n <= 5000
0 <= Node.val <= 1000

Follow-up: Can you solve the problem in O(1) extra memory space?

Approach
- Reusing reverse linkedlist function
- dummy root, it's next point to head of given linkedlist
- use two pointer curr and prev
- storing firstNode of every group
- index to find whether we have the complete group or not, as it determines whether we have reverse the that group or not
- if complete group is there prev.next points to the newly reversed list and
the prev points to firstNode(prev group, which is the last node of the prev group)
- else prev.next points to firstNode

Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {

ListNode root = new ListNode(0, head); // dummy head


ListNode curr = head, prev = root;

while(curr != null) {
ListNode tail = curr; // keep track of the 1st element of each group
int listIndex = 0;

while(curr != null && listIndex < k) {


curr = curr.next;
listIndex++;
}
// listIndex != k means we have a group less than k size
if(listIndex != k)
prev.next = tail;
// less than k size so simply pointing prev to the
// first element of the group
else {
// reverse the group
prev.next = reverse(tail, k);
// prev will move to the first element(now the last) of the group
// so that next of it would have the reverse of the group
prev = tail;
}
}
return root.next;
}

private ListNode reverse(ListNode head, int k) {


ListNode curr = head, prev = null, next = null;

while(curr != null && k > 0) {


k--;
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
head = prev;
return head;
}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(1)

Resources
226. Invert Binary Tree
(https://leetcode.com/problems/invert-binary-
tree/)
Given the root of a binary tree, invert the tree, and return its root.

Example 1:
Input: root = [4,2,7,1,3,6,9]
Output: [4,7,2,9,6,3,1]

Example 2:
Input: root = [2,1,3]
Output: [2,3,1]

Example 3:
Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 100].


-100 <= Node.val <= 100

Approach
- Recursion
- store one pointer to right or left subtree
- swap left with right and make call recursively

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null)
return null;
TreeNode tmp = root.right;
root.right = invertTree(root.left);
root.left = invertTree(tmp);

return root;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(H), atmost height of tree would be the number of recursive call stack at a given point of time

104. Maximum Depth of Binary Tree


Given the root of a binary tree, return its maximum depth.

A binary tree's maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

Example 1:
Input: root = [3,9,20,null,null,15,7]
Output: 3

Example 2:
Input: root = [1,null,2]
Output: 2

Constraints:

The number of nodes in the tree is in the range [0, 104].


-100 <= Node.val <= 100

Approach
- Recursion
- if node = null return 0
- return 1 + max(leftHeight, rightHeight)

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null)
return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(H), height of the tree

543. Diameter of Binary Tree


Easy

Given the root of a binary tree, return the length of the diameter of the tree.

The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

The length of a path between two nodes is represented by the number of edges between them.

Example 1:

Input: root = [1,2,3,4,5]


Output: 3
Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3].

Example 2:

Input: root = [1,2]


Output: 1

Constraints:

The number of nodes in the tree is in the range [1, 104].


-100 <= Node.val <= 100
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int maxDiameter;
public int diameterOfBinaryTree(TreeNode root) {
maxDiameter = 0;
height(root);
return maxDiameter;
}

public int height(TreeNode root) {


if(root == null)
return -1;
int left = height(root.left);
int right = height(root.right);
int h = 1 + Math.max(left, right);
maxDiameter = Math.max(maxDiameter, left + right + 2);
return h;
}
}

110. Balanced Binary Tree


Easy

Given a binary tree, determine if it is height-balanced.

For this problem, a height-balanced binary tree is defined as:

a binary tree in which the left and right subtrees of every node differ in height by no more than 1.

Example 1:

Input: root = [3,9,20,null,null,15,7]


Output: true

Example 2:

Input: root = [1,2,2,3,3,null,null,4,4]


Output: false

Example 3:

Input: root = []
Output: true

Constraints:

The number of nodes in the tree is in the range [0, 5000].


-104 <= Node.val <= 104
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null)
return true;
int res = helper(root);
return res == -1?false:true;
}

private int helper(TreeNode root) {


if(root == null)
return 0;
int leftH = helper(root.left);
if(leftH == -1)
return -1;
int rightH = helper(root.right);
if(rightH == -1)
return -1;
return Math.abs(leftH - rightH)>1?-1:1+Math.max(leftH, rightH);
}
}

100. Same Tree


Easy

Given the roots of two binary trees p and q, write a function to check if they are the same or not.

Two binary trees are considered the same if they are structurally identical, and the nodes have the same value.

Example 1:

Input: p = [1,2,3], q = [1,2,3]


Output: true

Example 2:

Input: p = [1,2], q = [1,null,2]


Output: false

Example 3:

Input: p = [1,2,1], q = [1,1,2]


Output: false

Constraints:

The number of nodes in both trees is in the range [0, 100].


-104 <= Node.val <= 104
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q == null) return true;
if(p == null || q == null) return false;
return (p.val == q.val) && (isSameTree(p.left, q.left)) && (isSameTree(p.right,q.right));
}
}

572. Subtree of Another Tree


Easy

Given the roots of two binary trees root and subRoot, return true if there is a subtree of root with the same structure and node values of subRoot and false otherwise.

A subtree of a binary tree tree is a tree that consists of a node in tree and all of this node's descendants. The tree tree could also be considered as a subtree of itself.

Example 1:

Input: root = [3,4,5,1,2], subRoot = [4,1,2]


Output: true

Example 2:

Input: root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]


Output: false

Constraints:

The number of nodes in the root tree is in the range [1, 2000].
The number of nodes in the subRoot tree is in the range [1, 1000].
-104 <= root.val <= 104
-104 <= subRoot.val <= 104
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null && subRoot == null)
return true;
if(root == null || subRoot == null)
return false;

Queue<TreeNode> q = new LinkedList<>();


q.offer(root);

while(!q.isEmpty()) {
TreeNode node = q.poll();
if(node.val == subRoot.val) {
if(isSameTree(node, subRoot))
return true;
}
if(node.left != null)
q.offer(node.left);
if(node.right != null)
q.offer(node.right);
}

return false;
}

private boolean isSameTree(TreeNode p, TreeNode q) {


if(p == null && q == null)
return true;
if(p == null || q == null)
return false;
return (p.val == q.val) && (isSameTree(p.left, q.left)) && (isSameTree(p.right, q.right));
}
}

235. Lowest Common Ancestor of a Binary


Search Tree
Easy

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants
(where we allow a node to be a descendant of itself).”

Example 1:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.

Example 2:

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4


Output: 2
Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

Example 3: Input: root = [2,1], p = 2, q = 1 Output: 2

Constraints:

The number of nodes in the tree is in the range [2, 105].

-109 <= Node.val <= 109


All Node.val are unique.
p != q
p and q will exist in the BST.

Approach
- Recursion
- if root's val < p's val & q's val then reduce the problem to right subtree of root
- else if root's val > p's val & q's val then reduce the problem to left subtree of root
- else return root (if p's val or q's val equal to root's val or for p & q lies in different subtree of the root)

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/

class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null)
return null;
int val = root.val;
if(val < p.val && val < q.val)
return lowestCommonAncestor(root.right, p, q);
else if(val > p.val && val > q.val)
return lowestCommonAncestor(root.left, p, q);
return root;
}
}

Complexity Analysis
- Time Complexity: O(logN)
- Space Complexity: O(logN) recursive calls

102. Binary Tree Level Order Traversal


Medium

Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

Example 1:

Input: root = [3,9,20,null,null,15,7]


Output: [[3],[9,20],[15,7]]

Example 2:

Input: root = [1]


Output: [[1]]

Example 3:

Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 2000].


-1000 <= Node.val <= 1000

Approach
- Use Queue to insert the nodes
- for each level we'll poll node and check if left or right of it is not null then add left or right of it to the queue

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if(root == null)
return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);

while(!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currLevel = new ArrayList<>(levelSize);

for(int i = 0; i < levelSize; i++) {


TreeNode currNode = queue.poll();
currLevel.add(currNode.val);
if(currNode.left != null)
queue.offer(currNode.left);
if(currNode.right != null)
queue.offer(currNode.right);
}

result.add(currLevel);
}
return result;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N), max number of nodes in a level

199. Binary Tree Right Side View


Medium

Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.

Example 1:

Input: root = [1,2,3,null,5,null,4]


Output: [1,3,4]

Example 2:
Input: root = [1,null,3]
Output: [1,3]

Example 3:

Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 100].


-100 <= Node.val <= 100

Approach
- BFS
- Queue
- add the last element of each level into the result list

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null)
return res;

Queue<TreeNode> queue = new LinkedList<>();


queue.offer(root);

while(!queue.isEmpty()) {
int levelSize = queue.size();
for(int i = 0; i < levelSize; i++) {
TreeNode currNode = queue.poll();
if(currNode.left != null)
queue.offer(currNode.left);
if(currNode.right != null)
queue.offer(currNode.right);
if(i == levelSize-1)
res.add(currNode.val);
}
}

return res;
}
}
Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

1448. Count Good Nodes in Binary Tree


(https://leetcode.com/problems/count-good-
nodes-in-binary-tree/)
Medium

Share Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X.

Return the number of good nodes in the binary tree.

Example 1:

Input: root = [3,1,4,3,null,1,5]


Output: 4
Explanation: Nodes in blue are good.
Root Node (3) is always a good node.
Node 4 -> (3,4) is the maximum value in the path starting from the root.
Node 5 -> (3,4,5) is the maximum value in the path
Node 3 -> (3,1,3) is the maximum value in the path.

Example 2:

Input: root = [3,3,null,4,2]


Output: 3
Explanation: Node 2 -> (3, 3, 2) is not good, because "3" is higher than it.

Example 3:

Input: root = [1]


Output: 1
Explanation: Root is considered as good.

Constraints:

The number of nodes in the binary tree is in the range [1, 10^5].
Each node's value is between [-104, 104].

Approach
- check if the node is null
- if node.val >= previously send max, res will be one
- res += left Subtree call with updated max
- res += right Subtree call with updated max

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int goodNodes(TreeNode root) {
return goodNodes(root, Integer.MIN_VALUE);
}

public int goodNodes(TreeNode root, int max) {


if (root == null) return 0;
int res = root.val >= max ? 1 : 0;
res += goodNodes(root.left, Math.max(max, root.val));
res += goodNodes(root.right, Math.max(max, root.val));
return res;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(height)

98. Validate Binary Search Tree


Medium

Given the root of a binary tree, determine if it is a valid binary search tree (BST).

A valid BST is defined as follows:

The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys greater than the node's key. Both the left
and right subtrees must also be binary search trees.

Example 1:

Input: root = [2,1,3]


Output: true

Example 2:
Input: root = [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.

Constraints:

The number of nodes in the tree is in the range [1, 104].


-231 <= Node.val <= 231 - 1

Approach
- Recursion
- for each node's val we check whether it's in the range (min, max)
- if it fails we return false
- recursively call left and right subtree
- start with (-infinity, +infinity)
- left child -> (min, node.val)
- right child -> (node.val, max)

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isValidBST(TreeNode root) {
Long min = Long.MIN_VALUE;
Long max = Long.MAX_VALUE;

return helper(root, min, max);

private boolean helper(TreeNode root, Long min, Long max) {


if(root == null)
return true;
Long val = (long) root.val;
if(val <= min || val >= max)
return false;
return helper(root.left, min, val) && helper(root.right, val, max);
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(H), H : height of tree, recursive calls
230. Kth Smallest Element in a BST
Medium

Given the root of a binary search tree, and an integer k, return the kth smallest value (1-indexed) of all the values of the nodes in the tree.

Example 1:

Input: root = [3,1,4,null,2], k = 1


Output: 1

Example 2:

Input: root = [5,3,6,2,4,null,null,1], k = 3


Output: 3

Constraints:

The number of nodes in the tree is n.


1 <= k <= n <= 104
0 <= Node.val <= 104

Follow up: If the BST is modified often (i.e., we can do insert and delete operations) and
you need to find the kth smallest frequently, how would you optimize?

Approach
- Recursion
- inorder traversal gives nodes in sorted order of a BST
- ans[] -> store the answer and k in the second index
- decrement ans[1] until it become 0
- when it become zero, that node is the ans

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int kthSmallest(TreeNode root, int k) {
int[] ans = new int[2];
ans[1] = k;
inorder(root, ans);
return ans[0];
}

private void inorder(TreeNode root, int[] ans) {


if(root == null)
return;
inorder(root.left, ans);
ans[1]--;
if(ans[1] == 0)
ans[0] = root.val;
inorder(root.right, ans);
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

105. Construct Binary Tree from Preorder


and Inorder Traversal
(https://leetcode.com/problems/construct-
binary-tree-from-preorder-and-inorder-
traversal/)
Medium

Given two integer arrays preorder and inorder where preorder is the preorder traversal of a binary tree and inorder is the inorder traversal of the same tree, construct and return the
binary tree.

Example 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

Example 2:

Input: preorder = [-1], inorder = [-1]


Output: [-1]

Constraints:

1 <= preorder.length <= 3000


inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder and inorder consist of unique values.
Each value of inorder also appears in preorder.
preorder is guaranteed to be the preorder traversal of the tree.
inorder is guaranteed to be the inorder traversal of the tree.

Approach
- start and end index
- inorderMap: mapping of inorder array with the index
- if start > end the return null
- root -> node with the preorder val with the preorder index
- root.left -> recursive call, in range (start, inorderMap.get(val)-1)
- root.right -> recursive call, in range (inorderMap.get(val)+1, end)

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int preInd;
public TreeNode buildTree(int[] preorder, int[] inorder) {
preInd = 0;
int n = inorder.length;
int startIndex = 0, endIndex = n-1;
Map<Integer, Integer> inorderMap = new HashMap<>();
for(int i = 0; i < n; i++)
inorderMap.put(inorder[i], i);
return helper(preorder, inorder, startIndex, endIndex, inorderMap);
}

public TreeNode helper(int[] preorder, int[] inorder, int startIndex, int endIndex, Map<Integer, Integer> inorderMap) {
if(startIndex > endIndex)
return null;
int val = preorder[preInd++];
TreeNode root = new TreeNode(val);
root.left = helper(preorder, inorder, startIndex, inorderMap.get(val)-1, inorderMap);
root.right = helper(preorder, inorder, inorderMap.get(val)+1, endIndex, inorderMap);
return root;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

124. Binary Tree Maximum Path Sum


(https://leetcode.com/problems/binary-tree-
maximum-path-sum/)
Hard

A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most
once. Note that the path does not need to pass through the root.

The path sum of a path is the sum of the node's values in the path.

Given the root of a binary tree, return the maximum path sum of any non-empty path.

Example 1:
Input: root = [1,2,3]
Output: 6
Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.

Example 2:

Input: root = [-10,9,20,null,null,15,7]


Output: 42
Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.

Constraints:

The number of nodes in the tree is in the range [1, 3 * 104].


-1000 <= Node.val <= 1000

Approach
- compare max at each node
- left call, right call
- compare max with node's val+left val, node's val+right val, node's val+left val+right val
- return max of node's val, node's val+left val, node's val+right val

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int max;
public int maxPathSum(TreeNode root) {
max = -1001;
helper(root);
return max;
}

private int helper(TreeNode root) {


if(root == null)
return 0;
int val = root.val;
max = Math.max(max, val);
int lVal = helper(root.left);
int rVal = helper(root.right);
max = Math.max(max, Math.max(rVal+val, Math.max(lVal+val, lVal+rVal+val)));

return Math.max(rVal+val, Math.max(lVal+val, val));


}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(logn), recursive call stacks

297. Serialize and Deserialize Binary Tree (https://leetcode.com/problems/serialize-and-deserialize-binary-tree/)

Hard

Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network
connection link to be reconstructed later in the same or another computer environment.

Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a
binary tree can be serialized to a string and this string can be deserialized to the original tree structure.

Clarification: The input/output format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with
different approaches yourself.

Example 1:

Input: root = [1,2,3,null,null,4,5]


Output: [1,2,3,null,null,4,5]

Example 2:

Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 104].


-1000 <= Node.val <= 1000

Approach

Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {

// Encodes a tree to a single string.


public String serialize(TreeNode root) {
StringBuilder result = new StringBuilder();
serialHelper(root, result);
return result.toString();
}

private void serialHelper(TreeNode root, StringBuilder result) {


if (root == null) {
return;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode curr = q.poll();
if (curr == null) {
result.append(",");
continue;
}
result.append(curr.val + ",");
q.offer(curr.left);
q.offer(curr.right);
}
}

// Decodes your encoded data to tree.


public TreeNode deserialize(String data) {
String[] nodes = data.split(",");

if (nodes[0] == "") return null;


return deserialHelper(nodes);
}

private TreeNode deserialHelper(String[] data) {


String val = data[0];
TreeNode root = new TreeNode(Integer.parseInt(val));
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
for (int i = 1; i < data.length; i += 2) {
TreeNode curr = q.poll();
if (!data[i].equals("")) {
curr.left = new TreeNode(Integer.parseInt(data[i]));
q.offer(curr.left);
}
if (i + 1 < data.length && !data[i + 1].equals("")) {
curr.right = new TreeNode(Integer.parseInt(data[i + 1]));
q.offer(curr.right);
}
}

return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

Tree
Patterns
BFS
DFS (Preorder, Inorder & Postorder)

BFS
Here are the steps of our algorithm:

1. Start by pushing the root node to the queue.


2. Keep iterating until the queue is empty.
3. In each iteration, first count the elements in the queue (let’s call it levelSize). We will have these many nodes in the current level.
4. Next, remove levelSize nodes from the queue and push their value in an array to represent the current level.
5. After removing each node from the queue, insert both of its children into the queue.
6. If the queue is not empty, repeat from step 3 for the next level.

Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

Example 1:

Input: root = [3,9,20,null,null,15,7]


Output: [[3],[9,20],[15,7]]

Example 2:

Input: root = [1]


Output: [[1]]

Example 3:
Input: root = []
Output: []

Constraints:

The number of nodes in the tree is in the range [0, 2000].


-1000 <= Node.val <= 1000

import java.util.*;

class TreeNode {
int val;
TreeNode left;
TreeNode right;

TreeNode(int x) {
val = x;
}
};

class Main {
public static List<List<Integer>> traverse(TreeNode root) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if (root == null)
return result;

Queue<TreeNode> queue = new LinkedList<>();


queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>(levelSize);
for (int i = 0; i < levelSize; i++) {
TreeNode currentNode = queue.poll();
// add the node to the current level
currentLevel.add(currentNode.val);
// insert the children of current node in the queue
if (currentNode.left != null)
queue.offer(currentNode.left);
if (currentNode.right != null)
queue.offer(currentNode.right);
}
result.add(currentLevel);
}

return result;
}

public static void main(String[] args) {


TreeNode root = new TreeNode(12);
root.left = new TreeNode(7);
root.right = new TreeNode(1);
root.left.left = new TreeNode(9);
root.right.left = new TreeNode(10);
root.right.right = new TreeNode(5);
List<List<Integer>> result = Main.traverse(root);
System.out.println("Level order traversal: " + result);
}
}

Time complexity

The time complexity of the above algorithm is O(N), where ‘N’ is the total number of nodes in the tree. This is due to the fact that we traverse each node once.

Space complexity
The space complexity of the above algorithm will be O(N) as we need to return a list containing the level order traversal. We will also need O(N) space for the queue. Since we can
have a maximum of N/2 nodes at any level (this could happen only at the lowest level), therefore we will need O(N) space to store them in the queue.

DFS

Inorder(tree)

1. Traverse the left subtree, i.e., call Inorder(left-subtree)


2. Visit the root.
3. Traverse the right subtree, i.e., call Inorder(right-subtree)

import java.util.*;

class TreeNode {
int val;
TreeNode left;
TreeNode right;

TreeNode(int x) {
val = x;
}
};

class Main {
public static void traverse(TreeNode root) {

if (root == null)
return;
traverse(root.left);
System.out.println(root.val);
traverse(root.right);
}

public static void main(String[] args) {


TreeNode root = new TreeNode(12);
root.left = new TreeNode(7);
root.right = new TreeNode(1);
root.left.left = new TreeNode(9);
root.right.left = new TreeNode(10);
root.right.right = new TreeNode(5);
Main.traverse(root);
}
}

Time complexity

The time complexity of the above algorithm is O(N), where ‘N’ is the total number of nodes in the tree. This is due to the fact that we traverse each node once.

Space complexity

The space complexity of the above algorithm will be O(H) where H is the height of the tree, needed for recursive call stacks.
208. Implement Trie (Prefix Tree)
(https://leetcode.com/problems/implement-
trie-prefix-tree/)
Medium

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.

Implement the Trie class:

Trie() Initializes the trie object.


void insert(String word) Inserts the string word into the trie.
boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise.
boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.

Example 1:

Input
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output
[null, null, true, false, true, null, true]

Explanation
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // return True
trie.search("app"); // return False
trie.startsWith("app"); // return True
trie.insert("app");
trie.search("app"); // return True

Constraints:

1 <= word.length, prefix.length <= 2000


word and prefix consist only of lowercase English letters.
At most 3 * 104 calls in total will be made to insert, search, and startsWith.

Approach
26 links, end marker
[ , , ...26]
/ . . . 26 \ /..26..\

Solution
class Trie {
TrieNode root;
public Trie() {
root = new TrieNode();
}

public void insert(String word) {


TrieNode curr = root;
for(int i = 0; i < word.length(); i++) {
int ind = word.charAt(i)-'a';

if(curr.node[ind] != null) {
curr = curr.node[ind];
} else {
curr.node[ind] = new TrieNode();
curr = curr.node[ind];
}
}
curr.end = true;
}

public boolean search(String word) {


TrieNode curr = root;
for(int i = 0; i < word.length(); i++) {
int ind = word.charAt(i)-'a';
if(curr.node[ind] != null)
curr = curr.node[ind];
else
return false;
}
return curr.end == true;
}

public boolean startsWith(String prefix) {


TrieNode curr = root;
for(int i = 0; i < prefix.length(); i++) {
int ind = prefix.charAt(i)-'a';
if(curr.node[ind] != null)
curr = curr.node[ind];
else
return false;
}
return true;
}
}

class TrieNode {
TrieNode[] node = new TrieNode[26];
boolean end;
}

/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/

Complexity Analysis
- Time Complexity:
- insert: O(m), where m is the key length.
- search: O(m)
- startsWith: O(m)
- Space Complexity:
- insert: O(m)
- search: O(1)
- startsWith: O(1)

211. Design Add and Search Words Data


Structure ()
Medium

Design a data structure that supports adding new words and finding if a string matches any previously added string.

Implement the WordDictionary class:

WordDictionary() Initializes the object.


void addWord(word) Adds word to the data structure, it can be matched later.
bool search(word) Returns true if there is any string in the data structure that matches word or false otherwise. word may contain dots '.' where dots can be matched with
any letter.

Example:

Input
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
Output
[null,null,null,null,false,true,true,true]

Explanation
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // return False
wordDictionary.search("bad"); // return True
wordDictionary.search(".ad"); // return True
wordDictionary.search("b.."); // return True

Constraints:

1 <= word.length <= 25


word in addWord consists of lowercase English letters.
word in search consist of '.' or lowercase English letters.
There will be at most 3 dots in word for search queries.
At most 104 calls will be made to addWord and search.

Approach
- Use Trie

Solution
class WordDictionary {
TrieNode root;

public WordDictionary() {
root = new TrieNode();
}

public void addWord(String word) {


TrieNode curr = root;
for (int i = 0; i < word.length(); i++) {
int ind = word.charAt(i) - 'a';

if (curr.node[ind] != null) {
curr = curr.node[ind];
} else {
curr.node[ind] = new TrieNode();
curr = curr.node[ind];
}
}
curr.end = true;
}

public boolean search(String word) {


return trivialSearch(word, root);
}

private boolean trivialSearch(String word, TrieNode root) {


TrieNode curr = root;
if (word.length() == 0)
return curr.end;
boolean ans = false;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
if (ch == '.') {
for (int j = 0; j < 26; j++) {
if (curr.node[j] != null) {
ans = trivialSearch(word.substring(i + 1), curr.node[j]);
if (ans)
return true;
}
}
return false;
} else {
int ind = ch - 'a';
if (curr.node[ind] != null)
curr = curr.node[ind];
else
return false;
}
}
return curr.end;
}

class TrieNode {
TrieNode[] node = new TrieNode[26];
boolean end;
}
}

/**
* Your WordDictionary object will be instantiated and called as such:
* WordDictionary obj = new WordDictionary();
* obj.addWord(word);
* boolean param_2 = obj.search(word);
*/
Complexity Analysis
- Time Complexity:
- Space Complexity:

Resources
703. Kth Largest Element in a Stream
Easy

Design a class to find the kth largest element in a stream. Note that it is the kth largest element in the sorted order, not the kth distinct element.

Implement KthLargest class:

KthLargest(int k, int[] nums) Initializes the object with the integer k and the stream of integers nums.
int add(int val) Appends the integer val to the stream and returns the element representing the kth largest element in the stream.

Example 1:

Input
["KthLargest", "add", "add", "add", "add", "add"]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
Output
[null, 4, 5, 5, 8, 8]

Explanation

KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);


kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8
kthLargest.add(4); // return 8

Constraints:

1 <= k <= 104


0 <= nums.length <= 104
-104 <= nums[i] <= 104
-104 <= val <= 104
At most 104 calls will be made to add.
It is guaranteed that there will be at least k elements in the array when you search for the kth element.
class KthLargest {
PriorityQueue<Integer> pq;
static int k;
public KthLargest(int k, int[] nums) {
this.k = k;
pq = new PriorityQueue<>();
for(int num: nums)
pq.offer(num);

while(pq.size() > k)
pq.poll();
}

public int add(int val) {


pq.offer(val);
if(pq.size() > k)
pq.poll();

return pq.peek();
}
}

/**
* Your KthLargest object will be instantiated and called as such:
* KthLargest obj = new KthLargest(k, nums);
* int param_1 = obj.add(val);
*/

1046. Last Stone Weight


Easy

You are given an array of integers stones where stones[i] is the weight of the ith stone.

We are playing a game with the stones. On each turn, we choose the heaviest two stones and smash them together. Suppose the heaviest two stones have weights x and y with x <=
y. The result of this smash is:

If x == y, both stones are destroyed, and If x != y, the stone of weight x is destroyed, and the stone of weight y has new weight y - x. At the end of the game, there is at most one stone
left.

Return the smallest possible weight of the left stone. If there are no stones left, return 0.

Example 1:

Input: stones = [2,7,4,1,8,1]


Output: 1
Explanation:
We combine 7 and 8 to get 1 so the array converts to [2,4,1,1,1] then,
we combine 2 and 4 to get 2 so the array converts to [2,1,1,1] then,
we combine 2 and 1 to get 1 so the array converts to [1,1,1] then,
we combine 1 and 1 to get 0 so the array converts to [1] then that's the value of the last stone.

Example 2:

Input: stones = [1]


Output: 1

Constraints:

1 <= stones.length <= 30


1 <= stones[i] <= 1000
class Solution {
public int lastStoneWeight(int[] stones) {
PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> b-a); // max heap, Collections.reverseOrder()

for(int stone : stones)


pq.offer(stone);

while(pq.size() > 1) {
int x = pq.poll();
int y = pq.poll();
if(x != y) {
pq.offer(x-y); // abs not rqd as x would always be greater than equal to y
}
}

return pq.isEmpty()?0:pq.peek();
}
}

973. K Closest Points to Origin


Medium

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).

The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2).

You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in).

Example 1:

Input: points = [[1,3],[-2,2]], k = 1


Output: [[-2,2]]
Explanation:
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]].

Example 2:

Input: points = [[3,3],[5,-1],[-2,4]], k = 2


Output: [[3,3],[-2,4]]
Explanation: The answer [[-2,4],[3,3]] would also be accepted.

Constraints:

1 <= k <= points.length <= 4


-104 < xi, yi < 4

Approach
- Have a Max PriorityQueue based on euclidean distance
- add each point to PQ, if size > k then poll
- remaining points inside the PQ is the answer

Solution
class Solution {
public int[][] kClosest(int[][] points, int k) {
int[][] res = new int[k][2];
PriorityQueue<Point> pq = new PriorityQueue<Point>((a, b) -> new Double(b.dist).compareTo(new Double(a.dist)));

for(int[] point: points) {


pq.offer(new Point(point[0], point[1]));
if(pq.size() > k)
pq.poll();
}
int ind = 0;
while(!pq.isEmpty()) {
Point p = pq.poll();
res[ind][0] = p.x;
res[ind][1] = p.y;
ind++;
}
return res;
}
}

class Point {
int x;
int y;

double dist;

Point(int x, int y) {
this.x = x;
this.y = y;
dist = Math.pow(x*x + y*y, 0.5);
}
}

class Solution {
public int[][] kClosest(int[][] points, int k) {
int[][] res = new int[k][2];
PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> (b[0]*b[0] + b[1]*b[1]) - (a[0]*a[0] + a[1]*a[1]));

for(int[] point: points) {


pq.offer(point);
if(pq.size() > k)
pq.poll();
}
int ind = 0;
while(!pq.isEmpty()) {
int[] p = pq.poll();
res[ind][0] = p[0];
res[ind][1] = p[1];
ind++;
}
return res;
}
}

Complexity Analysis
- Time Complexity: O(nlogk) -> Adding to/removing from the heap (or priority queue)
only takes O(\log k) time when the size of the heap is capped at k elements.
- Space Complexity: O(k) -> The heap (or priority queue) will contain at most kk elements.

TODO: Quick Select, BinarySearch solution of this problem


215. Kth Largest Element in an Array
Medium

Given an integer array nums and an integer k, return the kth largest element in the array.

Note that it is the kth largest element in the sorted order, not the kth distinct element.

Example 1:

Input: nums = [3,2,1,5,6,4], k = 2


Output: 5

Example 2:

Input: nums = [3,2,3,1,2,4,5,5,6], k = 4


Output: 4

Constraints:

1 <= k <= nums.length <= 104


-104 <= nums[i] <= 104

Approach
- Min PriorityQueue
- if size becomes > k, poll
- after going through the array, we'll have the kth largest element as the min element in the PQ

Solution
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<>((a,b)->a-b);
for(int num : nums) {
pq.offer(num);
if(pq.size() > k)
pq.poll();
}
return pq.peek();
}
}

Complexity Analysis
- Time Complexity: O(nlogk)
- Space Complexity: O(k)

TODO
Quick Select

621. Task Scheduler


(https://leetcode.com/problems/task-
scheduler/)
Medium

Given a characters array tasks, representing the tasks a CPU needs to do, where each letter represents a different task. Tasks could be done in any order. Each task is done in one
unit of time. For each unit of time, the CPU could complete either one task or just be idle.

However, there is a non-negative integer n that represents the cooldown period between two same tasks (the same letter in the array), that is that there must be at least n units of
time between any two same tasks.

Return the least number of units of times that the CPU will take to finish all the given tasks.

Example 1:

Input: tasks = ["A","A","A","B","B","B"], n = 2


Output: 8
Explanation:
A -> B -> idle -> A -> B -> idle -> A -> B
There is at least 2 units of time between any two same tasks.

Example 2:

Input: tasks = ["A","A","A","B","B","B"], n = 0


Output: 6
Explanation: On this case any permutation of size 6 would work since n = 0.
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
And so on.

Example 3:

Input: tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2


Output: 16
Explanation:
One possible solution is
A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> idle -> idle -> A -> idle -> idle -> A

Constraints:

1 <= task.length <= 104


tasks[i] is upper-case English letter.
The integer n is in the range [0, 100].

Approach

Solution
class Solution {
public int leastInterval(char[] tasks, int n) {
int[] freq = new int[26];
int max = 0; // keep the max value
int maxCount = 0; // number of tasks with the max value

for(char task : tasks) {


freq[task-'A']++;
if(freq[task-'A'] == max) {
maxCount++;
} else if(freq[task-'A'] > max) {
max = freq[task-'A'];
maxCount = 1;
}
}

// number of blank sequence


int blankSeq = max - 1;
// number of blanks in each sequence
int blankSeqLen = n - (maxCount - 1);
// total empty slots
int emptySlots = blankSeq * blankSeqLen;
// non-max tasks count
int availableSlots = tasks.length - (maxCount * max);
// total amount of idle tiem
int idleTime = Math.max(0, emptySlots - availableSlots);

return tasks.length + idleTime;


}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(26)

295. Find Median from Data Stream


Hard

The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value and the median is the mean of the two middle values.

For example, for arr = [2,3,4], the median is 3. For example, for arr = [2,3], the median is (2 + 3) / 2 = 2.5. Implement the MedianFinder class:

MedianFinder() initializes the MedianFinder object. void addNum(int num) adds the integer num from the data stream to the data structure. double findMedian() returns the median of
all elements so far. Answers within 10-5 of the actual answer will be accepted.

Example 1:
Input
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
Output
[null, null, null, 1.5, null, 2.0]

Explanation
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1, 2]
medianFinder.findMedian(); // return 1.5 (i.e., (1 + 2) / 2)
medianFinder.addNum(3); // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

Constraints:

-105 <= num <= 105


There will be at least one element in the data structure before calling findMedian.
At most 5 * 104 calls will be made to addNum and findMedian.

Follow up:

If all integer numbers from the stream are in the range [0, 100], how would you optimize your solution?
If 99% of all integer numbers from the stream are in the range [0, 100], how would you optimize your solution?

Approach
- Use two heaps, one max heap(smallerList), other min heap(largerList)
- if smallList is not empty and first element is less than equal to num then add num to largeList
- else add num to smallList
- balance the size of both the heaps
- so that smallList never become more than 1 + size of largeList
- largeList's size always remain less or equal to the size of smallList

Solution
class MedianFinder {
int size;
PriorityQueue<Integer> smallList;
PriorityQueue<Integer> largeList;
public MedianFinder() {
smallList = new PriorityQueue<>(Collections.reverseOrder()); // maxHeap
largeList = new PriorityQueue<>(); // minHeap
}

public void addNum(int num) {

if(!smallList.isEmpty() && smallList.peek() <= num)


largeList.offer(num);
else
smallList.offer(num);
// size balance
if(smallList.size() > largeList.size()+1) {
int tmp = smallList.poll();
largeList.offer(tmp);
} else if(smallList.size() < largeList.size()) {
int tmp = largeList.poll();
smallList.offer(tmp);
}
size++;
}

public double findMedian() {

if(size % 2 != 0) {
return (double) smallList.peek();
} else {
return (double) (smallList.peek() + largeList.peek())/2.0;
}
}
}

/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/

Complexity Analysis
- Time Complexity:
- add operation: O(logN)
- find median operation: O(1)
- Space Complexity:
- add operation: O(1)
- find median operation: O(1)

Resources
78. Subsets
(https://leetcode.com/problems/subsets/)
Medium
Given an integer array nums of unique elements, return all possible subsets (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

Input: nums = [1,2,3]


Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

Example 2:

Input: nums = [0]


Output: [[],[0]]

Constraints:

1 <= nums.length <= 10


-10 <= nums[i] <= 10
All the numbers of nums are unique.

Approach
Backtracking:
- Either select the element or not
- recursive call
tmp.add(nums[index]);
backtrack(nums, index+1, tmp, ans);
tmp.remove(tmp.size()-1);
backtrack(nums, index+1, tmp, ans);

Solution
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<Integer> tmp = new ArrayList<>();
List<List<Integer>> ans = new ArrayList<>();
backtrack(nums, 0, tmp, ans);
return ans;
}

private void backtrack(int[] nums, int index, List<Integer> tmp, List<List<Integer>> ans) {
if(index > nums.length)
return;
if(index == nums.length) {
ans.add(new ArrayList<>(tmp));
return;
}
tmp.add(nums[index]);
backtrack(nums, index+1, tmp, ans);
tmp.remove(tmp.size()-1);
backtrack(nums, index+1, tmp, ans);
}
}

Complexity Analysis
- Time Complexity: O(2^N)
- Space Complexity: O(N)
39. Combination Sum
(https://leetcode.com/problems/combination-
sum/)
Medium

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.

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.

It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input.

Example 1:

Input: candidates = [2,3,6,7], target = 7


Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.

Example 2:

Input: candidates = [2,3,5], target = 8


Output: [[2,2,2,2],[2,3,3],[3,5]]

Example 3:

Input: candidates = [2], target = 1


Output: []

Constraints:

1 <= candidates.length <= 30


1 <= candidates[i] <= 200
All elements of candidates are distinct.
1 <= target <= 500

Approach
Backtrack:
- We can choose any of the element present in the array(n choices)
- In subsets, we have only 2 choices
- Looping through elements to create a n way recursive call
for(int i = index; i < nums.length; i++) {
tmp.add(nums[i]);
backtrack(nums, i, tmp, ans, target - nums[i]);
tmp.remove(tmp.size()-1);
}

Solution
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
backtrack(candidates, 0, new ArrayList<>(), res, target);

return res;
}

private void backtrack(int[] nums, int index, List<Integer> tmp, List<List<Integer>> ans, int target) {
if(index >= nums.length || target < 0)
return;
if(target == 0) {
ans.add(new ArrayList<>(tmp));
return;
}

for(int i = index; i < nums.length; i++) {


tmp.add(nums[i]);
backtrack(nums, i, tmp, ans, target - nums[i]);
tmp.remove(tmp.size()-1);
}
}
}

Complexity Analysis
- Time Complexity: O(N^N)
- Space Complexity: O(N)

46. Permutations
(https://leetcode.com/problems/permutations/)
Medium

Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.

Example 1:

Input: nums = [1,2,3]


Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

Example 2:

Input: nums = [0,1]


Output: [[0,1],[1,0]]

Example 3:

Input: nums = [1]


Output: [[1]]

Constraints:

1 <= nums.length <= 6


-10 <= nums[i] <= 10
All the integers of nums are unique.

Approach
- we have n choices initially, after each selection we can't take that element again
- take a mark array to keep track of what elements fron the nums array which are already select in the current path
for(int i = 0; i < nums.length; i++) {
if(mark[i] == false) {
mark[i] = true;
tmp.add(nums[i]);
backtrack(nums, mark, tmp, ans);
tmp.remove(tmp.size()-1);
mark[i] = false;
}
}

Solution
class Solution {
public List<List<Integer>> permute(int[] nums) {
boolean[] mark = new boolean[nums.length];
List<List<Integer>> ans = new ArrayList<>();
backtrack(nums, mark, new ArrayList<>(), ans);
return ans;
}

private void backtrack(int[] nums, boolean[] mark, List<Integer> tmp, List<List<Integer>> ans) {
if(tmp.size() == nums.length) {
ans.add(new ArrayList<>(tmp));
return;
}

for(int i = 0; i < nums.length; i++) {


if(mark[i] == false) {
mark[i] = true;
tmp.add(nums[i]);
backtrack(nums, mark, tmp, ans);
tmp.remove(tmp.size()-1);
mark[i] = false;
}
}
}
}

Complexity Analysis
- Time Complexity: O(N^N)
- Space Complexity: O(N)

90. Subsets II
(https://leetcode.com/problems/subsets-ii/)
Medium

Given an integer array nums that may contain duplicates, return all possible subsets (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

Input: nums = [1,2,2]


Output: [[],[1],[1,2],[1,2,2],[2],[2,2]]
Example 2:

Input: nums = [0]


Output: [[],[0]]

Constraints:

1 <= nums.length <= 10


-10 <= nums[i] <= 10

Approach
Ref: 1.Subsets (https://github.com/dipjul/NeetCode-150/blob/13aea2145a56c73ef5e1b8ea0b5cbe94745415e9/10.%20Backtracking/1.Subsets.md)

Same as Subsets
- to skip duplicates, before making the 2nd backtracking call we check the current and next element
- while those are equal we keep skipping to the next index

Solution
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(nums);
backtrack(nums, 0, new ArrayList<>(), ans);
return ans;
}

private void backtrack(int[] nums, int index, List<Integer> tmp, List<List<Integer>> ans) {
if(index > nums.length)
return;
if(index == nums.length) {
ans.add(new ArrayList<>(tmp));
return;
}
tmp.add(nums[index]);
backtrack(nums, index+1, tmp, ans);
tmp.remove(tmp.size()-1);
while(index+1 < nums.length && nums[index] == nums[index+1])
index++;
backtrack(nums, index+1, tmp, ans);
}
}

Complexity Analysis
- Time Complexity: O(2^N)
- Space Complexity: O(N)

40. Combination Sum II


(https://leetcode.com/problems/combination-
sum-ii/)
Medium

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.
Each number in candidates may only be used once in the combination.

Note: The solution set must not contain duplicate combinations.

Example 1:

Input: candidates = [10,1,2,7,6,1,5], target = 8


Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

Example 2:

Input: candidates = [2,5,2,1,2], target = 5


Output:
[
[1,2,2],
[5]
]

Constraints:

1 <= candidates.length <= 100


1 <= candidates[i] <= 50
1 <= target <= 30

Approach
Ref: 2.Combination Sum (https://github.com/dipjul/NeetCode-150/blob/76226b171098f898f8263acd34b2d3236d30f471/10.%20Backtracking/2.CombinationSum.md)

- We can't contain duplicates, so we can't use the same numbers twice


- Before making the 2nd recursive call, we check the current and next element
- while those are same we keep skipping them

Solution
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(candidates);
backtrack(candidates, 0, target, new ArrayList<>(), ans);
return ans;
}

private void backtrack(int[] nums, int index, int target, List<Integer> tmp, List<List<Integer>> ans) {
if(target == 0) {
ans.add(new ArrayList<>(tmp));
return;
}
if(index >= nums.length || target < 0)
return;

tmp.add(nums[index]);
backtrack(nums, index+1, target-nums[index], tmp, ans);
tmp.remove(tmp.size()-1);
while(index+1 < nums.length && nums[index] == nums[index+1])
index++;
backtrack(nums, index+1, target, tmp, ans);
}
}

Complexity Analysis
- Time Complexity: O(2^N)
- Space Complexity: O(N)

79. Word Search


(https://leetcode.com/problems/word-search/)
Medium

Given an m x n grid of characters board and a string word, return true if word exists in the grid.

The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than
once.

Example 1:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"


Output: true

Example 2:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"


Output: true

Example 3:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"


Output: false

Constraints:

m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board and word consists of only lowercase and uppercase English letters.

Follow up: Could you use search pruning to make your solution faster with a larger board?

Approach
Backtracking:
- 4 choices top, down, left & right moves
- mark the element we are accessing
- if we match the word, we get the ans

Solution
class Solution {
public boolean exist(char[][] board, String word) {
boolean[] res = new boolean[1];
boolean[][] mark = new boolean[board.length][board[0].length];
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
if(board[i][j] == word.charAt(0)) {
backtrack(board, word, mark, i, j, res);
if(res[0] == true)
return true;
}
}
}
return false;
}

private void backtrack(char[][] board, String word, boolean[][] mark, int i, int j, boolean[] res) {
if (word.length() == 0) {
res[0] = true;
return;
}
if (i >= board.length || j >= board[0].length || i < 0 || j < 0 || mark[i][j])
return;

if (word.charAt(0) == board[i][j]) {
mark[i][j] = true;
if (i >= 0) {
backtrack(board, word.substring(1), mark, i - 1, j, res);
}
if (j >= 0) {
backtrack(board, word.substring(1), mark, i, j - 1, res);
}
if (i < board.length) {
backtrack(board, word.substring(1), mark, i + 1, j, res);
}
if (j < board[0].length) {
backtrack(board, word.substring(1), mark, i, j + 1, res);
}
mark[i][j] = false;
}
}
}
// little optimized
class Solution {
boolean[][] visited;
public boolean exist(char[][] board, String word) {
int rows = board.length;
int cols = board[0].length;
visited = new boolean[rows][cols];

for(int i = 0; i < rows; i++) {


for(int j = 0; j < cols; j++) {
if(word.charAt(0) == board[i][j] && search(i, j, 0, word, board))
return true;
}
}
return false;
}

public boolean search(int i, int j, int index, String word, char[][] board) {
if(index == word.length())
return true;

if(i < 0 || i >= board.length || j < 0 || j >= board[0].length || word.charAt(index) != board[i][j] || visited[i][j]) {
return false;
}

visited[i][j] = true;

if(
search(i+1, j, index+1, word, board) ||
search(i-1, j, index+1, word, board) ||
search(i, j+1, index+1, word, board) ||
search(i, j-1, index+1, word, board)
) {
return true;
}

visited[i][j] = false;
return false;
}
}

Complexity Analysis
- Time Complexity: O(4^N)
- Space Complexity: O(N)

131. Palindrome Partitioning


(https://leetcode.com/problems/palindrome-
partitioning/)
Medium

Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s.

A palindrome string is a string that reads the same backward as forward.

Example 1:
Input: s = "aab"
Output: [["a","a","b"],["aa","b"]]

Example 2:

Input: s = "a"
Output: [["a"]]

Constraints:

1 <= s.length <= 16


s contains only lowercase English letters.

Approach
- backtracking
- go through all possible combination of substring
- if the substring is palindrome add it to the tmp result and backtrack
- if substring becomes empty then add tmp result to the result

Solution
class Solution {
public List<List<String>> partition(String s) {
List<List<String>> result = new ArrayList();
helper(s, new ArrayList<String>(), result);
return result;
}

public void helper(String s, List<String> step, List<List<String>> result) {


if(s == null || s.length() == 0) {
result.add(new ArrayList<>(step));
return;
}

for(int i = 1; i <= s.length(); i++) {


String temp = s.substring(0, i);
if(isPalindrome(temp)){
step.add(temp);
helper(s.substring(i, s.length()), step, result);
step.remove(step.size()-1);
}
}
return;
}

private boolean isPalindrome(String s) {


int start = 0, end = s.length()-1;
while(start <= end) {
if(s.charAt(start++) != s.charAt(end--))
return false;
}
return true;
}
}

Complexity Analysis
- Time Complexity: O(N*2^N)
- Space Complexity: O(N)

17. Letter Combinations of a Phone Number


(https://leetcode.com/problems/letter-
combinations-of-a-phone-number/)
Medium

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.

A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

Example 1:

Input: digits = "23"


Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]

Example 2:

Input: digits = ""


Output: []

Example 3:

Input: digits = "2"


Output: ["a","b","c"]

Constraints:

0 <= digits.length <= 4


digits[i] is a digit in the range ['2', '9'].

Approach
Backtrack:
char chs[] = mp.get(str.charAt(i));
for(int j = 0; j < chs.length; j++) {
recursive(str, i+1, s+chs[j], res); - stmt
}
stmt is equivalent to:
String s = s+chs[j];
recursive(str, i+1, s, res);
s = s.substring(0,s.length()-1);

Solution
class Solution {
Map<Character, char[]> mp;
public List<String> letterCombinations(String digits) {
if(digits.equals(""))
return new ArrayList<>();
mp = new HashMap<>();
mp.put('2', new char[]{'a', 'b', 'c'});
mp.put('3', new char[]{'d', 'e', 'f'});
mp.put('4', new char[]{'g', 'h', 'i'});
mp.put('5', new char[]{'j', 'k', 'l'});
mp.put('6', new char[]{'m', 'n', 'o'});
mp.put('7', new char[]{'p', 'q', 'r', 's'});
mp.put('8', new char[]{'t', 'u', 'v'});
mp.put('9', new char[]{'w', 'x', 'y', 'z'});
List<String> res = new ArrayList<>();
recursive(digits, 0, "", res);
return res;
}

private void recursive(String str, int i, String s, List<String> res) {


if(i > str.length())
return;
if(i == str.length()) {
res.add(s);
return;
}
char chs[] = mp.get(str.charAt(i));
for(int j = 0; j < chs.length; j++) {
recursive(str, i+1, s+chs[j], res);
}

}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

51. N-Queens
(https://leetcode.com/problems/n-queens/)
Hard

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.

Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively.

Example 1:

Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above

Example 2:

Input: n = 1
Output: [["Q"]]
Constraints:

1 <= n <= 9

Approach
- Backtracking
- Used 3 boolean arrays to mark row, left diagonal and rightdiagonal
- r-c could be -ve so we add the n-1 to it
- for each column,
- we go through each row and check whether we can place the queen or not
- if possible then we place the column index at the row[]
- recursive call to next column

Solution
// Do not try this at home
class Solution {
int[] row;
boolean[] rw, ld, rd;
public List<List<String>> solveNQueens(int n) {
row = new int[n];
rw = new boolean[n];
ld = new boolean[2*n-1];
rd = new boolean[2*n-1];
List<List<String>> ans = new ArrayList<>();
backtrack(0, n, ans);
return ans;
}

private void backtrack(int c, int n, List<List<String>> ans) {


if(c == n) {
ans.add(printer(row, n));
}
for(int r = 0; r < n; r++) {
if(!rw[r] && !ld[r - c + n - 1] && !rd[r + c]) {
rw[r] = ld[r - c + n - 1] = rd[r + c] = true;
row[c] = r;
backtrack(c+1,n,ans);
rw[r] = ld[r - c + n - 1] = rd[r + c] = false;
}
}
}

private List<String> printer(int[] row, int n) {


List<String> res = new ArrayList<>();
for(int i = 0; i < n; i++) {
StringBuilder sb = new StringBuilder(n);
for(int j = 0; j < n; j++) {
if(j == row[i])
sb.append('Q');
else
sb.append('.');
}
res.add(sb.toString());
}
return res;
}
}

Complexity Analysis
- Time Complexity: O(n!)
- Space Complexity: O(n^2), building the result

Resources
200. Number of Islands
(https://leetcode.com/problems/number-of-
islands/)
Medium

Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands.

An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

Input: grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1

Example 2:

Input: grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 3

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] is '0' or '1'.

Approach
- DFS
- number of time DFS called is the ans

Soluiton
class Solution {
public int numIslands(char[][] grid) {
boolean[][] mark = new boolean[grid.length][grid[0].length];
int res = 0;
for(int i = 0; i < grid.length; i++) {
for(int j = 0; j < grid[0].length; j++) {
if(!mark[i][j] && grid[i][j] == '1') {
res++;
dfs(grid, i, j, mark);
}
}
}
return res;
}

private void dfs(char[][] grid, int i, int j, boolean[][] mark) {


if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || mark[i][j] || grid[i][j] == '0')
return;
mark[i][j] = true;
dfs(grid, i+1, j, mark);
dfs(grid, i, j-1, mark);
dfs(grid, i-1, j, mark);
dfs(grid, i, j+1, mark);
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n*m)

133. Clone Graph


(https://leetcode.com/problems/clone-graph/)
Medium

Given a reference of a node in a connected undirected graph.

Return a deep copy (clone) of the graph.

Each node in the graph contains a value (int) and a list (List[Node]) of its neighbors.

class Node {
public int val;
public List<Node> neighbors;
}

Test case format:

For simplicity, each node's value is the same as the node's index (1-indexed). For example, the first node with val == 1, the second node with val == 2, and so on. The graph is
represented in the test case using an adjacency list.

An adjacency list is a collection of unordered lists used to represent a finite graph. Each list describes the set of neighbors of a node in the graph.

The given node will always be the first node with val = 1. You must return the copy of the given node as a reference to the cloned graph.

Example 1:
Input: adjList = [[2,4],[1,3],[2,4],[1,3]]
Output: [[2,4],[1,3],[2,4],[1,3]]
Explanation: There are 4 nodes in the graph.
1st node (val = 1)'s neighbors are 2nd node (val = 2) and 4th node (val = 4).
2nd node (val = 2)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).
3rd node (val = 3)'s neighbors are 2nd node (val = 2) and 4th node (val = 4).
4th node (val = 4)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).

Example 2:

Input: adjList = [[]]


Output: [[]]
Explanation: Note that the input contains one empty list. The graph consists of only one node with val = 1 and it does not have any n

Example 3:

Input: adjList = []
Output: []
Explanation: This an empty graph, it does not have any nodes.

Constraints:

The number of nodes in the graph is in the range [0, 100].


1 <= Node.val <= 100
Node.val is unique for each node.
There are no repeated edges and no self-loops in the graph.
The Graph is connected and all nodes can be visited starting from the given node.

Approach

Solution
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> neighbors;
public Node() {
val = 0;
neighbors = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
neighbors = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _neighbors) {
val = _val;
neighbors = _neighbors;
}
}
*/

class Solution {

public Node cloneGraph(Node node) {


if (node == null) return null;
Map<Integer, Node> mp = new HashMap<>();
Queue<Node> q = new LinkedList<>();
Set<Node> set = new HashSet<>();
q.offer(node);

while (!q.isEmpty()) {
Node curr = q.poll();
set.add(curr);

List<Node> nei = curr.neighbors;


ArrayList<Node> newNei = new ArrayList<>();
for (Node n : nei) {
if (!set.contains(n)) q.offer(n);
if (!mp.containsKey(n.val)) {
Node newN = new Node(n.val);
mp.put(newN.val, newN);
newNei.add(newN);
} else {
Node newN = mp.get(n.val);
newNei.add(newN);
}
}
if (!mp.containsKey(curr.val)) {
Node newNode = new Node(curr.val, newNei);
mp.put(curr.val, newNode);
} else {
Node newNode = mp.get(curr.val);
newNode.neighbors = newNei;
mp.put(curr.val, newNode);
}
}

return mp.get(node.val);
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n+m)

695. Max Area of Island


(https://leetcode.com/problems/max-area-of-
island/)
Medium

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.

The area of an island is the number of cells with a value 1 in the island.

Return the maximum area of an island in grid. If there is no island, return 0.

Example 1:

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]]
Output: 6
Explanation: The answer is not 11, because the island must be connected 4-directionally.

Example 2:

Input: grid = [[0,0,0,0,0,0,0,0]]


Output: 0

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j] is either 0 or 1.

Approach
- DFS
- count number of dfs call inside

Solution
class Solution {
public int maxAreaOfIsland(int[][] grid) {
boolean[][] mark = new boolean[grid.length][grid[0].length];
int res = 0;

for (int i = 0; i < grid.length; i++) {


for (int j = 0; j < grid[0].length; j++) {
if (!mark[i][j] && grid[i][j] == 1) {
res = Math.max(res, dfs(grid, i, j, mark));
}
}
}
return res;
}

private int dfs(int[][] grid, int i, int j, boolean[][] mark) {


if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || mark[i][j] || grid[i][j] == 0) {
return 0;
}
mark[i][j] = true;
return (1 + dfs(grid, i + 1, j, mark) +
dfs(grid, i, j - 1, mark) +
dfs(grid, i - 1, j, mark) +
dfs(grid, i, j + 1, mark));
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n*m)

417. Pacific Atlantic Water Flow ()


Medium

There is an m x n rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island's left and top edges, and the Atlantic Ocean
touches the island's right and bottom edges.

The island is partitioned into a grid of square cells. You are given an m x n integer matrix heights where heights[r][c] represents the height above sea level of the cell at coordinate (r,
c).

The island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell's height is less than or equal to the current
cell's height. Water can flow from any cell adjacent to an ocean into the ocean.

Return a 2D list of grid coordinates result where result[i] = [ri, ci] denotes that rain water can flow from cell (ri, ci) to both the Pacific and Atlantic oceans.

Example 1:

Input: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]


Output: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]

Example 2:

Input: heights = [[2,1],[1,2]]


Output: [[0,0],[0,1],[1,0],[1,1]]

Constraints:

m == heights.length
n == heights[r].length
1 <= m, n <= 200
0 <= heights[r][c] <= 105

Approach
- dfs: start from all the boundary along with respective set for pacific and atlantic
- intersection of both set

Solution
class Solution {

public List<List<Integer>> pacificAtlantic(int[][] heights) {


List<List<Integer>> res = new ArrayList<>();
Set<String> pacific = new HashSet<>();
Set<String> atlantic = new HashSet<>();
int rows = heights.length, cols = heights[0].length;
for (int i = 0; i < cols; i++) {
dfs(heights, 0, i, pacific, heights[0][i]);
dfs(heights, rows-1, i, atlantic, heights[rows-1][i]);
}
for (int i = 0; i < rows; i++) {
dfs(heights, i, 0, pacific, heights[i][0]);
dfs(heights, i, cols-1, atlantic, heights[i][cols-1]);
}
pacific.retainAll(atlantic);
for (String s : pacific) {
String[] arr = s.split(",");
List<Integer> a = new ArrayList<>();
a.add(Integer.parseInt(arr[0]));
a.add(Integer.parseInt(arr[1]));
res.add(a);
}
return res;
}

private void dfs(int[][] grid, int i, int j, Set<String> visited, int prev) {
if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] < prev || visited.contains(i + "," + j)) return;

visited.add(i + "," + j);


dfs(grid, i, j - 1, visited, grid[i][j]);
dfs(grid, i, j + 1, visited, grid[i][j]);
dfs(grid, i - 1, j, visited, grid[i][j]);
dfs(grid, i + 1, j, visited, grid[i][j]);
}
}

Complexity Analysis
- Time Complexity: O(m*n)
- Space Complexity: O(m*n)

130. Surrounded Regions


(https://leetcode.com/problems/surrounded-
regions/)
Medium

Given an m x n matrix board containing 'X' and 'O', capture all regions that are 4-directionally surrounded by 'X'.

A region is captured by flipping all 'O's into 'X's in that surrounded region.

Example 1:

Input: board = [
["X","X","X","X"],
["X","O","O","X"],
["X","X","O","X"],
["X","O","X","X"]]
Output: [
["X","X","X","X"],
["X","X","X","X"],
["X","X","X","X"],
["X","O","X","X"]]
Explanation: Surrounded regions should not be on the border, which means that any 'O' on the border of the board are not flipped to

Example 2:

Input: board = [["X"]]


Output: [["X"]]

Constraints:

m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j] is 'X' or 'O'.

Approach
- start dfs from all the boundaries
- mark all the cells that can be reach from the dfs as 1
- all those which were not marked 1 and in give board is "0", set them "X"

Solution
class Solution {
public void solve(char[][] board) {
int[][] mark = new int[board.length][board[0].length];
for (int j = 0; j < board[0].length; j++) {
if (board[0][j] == 'O')
dfs(board, 0, j, mark);
}
for (int j = 0; j < board[0].length; j++) {
if (board[board.length - 1][j] == 'O')
dfs(board, board.length - 1, j, mark);
}
for (int j = 0; j < board.length; j++) {
if (board[j][0] == 'O')
dfs(board, j, 0, mark);
}
for (int j = 0; j < board.length; j++) {
if (board[j][board[0].length - 1] == 'O')
dfs(board, j, board[0].length - 1, mark);
}
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
if(mark[i][j] == 0 && board[i][j] == 'O')
board[i][j] = 'X';
}
}
}

private void dfs(char[][] grid, int i, int j, int[][] mark) {


if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || mark[i][j] == 1 || grid[i][j] == 'X')
return;
mark[i][j] = 1;
dfs(grid, i, j - 1, mark);
dfs(grid, i, j + 1, mark);
dfs(grid, i - 1, j, mark);
dfs(grid, i + 1, j, mark);
}
}

Complexity Analysis
- Time Complexity: O(m*n)
- Space Complexity: O(m*n)

994. Rotting Oranges


(https://leetcode.com/problems/rotting-
oranges/)
Medium

You are given an m x n grid where each cell can have one of three values:

0 representing an empty cell,


1 representing a fresh orange, or
2 representing a rotten orange.

Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten.

Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1.
Example 1:

Input: grid = [[2,1,1],[1,1,0],[0,1,1]]


Output: 4

Example 2:

Input: grid = [[2,1,1],[0,1,1],[1,0,1]]


Output: -1
Explanation: The orange in the bottom left corner (row 2, column 0) is never rotten, because rotting only happens 4-directionally.

Example 3:

Input: grid = [[0,2]]


Output: 0
Explanation: Since there are already no fresh oranges at minute 0, the answer is just 0.

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 10
grid[i][j] is 0, 1, or 2.

Approaches
- Implementation of Topological Sort
- Insert all the rotten tomatoes inside the queue as sources
- have an fresh tomato count
- while sources queue not empty,
- we pull one pos at a time and check it's neighbour if there is any fresh tomato
- if we found fresh tomato, we make it rotten and add to our sources queue, decrement the fresh count
- for each run of while loop,
- we check if the sources is not empty, increment the time

Solution
class Solution {
public int orangesRotting(int[][] grid) {
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1} };
Queue<int[]> q = new LinkedList<>();
int n = grid.length, m = grid[0].length;
int countFresh = 0;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(grid[i][j] == 1)
countFresh++;
if(grid[i][j] == 2)
q.offer(new int[]{i, j});
}
}

if(countFresh == 0) return 0;
int time = 0;
while(!q.isEmpty()) {
int size = q.size();
for(int i = 0; i < size; i++) {
int[] currPos = q.poll();
int currI = currPos[0], currJ = currPos[1];
for(int[] dir : dirs) {
if(currI+dir[0] < 0 || currJ+dir[1] < 0 || currI+dir[0] >= n || currJ+dir[1] >= m || grid[currI+dir[0]][currJ+dir
continue;
if(grid[currI+dir[0]][currJ+dir[1]] == 1) {
grid[currI+dir[0]][currJ+dir[1]] = 2;
q.offer(new int[]{currI+dir[0], currJ+dir[1]});
countFresh--;
}
}

}
if(!q.isEmpty())
time++;

}
return countFresh!=0?-1:time;
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n*m)

663 · Walls and Gates


Medium

Description You are given a m x n 2D grid initialized with these three possible values.

-1 : A wall or an obstacle.

0 : A gate.

INF - Infinity means an empty room. We use the value 2^31 - 1 = 2147483647 to represent INF as you may assume that the distance to a gate is less than 2147483647.

Fill each empty room with the distance to its nearest gate. If it is impossible to reach a Gate, that room should remain filled with INF

Example 1
Input:
[
[2147483647,-1,0,2147483647],
[2147483647,2147483647,2147483647,-1],
[2147483647,-1,2147483647,-1],
[0,-1,2147483647,2147483647]]
Output:
[
[3,-1,0,1],
[2,2,1,-1],
[1,-1,2,-1],
[0,-1,3,4]]

Explanation:
the 2D grid is:
INF -1 0 INF
INF INF INF -1
INF -1 INF -1
0 -1 INF INF
the answer is:
3 -1 0 1
2 2 1 -1
1 -1 2 -1
0 -1 3 4

Example 2

Input:
[
[0,-1],
[2147483647,2147483647]]
Output:
[
[0,-1],
[1,2]]

Tags

Breadth First Search/BFS

Company

Facebook
Google

Approach
- BFS or Topological Sort
- Start from the grid having 0 (add them to a queue)
- while queue is not empty:
- if neighbour of current position is INF, change it to 1+val coming from the queue and add it to queue
- otherwise take min of neighour's value and 1+val

Solution
public class Solution {
/**
* @param rooms: m x n 2D grid
* @return: nothing
*/
public void wallsAndGates(int[][] grid) {
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1} };
Queue<int[]> q = new LinkedList<>();
int n = grid.length, m = grid[0].length;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(grid[i][j] == 0)
q.offer(new int[]{i, j});
}
}

while (!q.isEmpty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
int[] currPos = q.poll();
int currI = currPos[0], currJ = currPos[1];
int val = grid[currI][currJ];
for (int[] dir : dirs) {
if (currI + dir[0] < 0 || currJ + dir[1] < 0 || currI + dir[0] >= n || currJ + dir[1] >= m
|| grid[currI + dir[0]][currJ + dir[1]] == -1)
continue;
else if (grid[currI + dir[0]][currJ + dir[1]] == 2147483647) {
grid[currI + dir[0]][currJ + dir[1]] = 1 + val;
q.offer(new int[] { currI + dir[0], currJ + dir[1] });
} else {
grid[currI + dir[0]][currJ + dir[1]] = Math.min(grid[currI + dir[0]][currJ + dir[1]], 1 + val);
}
}

}
}
}
}

Complexity Analysis
- Time Complexity: O(k*n*m)
- Space Complexity: O(n*m)

207. Course Schedule


(https://leetcode.com/problems/course-
schedule/)
Medium

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you
must take course bi first if you want to take course ai.

For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1. Return true if you can finish all courses. Otherwise, return false.

Example 1:
Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.

Example 2:

Input: numCourses = 2, prerequisites = [[1,0],[0,1]]


Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible

Constraints:

1 <= numCourses <= 2000


0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
All the pairs prerequisites[i] are unique.

Approach
Topological Sort
- Have look a the solution for understanding the Topological sort algorithm

Solution
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// List<Integer> result = new ArrayList<>();
int result = 0;

// 1. Initialize the graph


Map<Integer, List<Integer>> graph = new HashMap<>();
Map<Integer, Integer> inDegree = new HashMap<>();

for(int i = 0; i < numCourses; i++) {


graph.put(i, new ArrayList<>());
inDegree.put(i, 0);
}

// 2. Build the graph


for(int i = 0; i < prerequisites.length; i++) {
int child = prerequisites[i][0], parent = prerequisites[i][1];
graph.get(parent).add(child);
inDegree.put(child, inDegree.get(child)+1);
}

// 3. Add all the sources(i.e, vertices with in-degree 0) to a queue


Queue<Integer> sources = new LinkedList<>();
for(Map.Entry<Integer, Integer> entry: inDegree.entrySet())
if(entry.getValue() == 0)
sources.offer(entry.getKey());

// 4. For each source, add it to the result, subtract 1 from all of it's children's in-degree
// & add if any child has in-degree 0, add it to sources queue
while(!sources.isEmpty()) {
int vertex = sources.poll();
result++;
for(int child:graph.get(vertex)) {
inDegree.put(child, inDegree.get(child)-1);
if(inDegree.get(child) == 0)
sources.offer(child);
}
}

// 5. If size of result equal to numCourses then return true else return false
return result == numCourses;
}
}

Complexity Analysis
- Time Complexity: O(m*n)
- Space Complexity: O(m*n)

210. Course Schedule II


(https://leetcode.com/problems/course-
schedule-ii/)
Medium

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you
must take course bi first if you want to take course ai.
For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1. Return the ordering of courses you should take to finish all courses. If there are many valid
answers, return any of them. If it is impossible to finish all courses, return an empty array.

Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]


Output: [0,1]
Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order

Example 2:

Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]


Output: [0,2,1,3]
Explanation: There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 a
So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3].

Example 3:

Input: numCourses = 1, prerequisites = []


Output: [0]

Constraints:

1 <= numCourses <= 2000


0 <= prerequisites.length <= numCourses * (numCourses - 1)
prerequisites[i].length == 2
0 <= ai, bi < numCourses
ai != bi
All the pairs [ai, bi] are distinct.

Aprroach
Ref: 8. Course Schedule (https://github.com/dipjul/NeetCode-150/blob/1db1597fe0d82d4741ecd5ee3600aea518824bb1/11.%20Graphs/8.CourseSchedule.md)

Topologicalo Sort

Solution
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
List<Integer> result = new ArrayList<>();

// 1. Initialize the graph


Map<Integer, List<Integer>> graph = new HashMap<>();
Map<Integer, Integer> inDegree = new HashMap<>();

for(int i = 0; i < numCourses; i++) {


graph.put(i, new ArrayList<>());
inDegree.put(i, 0);
}

// 2. Build the graph


for(int i = 0; i < prerequisites.length; i++) {
int child = prerequisites[i][0], parent = prerequisites[i][1];
graph.get(parent).add(child);
inDegree.put(child, inDegree.get(child)+1);
}

// 3. Add all the sources(i.e, vertices with in-degree 0) to a queue


Queue<Integer> sources = new LinkedList<>();
for(Map.Entry<Integer, Integer> entry: inDegree.entrySet())
if(entry.getValue() == 0)
sources.offer(entry.getKey());

// 4. For each source, add it to the result, subtract 1 from all of it's children's in-degree
// & add if any child has in-degree 0, add it to sources queue
while(!sources.isEmpty()) {
int vertex = sources.poll();
result.add(vertex);
for(int child:graph.get(vertex)) {
inDegree.put(child, inDegree.get(child)-1);
if(inDegree.get(child) == 0)
sources.offer(child);
}
}

if(result.size() != numCourses)
return new int[]{};
return result.stream().mapToInt(i->i).toArray();
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n*m)

684. Redundant Connection


(https://leetcode.com/problems/redundant-
connection/)
Medium

In this problem, a tree is an undirected graph that is connected and has no cycles.
You are given a graph that started as a tree with n nodes labeled from 1 to n, with one additional edge added. The added edge has two different vertices chosen from 1 to n, and was
not an edge that already existed. The graph is represented as an array edges of length n where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the graph.

Return an edge that can be removed so that the resulting graph is a tree of n nodes. If there are multiple answers, return the answer that occurs last in the input.

Example 1:

Input: edges = [[1,2],[1,3],[2,3]]


Output: [2,3]

Example 2:

Input: edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]


Output: [1,4]

Constraints:

n == edges.length
3 <= n <= 1000
edges[i].length == 2
1 <= ai < bi <= edges.length
ai != bi
There are no repeated edges.
The given graph is connected.

Approach
Ref: Union Find (https://github.com/dipjul/NeetCode-150/blob/e7002953ae531e571f4d148f591a265dda256d7d/Algorithms/1.UnionFind.md)

- Using UnionFind we'll check whether the nodes of the given edge are already connected without using the current edge
- if already connect which means this edge is reduntant
- else union the nodes of the current edge

Solution
class Solution {
int[] parent;
int[] rank;
public int[] findRedundantConnection(int[][] edges) {
int n = edges.length;
init(n);
int[] res = new int[2];
for(int[] edge:edges) {
if(!union(edge[0], edge[1]))
res = new int[]{ edge[0], edge[1] };
}
return res;
}

private void init(int n) {


parent = new int[n+1];
rank = new int[n+1];
for(int i = 1; i <= n; i++) {
parent[i] = i;
rank[i] = 1;
}
}

private int find(int val) {


while(val != parent[val]) {
parent[val] = parent[parent[val]];
val = parent[val];
}
return parent[val];
}

private boolean union(int x, int y) {


int p1 = find(x);
int p2 = find(y);
if(p1 == p2)
return false;
if(rank[p1] > rank[p2]) {
parent[p2] = p1;
rank[p1] += rank[p2];
} else {
parent[p1] = p2;
rank[p2] += rank[p1];
}
return true;
}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(n)

323. Number of Connected Components in


an Undirected Graph
(https://leetcode.com/problems/number-of-
connected-components-in-an-undirected-
graph/)
Medium

Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to find the number of connected components in an undirected
graph.

Example 1:

Input: n = 5 and edges = [[0, 1], [1, 2], [3, 4]]

0 3
| |
1 --- 2 4

Output: 2

Example 2:

Input: n = 5 and edges = [[0, 1], [1, 2], [2, 3], [3, 4]]

0 4
| |
1 --- 2 --- 3

Output: 1

Note: You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Approach
1. DFS
- Number of times dfs called is equal to the number of components
2. Union Find
- Take number of nodes as the number of components initially
- if the nodes of an edge are not already connected then decrease the count and union the nodes

Solution
// 1. DFS
class Solution {
public int countComponents(int n, int[][] edges) {
HashMap<Integer, List<Integer>> graph = new HashMap<Integer, List<Integer>>();
boolean[] visited = new boolean[n];

int count = 0;
// Step - 1 Build the graph
for(int i = 0; i < n; i++) {
graph.put(i, new ArrayList<Integer>());
}

for (int i = 0; i < edges.length; i++){


// Make Undirected Graph
graph.get(edges[i][0]).add(edges[i][1]);
graph.get(edges[i][1]).add(edges[i][0]);
}

// Step -2 run algorithm


for (int i = 0; i < n; i++) {
if(!visited[i]) {
count++;
dfs(i, graph, visited);
}
}
return count;

private void dfs(int at, HashMap<Integer, List<Integer>> graph, boolean[] visited) {


visited[at] = true;
for(Integer child: graph.get(at)) {
if(!visited[child]) {
dfs(child, graph, visited);
}
}
}
}
// 2. Union Find
class Solution {
public int countComponents(int n, int[][] edges) {
UnionFind uf = new UnionFind(n);
int count = n;
for(int[] edge: edges) {
if(uf.isConnected(edge[0], edge[1]))
continue;
count--;
uf.union(edge[0], edge[1]);
}
return count;
}
}
class UnionFind {
int[] root;
int[] rank;

UnionFind(int size) {
root = new int[size];
rank = new int[size];

for(int i = 0; i < size; i++) {


root[i] = i;
rank[i] = 0;
}
}

public int find(int x) {


if(x == root[x])
return x;
return root[x] = find(root[x]);
}

public void union(int x, int y) {


int rootX = find(x);
int rootY = find(y);

if(rootX != rootY) {
if(rank[rootX] > rank[rootY])
root[rootY] = rootX;
else if(rank[rootX] < rank[rootY])
root[rootX] = rootY;
else {
root[rootY] = rootX;
rank[rootX]++;
}
}
}

public boolean isConnected(int x, int y) {


return find(x) == find(y);
}
}

Complexity Analysis
- Time Complexity:
- DFS: O(n)
- Union Find: O(n)
- Space Complexity:
- DFS: O(n+m)
- Union Find: O(n)
178 · Graph Valid Tree
(https://www.lintcode.com/problem/178/)
Medium

Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.

You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Example 1:

Input: n = 5 edges = [[0, 1], [0, 2], [0, 3], [1, 4]]
Output: true.

Example 2:

Input: n = 5 edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]]
Output: false.

Approach
- check if number of edges is equal to n-1 or not
- if not equal return false;
- do dfs and put it in a set, size of set should be equal to number of nodes

Solution
public class Solution {
/**
* @param n: An integer
* @param edges: a list of undirected edges
* @return: true if it's a valid tree, or false
*/
public boolean validTree(int n, int[][] edges) {
// write your code here
if(edges.length != n-1)
return false;
if(n == 0 || n == 1)
return true;
Set<Integer> mark = new HashSet<>();
Map<Integer, List<Integer>> graph = new HashMap<>();

for(int i = 0; i < edges.length; i++) {


int n1 = edges[i][0];
int n2 = edges[i][1];
List<Integer> arr1 = graph.getOrDefault(n1, new ArrayList<>());
arr1.add(n2);
List<Integer> arr2 = graph.getOrDefault(n2, new ArrayList<>());
arr2.add(n1);
graph.put(n1, arr1);
graph.put(n2, arr2);
}
dfs(graph, 0, mark);
return mark.size() == n;
}

private void dfs(Map<Integer, List<Integer>> graph, int i, Set<Integer> mark) {


mark.add(i);
for(int node : graph.get(i)) {
if(!mark.contains(node))
dfs(graph, node, mark);
}
}
}

Complexity Analysis
- Time Complexity: O(n*m)
- Space Complexity: O(n+m)

127. Word Ladder


(https://leetcode.com/problems/word-ladder/)
Hard

A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:

Every adjacent pair of words differs by a single letter.


Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList.
sk == endWord

Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence from beginWord to endWord, or 0 if no
such sequence exists.

Example 1:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is 5 words long.

Example 2:

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]


Output: 0
Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.

Constraints:

1 <= beginWord.length <= 10


endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord, endWord, and wordList[i] consist of lowercase English letters.
beginWord != endWord
All the words in wordList are unique.

Approach
- Add the begin word to the wordList
- Create a graph such that neighbours of each node is differ by one character
- eg. fog would be neighbour of cog
- if endWord not present in the graph return 0
- How to find if two words are neighbour?
- Loop through the two words, whenever it differs increment the count
- if count == 1 return true else false
- Start bfs search from the beginWord untill we reach the endWord
- Keep count of distance from beginWord, return distance

Solution
class Solution {

public int ladderLength(String beginWord, String endWord, List<String> wordList) {


Map<String, List<String>> graph = new HashMap<>();
wordList.add(0, beginWord);
for (int i = 0; i < wordList.size(); i++) {
for (int j = i + 1; j < wordList.size(); j++) {
String s1 = wordList.get(i);
String s2 = wordList.get(j);
if (differByOne(s1, s2)) {
List<String> arr1 = graph.getOrDefault(s1, new ArrayList<>());
arr1.add(s2);
List<String> arr2 = graph.getOrDefault(s2, new ArrayList<>());
arr2.add(s1);
graph.put(s1, arr1);
graph.put(s2, arr2);
}
}
}

if (!graph.containsKey(endWord)) return 0;

Queue<String> q = new LinkedList<>();

q.offer(beginWord);

if (q.size() == 0) return 0;
Set<String> visited = new HashSet<>();
int pathSize = 0;
while (!q.isEmpty()) {
int size = q.size();
pathSize++;
for (int i = 0; i < size; i++) {
String s1 = q.poll();
visited.add(s1);
if (s1.equals(endWord)) return pathSize;
for (String s : graph.get(s1)) {
if (!visited.contains(s)) q.offer(s);
}
}
}

return 0;
}

private boolean differByOne(String word1, String word2) {


if (word1.length() != word2.length()) return false;
int n = word1.length();
int count = 0;
for (int i = 0; i < n; i++) {
if (word1.charAt(i) != word2.charAt(i)) {
count++;
if (count > 1) return false;
}
}
return count == 1;
}
}

Complexity Analysis
- Time Complexity: O(n*n)
- Space Complexity: O(n+m)

Resources
1584. Min Cost to Connect All Points
(https://leetcode.com/problems/min-cost-to-
connect-all-points/)
Medium

You are given an array points representing integer coordinates of some points on a 2D-plane, where points[i] = [xi, yi].

The cost of connecting two points [xi, yi] and [xj, yj] is the manhattan distance between them: |xi - xj| + |yi - yj|, where |val| denotes the absolute value of val.

Return the minimum cost to make all points connected. All points are connected if there is exactly one simple path between any two points.

Example 1:

Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]]


Output: 20
Explanation:

We can connect the points as shown above to get the minimum cost of 20.
Notice that there is a unique path between every pair of points.

Example 2:

Input: points = [[3,12],[-2,5],[-4,1]]


Output: 18

Constraints:

1 <= points.length <= 1000 -106 <= xi, yi <= 106 All pairs (xi, yi) are distinct.

Approach
- MST
- PRIMS Algorithm

Solution
class Solution {
class Edge {
int[] x;
int[] y;
int cost;
Edge(int[] x, int[] y) {
this.x = x;
this.y = y;
this.cost = Math.abs(x[0]-y[0])+Math.abs(x[1]-y[1]);
}
}

public int minCostConnectPoints(int[][] points) {


// MST
// prims
int cost = 0;
Set<int[]> visited = new HashSet<>(); // to store the visited vertices
int numOfVertices = points.length;
PriorityQueue<Edge> q = new PriorityQueue<>((a, b)->a.cost-b.cost); // to store the edges based on cost
visited.add(points[0]);
Queue<int[]> source = new LinkedList<>(); // sources to determine which node to relax
source.add(points[0]);
while(visited.size() != numOfVertices) { // till all nodes are visited or n-1 edges are added
int[] src = source.poll();
putEdges(src, visited, points, q); // put hte edges to the queue
while(!q.isEmpty()) {
Edge edge = q.poll(); // get the best edge
if (!detectCycle(src, edge.y, visited)) { // if cycle is not form after adding the edge
cost += edge.cost;
visited.add(edge.y);
source.add(edge.y);
break; // so that it doesn't look to add the other edges right away
}
}
}
return cost;
}

private void putEdges(int[] point, Set<int[]> set, int[][] points, PriorityQueue<Edge> q) {


for(int[] pnt : points) {
if(pnt != point && !set.contains(pnt))
q.offer(new Edge(point, pnt));
}
}

// to detect cycle
private boolean detectCycle(int[] a, int[] b, Set<int[]> set) {
return set.contains(a) && set.contains(b);
}
}

Complexity Analysis
- Time Complexity: O(V*E)
- Space Complexity: O(V+E)

743. Network Delay Time


(https://leetcode.com/problems/network-
delay-time/)
Medium

You are given a network of n nodes, labeled from 1 to n. You are also given times, a list of travel times as directed edges times[i] = (ui, vi, wi), where ui is the source node, vi is the
target node, and wi is the time it takes for a signal to travel from source to target.

We will send a signal from a given node k. Return the minimum time it takes for all the n nodes to receive the signal. If it is impossible for all the n nodes to receive the signal, return
-1.

Example 1:

Input: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2


Output: 2

Example 2:

Input: times = [[1,2,1]], n = 2, k = 1


Output: 1

Example 3:

Input: times = [[1,2,1]], n = 2, k = 2


Output: -1

Constraints:

1 <= k <= n <= 100


1 <= times.length <= 6000
times[i].length == 3
1 <= ui, vi <= n
ui != vi
0 <= wi <= 100
All the pairs (ui, vi) are unique. (i.e., no multiple edges.)

Approach

Solution
public class Solution {

public int networkDelayTime(int[][] times, int n, int k) {


Map<Integer, List<int[]>> graph = new HashMap<>();
for (int[] time : times) {
List<int[]> neighbours = graph.getOrDefault(time[0], new ArrayList<>());
neighbours.add(new int[] { time[1], time[2] });
graph.put(time[0], neighbours);
}
int[] cost = new int[n + 1];
for (int i = 1; i <= n; i++) cost[i] = 100005;
cost[k] = 0;
Queue<Integer> q = new LinkedList<>();
q.offer(k);
while (!q.isEmpty()) {
int vertex = q.poll();
if (!graph.containsKey(vertex)) continue;

List<int[]> neighbours = graph.get(vertex);


for (int[] nei : neighbours) {
int newCost = cost[vertex] + nei[1];
if (newCost < cost[nei[0]]) {
cost[nei[0]] = newCost;
q.offer(nei[0]);
}
}
}
int ans = -1;
for (int i = 1; i <= n; i++) {
if (cost[i] >= 100005) return -1;
if (cost[i] > ans) ans = cost[i];
}
return ans;
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

787. Cheapest Flights Within K Stops


(https://leetcode.com/problems/cheapest-
flights-within-k-stops/)
Medium

There are n cities connected by some number of flights. You are given an array flights where flights[i] = [fromi, toi, pricei] indicates that there is a flight from city fromi to city toi with
cost pricei.

You are also given three integers src, dst, and k, return the cheapest price from src to dst with at most k stops. If there is no such route, return -1.

Example 1:
Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1
Output: 700
Explanation:
The graph is shown above.
The optimal path with at most 1 stop from city 0 to 3 is marked in red and has cost 100 + 600 = 700.
Note that the path through cities [0,1,2,3] is cheaper but is invalid because it uses 2 stops.

Example 2:

Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1


Output: 200
Explanation:
The graph is shown above.
The optimal path with at most 1 stop from city 0 to 2 is marked in red and has cost 100 + 100 = 200.

Example 3:

Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0


Output: 500
Explanation:
The graph is shown above.
The optimal path with no stops from city 0 to 2 is marked in red and has cost 500.

Constraints:

1 <= n <= 100


0 <= flights.length <= (n * (n - 1) / 2)
flights[i].length == 3
0 <= fromi, toi < n
fromi != toi
1 <= pricei <= 104
There will not be any multiple flights between two cities.
0 <= src, dst, k < n
src != dst

Approach
Ref: Bellman Ford (https://github.com/dipjul/NeetCode-
150/blob/9a8121cc3db395bc2b180b56c88524c678b72d03/Algorithms/4.Bellman-ford.md)

- Bellman ford algorithm


- It runs for n * E time, n : number of nodes & E : edges
- here modified to run for k+1 times

Solution
class Solution {

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
int[] cost = new int[n];
Arrays.fill(cost, Integer.MAX_VALUE);
int[] tmp = new int[n];
Arrays.fill(tmp, Integer.MAX_VALUE);
cost[src] = 0;
tmp[src] = 0;
while(k >= 0) {
for(int[] flight : flights) {
if(cost[flight[0]] != Integer.MAX_VALUE) {
int newCost = cost[flight[0]]+flight[2];
if(newCost < tmp[flight[1]])
tmp[flight[1]] = newCost;
}
}
cost = Arrays.copyOfRange(tmp, 0, n);
k--;
}
return cost[dst] == Integer.MAX_VALUE?-1:cost[dst];
}
}

Complexity Analysis
- Time Complexity: O(k*E), E size of flights array
- Space Complexity: O(n)

Resources
70. Climbing Stairs
(https://leetcode.com/problems/climbing-
stairs/)
Easy

You are climbing a staircase. It takes n steps to reach the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Example 1:

Input: n = 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

Example 2:
Input: n = 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

Constraints:

1 <= n <= 45

Approach
- Problem effectively becomes fibonacci

Solution
class Solution {
public int climbStairs(int n) {
int one = 1, two = 1;
for(int i = 2; i <= n; i++) {
int tmp = one;
one = one + two;
two = tmp;
}
return one;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(1)

746. Min Cost Climbing Stairs


(https://leetcode.com/problems/min-cost-
climbing-stairs/)
Easy

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.

You can either start from the step with index 0, or the step with index 1.

Return the minimum cost to reach the top of the floor.

Example 1:

Input: cost = [10,15,20]


Output: 15
Explanation: You will start at index 1.
- Pay 15 and climb two steps to reach the top.
The total cost is 15.

Example 2:
Input: cost = [1,100,1,1,1,100,1,1,100,1]
Output: 6
Explanation: You will start at index 0.
- Pay 1 and climb two steps to reach index 2.
- Pay 1 and climb two steps to reach index 4.
- Pay 1 and climb two steps to reach index 6.
- Pay 1 and climb one step to reach index 7.
- Pay 1 and climb two steps to reach index 9.
- Pay 1 and climb one step to reach the top.
The total cost is 6.

Constraints:

2 <= cost.length <= 1000


0 <= cost[i] <= 999

Approach
- Start from the end of the array
- Recursive formula:
- cost[i] = cost[i] + min(cost[i+1], cost[i+2])
- return min(cost[o], cost[1])

Solution
class Solution {
public int minCostClimbingStairs(int[] cost) {

for(int i = cost.length-3; i >= 0; i--) {


cost[i] += Math.min(cost[i+1], cost[i+2]);
}

return Math.min(cost[0], cost[1]);


}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity O(1)

198. House Robber


(https://leetcode.com/problems/house-
robber/)
Medium

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.

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.

Example 1:
Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: nums = [2,7,9,3,1]


Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.

Constraints:

1 <= nums.length <= 100


0 <= nums[i] <= 400

Approach
Pattern:
for loop
dp[i] = max(dp[i-1], nums[i]+dp[i-2]);
- either take the amount till previous element or else add current element to the amount till pre previous elements

Solution
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n == 1)
return nums[0];
int[] dp = new int[n];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < n; i++) {
dp[i] = Math.max(dp[i-1], nums[i]+dp[i-2]);
}

return dp[n-1];
}

// optimized
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n == 1)
return nums[0];
int prev2 = nums[0];
int prev1 = Math.max(nums[0], nums[1]);

for(int i = 2; i < n; i++) {


int temp = prev1;
prev1 = Math.max(prev1, nums[i]+prev2);
prev2 = temp;
}
return prev1;
}
}
Complexity Analysis
1st solution:
- Time Complexity: O(N)
- Space Complexity: O(N)
2nd solution:
- Time Complexity: O(N)
- Space Complexity: O(1)

213. House Robber II


(https://leetcode.com/problems/house-
robber-ii/)
Medium

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That
means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system connected, and it will automatically contact the police if two adjacent houses
were broken into on the same night.

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.

Example 1:

Input: nums = [2,3,2]


Output: 3
Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2), because they are adjacent houses.

Example 2:

Input: nums = [1,2,3,1]


Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 3:

Input: nums = [1,2,3]


Output: 3

Constraints:

1 <= nums.length <= 100


0 <= nums[i] <= 1000

Approach
Reference: 3. House Robber (https://github.com/dipjul/NeetCode-150/blob/d95d91ca2c7e0e25f421e3959ce0c0b62d9ba5a0/13.%201-
D%20Dynamic%20Programming/3.HouseRobber.md)

- Reuse house robber, 1st time for [0, n-1] & 2nd time for [1, n]
- Return max out of them

Solution
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n == 1)
return nums[0];
if(n == 2)
return Math.max(nums[0], nums[1]);
int prev2 = nums[0];
int prev1 = Math.max(nums[0], nums[1]);

for(int i = 2; i < n-1; i++) {


int temp = prev1;
prev1 = Math.max(prev1, nums[i]+prev2);
prev2 = temp;
}
int ans1 = prev1;
prev2 = nums[1];
prev1 = Math.max(nums[1], nums[2]);

for(int i = 3; i < n; i++) {


int temp = prev1;
prev1 = Math.max(prev1, nums[i]+prev2);
prev2 = temp;
}
int ans2 = prev1;
return Math.max(ans1, ans2);
}
}

Complexity Analysis
- Time Complexity: O(2 * N) ~ O(N)
- Space Complexity: O(1)

5. Longest Palindromic Substring


Medium

Given a string s, return the longest palindromic substring in s.

Example 1:

Input: s = "babad"
Output: "bab"
Explanation: "aba" is also a valid answer.

Example 2:

Input: s = "cbbd"
Output: "bb"

Constraints:

1 <= s.length <= 1000


s consist of only digits and English letters.

Approach
Approach 1:
- for each index, try to find odd length & even length palindromes
- odd: start the inner loop from the same index
- even: start the inner loop one pointer on i and other at i+1
- expand towards the left and right of i while the chars are same
- keep checking the len, and store the start and end
- substring(start,end+1), end+1 because the substring method takes end index one more than the original end index

Approach 2:
- Manacher Algorithm

Solution
class Solution {
public String longestPalindrome(String s) {
int resLen = 0, start = 0, end = 0;

if(s == null || s.length() == 0)


return "";

for(int i = 0; i < s.length(); i++) {

// odd length palindromes


int left = i, right = i;
while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
if(right-left+1 > resLen) {
start = left;
end = right;
resLen = right-left+1;
}
left--;
right++;
}

// even length palindromes


left = i;
right = i+1;
while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
if(right-left+1 > resLen) {
start = left;
end = right;
resLen = right-left+1;
}
left--;
right++;
}

return s.substring(start, end+1);


}
}
Manacher Algorithm

class Solution {
public String longestPalindrome(String s) {
StringBuilder sb = new StringBuilder();
// appending chars before, inbetween and end to make the string
// such that we can use the manacher odd algorithm
sb.append("$");
for(int i = 0; i < s.length(); i++) {
sb.append("#");
sb.append(s.charAt(i));
}
sb.append("#");
sb.append("&");

int[] p = manacher_odd(sb.toString());
int center = 0, resLen = 0;
for(int i = 0; i < p.length; i++) {
if(p[i] > resLen) {
center = i;
resLen = p[i];
}
}

return s.substring((center-resLen)/2, (center+resLen)/2);


}

private int[] manacher_odd(String s) {


int[] p = new int[s.length()]; // max palindromes length centered at the index
int center = 0; // center is to keep the new center
int rightBoundary = 0; // rightBoundary to keep the boundary

for(int i = 1; i < s.length()-1; i++) {


// find the mirror [..mirror...center...i..]
int mirror = 2*center-i;

// if i is inside the boundary then only we can assign the value


if(i < rightBoundary)
p[i] = Math.min(p[mirror], rightBoundary-i);

// while the chars are matching keep incrementing p[i]


// which interns result into match the next chars to the left and right
while(s.charAt(i-(p[i]+1)) == s.charAt(i+(p[i]+1)))
p[i]++;

// if the palindrome with centered i, exceeds the right boundary


// then we need to update the right boundary and the new center
if(i + p[i] > rightBoundary) {
center = i;
rightBoundary = i + p[i];
}
}
return p;
}
}

Complexity Analysis
- Time Complexity:
- Approach 1: O(N*N)
- Approach 2: O(N)
- Space Complexity:
- Approach 1: O(1)
- Approach 2: O(N)

647. Palindromic Substrings


(https://leetcode.com/problems/palindromic-
substrings/)
Medium

Given a string s, return the number of palindromic substrings in it.

A string is a palindrome when it reads the same backward as forward.

A substring is a contiguous sequence of characters within the string.

Example 1:

Input: s = "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".

Example 2:

Input: s = "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".

Constraints:

1 <= s.length <= 1000


s consists of lowercase English letters.

Approach
Reference: 5.LongestPalindromicSubstring (https://github.com/dipjul/NeetCode-150/blob/7961198c07e09a081fec9fbcc445e315bab042a7/13.%201-
D%20Dynamic%20Programming/5.LongestPalindromicSubstring.md)

Approach 1:
- expand around every element as a center
- ood length (i,i)
- even length (i,i+1)
- increment for each match

Approach 2:
- Manacher Algorithm
- every element in p[i]
- (p[i]+1)/2

Solution
class Solution {
public int countSubStrings(String s) {
if (s.length() < 2) {
return s.length();
}
int result = 0;
for (int i = 0; i < s.length(); i++) {
// Odd Length
int left = i, right = i;
while (left >=0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
result++;
left -=1;
right +=1;
}
// Even Length
left = i;
right = i + 1;
while (left >=0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
result++;
left -=1;
right +=1;
}
}
return result;
}
}
class Solution {
public int countSubstrings(String s) {
StringBuilder sb = new StringBuilder();
sb.append("$");
for(int i = 0; i < s.length(); i++) {
sb.append("#");
sb.append(s.charAt(i));
}
sb.append("#");
sb.append("@");
int[] p = manacher_odd(sb.toString());
int ans = 0;
for(int i = 0; i < p.length; i++)
ans += (p[i]+1)/2;
return ans;
}

private int[] manacher_odd(String s) {


int[] p = new int[s.length()];
int center = 0, rightBoundary = 0;

for(int i = 1; i < s.length()-1; i++) {

int mirror = 2*center-i;


if(i < rightBoundary)
p[i] = Math.min(p[mirror], rightBoundary-i);

while(s.charAt(i-(p[i]+1)) == s.charAt(i+(p[i]+1)))
p[i]++;

if(i + p[i] > rightBoundary) {


center = i;
rightBoundary = i + p[i];
}
}

return p;
}
}

Complexity Analysis
- Time Complexity:
- Approach 1: O(N*N)
- Approach 2: O(N)
- Space Complexity:
- Approach 1: O(1)
- Approach 2: O(N)

91. Decode Ways


(https://leetcode.com/problems/decode-
ways/)
Medium

A message containing letters from A-Z can be encoded into numbers using the following mapping:

'A' -> "1"


'B' -> "2"

...

'Z' -> "26"

To decode an encoded message, all the digits must be grouped then mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example,
"11106" can be mapped into:

"AAJF" with the grouping (1 1 10 6)

"KJF" with the grouping (11 10 6)

Note that the grouping (1 11 06) is invalid because "06" cannot be mapped into 'F' since "6" is different from "06".

Given a string s containing only digits, return the number of ways to decode it.

The test cases are generated so that the answer fits in a 32-bit integer.

Example 1:

Input: s = "12"
Output: 2
Explanation: "12" could be decoded as "AB" (1 2) or "L" (12).

Example 2:

Input: s = "226"
Output: 3
Explanation: "226" could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

Example 3:

Input: s = "06"
Output: 0
Explanation: "06" cannot be mapped to "F" because of the leading zero ("6" is different from "06").

Constraints:

1 <= s.length <= 100


s contains only digits and may contain leading zero(s).

Approach
- Approach
- take an dp array of size 1 greater than len(s)
- at the last index we'll keep 1
- for every char, if char not equal of 0
- dp[i] += dp[i+1]
- if s(i,i+2) is < 26
- dp[i] += dp[i+2]
- at last we'll have the ans at dp[0]

- Optimize
- pattern seems like fibonacci
- we can replace the array with three variables
- curr will have dp[i]
- prev will have dp[i+1]
- prev2 will have dp[i+2]

Solution
class Solution {

public int numDecodings(String s) {


int n = s.length();
int[] dp = new int[n+1];
dp[n] = 1;

for(int i = n-1; i >= 0; i--) {


char ch = s.charAt(i);
if(ch != '0') {
dp[i] += dp[i+1];
if(i < n-1 && Integer.valueOf(s.substring(i,i+2)) <= 26)
dp[i] += dp[i+2];
}
}
return dp[0];
}
// Optimized
public int numDecodings(String s) {
int n = s.length();

int prev = 1, prev2 = 0, curr = 0;

for(int i = n-1; i >= 0; i--) {


char ch = s.charAt(i);
curr = 0;
if(ch != '0') {
curr += prev;
if(i < n-1 && ((ch-'0' == 1) || (ch-'0' <= 2 && s.charAt(i+1)-'0' <= 6)))
curr += prev2;
}
int tmp = prev;
prev = curr;
prev2 = tmp;
}
return curr;
}
}

Complexity Analysis
- Time Complexity:
- Approach 1 & Approach 2: O(N)
- Space Complexity:
- Approach 1: O(N), dp array of n size
- Approach 2: O(1), 3 variables

322. Coin Change


(https://leetcode.com/problems/coin-change/)
Medium

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

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.

You may assume that you have an infinite number of each kind of coin.

Example 1:
Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3


Output: -1

Example 3:

Input: coins = [1], amount = 0


Output: 0

Constraints:

1 <= coins.length <= 12

1 <= coins[i] <= 231 - 1


0 <= amount <= 104

Approach
- we'll have dp array of size amount+1
- we'll initiaze the array with large value
- dp[0] would be 0
- for in range(1, amount)
- for each coin
- dp[i] = min(dp[i], 1 + dp[i-coin])
- dp[amount] will either have the ans or else it'll have the default value

Solution
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}

Complexity Analysis
- Time Complex: O(N*K), N: amount+1, K = coins.length
- Space complexity: O(N), N: amount+1

152. Maximum Product Subarray


(https://leetcode.com/problems/maximum-
product-subarray/)
Medium

Given an integer array nums, find a contiguous non-empty subarray within the array that has the largest product, and return the product.

The test cases are generated so that the answer will fit in a 32-bit integer.

A subarray is a contiguous subsequence of the array.

Example 1:

Input: nums = [2,3,-2,4]


Output: 6
Explanation: [2,3] has the largest product 6.

Example 2:

Input: nums = [-2,0,-1]


Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.

Constraints:

1 <= nums.length <= 2 * 104


-10 <= nums[i] <= 10
The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.

Approach
- two variable to keep max & min at that index
- result, initially stores the max element present in the array
- if we encounter 0, min and max set to 1
- else
- min = min(min*arr[i],max*arr[i],arr[i])
- max = max(prevMin*arr[i], max*arr[i], arr[i])
- result = max(max, result)

Solution
class Solution {
public int maxProduct(int[] nums) {
int n = nums.length;
int min = nums[0], max = nums[0];
int result = nums[0];

for(int i = 1; i < n; i++)


result = Math.max(result, nums[i]);

for(int i = 1; i < n; i++) {


if(nums[i] == 0) {
min = 1;
max = 1;
} else {
int tmp1 = min;
min = Math.min(min*nums[i], Math.min(max*nums[i], nums[i]));
max = Math.max(tmp1*nums[i], Math.max(max*nums[i], nums[i]));
result = Math.max(result, max);
}
}

return result;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(1)

139. Word Break


(https://leetcode.com/problems/word-break/)
Medium

Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.

Note that the same word in the dictionary may be reused multiple times in the segmentation.

Example 1:

Input: s = "leetcode", wordDict = ["leet","code"]


Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".

Example 2:

Input: s = "applepenapple", wordDict = ["apple","pen"]


Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.

Example 3:

Input: s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]


Output: false

Constraints:

1 <= s.length <= 300


1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s and wordDict[i] consist of only lowercase English letters.
All the strings of wordDict are unique.

Approach
Decision tree of Backtracking

Cache

Bottom up

- start from last index


- if index + len of word from dict is less than eqaul to n and substring of s from index to index + len(word) is equal to word
- dp[index] = dp[index + len(word)]
- if dp[index] is true move to next index
- dp[0] would have the result

Solution
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
int n = s.length();
boolean dp[] = new boolean[n+1];
dp[n] = true;

for(int i = n-1; i >= 0; i--) {


for(String w : wordDict) {
if(i + w.length() <= n && w.equals(s.substring(i, i+w.length())))
dp[i] = dp[i + w.length()];

if(dp[i])
break;
}
}
return dp[0];
}
}

Complexity Analysis
- Time Complexity: O(N*len(wordInDict))
- Space Complexity: O(N)

300. Longest Increasing Subsequence


(https://leetcode.com/problems/longest-
increasing-subsequence/)
Medium

Given an integer array nums, return the length of the longest strictly increasing subsequence.

A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements. For example, [3,6,2,7] is a
subsequence of the array [0,3,1,6,2,2,7].

Example 1:

Input: nums = [10,9,2,5,3,7,101,18]


Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.

Example 2:

Input: nums = [0,1,0,3,2,3]


Output: 4

Example 3:

Input: nums = [7,7,7,7,7,7,7]


Output: 1

Constraints:

1 <= nums.length <= 2500


-104 <= nums[i] <= 104
Follow up: Can you come up with an algorithm that runs in O(n log(n)) time complexity?

Approach
- Starting from 2nd last element, lis[] will store 1 for all the elements
- compare it with the next element, if the next element is less than current element
- add max of lis[curr], 1+lis[next]

Solution
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
int[] lis = new int[n];
Arrays.fill(lis, 1);
lis[n-1] = 1;
int res = 1;
for(int i = n-2; i >= 0; i--) {
for(int j = i+1; j < n; j++) {
if(nums[i] < nums[j])
lis[i] = Math.max(lis[i], 1+lis[j]);
}
res = Math.max(res, lis[i]);
}
return res;
}
}

Complexity Analysis
- Time Complexity: O(n^2)
- Space Complexity: O(n)

416. Partition Equal Subset Sum


(https://leetcode.com/problems/partition-
equal-subset-sum/)
Medium

Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Example 1:

Input: nums = [1,5,11,5]


Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:

Input: nums = [1,2,3,5]


Output: false
Explanation: The array cannot be partitioned into equal sum subsets.
Constraints:

1 <= nums.length <= 200


1 <= nums[i] <= 100

Approach
- sum of all the elements, if it's odd return false
- otherwise, find whther there is a subset such that it's equal to sum/2

Solution
class Solution {
Boolean[][] dp;
public boolean canPartition(int[] nums) {
int sum = 0;
for(int num : nums) {
sum += num;
}
if(sum%2 != 0)
return false;
dp = new Boolean[nums.length][sum/2+1];
return subsetSum(nums, 0, sum/2);
}

// DP
private boolean subsetSum(int[] nums, int ind, int sum) {
if(ind >= nums.length || sum < 0)
return false;
if(sum == 0)
return true;
if(dp[ind][sum] != null)
return dp[ind][sum];
dp[ind][sum] = subsetSum(nums, ind+1, sum-nums[ind]) || subsetSum(nums, ind+1, sum);
return dp[ind][sum];
}

// Recursion
private boolean subsetSum2(int[] nums, int ind, int sum) {
if(ind >= nums.length)
return false;
if(sum == 0)
return true;
return subsetSum(nums, ind+1, sum-nums[ind]) || subsetSum(nums, ind+1, sum);
}
}

Complexity Analysis
- Time Complexity: O(N*Sum)
- Space Complexity: O(N*Sum)

Resources
62. Unique Paths
(https://leetcode.com/problems/unique-
paths/)
Medium

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.

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.

The test cases are generated so that the answer will be less than or equal to 2 * 109.

Example 1:

Input: m = 3, n = 7
Output: 28

Example 2:

Input: m = 3, n = 2
Output: 3
Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Down -> Down
2. Down -> Down -> Right
3. Down -> Right -> Down

Constraints:

1 <= m, n <= 100

Approach
- Last row and last column would have only one way
- others:
- dp[i][j] = dp[i+1][j]+dp[i][j+1];
- dp[0][0] would have the result

Solution
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
// last column
for(int i = 0; i < m; i++) {
dp[i][n-1] = 1;
}
// last row
for(int i = 0; i < n; i++) {
dp[m-1][i] = 1;
}

for(int i = m-2; i >= 0; i--) {


for(int j = n-2; j >= 0; j--) {
dp[i][j] = dp[i+1][j]+dp[i][j+1];
}
}
return dp[0][0];
}
}

Complexity Analysis
- Time Complexity: O(m*n)
- Space Complexity: O(m*n)

1143. Longest Common Subsequence


(https://leetcode.com/problems/longest-
common-subsequence/)
Medium

Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.

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.

For example, "ace" is a subsequence of "abcde". A common subsequence of two strings is a subsequence that is common to both strings.

Example 1:

Input: text1 = "abcde", text2 = "ace"


Output: 3
Explanation: The longest common subsequence is "ace" and its length is 3.

Example 2:

Input: text1 = "abc", text2 = "abc"


Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.

Example 3:

Input: text1 = "abc", text2 = "def"


Output: 0
Explanation: There is no such common subsequence, so the result is 0.

Constraints:

1 <= text1.length, text2.length <= 1000


text1 and text2 consist of only lowercase English characters.

Approach
- Classic DP problem
- create a table, chars of one string as row and other as column
- fill out the value
- Formula that would appear
- if chars are equal: dp[i][j] = 1 + dp[i-1][j-1], (diagonally prev element)
- else: dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]), max(top element, left element)

Solution
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int n1 = text1.length(), n2 = text2.length();
int[][] dp = new int[n1+1][n2+1];

for(int i = 1; i <= n1; i++) {


char c1 = text1.charAt(i-1);
for(int j = 1; j <= n2; j++) {
char c2 = text2.charAt(j-1);
if(c1 == c2) {
dp[i][j] = 1 + dp[i-1][j-1];
} else {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[n1][n2];
}
}

Complexity Analysis
- Time Compolexity: O(N*M), N : length of string 1, M : length of string 2
- Space Complexity: O(N*M)

309. Best Time to Buy and Sell Stock with


Cooldown
(https://leetcode.com/problems/best-time-to-
buy-and-sell-stock-with-cooldown/)
Medium

You are given an array prices where prices[i] is the price of a given stock on the ith day.

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:

After you sell your stock, you cannot buy stock on the next day (i.e., cooldown one day). Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the
stock before you buy again).

Example 1:

Input: prices = [1,2,3,0,2]


Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]

Example 2:

Input: prices = [1]


Output: 0

Constraints:

1 <= prices.length <= 5000


0 <= prices[i] <= 1000
Approach

- From the above options diagram


- we can have two states:
- buying
- selling
- for buying we can either sell or cooldown
- for selling state we must give cooldown before next buying

Solution
class Solution {
public int maxProfit(int[] prices) {
boolean buy = false;
Map<String, Integer> mp = new HashMap<>();
return helper(prices, 0, mp, true);
}

private int helper(int[] prices, int index, Map<String, Integer> mp, boolean buying) {
if(index >= prices.length)
return 0;
if(mp.containsKey("("+index+","+buying+")"))
return mp.get("("+index+","+buying +")");
// in both the cases we have the cooldown
int cooldown = helper(prices, index+1, mp, buying);
if(buying) {
int buy = helper(prices, index+1, mp, !buying) - prices[index];
mp.put("("+index+","+buying +")", Math.max(cooldown, buy));
} else {
// we can't buy in next index so we pass the index+2
int sell = helper(prices, index+2, mp, !buying) + prices[index];
mp.put("("+index+","+buying +")", Math.max(cooldown, sell));
}
return mp.get("("+index+","+buying +")");
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(N)

518. Coin Change 2


(https://leetcode.com/problems/coin-change-
2/)
Medium

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0.

You may assume that you have an infinite number of each kind of coin.

The answer is guaranteed to fit into a signed 32-bit integer.

Example 1:

Input: amount = 5, coins = [1,2,5]


Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

Example 2:

Input: amount = 3, coins = [2]


Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.

Example 3:

Input: amount = 10, coins = [10]


Output: 1

Constraints:

1 <= coins.length <= 300


1 <= coins[i] <= 5000
All the values of coins are unique.
0 <= amount <= 5000

Approach
- Make a table and go through a example
- generic recursive formula:
- dp[i][j] = dp[i-1][j] + dp[i][j-coin]
- take care of edge cases

Solution
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int dp[][] = new int[n][amount+1];

for(int i = 0; i < n; i++)


dp[i][0] = 1;

for(int i = 0; i < n; i++) {


for(int j = 1; j <= amount; j++) {
int val = j-coins[i];
if(i > 0) {
if(val >= 0)
dp[i][j] = dp[i-1][j] + dp[i][val];
else
dp[i][j] = dp[i-1][j];
}
else {
if(val >= 0)
dp[i][j] = dp[i][val];
else
dp[i][j] = 0;
}
}
}

return dp[n-1][amount];
}

// Consise
public int change(int amount, int[] coins) {
int[][] dp = new int[coins.length+1][amount+1];
dp[0][0] = 1;

for (int i = 1; i <= coins.length; i++) {


dp[i][0] = 1;
for (int j = 1; j <= amount; j++) {
dp[i][j] = dp[i-1][j] + (j >= coins[i-1] ? dp[i][j-coins[i-1]] : 0);
}
}
return dp[coins.length][amount];
}

// Optimal
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i-coin];
}
}
return dp[amount];
}
}

Complexity Analysis
- Time Complexity: O(Amount*No. of coins)
- Space Complexity:
- 2D: O(Amount*No. of coins)
- Optimal: O(Amount+1)
329. Longest Increasing Path in a Matrix
(https://leetcode.com/problems/longest-
increasing-path-in-a-matrix/)
Hard

Given an m x n integers matrix, return the length of the longest increasing path in matrix.

From each cell, you can either move in four directions: left, right, up, or down. You may not move diagonally or move outside the boundary (i.e., wrap-around is not allowed).

Example 1:

Input: matrix = [[9,9,4],[6,6,8],[2,1,1]]


Output: 4
Explanation: The longest increasing path is [1, 2, 6, 9].

Example 2:

Input: matrix = [[3,4,5],[3,2,6],[2,2,1]]


Output: 4
Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.

Example 3:

Input: matrix = [[1]]


Output: 1

Constraints:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 200

0 <= matrix[i][j] <= 231 - 1

Approach
- DFS/backtracking with DP
- Start from each element in the matrix
- Find the longest increasing path we can make from that position
- by moving towards the 4 directions
- Store the result in a table
- Reuse the value

Solution
class Solution {

public int longestIncreasingPath(int[][] matrix) {


int[][] cache = new int[matrix.length][matrix[0].length];
int res = 1;
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[0].length; j++)
res = Math.max(res, backtrack(matrix, i, j, -1, cache));
}
return res;
}

private int backtrack(int[][] matrix, int i, int j, int prev, int[][] cache) {
if(i < 0 || j < 0 || i >= matrix.length || j >= matrix[0].length || prev >= matrix[i][j])
return 0;

if(cache[i][j] != 0)
return cache[i][j];

int max = 1;

max = Math.max(max, 1 + backtrack(matrix, i-1, j, matrix[i][j], cache));


max = Math.max(max, 1 + backtrack(matrix, i, j-1, matrix[i][j], cache));
max = Math.max(max, 1 + backtrack(matrix, i+1, j, matrix[i][j], cache));
max = Math.max(max, 1 + backtrack(matrix, i, j+1, matrix[i][j], cache));

cache[i][j] = max;
return max;
}
}

Complexity Analysis
- Time Complexity: dfs: O(n*m), we'll go through each position, after that it'll return from any position in O(1)
- Space Complexity: O(n*m)

72. Edit Distance


(https://leetcode.com/problems/edit-
distance/)
Hard

Share Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2.

You have the following three operations permitted on a word:

Insert a character
Delete a character
Replace a character

Example 1:

Input: word1 = "horse", word2 = "ros"


Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')
Example 2:

Input: word1 = "intention", word2 = "execution"


Output: 5
Explanation:
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')

Constraints:

0 <= word1.length, word2.length <= 500


word1 and word2 consist of lowercase English letters.

Aproach
- make a table with one word as row and other as column,
- append same char at the start of the both words to represent that both are empty string then 0 operation rqd to convert from word
- generic recursive equation:
- dp[i][j] = dp[i-1][j-1], if char are same
- dp[i][j] = 1 + min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]), i.e. 1 + min(left, top, diagonally prev)
- take care of base cases

Solution
class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length(), n = word2.length();
int dp[][] = new int[n+1][m+1];
for(int i = 1; i <= n; i++) {
dp[i][0] = i;
}

for(int i = 1; i <= m; i++) {


dp[0][i] = i;
}

for(int i = 1; i <= n; i++) {


char ch1 = word2.charAt(i-1);
for(int j = 1; j <= m; j++) {
char ch2 = word1.charAt(j-1);

if(ch1 == ch2)
dp[i][j] = dp[i-1][j-1];
else
dp[i][j] = 1 + Math.min(dp[i-1][j], Math.min(dp[i-1][j-1], dp[i][j-1]));
}
}

return dp[n][m];
}
}

Complexity Analysis
- Time Complexity: O(len(word1)*len(word2))
- Space Complexity: O(len(word1)*len(word2))
Resources
53. Maximum Subarray
Easy

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

A subarray is a contiguous part of an array.

Example 1:

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]


Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Example 2:

Input: nums = [1]


Output: 1

Example 3:

Input: nums = [5,4,-1,7,8]


Output: 23

Constraints:

1 <= nums.length <= 105


-104 <= nums[i] <= 104

Follow up: If you have figured out the O(n) solution, try coding another solution using the
divide and conquer approach, which is more subtle.

class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;

int total = 0;
int result = nums[0];

for(int num : nums) {


if(total < 0)
total = 0;
total = total+num;
result = Math.max(result, total);

return result;
}
}

55. Jump Game


(https://leetcode.com/problems/jump-game/)
Medium
You are given an integer array nums. You are initially positioned at the array's first index, and each element in the array represents your maximum jump length at that position.

Return true if you can reach the last index, or false otherwise.

Example 1:

Input: nums = [2,3,1,1,4]


Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: nums = [3,2,1,0,4]


Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the l

Constraints:

1 <= nums.length <= 104


0 <= nums[i] <= 105

Aprroach
- one variable(for eg. ans) is keeping upto which index can we move from the current index
- if ans value is less than the current index, which means we can't move to the current index
- at every index we check if you can better the ans,
which means if we can move to a higher index than the current index stored in ans
- if ans is sotring the value more than equal to the length of the given array

Solution
class Solution {
public boolean canJump(int[] nums) {
int ans = 0, n = nums.length;
for(int i = 0; i < n-1; i++) {
if(ans < i)
return false;
if(ans < i+nums[i])
ans = i+nums[i];
if(ans >= n-1)
return true;
}
return ans >= n-1;
}
}

Complexity Analysis
- Time Complexity: O(N)
- Space Complexity: O(1)

45. Jump Game II


(https://leetcode.com/problems/jump-game-
ii/)
Medium

Given an array of non-negative integers nums, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Your goal is to reach the last index in the minimum number of jumps.

You can assume that you can always reach the last index.

Example 1:

Input: nums = [2,3,1,1,4]


Output: 2
Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index

Example 2:

Input: nums = [2,3,0,1,4]


Output: 2

Constraints:

1 <= nums.length <= 104


0 <= nums[i] <= 1000

Approach
- dp array initialized to large value, later on it will store the min jumps required to reach last index from each index
- we start from end, second last index, try to see if we can move to the last index
- for each index you look for the min step we need to reach last index
- Pattern:
- for each nums's index(i):
- for range(0,num) (j):
- dp[i] = min(dp[i], 1 + dp[i+j]

TODO : Optimization

Solution
class Solution {
public int jump(int[] nums) {
int n = nums.length;
int dp[] = new int[n];
Arrays.fill(dp, 10000);
dp[n - 1] = 0;

for (int i = n - 2; i >= 0; i--) {


for (int j = nums[i]; j >= 0; j--) {
if (i + j < n)
dp[i] = Math.min(dp[i], 1 + dp[i + j]);
}
}
return dp[0];
}
}

Complexity Analysis
- Time Complexity: O(k*N)
- Space Complexity: O(N)

134. Gas Station


(https://leetcode.com/problems/gas-station/)
Medium

There are n gas stations along a circular route, where the amount of gas at the ith station is gas[i].

You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from the ith station to its next (i + 1)th station. You begin the journey with an empty tank at one of the gas
stations.

Given two integer arrays gas and cost, return the starting gas station's index if you can travel around the circuit once in the clockwise direction, otherwise return -1. If there exists a
solution, it is guaranteed to be unique

Example 1:

Input: gas = [1,2,3,4,5], cost = [3,4,5,1,2]


Output: 3
Explanation:
Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 4. Your tank = 4 - 1 + 5 = 8
Travel to station 0. Your tank = 8 - 2 + 1 = 7
Travel to station 1. Your tank = 7 - 3 + 2 = 6
Travel to station 2. Your tank = 6 - 4 + 3 = 5
Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3.
Therefore, return 3 as the starting index.

Example 2:

Input: gas = [2,3,4], cost = [3,4,3]


Output: -1
Explanation:
You can't start at station 0 or 1, as there is not enough gas to travel to the next station.
Let's start at station 2 and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 0. Your tank = 4 - 3 + 2 = 3
Travel to station 1. Your tank = 3 - 3 + 3 = 3
You cannot travel back to station 2, as it requires 4 unit of gas but you only have 3.
Therefore, you can't travel around the circuit once no matter where you start.

Constraints:

n == gas.length == cost.length
1 <= n <= 105

0 <= gas[i], cost[i] <= 104

Approach
- Trivial:
- we'll store the difference of gas[i]-cost[i]
- for all the +ve differences we do the process

TODO:Optimization

Solution
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int ind = 0, sum = 0;
int[] diff = new int[gas.length];
PriorityQueue<Pair> pq = new PriorityQueue<>((a,b)->b.value-a.value);
for (int i = 0; i < gas.length; i++) {
diff[i] = gas[i] - cost[i];
sum += diff[i];
if(diff[i]>=0)
pq.offer(new Pair(diff[i], i));
}
if (sum < 0)
return -1;

while(!pq.isEmpty()) {
Pair p = pq.poll();
if (p.value >= 0) {
int pathSum = 0;
ind = p.index;
int j = p.index;
do {
pathSum += diff[j];
if (pathSum < 0)
break;
j = (j + 1) % gas.length;
} while(j != p.index);

if (pathSum >= 0)
break;
}
}

return ind;
}
}

class Pair {
int value;
int index;

Pair(int v, int i) {
value = v;
index = i;
}
}

Complexity Analysis
- Time Complexity: O(k*N), k is number of elements in diff array that are 0 or +ve
- Space Complexity: O(N)

846. Hand of Straights


(https://leetcode.com/problems/hand-of-
straights/)
Medium

Alice has some number of cards and she wants to rearrange the cards into groups so that each group is of size groupSize, and consists of groupSize consecutive cards.
Given an integer array hand where hand[i] is the value written on the ith card and an integer groupSize, return true if she can rearrange the cards, or false otherwise.

Example 1:

Input: hand = [1,2,3,6,2,3,4,7,8], groupSize = 3


Output: true
Explanation: Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8]

Example 2:

Input: hand = [1,2,3,4,5], groupSize = 4


Output: false
Explanation: Alice's hand can not be rearranged into groups of 4.

Constraints:

1 <= hand.length <= 104


0 <= hand[i] <= 109
1 <= groupSize <= hand.length

Note: This question is the same as 1296: https://leetcode.com/problems/divide-array-in-sets-of-k-consecutive-numbers/ (https://leetcode.com/problems/divide-array-in-sets-of-


k-consecutive-numbers/)

Approach
// TODO

Solution
class Solution {
public boolean isNStraightHand(int[] hand, int groupSize) {
if(hand.length % groupSize != 0)
return false;

Map<Integer, Integer> mp = new HashMap<>();


PriorityQueue<Integer> pq = new PriorityQueue<>((a,b)->a-b);

for(int i = 0; i < hand.length; i++) {


if(!mp.containsKey(hand[i])) {
pq.offer(hand[i]);
mp.put(hand[i], 1);
} else {
mp.put(hand[i], mp.get(hand[i])+1);
}
}

while(!pq.isEmpty()) {
int min = pq.peek();
int sz = 0;
while(sz < groupSize) {
if(!mp.containsKey(min))
return false;
mp.put(min, mp.get(min)-1);
if(mp.get(min) == 0) {
mp.remove(min);
int val = pq.poll();
if(val != min)
return false;
}
min++;
sz++;
}

}
return true;
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

678. Valid Parenthesis String


(https://leetcode.com/problems/valid-
parenthesis-string/)
Medium

Given a string s containing only three types of characters: '(', ')' and '*', return true if s is valid.

The following rules define a valid string:

Any left parenthesis '(' must have a corresponding right parenthesis ')'. Any right parenthesis ')' must have a corresponding left parenthesis '('. Left parenthesis '(' must go before the
corresponding right parenthesis ')'. '*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string "".
Example 1:

Input: s = "()"
Output: true

Example 2:

Input: s = "(*)"
Output: true

Example 3:

Input: s = "(*))"
Output: true

Constraints:

1 <= s.length <= 100 s[i] is '(', ')' or '*'.

Approach
Approach 1:
- backtrack
- count+1 if '('
- count-1 if ')'
- For '*':
- 3 calls: count+1, count-1, count+0
- dp (index, count) = true/false

Approach 2: greedy

Solution
class Solution {
Boolean[][] dp;

public boolean checkValidString(String s) {


dp = new Boolean[s.length()][s.length()];
return backtrack(s, 0, 0);
}

private boolean backtrack(String s, int index, int count) {


if (count < 0 || (index >= s.length() && count > 0))
return false;
if (index >= s.length() && count == 0)
return true;
if (dp[index][count] != null)
return dp[index][count];
if (s.charAt(index) == '*') {
dp[index][count] = (backtrack(s, index + 1, count + 1) ||
backtrack(s, index + 1, count - 1) ||
backtrack(s, index + 1, count));
} else if (s.charAt(index) == '(') {
dp[index][count] = backtrack(s, index + 1, count + 1);
} else {
dp[index][count] = backtrack(s, index + 1, count - 1);
}
return dp[index][count];
}
}
public class ValidString {
public static boolean isValid(String str) {
int leftMin = 0, leftMax = 0;
for (char ch: str.toCharArray()) {
if (ch == '(') {
leftMin++;
leftMax++;
}
else if (ch == ')') {
leftMin--;
leftMax--;
}
else {
leftMin--;
leftMax++;
}
if (leftMax < 0) { // Means we encounter ) as a starting character or # of occurrences of ) is > (
return false;
}
if (leftMin < 0) {
leftMin = 0;
}
}
return leftMin == 0;
}
}

Complexity Analysis
- Time Complexity:
- Approach 1: O(n^3)
- Approach 2: O(n)
- Space Complexity:
- Approach 1: O(n)
- Approach 2: O(1)

Resources
57. Insert Interval
(https://leetcode.com/problems/insert-
interval/)
Medium

You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending
order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval.

Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if
necessary).

Return intervals after the insertion.

Example 1:

Input: intervals = [[1,3],[6,9]], newInterval = [2,5]


Output: [[1,5],[6,9]]
Example 2:

Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]


Output: [[1,2],[3,10],[12,16]]
Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

Constraints:

0 <= intervals.length <= 104


intervals[i].length == 2
0 <= starti <= endi <= 105
intervals is sorted by starti in ascending order.
newInterval.length == 2
0 <= start <= end <= 105

Approach
- Step 1: while interval's end is lesser than new interval's start, keep adding it to result
- After step 1, we'll be at the position where new interval will start(either overlap or as the initial interval or as the last inter
- Step 2: while intervals's start is <= new interval's end
- Keep taking the min start & max end and assign into the new interval(overlapping)
- After step 2, we'll have the merged new interval, add it to result
- Step 3: while intervals not finished processing
- keep adding them to the result

Solution
class Solution {

public int[][] insert(int[][] intervals, int[] newInterval) {


if (intervals.length < 1) return new int[][] { newInterval };

List<int[]> mergedList = new ArrayList<>();


int index = 0;
while (index < intervals.length && intervals[index][1] < newInterval[0]) mergedList.add(intervals[index++]);

while (index < intervals.length && intervals[index][0] <= newInterval[1]) {


newInterval[0] = Math.min(newInterval[0], intervals[index][0]);
newInterval[1] = Math.max(newInterval[1], intervals[index][1]);
index++;
}
mergedList.add(newInterval);

while (index < intervals.length) mergedList.add(intervals[index++]);

return mergedList.toArray(new int[mergedList.size()][]);


}
}

Complexity Analysis
- Time Complexity: O(n)
- Space Complexity: O(n)

56. Merge Intervals


(https://leetcode.com/problems/merge-
intervals/)
Medium

Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.

Example 1:

Input: intervals = [[1,3],[2,6],[8,10],[15,18]]


Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].

Example 2:

Input: intervals = [[1,4],[4,5]]


Output: [[1,5]]
Explanation: Intervals [1,4] and [4,5] are considered overlapping.

Constraints:

1 <= intervals.length <= 104


intervals[i].length == 2
0 <= starti <= endi <= 104

Approach
- Sort the intervals by start time
- Use arr to store the merged list
- Start by inserting the first element
- looping through all the other intervals
- check if last interval inserted into arr overlapped with current interval
- if yes then create a new interval and push to arr
- else insert the current interval to arr

Solution
class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> arr = new ArrayList<>();
arr.add(intervals[0]);
int start = intervals[0][0];
int end = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
int[] curr = intervals[i];
int[] prev = arr.get(arr.size() - 1);
if (curr[0] <= prev[1]) {
arr.remove(arr.size() - 1);
start = Math.min(curr[0], prev[0]);
end = Math.max(curr[1], prev[1]);
arr.add(new int[]{start, end});
} else {
arr.add(curr);
}
}
return arr.toArray(new int[arr.size()][]);
}
}

Complexity Analysis
- Time Complexity: O(nlogn)
- Space Complexity: O(n), for arr

435. Non-overlapping Intervals


(https://leetcode.com/problems/non-
overlapping-intervals/)
Medium

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.

Example 1:

Input: intervals = [[1,2],[2,3],[3,4],[1,3]]


Output: 1
Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping.

Example 2:

Input: intervals = [[1,2],[1,2],[1,2]]


Output: 2
Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping.

Example 3:

Input: intervals = [[1,2],[2,3]]


Output: 0
Explanation: You don't need to remove any of the intervals since they're already non-overlapping.

Constraints:

1 <= intervals.length <= 105


intervals[i].length == 2
-5 * 104 <= starti < endi <= 5 * 104

Approach
- Sort the intervals by end time
- store the first interval (we're storing it as prev)
- loop through the intervals starting the 2nd interval
- if current interval overlap with prev the increment the count
- else assign the current interval to the prev

Solution
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if(intervals.length <= 1)
return 0;
Arrays.sort(intervals, (a,b)->a[1]-b[1]);

int count = 0;
int[] prev = intervals[0];
for(int i = 1; i < intervals.length; i++) {
int[] curr = intervals[i];
if(prev[1] > curr[0])
count++;
else
prev = intervals[i];
}

return count;
}
}

Complexity Analysis
- Time Complexity: O(nlogn)
- Space Complexity: O(1)

920 · Meeting Rooms [LintCode]


Easy

Description Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), determine if a person could attend all meetings.

Wechat reply the【Video】get the free video lessons , the latest frequent Interview questions , etc. (wechat id :jiuzhang15)

(0,8),(8,10) is not conflict at 8

Example 1

Input: intervals = [(0,30),(5,10),(15,20)]


Output: false
Explanation:
(0,30), (5,10) and (0,30),(15,20) will conflict

Example 2

Input: intervals = [(5,8),(9,15)]


Output: true
Explanation:
Two times will not conflict
/**
* Definition of Interval:
* public classs Interval {
* int start, end;
* Interval(int start, int end) {
* this.start = start;
* this.end = end;
* }
* }
*/

public class Solution {


/**
* @param intervals: an array of meeting time intervals
* @return: if a person could attend all meetings
*/
public boolean canAttendMeetings(List<Interval> intervals) {

if(intervals.size() == 0 || intervals.size() == 1)
return true;
Collections.sort(intervals, (a,b)->a.end-b.end);
Interval next = intervals.get(intervals.size()-1);
for(int i = intervals.size()-2; i >= 0; i--) {
Interval current = intervals.get(i);

if(current.end > next.start && current.end <= next.end)


return false;
next = current;
}
return true;
}
}

919 · Meeting Rooms II ()


Medium

Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), find the minimum number of conference rooms required.)

(0,8),(8,10) is not conflict at 8

Example 1

Input: intervals = [(0,30),(5,10),(15,20)]


Output: 2
Explanation:
We need two meeting rooms
room1: (0,30)
room2: (5,10),(15,20)

Example2

Input: intervals = [(2,7)]


Output: 1
Explanation:
Only need one meeting room

Approach
Approach 1:
- Create a array of size, largest element - smallest element + 1
- for each interval:
- increment all the index between [start, end)
- keep tracking the max element in that array, that's the answer

Approach 2(Better):
- Sort starting time and ending seperatedly
- Two pointers one from traversing through the start[] and the other through the end[]
- keep track of max count(it would have the answer)
- if pointer pointing at start < pointer pointing at end
- increment the count & move the start pointer forward
- else
- decrement the count & move the end pointer forward

Solution
/**
* Definition of Interval:
* public class Interval {
* int start, end;
* Interval(int start, int end) {
* this.start = start;
* this.end = end;
* }
* }
*/

public class Solution {


/**
* @param intervals: an array of meeting time intervals
* @return: the minimum number of conference rooms required
*/
public int minMeetingRooms(List<Interval> intervals) {
// Write your code here
if(intervals == null || intervals.size() == 0)
return 0;
int rooms = 0;
int max = intervals.get(0).end;
int min = intervals.get(0).start;

for(int i = 1; i < intervals.size(); i++) {


max = Math.max(max, intervals.get(i).end);
min = Math.min(min, intervals.get(i).start);
}
int[] arr = new int[max-min+1];
for(Interval interval: intervals) {
int s = interval.start-min;
int e = interval.end-min;
for(int i = s; i < e; i++) {
arr[i]++;
rooms = Math.max(rooms, arr[i]);
}
}
return rooms;
}
}
// Better Solution
public class Solution {
public int minMeetingRooms(List<Interval> intervals) {
// Check for the base case. If there are no intervals, return 0
if (intervals.size() == 0) {
return 0;
}

Integer[] start = new Integer[intervals.size()];


Integer[] end = new Integer[intervals.size()];

for (int i = 0; i < intervals.size(); i++) {


start[i] = intervals.get(i).start;
end[i] = intervals.get(i).end;
}

// Sort the intervals by end time


Arrays.sort(end);

// Sort the intervals by start time


Arrays.sort(start);

// The two pointers in the algorithm: e_ptr and s_ptr.


int startPointer = 0, endPointer = 0;

// Variables to keep track of maximum number of rooms used.


int usedRooms = 0, count = 0;

// Iterate over intervals.


while (startPointer < intervals.size()) {

// If there is a meeting that has started before the time


// the meeting at `end_pointer` starts means overlapping
if (start[startPointer] < end[endPointer]) {
count += 1;
startPointer += 1;
}
// a room is free now
else {
count -= 1;
endPointer += 1;
}
usedRooms = Math.max(usedRooms, count);
}

return usedRooms;
}
}

Complexity Analysis
- Time Complexity:
- Better approach: O(nlogn)
- Space Complexity:
- Better approach: O(n)

Resources
48. Rotate Image
Medium

You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise).

You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.

Example 1:

Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]


Output: [[7,4,1],[8,5,2],[9,6,3]]

Example 2:

Input: matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]


Output: [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

Constraints:

n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000

Approach
- Roatate = Transpose + Reverse

Solution
class Solution {
public void rotate(int[][] matrix) {
transpose(matrix);
for(int[] nums : matrix) {
reverse(nums);
}
}

private void transpose(int[][] matrix) {


for(int i = 0; i < matrix.length; i++) {
for(int j = i + 1; j < matrix.length; j++) {
int tmp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = tmp;
}
}
}

private void reverse(int[] nums) {


for(int i = 0, j = nums.length-1; i < j; i++, j--) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
}

Complexity Analysis
- Time Complexity: O(n), n : number of elements in the matrix
- Space Complexity: O(1)
54. Spiral Matrix
Medium

Given an m x n matrix, return all elements of the matrix in spiral order.

Example 1:

Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]


Output: [1,2,3,6,9,8,7,4,5]

Example 2:

Input: matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]


Output: [1,2,3,4,8,12,11,10,9,5,6,7]

Constraints:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100

Approach

Solution
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<>();
int m = matrix.length - 1;
int n = matrix[0].length - 1;
int sr = 0, sc = 0;
int i = sr, j = sc;
while (sc <= n || sr <= m) {

// 1st row
while (j <= n) {
res.add(matrix[i][j]);
j++;
}
sr++;
j = n;
i = sr;

// condition
if (i > m || j > n) {
break;
}
// last colunm
while (i <= m) {
res.add(matrix[i][j]);
i++;
}
n--;
i = m;
j = n;

if (i > m || j > n) {
break;
}

// last row
while (j >= sc) {
res.add(matrix[i][j]);
j--;
}
m--;
j = sc;
i = m;

if (i > m || j > n) {
break;
}
// 1st column
while (i >= sr) {
res.add(matrix[i][j]);
i--;
}
sc++;
i = sr;
j = sc;

if (i > m || j > n) {
break;
}
}
return res;
}
}
Complexity Analysis
- Time Complexity: O(n), n : number of elements in the matrix
- Space Complexity: O(1)

73. Set Matrix Zeroes


Medium

Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0's.

You must do it in place.

Example 1:

Input: matrix = [[1,1,1],[1,0,1],[1,1,1]]


Output: [[1,0,1],[0,0,0],[1,0,1]]

Example 2:

Input: matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]


Output: [[0,0,0,0],[0,4,5,0],[0,3,1,0]]

Constraints:

m == matrix.length
n == matrix[0].length
1 <= m, n <= 200
-231 <= matrix[i][j] <= 231 - 1

Follow up:

A straightforward solution using O(mn) space is probably a bad idea.


A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?

Approach
- Use the first row and column to keep track of zeroes
- Use one variable for the 0th row or 0 column depending upon our choice

Solution
class Solution {
public void setZeroes(int[][] matrix) {
int col0 = 1;
int n = matrix.length, m = matrix[0].length;
// first column
for(int i = 0; i < n; i++) {
if(matrix[i][0] == 0)
col0 = 0;
}
// first row
for(int i = 0; i < m; i++) {
if(matrix[0][i] == 0)
matrix[0][0] = 0;
}
// starting from (1,1)
for(int i = 1; i < n; i++) {
for(int j = 1; j < m; j++) {
if(matrix[i][j] == 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}

for(int i = 1; i < n; i++) {


for(int j = 1; j < m; j++) {
if(matrix[i][0] == 0 || matrix[0][j] == 0)
matrix[i][j] = 0;
}
}

if(matrix[0][0] == 0) {
for(int i = 1; i < m; i++)
matrix[0][i] = 0;
}

if(col0 == 0) {
for(int i = 0; i < n; i++)
matrix[i][0] = 0;
}
}
}

Complexity Analysis
- Time Complexity: O(N), N : numbero of elements in the matrix
- Space Complexity: O(1)

202. Happy Number


Easy

Write an algorithm to determine if a number n is happy.

A happy number is a number defined by the following process:

Starting with any positive integer, replace the number by the sum of the squares of its digits.
Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.
Those numbers for which this process ends in 1 are happy.

Return true if n is a happy number, and false if not.


Example 1:

Input: n = 19
Output: true
Explanation:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

Example 2:

Input: n = 2
Output: false

Constraints:

1 <= n <= 231 - 1

class Solution {
public boolean isHappy(int n) {
int slow = n, fast = n;
do {
slow = sum(slow);
fast = sum(sum(fast));
} while(slow != fast);
return slow == 1;

private int sum(int n) {


int sum = 0;
while(n > 0) {
int digit = n % 10;
sum += (digit*digit);
n /= 10;
}
return sum;
}
}

66. Plus One


Easy

You are given a large integer represented as an integer array digits, where each digits[i] is the ith digit of the integer. The digits are ordered from most significant to least significant in
left-to-right order. The large integer does not contain any leading 0's.

Increment the large integer by one and return the resulting array of digits.

Example 1:

Input: digits = [1,2,3]


Output: [1,2,4]
Explanation: The array represents the integer 123.
Incrementing by one gives 123 + 1 = 124.
Thus, the result should be [1,2,4].

Example 2:
Input: digits = [4,3,2,1]
Output: [4,3,2,2]
Explanation: The array represents the integer 4321.
Incrementing by one gives 4321 + 1 = 4322.
Thus, the result should be [4,3,2,2].

Example 3:

Input: digits = [9]


Output: [1,0]
Explanation: The array represents the integer 9.
Incrementing by one gives 9 + 1 = 10.
Thus, the result should be [1,0].

Constraints:

1 <= digits.length <= 100


0 <= digits[i] <= 9
digits does not contain any leading 0's.

class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length;

for(int i = n-1; i >= 0; i--) {


if(digits[i] < 9) {
digits[i]++;
return digits;
}
digits[i] = 0;
}
int[] newDigits = new int[n+1];
newDigits[0] = 1;
return newDigits;

}
}

50. Pow(x, n)
(https://leetcode.com/problems/powx-n/)
Medium

Implement pow(x, n), which calculates x raised to the power n (i.e., xn).

Example 1:

Input: x = 2.00000, n = 10
Output: 1024.00000

Example 2:

Input: x = 2.10000, n = 3
Output: 9.26100

Example 3:

Input: x = 2.00000, n = -2
Output: 0.25000
Explanation: 2-2 = 1/22 = 1/4 = 0.25
Constraints:

-100.0 < x < 100.0

-231 <= n <= 231-1


-104 <= xn <= 104

Approach
- Recursiom:
- we can divide the power by 2 and same value can be reused
- For non-negative:
- for even: pow(x, n) = pow(x, n/2)*pow(x,n/2)
- for odd: - for even: pow(x, n) = x*pow(x, n/2)*pow(x,n/2)
- For negative:
- for even: pow(x, n) = pow(x, n/2)*pow(x,n/2)
- for odd: - for even: pow(x, n) = 1/x*pow(x, n/2)*pow(x,n/2)

Solution
class Solution {
public double myPow(double x, int n) {
if(n < 0)
return myPowNeg(x, n);
return myPowPos(x, n);
}

private double myPowNeg(double x, int n) {


if(x == 1 || n == 0)
return 1;
if(n == -1)
return 1/x;
double ans = myPow(x, n/2);
ans *= ans;
if(n%2 != 0)
return ans/x;
return ans;
}

private double myPowPos(double x, int n) {


if(x == 1 || n == 0)
return 1;
double ans = myPow(x, n/2);
ans *= ans;
if(n%2 != 0)
return ans*x;
return ans;
}
}

Complexity Analysis
- Time Complexity: O(logn)
- Space Complexity: O(logn)

43. Multiply Strings


(https://leetcode.com/problems/multiply-
strings/)
Medium

Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string.

Note: You must not use any built-in BigInteger library or convert the inputs to integer directly.

Example 1:

Input: num1 = "2", num2 = "3"


Output: "6"

Example 2:

Input: num1 = "123", num2 = "456"


Output: "56088"

Constraints:

1 <= num1.length, num2.length <= 200


num1 and num2 consist of digits only.
Both num1 and num2 do not contain any leading zero, except the number 0 itself.

Approach
- Reverse the number strings
- StringBuilder(mutable string) of 400 size, as num1 & num2 of 200 length at max
- calculate the multiplication and store overwrite it at the i+j position
- after each inner loop, we insert the carry at i+j position, j would be 1 position more the the original length of the second number
- reverse the string
- remove all the leading 0s
- return 0 if result string size is zero or else return thr result string

Solution
class Solution {
public String multiply(String num1, String num2) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 400; i++)
sb.append(0);
num1 = reverse(num1);
num2 = reverse(num2);
if(num1.length() > num2.length()) {
String tmp = num1;
num1 = num2;
num2 = tmp;
}
int carry = 0;
int i = 0, j = 0;
for (i = 0; i < num2.length(); i++) {
carry = 0;
for (j = 0; j < num1.length(); j++) {
int a = num2.charAt(i)-'0';
int b = num1.charAt(j)-'0';
int n = a * b + carry;
int prev = sb.charAt(i+j)-'0';
int sum = (prev + n) % 10;
carry = (n+prev) / 10;
sum +='0';
sb.setCharAt(i + j, (char) sum);
}
sb.setCharAt(i+j, (char) (carry+'0'));
}

sb.setCharAt(i+j-1, (char) (carry+'0'));


sb.reverse();
int ind = 0;
while(sb.length() > 0 && sb.charAt(ind) == '0')
sb.deleteCharAt(ind);
return sb.length() == 0?"0":sb.toString();
}

private String reverse(String s) {


StringBuilder sb = new StringBuilder(s);
sb.reverse();
return sb.toString();
}
}

Complexity Analysis
- Time Compexity: O(n*m)
- Space Complexity: O(400)

Resources
136. Single Number
Easy

Given a non-empty array of integers nums, every element appears twice except for one. Find that single one.

You must implement a solution with a linear runtime complexity and use only constant extra space.

Example 1:
Input: nums = [2,2,1]
Output: 1

Example 2:

Input: nums = [4,1,2,1,2]


Output: 4

Example 3:

Input: nums = [1]


Output: 1

Constraints:

1 <= nums.length <= 3 * 104


-3 * 104 <= nums[i] <= 3 * 104
Each element in the array appears twice except for one element which appears only once.

class Solution {
public int singleNumber(int[] nums) {
int res = 0;

for(int num : nums) {


res ^= num;
}

return res;
}
}

191. Number of 1 Bits


Easy

Write a function that takes an unsigned integer and returns the number of '1' bits it has (also known as the Hamming weight).

Note:

Note that in some languages, such as Java, there is no unsigned integer type. In this case, the input will be given as a signed integer type. It should not affect your implementation, as
the integer's internal binary representation is the same, whether it is signed or unsigned. In Java, the compiler represents the signed integers using 2's complement notation.
Therefore, in Example 3, the input represents the signed integer. -3.

Example 1:

Input: n = 00000000000000000000000000001011
Output: 3
Explanation: The input binary string 00000000000000000000000000001011 has a total of three '1' bits.

Example 2:

Input: n = 00000000000000000000000010000000
Output: 1
Explanation: The input binary string 00000000000000000000000010000000 has a total of one '1' bit.

Example 3:

Input: n = 11111111111111111111111111111101
Output: 31
Explanation: The input binary string 11111111111111111111111111111101 has a total of thirty one '1' bits.

Constraints:
The input must be a binary string of length 32.

Follow up: If this function is called many times, how would you optimize it?

public class Solution {


// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count = 0;

while(n != 0) {
n = n & (n-1);
count++;
}

return count;
}
}

338. Counting Bits


Easy

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.

Example 1:

Input: n = 2
Output: [0,1,1]
Explanation:
0 --> 0
1 --> 1
2 --> 10

Example 2:

Input: n = 5
Output: [0,1,1,2,1,2]
Explanation:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101

Constraints:

0 <= n <= 105

Follow up:
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?
Can you do it without using any built-in function (i.e., like __builtin_popcount in C++)?
class Solution {
public int[] countBits(int n) {
int[] dp = new int[n+1];

int offset = 1;

for(int i = 1; i < n+1; i++) {


if(offset * 2 == i)
offset = i;
dp[i] = 1 + dp[i-offset];
}

return dp;
}
}

class Solution {
public int[] countBits(int n) {
int[] dp = new int[n+1];

for(int i = 1; i < n+1; i++) {

dp[i] = dp[i/2] + i % 2;
}

return dp;
}
}

190. Reverse Bits


Easy

Reverse bits of a given 32 bits unsigned integer.

Note:

Note that in some languages, such as Java, there is no unsigned integer type. In this case, both input and output will be given as a signed integer type. They should not affect
your implementation, as the integer's internal binary representation is the same, whether it is signed or unsigned. In Java, the compiler represents the signed integers using 2's
complement notation. Therefore, in Example 2 above, the input represents the signed integer -3 and the output represents the signed integer -1073741825.

Example 1:

Input: n = 00000010100101000001111010011100
Output: 964176192 (00111001011110000010100101000000)
Explanation: The input binary string 00000010100101000001111010011100 represents the unsigned integer 43261596, so return 964176192 w

Example 2:

Input: n = 11111111111111111111111111111101
Output: 3221225471 (10111111111111111111111111111111)
Explanation: The input binary string 11111111111111111111111111111101 represents the unsigned integer 4294967293, so return 322122547

Constraints:

The input must be a binary string of length 32

Follow up: If this function is called many times, how would you optimize it?
public class Solution {
// you need treat n as an unsigned value
public int reverseBits(int n) {
int res = 0;
for(int i = 0; i < 32; i++) {
int bit = (n >> i) & 1; // check whether the bit is set or not [right shift each bit & 000..1] -> gives the value present
res = res | (bit << (31 - i)); // put the bit at 31 - i th position i.e, reverse
}
return res;
}
}

268. Missing Number


Easy

Given an array nums containing n distinct numbers in the range [0, n], return the only number in the range that is missing from the array.

Example 1:

Input: nums = [3,0,1]


Output: 2
Explanation: n = 3 since there are 3 numbers, so all numbers are in the range [0,3]. 2 is the missing number in the range since it do

Example 2:

Input: nums = [0,1]


Output: 2
Explanation: n = 2 since there are 2 numbers, so all numbers are in the range [0,2]. 2 is the missing number in the range since it do

Example 3:

Input: nums = [9,6,4,2,3,5,7,0,1]


Output: 8
Explanation: n = 9 since there are 9 numbers, so all numbers are in the range [0,9]. 8 is the missing number in the range since it do

Constraints:

n == nums.length
1 <= n <= 104
0 <= nums[i] <= n
All the numbers of nums are unique.

Follow up: Could you implement a solution using only O(1) extra space complexity and
O(n) runtime complexity?

class Solution {
public int missingNumber(int[] nums) {
int n = nums.length;
int sum = n * (n + 1) / 2;

for(int i = 0; i < n; i++) {


sum -= nums[i];
}
return sum;
}
}
371. Sum of Two Integers
(https://leetcode.com/problems/sum-of-two-
integers/)
Medium

Given two integers a and b, return the sum of the two integers without using the operators + and -.

Example 1:

Input: a = 1, b = 2
Output: 3

Example 2:

Input: a = 2, b = 3
Output: 5

Constraints:

-1000 <= a, b <= 1000

Approach

Solution

Complexity Analysis
- Time Complexity:
- Space Complexity:

Resources
class UnionFind {
int[] root;
int[] rank;

UnionFind(int size) {
root = new int[size];
rank = new int[size];

for(int i = 0; i < size; i++) {


root[i] = i;
rank[i] = 0;
}
}

public int find(int x) {


if(x == root[x])
return x;
return root[x] = find(root[x]);
}

public void union(int x, int y) {


int rootX = find(x);
int rootY = find(y);

if(rootX != rootY) {
if(rank[rootX] > rank[rootY])
root[rootY] = rootX;
else if(rank[rootX] < rank[rootY])
root[rootX] = rootY;
else {
root[rootY] = rootX;
rank[rootX]++;
}
}
}

public boolean isConnected(int x, int y) {


return find(x) == find(y);
}
}

2290. Minimum Obstacle Removal to Reach


Corner
(https://leetcode.com/problems/minimum-
obstacle-removal-to-reach-corner/)
Hard

You are given a 0-indexed 2D integer array grid of size m x n. Each cell has one of two values:

0 represents an empty cell,


1 represents an obstacle that may be removed.
You can move up, down, left, or right from and to an empty cell.

Return the minimum number of obstacles to remove so you can move from the upper left corner (0, 0) to the lower right corner (m - 1, n - 1).

Example 1:
Input: grid = [[0,1,1],[1,1,0],[1,1,0]]
Output: 2
Explanation: We can remove the obstacles at (0, 1) and (0, 2) to create a path from (0, 0) to (2, 2).
It can be shown that we need to remove at least 2 obstacles, so we return 2.
Note that there may be other ways to remove 2 obstacles to create a path.

Example 2:

Input: grid = [[0,1,0,0,0],[0,1,0,1,0],[0,0,0,1,0]]


Output: 0
Explanation: We can move from (0, 0) to (2, 4) without removing any obstacles, so we return 0.

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 105
2 <= m * n <= 105
grid[i][j] is either 0 or 1.
grid[0][0] == grid[m - 1][n - 1] == 0

Approach
- Node class to store distance, row number, column number, done processing
- Map to store the key and the node for that key
- priority queue based on distance
- add the source to pq, with distance 0
- while pq not empty, poll a node
- if node is not done
- mark it done
- check all it's neighbour's distance with the node.dsitance+grid[neighbour's row][neighbour's col]
- update the distance of neighbour if it's greater than above calculated distance
- and add the neighbour into the pq

Solution
class Solution {
class Node {
int dist;
int i;
int j;
boolean done;
Node(int d, int ii, int jj, boolean done) {
dist = d;
i = ii;
j = jj;
done = done;
}
}

public int minimumObstacles(int[][] grid) {


int[][] dirs = {{0,-1},{-1,0},{0,1},{1,0}};
int n = grid.length, m = grid[0].length;
PriorityQueue<Node> pq = new PriorityQueue<>((a, b)->a.dist-b.dist);
Map<String, Node> mp = new HashMap<>();
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
String key = ""+i+","+j;
Node node = null;
if(i == 0 && j == 0)
node = new Node(0,i,j,false);
else
node = new Node(100001,i,j,false);
mp.put(key, node);
}
}

pq.offer(mp.get("0,0"));
while(!pq.isEmpty()) {
Node node = pq.poll();
if(node.done == false) {
node.done = true;
int i = node.i;
int j = node.j;
for(int[] dir:dirs) {
int new_i = i+dir[0];
int new_j = j+dir[1];
if(new_i >= 0 && new_i < n && new_j >= 0 && new_j < m) {
String neiK = ""+new_i+","+new_j;
Node nei = mp.get(neiK);
int cost = node.dist+grid[new_i][new_j];
if(cost < nei.dist) {
nei.dist = cost;
pq.offer(mp.get(neiK));
}
}
}
}

}
return mp.get(""+(n-1)+","+(m-1)).dist;
}
}

Complexity Analysis
- Time Complexity: O(n*m log (n*m))
- Space Complexity: O(n*m)
BFS on Graph
• Undiscovered – the vertex is in its initial, virgin state.

• Discovered – the vertex has been found, but we have not yet checked out all its incident edges.

• Processed – the vertex after we have visited all of its incident edges

public class Graph {


public Map<Integer, List<int[]>> adjList;

public int nvertices;

public int nedges;

public int[] degree;

public boolean directed;

public Graph(int v) {
degree = new int[v];
nvertices = v;
nedges = 0;
adjList = new HashMap<>();
for(int i = 0; i < v; i++) {
adjList.put(i, new ArrayList<>());
}
}

public Graph(int v, boolean directed) {


this(v);
if(directed)
this.directed = true;
}

public void insertEdge(int x, int y, int weight, boolean directed) {


List<int[]> list = adjList.get(x);
int[] tmp = new int[2];
tmp[0] = y;
tmp[1] = weight;
list.add(tmp);
adjList.put(x, list);
if(!directed) {
List<int[]> list2 = adjList.get(y);
int[] tmp2 = new int[2];
tmp2[0] = x;
tmp2[1] = weight;
list2.add(tmp2);
adjList.put(y, list2);
}
}

public void insertEdge(int x, int y, boolean directed) {


insertEdge(x, y, 0, directed);
}
}
public class BfsGraph {
boolean[] processed;
boolean[] discovered;
int[] parent;

Graph g;

public BfsGraph(Graph g) {
this.g = g;
processed = new boolean[g.nvertices];
discovered = new boolean[g.nvertices];
parent = new int[g.nvertices];
for(int i = 0; i < g.nvertices; i++)
parent[i] = -1;
}

public void bfs(int start) {

Queue<Integer> q = new LinkedList<>();


int v;
int y;

q.offer(start);
discovered[start] = true;

while (!q.isEmpty()) {
v = q.poll();
// process vertex early - v
System.out.println(v);
processed[v] = true;
List<int[]> neighbors = g.adjList.get(v);
for(int[] neighbor : neighbors) {
y = neighbor[0];
if(!processed[y] || g.directed) {
// process edge(v, y)
System.out.println(v+"-"+y);
}
if(!discovered[y]) {
q.offer(y);
discovered[y] = true;
parent[y] = v;
}
}
// process vertex late - v
}
}

public void processEdge(int x, int y) {


g.nedges++;
}

public void findPath(int start, int end) {


if(start == end || (end == -1))
System.out.println(start);
else {
findPath(start, parent[end]);
System.out.println(end);
}
}
}
Bellman Ford Algorithm for Single Source
Shortest Path
In graph theory, the Bellman-Ford (BF) algorithm is a Single Source Shortest Path (SSSP) algorithm. This means it can find the shortest path from one node to any other
node.However, BF is not ideal for most SSSP problems because it has a time complexity of O(EV). It is better to use Dijkstra’s algorithm which is much faster. It is on the order of
Θ((E+V)log(V)) when using a binary heap priority queue.

However, Dijkstra’s algorithm can fail when the graph has negative edge weights. This is when BF becomes really handy because it can be used to detect negative cycles and
determine where they occur.Finding negative cycles can be useful in many types of applications. One particularly neat application arises in finance when performing an
arbitragebetween two or more markets.

Approach
public class BellmanFordEdgeList {

// A directed edge
public static class Edge {
double cost;
int from, to;

public Edge(int from, int to, double cost) {


this.to = to;
this.from = from;
this.cost = cost;
}
}

/**
* An implementation of the Bellman-Ford algorithm. The algorithm finds the shortest path between
* a starting node and all other nodes in the graph. The algorithm also detects negative cycles.
* If a node is part of a negative cycle then the minimum cost for that node is set to
* Double.NEGATIVE_INFINITY.
*
* @param edges - An edge list containing directed edges forming the graph
* @param V - The number of vertices in the graph.
* @param start - The id of the starting node
*/
public static double[] bellmanFord(Edge[] edges, int V, int start) {

double[] dist = new double[V];


java.util.Arrays.fill(dist, Double.POSITIVE_INFINITY);
dist[start] = 0;

// Only in the worst case does it take V-1 iterations for the Bellman-Ford
// algorithm to complete. Another stopping condition is when we're unable to
// relax an edge, this means we have reached the optimal solution early.
boolean relaxedAnEdge = true;

// For each vertex, apply relaxation for all the edges


for (int v = 0; v < V - 1 && relaxedAnEdge; v++) {
relaxedAnEdge = false;
for (Edge edge : edges) {
if (dist[edge.from] + edge.cost < dist[edge.to]) {
dist[edge.to] = dist[edge.from] + edge.cost;
relaxedAnEdge = true;
}
}
}

// Run algorithm a second time to detect which nodes are part


// of a negative cycle. A negative cycle has occurred if we
// can find a better path beyond the optimal solution.
relaxedAnEdge = true;
for (int v = 0; v < V - 1 && relaxedAnEdge; v++) {
relaxedAnEdge = false;
for (Edge edge : edges) {
if (dist[edge.from] + edge.cost < dist[edge.to]) {
dist[edge.to] = Double.NEGATIVE_INFINITY;
relaxedAnEdge = true;
}
}
}

// Return the array containing the shortest distance to every node


return dist;
}
}
Complexity Analysis
- Time Complexity: O(V*E)
- Space Complexity: O(V)

KMP String matching algorithm


28. Implement strStr()
Easy

Implement strStr().

Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Clarification:

What should we return when needle is an empty string? This is a great question to ask during an interview.

For the purpose of this problem, we will return 0 when needle is an empty string. This is consistent to C's strstr() and Java's indexOf().

Example 1:

Input: haystack = "hello", needle = "ll" Output: 2 Example 2:

Input: haystack = "aaaaa", needle = "bba" Output: -1

Constraints:

1 <= haystack.length, needle.length <= 104


haystack and needle consist of only lowercase English characters.

Approach
- build pattern:
- we build the pattern for the substring
- it would be use in matching the pattern
- how:
- initialize two pointers, starting from 0(j) and 1(i)
- while i < len(substring)
- if s[i] == s[j] then set pattern[i] = j and increment both pointers, (which means we found a suffix that matches a prefix)
- else if j > 0 then set j to pattern[j-1]+1, (j-1 holds the last index where it had a successful match)
- else increment i
- matching pattern
- similar to build pattern
- how:
- initialize two pointers, starting from 0(j) points to sunstring and 0(i) points to string
- while i + len(substring) - j <= len(string)
- if s[i] == s[j]
- if j = len(substring)-1
- we found the match
- increment both pointers,
- else if j > 0 then set j to pattern[j-1]+1, (j-1 holds the last index where it had a successful match)
- else increment i

Solution
class Solution {
public int strStr(String haystack, String needle) {
int[] pattern = new int[needle.length()];
Arrays.fill(pattern, -1);
buildPattern(needle, pattern);
return matchIndex(haystack, needle, pattern);
}

private void buildPattern(String str, int[] pattern) {


int j = 0;
int i = 1;
while(i < str.length()) {
if(str.charAt(i) == str.charAt(j)) {
pattern[i] = j;
i++;
j++;
} else if(j > 0) {
j = pattern[j-1] + 1;
} else {
i++;
}
}
}

private int matchIndex(String str, String substr, int[] pattern) {


int j = 0, i = 0;

while(i + substr.length() - j <= str.length()) {


if(str.charAt(i) == substr.charAt(j)) {
if(j == substr.length()-1)
return i-j;
i++;
j++;
} else if(j > 0) {
j = pattern[j-1] + 1;
} else {
i++;
}
}
return -1;
}
}

Complexity Analysis
- Time Complexity: O(n+m), n - length of string, m - length of substring
- Space Complexity: O(m), to build the pattern array

Segment Tree
Introduction
Approach
Implementation
class SegmentTree { // the segment tree is stored like a heap array
private int[] st, A;
private int n;
private int left(int p) {
return p << 1;
} // same as binary heap operations
private int right(int p) {
return (p << 1) + 1;
}

private void build(int p, int L, int R) {


if (L == R) // as L == R, either one is fine
st[p] = L; // store the index
else { // recursively compute the values
build(left(p), L, (L + R) / 2);
build(right(p), (L + R) / 2 + 1, R);
int p1 = st[left(p)], p2 = st[right(p)];
st[p] = (A[p1] <= A[p2]) ? p1 : p2;
}
}

private int rmq(int p, int L, int R, int i, int j) { // O(log n)


if (i > R || j < L) return -1; // current segment outside query range
if (L >= i && R <= j) return st[p]; // inside query range

// compute the min position in the left and right part of the interval
int p1 = rmq(left(p), L, (L + R) / 2, i, j);
int p2 = rmq(right(p), (L + R) / 2 + 1, R, i, j);

if (p1 == -1) return p2; // if we try to access segment outside query


if (p2 == -1) return p1; // same as above
return (A[p1] <= A[p2]) ? p1 : p2;
} // as as in build routine

private int update_point(int p, int L, int R, int idx, int new_value) {


// this update code is still preliminary, i == j
// must be able to update range in the future!
int i = idx, j = idx;

// if the current interval does not intersect


// the update interval, return this st node value!
if (i > R || j < L)
return st[p];

// if the current interval is included in the update range,


// update that st[node]
if (L == i && R == j) {
A[i] = new_value; // update the underlying array
return st[p] = L; // this index
}

// compute the minimum position in the


// left and right part of the interval
int p1, p2;
p1 = update_point(left(p), L, (L + R) / 2, idx, new_value);
p2 = update_point(right(p), (L + R) / 2 + 1, R, idx, new_value);

// return the position where the overall minimum is


return st[p] = (A[p1] <= A[p2]) ? p1 : p2;
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:

Fenwick Tree
Introduction
Approach
Implementation
class FenwickTree {
int[] ft, nums;

private int LSOne(int n) {


return (n & (-n));
}

public FenwickTree(int n, int[] nums) {


ft = new int[n + 1];
this.nums = nums;
build(nums);
}

private void build(int[] nums) {


for (int j = 0; j < nums.length; j++) {
for (int i = j + 1; i <= nums.length; i += LSOne(i)) {
ft[i] += nums[j];
}
}
}

private int rsq(int i) {


int sum = 0;
for (; i > 0; i -= LSOne(i))
sum += ft[i];
return sum;
}

public int sumRange(int i, int j) {


return rsq(j + 1) - rsq(i);
}

public void update(int ind, int val) {


int tmp = nums[ind];
nums[ind] = val;
ind++;
for (; ind < ft.length; ind += LSOne(ind))
ft[ind] += (val - tmp);
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:
Fenwick Tree
Introduction
Approach
Implementation
public class FenwickTree2D {
int[][] ft;

public FenwickTree2D(int[][] arr) {


int n = arr.length, m = arr[0].length;
ft = new int[n + 1][m + 1];
build(arr);
}

private int LSOne(int n) {


return (n & (-n));
}
private void build(int[][] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
update(i + 1, j + 1, arr[i][j]);
}
}
}

public void update(int row, int col, int value) {


for (int i = row; i < ft.length; i += LSOne(i)) {
for (int j = col; j < ft[0].length; j += LSOne(j)) {
ft[i][j] += value;
}
}
}

private int querySum(int row, int col) {


int sum = 0;
for (int i = row; i > 0; i -= LSOne(i)) {
for (int j = col; j > 0; j -= LSOne(j)) {
sum += ft[i][j];
}
}
return sum;
}

public int rangeSum(int x1, int y1, int x2, int y2) {
// Inclusion & Exclusion principle
return querySum(x2, y2) - querySum(x1 - 1, y2) - querySum(x2, y1 - 1) + querySum(x1 - 1, y1 - 1);
}
}

Complexity Analysis
- Time Complexity:
- Space Complexity:
NeetCode-150
1. Arrays & Hashing Resources
(https://github.com/dipjul/NeetCode-
150/blob/main/01.%20Arrays%20&%20Hashing/)
1. Contains Duplicate Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/01.%20ContainsDuplicate.md)
2. Valid Anagram Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/02.ValidAnagram.md)
3. Two Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/03.TwoSum.md)
4. Group Anagrams Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/04.GroupAnagrams.md)
5. Top K Frequent Elements Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/05.TopKElements.md)
6. Product of Array Except Self Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/06.ProductOfArrayExceptSelf.md)
7. Valid Sudoku Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/07.ValidSodoku.md)
8. Encode And Decode Strings Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/08.EncodeAndDecodeStrings.md)
9. Longest Consecutive Sequence Solution (https://github.com/dipjul/NeetCode-150/blob/main/01.%20Arrays%20&%20Hashing/09.LongestConsecutiveSequence.md)

2. Two Pointers Resources (https://github.com/dipjul/NeetCode-


150/blob/main/02.%20Two%20Pointer/)
1. Valid Palindrome Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/01.ValidPalindrome.md)
2. Two Sum II Input Array Is Sorted Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/02.TwoSumII.md)
3. 3Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/03.3Sum.md)
4. Container With Most Water Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/04.ContainerWithMostWater.md)
5. Trapping Rain Water Solution (https://github.com/dipjul/NeetCode-150/blob/main/02.%20Two%20Pointer/05.TrappingRainWater.md)

3. Sliding Window Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/03.%20Sliding%20Window/)
1. Best Time to Buy And Sell Stock Solution (https://github.com/dipjul/NeetCode-150/blob/main/03.%20Sliding%20Window/01.BestTimeToBuy&SellStock.md)
2. Longest Substring Without Repeating Characters Solution (https://github.com/dipjul/NeetCode-
150/blob/main/03.%20Sliding%20Window/02.LongestSubstringWithoutRepeatingCharacters.md)
3. Longest Repeating Character Replacement Solution (https://github.com/dipjul/NeetCode-
150/blob/main/03.%20Sliding%20Window/03.LongestRepeatingCharacterReplacement.md)
4. Permutation In String Solution (https://github.com/dipjul/NeetCode-150/blob/main/03.%20Sliding%20Window/04.PermutationInString.md)
5. Minimum Window Substring Solution (https://github.com/dipjul/NeetCode-150/blob/main/03.%20Sliding%20Window/05.MinimumWindowSubstring.md)
6. Sliding Window Maximum Solution (https://github.com/dipjul/NeetCode-150/blob/main/03.%20Sliding%20Window/06.SlidingWindowMaximum.md)

4. Stack Resources (https://github.com/dipjul/NeetCode-


150/blob/main/04.%20Stack/)
1. Valid Parentheses Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/01.ValidParentheses.md)
2. Min Stack Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/02.MinStack.md)
3. Evaluate Reverse Polish Notation Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/03.EvaluateReversePolishNotation.md)
4. Generate Parentheses Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/04.GenerateParentheses.md)
5. Daily Temperatures Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/05.DailyTemperatures.md)
6. Car Fleet Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/06.CarFleet.md)
7. Largest Rectangle In Histogram Solution (https://github.com/dipjul/NeetCode-150/blob/main/04.%20Stack/07.LargestRectangleInHistogram.md)
5. Binary Search Resources
(https://github.com/dipjul/NeetCode-
150/blob/main/05.%20Binary%20Search/)
1. Binary Search Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/01.BinarySearch.md)
2. Search a 2D Matrix Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/02.SearchIn2DMatrix.md)
3. Koko Eating Bananas Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/03.KokoEatingBananas.md)
4. Search In Rotated Sorted Array Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/04.SearchInRotatedSortedArray.md)
5. Find Minimum In Rotated Sorted Array Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/05.FindMinimumInRotatedSortedArray.md)
6. Time Based Key Value Store Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/06.TimeBasedKeyValueStore.md)
7. Median of Two Sorted Arrays Solution (https://github.com/dipjul/NeetCode-150/blob/main/05.%20Binary%20Search/07.MedianOfTwoSortedArrays.md)

6. Lined List Resources (https://github.com/dipjul/NeetCode-


150/blob/main/06.%20LinkedList/)
1. Reverse Linked List Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/01.ReverseLinkedList.md)
2. Merge Two Sorted Lists Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/02.MergeTwoLinkedList.md)
3. Reorder List Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/03.ReorderList.md)
4. Remove Nth Node From End of List Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/04.RemoveNthNodeFromEndOfTheList.md)
5. Copy List With Random Pointer Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/05.CopyListWithRandomPointer.md)
6. Add Two Numbers Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/06.AddTwoNumbers.md)
7. Linked List Cycle Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/07.LinkedListCycle.md)
8. Find The Duplicate Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/08.FindDuplicateNumber.md)
9. LRU Cache Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/09.LRUCache.md)
10. Merge K Sorted Lists Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/10.MergeKSortedLists.md)
11. Reverse Nodes In K Group Solution (https://github.com/dipjul/NeetCode-150/blob/main/06.%20LinkedList/11.ReverseNodesInK-Group.md)

7. Trees Resources (https://github.com/dipjul/NeetCode-


150/blob/main/07.%20Tree/)
1. Invert Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/01.InvertBinaryTree.md)
2. Maximum Depth of Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/02.MaxDepthOfBinaryTree.md)
3. Diameter of Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/03.DiameterOfABinaryTree.md)
4. Balanced Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/04.BalancedBinaryTree.md)
5. Same Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/05.SameTree.md)
6. Subtree of Another Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/06.SubtreeOfAnotherTree.md)
7. Lowest Common Ancestor of a Binary Search Tree Solution (https://github.com/dipjul/NeetCode-
150/blob/main/07.%20Tree/07.LowsetCommonAncestorOfABinarySearchTree.md)
8. Binary Tree Level Order Traversal Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/08.BinaryTreeLevelOrderTraversal.md)
9. Binary Tree Right Side View Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/09.BinaryTreeRightSideView.md)
10. Count Good Nodes In Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/10.CountGoodNodesInBinaryTree.md)
11. Validate Binary Search Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/11.ValidateBinarySearchTree.md)
12. Kth Smallest Element In a Bst Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/12.KthSmallestElementInABST.md)
13. Construct Binary Tree From Preorder And Inorder Traversal Solution (https://github.com/dipjul/NeetCode-
150/blob/main/07.%20Tree/13.ConstructBinaryTreeFromPreorderAndInorderTraversal.md)
14. Binary Tree Maximum Path Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/14.BinaryTreeMaximumPathSum.md)
15. Serialize And Deserialize Binary Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/07.%20Tree/15.SerializeAndDeserializeBinaryTree.md)

8. Tries Resources (https://github.com/dipjul/NeetCode-


150/blob/main/08.%20Trie/)
1. Implement Trie Prefix Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/08.%20Trie/01.ImplementTrie.md)
2. Design Add And Search Words Data Structure Solution (https://github.com/dipjul/NeetCode-150/blob/main/08.%20Trie/02.DesignAddandSearchWordDataStructure.md)
3. Word Search II Solution ()
9. Heap / PriorityQueue Resources
(https://github.com/dipjul/NeetCode-
150/blob/main/09.%20Heap%20or%20PriorityQueue/)
1. Kth Largest Element In a Stream Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/01.kthLargestElementInAStream.md)
2. Last Stone Weight Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/02.LastStoneWeight.md)
3. K Closest Points to Origin Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/03.KClosestPointsToOrigin.md)
4. Kth Largest Element In An Array Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/04.KthLargestElementInAnArray.md)
5. Task Scheduler Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/05.TaskScheduler.md)
6. Design Twitter
7. Find Median From Data Stream Solution (https://github.com/dipjul/NeetCode-150/blob/main/09.%20Heap%20or%20PriorityQueue/07.FindMedianFromDataStream.md)

10. Backtracking Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/10.%20Backtracking/)
1. Subsets Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/01.Subsets.md)
2. Combination Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/02.CombinationSum.md)
3. Permutations Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/03.Permutations.md)
4. Subsets II Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/04.SubsetsII.md)
5. Combination Sum II Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/05.CombinationSumII.md)
6. Word Search Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/06.WordSearch.md)
7. Palindrome Partitioning Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/07.PalindromePartitioning.md)
8. Letter Combinations of a Phone Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/08.LetterCombinationsOfAPhone.md)
9. N Queens Solution (https://github.com/dipjul/NeetCode-150/blob/main/10.%20Backtracking/09.N-Queens.md)

11. Graphs Resources (https://github.com/dipjul/NeetCode-


150/blob/main/11.%20Graphs/)
1. Number of Islands Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/01.NumberOfIslands.md)
2. Clone Graph Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/02.CloneGraph.md)
3. Max Area of Island Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/03.MaxAreaOfIsland.md)
4. Pacific Atlantic Water Flow Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/04.PacificAtlanticWaterFlow.md)
5. Surrounded Regions Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/05.SurroundedRegions.md)
6. Rotting Oranges Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/06.RottenOranges.md)
7. Walls And Gates Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/07.WallsAndGates.md)
8. Course Schedule Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/08.CourseSchedule.md)
9. Course Schedule II Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/09.CourseScheduleII.md)
10. Redundant Connection Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/10.RedundantConnection.md)
11. Number of Connected Components In An Undirected Graph Solution (https://github.com/dipjul/NeetCode-
150/blob/main/11.%20Graphs/11.NumberOfConnectedComponentsInAnUndirectedGraph.md)
12. Graph Valid Tree Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/12.GraphValidTree.md)
13. Word Ladder Solution (https://github.com/dipjul/NeetCode-150/blob/main/11.%20Graphs/13.WordLadder.md)

12. Advanced Graphs Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/12.%20Advanced%20Graphs/)
1. Reconstruct Itinerary Solution ()
2. Min Cost to Connect All Points Solution (https://github.com/dipjul/NeetCode-150/blob/main/12.%20Advanced%20Graphs/02.MinCostToConnectAllPoints.md)
3. Network Delay Time Solution (https://github.com/dipjul/NeetCode-150/blob/main/12.%20Advanced%20Graphs/03.NetworkDelayTime.md)
4. Swim In Rising Water Solution ()
5. Alien Dictionary Solution ()
6. Cheapest Flights Within K Stops Solution (https://github.com/dipjul/NeetCode-150/blob/main/12.%20Advanced%20Graphs/06.CheapestFlightsWithinKStops.md)
13. 1-D Dynamic Programming Resources
(https://github.com/dipjul/NeetCode-150/blob/main/13.%201-
D%20Dynamic%20Programming/)
1. Climbing Stairs Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/01.ClimbingStairs.md)
2. Min Cost Climbing Stairs Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/02.MinCostClimbingStair.md)
3. House Robber Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/03.HouseRobber.md)
4. House Robber II Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/04.HouseRobberII.md)
5. Longest Palindromic Substring Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/05.LongestPalindromicSubstring.md)
6. Palindromic Substrings Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/06.PalindromicSubstrings.md)
7. Decode Ways Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/07.DecodeWays.md)
8. Coin Change Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/08.CoinChange.md)
9. Maximum Product Subarray Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/09.MaxProductSubArray.md)
10. Word Break Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/10.WordBreak.md)
11. Longest Increasing Subsequence Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-
D%20Dynamic%20Programming/11.LongestIncreasingSubsequence.md)
12. Partition Equal Subset Sum Solution (https://github.com/dipjul/NeetCode-150/blob/main/13.%201-D%20Dynamic%20Programming/12.PartitionEqualSubsetSum.md)

14. 2-D Dynamic Programming Resources


(https://github.com/dipjul/NeetCode-150/blob/main/14.%202-
D%20Dynamic%20Programming/)
1. Unique Paths Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-D%20Dynamic%20Programming/01.UniquePaths.md)
2. Longest Common Subsequence Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-
D%20Dynamic%20Programming/02.LongestCommonSubsequence.md)
3. Best Time to Buy And Sell Stock With Cooldown Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-
D%20Dynamic%20Programming/03.BestTimeToBuyAndSellStockWithCooldown.md)
4. Coin Change II Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-D%20Dynamic%20Programming/04.CoinChange2.md)
5. Target Sum
6. Interleaving String
7. Longest Increasing Path In a Matrix Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-
D%20Dynamic%20Programming/07.LongestIncreasingPathInAMatrix.md)
8. Distinct Subsequences Solution ()
9. Edit Distance Solution (https://github.com/dipjul/NeetCode-150/blob/main/14.%202-D%20Dynamic%20Programming/09.EditDistance.md)
10. Burst Balloons
11. Regular Expression Matching

15. Greedy Resources (https://github.com/dipjul/NeetCode-


150/blob/main/15.%20Greedy/)
1. Maximum Subarray Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/01.MaximumSubarray.md)
2. Jump Game Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/02.Jump.md)
3. Jump Game II Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/03.JumpII.md)
4. Gas Station Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/04.GasStation.md)
5. Hand of Straights Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/05.HandOfStraights.md)
6. Merge Triplets to Form Target Triplet Solution ()
7. Partition Labels Solution ()
8. Valid Parenthesis String Solution (https://github.com/dipjul/NeetCode-150/blob/main/15.%20Greedy/08.ValidParenthesisString.md)

16. Intervals Resources (https://github.com/dipjul/NeetCode-


150/blob/main/16.%20Intervals/)
1. Insert Interval Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/01.InsertInterval.md)
2. Merge Intervals Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/02.MergeInterval.md)
3. Non Overlapping Intervals Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/03.Non-OverlappingIntervals.md)
4. Meeting Rooms Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/04.MeetingRooms.md)
5. Meeting Rooms II Solution (https://github.com/dipjul/NeetCode-150/blob/main/16.%20Intervals/05.MeetingRoomsII.md)
6. Minimum Interval to Include Each Query

17. Math & Geometry Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/17.%20Math%20&%20Geometry/)
1. Rotate Image Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/01.RotateImage.md)
2. Spiral Matrix Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/02.SpiralMatrix.md)
3. Set Matrix Zeroes Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/03.SetMatrixZeroes.md)
4. Happy Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/04.HappyNumber.md)
5. Plus One Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/05.PlusOne.md)
6. Pow(x, n) Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/06.Pow(x,n).md)
7. Multiply Strings Solution (https://github.com/dipjul/NeetCode-150/blob/main/17.%20Math%20&%20Geometry/07.MultiplyStrings.md)
8. Detect Squares

18. Bit Manipulation Resources


(https://github.com/dipjul/NeetCode-
150/blob/main/18.%20Bit%20Manipulation/)
1. Single Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/01.SingleNumber.md)
2. Number of 1 Bits Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/02.NumberOf1Bits.md)
3. Counting Bits Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/03.CountingBits.md)
4. Reverse Bits Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/04.ReverseBits.md)
5. Missing Number Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/05.MissingNumber.md)
6. Sum of Two Integers Solution (https://github.com/dipjul/NeetCode-150/blob/main/18.%20Bit%20Manipulation/06.SumOfTwoIntegers.md)
7. Reverse Integer

19. Misc-DSA (Not from Neetcode)


1. Union Find (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/01.UnionFind.md)
2. Dijkstra's Algorithm (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/02.Dijkstra%27s%20Algorithm.md)
3. BFS on Graph (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/03.BFS-Graph.md)
4. Bellman Ford (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/04.Bellman-ford.md)
5. KMP Algorithm (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/05.KMP-Algorithm.md)
6. Segment Tree (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/06.SegmentTree.md)
7. Fenwick Tree (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/07.FenwickTree.md)
8. Fenwick Tree 2D (https://github.com/dipjul/NeetCode-150/blob/main/Misc-DSA/08.FenwickTree2D.md)

You might also like