Leetcode Note
String
Longest Substring Without Repeating Characters - 3
Medium
Substring
Problem
Given a string, find the length of the longest substring without repeating characters.
Example 1:
Input: "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
Example 2:
Input: "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.
Example 3:
Input: "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
Solution
Basic idea, use sliding window
to slide through the whole string.
Used a low pointer to log where current window starts, and use a map to log where each letters appeared fot the last time.
Caution: when resetting the low pointer, we should take the max of lo
and dic[c] + 1
.
E.g.abcaab
, when the window slides to the last b, low pointer is at a with index of -2 and current window is a
, but the former b with index of 1 is still in the hashmap. If we use dic[c] + 1
at this point, the window will become caab
instead of ab
.
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
res = 0
lo = 0
dic = {}
for i, c in enumerate(s):
if c in dic:
lo = max(lo, dic[c] + 1) # caution: abcaab
dic[c] = i
res = max(i - lo + 1, res)
return res
Longest Palindromic Substring - 5
Medium
Palindrome
Problem
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
Solution
Since a palindrome mirrors around its center, and there are totally 2n - 1
such centers, we could expand around these centers to find the longest palindrome. Time complexity: O(n^2)
Note: use //
in python for floor division.
class Solution:
def longestPalindrome(self, s: str) -> str:
start, end = 0, 0
for i in range(len(s)):
cur_len = max(self.expand(s, i, i), self.expand(s, i, i + 1))
if cur_len > end - start:
start = i - (cur_len - 1) // 2 # trick here, remember to -1 since it could expand at center of two char
end = i + cur_len // 2
return s[start:end + 1]
def expand(self, s, left, right):
l, r = left, right
while l >= 0 and r < len(s) and s[l] == s[r]:
l -= 1
r += 1
return r - l - 1
TODO: DP, Manacher's Algorithm
Reverse Integer - 7
Easy
Problem
Given a 32-bit signed integer, reverse digits of an integer.
Example 1:
Input: 123
Output: 321
Example 2:
Input: -123
Output: -321
Example 3:
Input: 120
Output: 21
Note:
Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.
Solution
The most challenge part in this problem is not letting the result overflow, so the result has to be checked each time during calculation.
class Solution {
public int reverse(int x) {
int result = 0;
while (x != 0) {
int tail = x % 10;
x /= 10;
if (result > Integer.MAX_VALUE / 10 || result == Integer.MAX_VALUE / 10 && tail > 7) return 0;
if (result < Integer.MIN_VALUE / 10 || result == Integer.MIN_VALUE / 10 && tail < -8) return 0;
result = result * 10 + tail;
}
return result;
}
}
Another brilliant solution from discussion.
I’m not sure if it’s a good solution, because the integer could overflow during calculation (even though it is the intention).
During calculation, the temp result is result * 10 + tail
, but if it’s smaller than -2^31
or larger than 2^31 - 1
, it will overflow and return a number that is not correct.
class Solution {
public int reverse(int x) {
int result = 0;
while (x != 0) {
int tail = x % 10;
// overflow here, brilliant here
if (((result * 10 + tail) - tail) / 10 != result) return 0;
result = result * 10 + tail;
x /= 10;
}
return result;
}
}
String to Integer (atoi) - 8
Easy
Problem
Implement atoi which converts a string to an integer.
The function first discards as many whitespace characters as necessary until
the first non-whitespace character is found. Then, starting from this
character, takes an optional initial plus or minus sign followed by as many
numerical digits as possible, and interprets them as a numerical value.
The string can contain additional characters after those that form the
integral number, which are ignored and have no effect on the behavior of
this function.
If the first sequence of non-whitespace characters in str is not a valid
integral number, or if no such sequence exists because either str is empty
or it contains only whitespace characters, no conversion is performed.
If no valid conversion could be performed, a zero value is returned.
Note:
Only the space character ' ' is considered as whitespace character.
Assume we are dealing with an environment which could only store integers
within the 32-bit signed integer range: [−2^31, 2^31 − 1]. If the numerical
value is out of the range of representable values, INT_MAX (2^31 − 1) or
INT_MIN (−2^31) is returned.
Example 1:
Input: "42"
Output: 42
Example 2:
Input: " -42"
Output: -42
Explanation: The first non-whitespace character is '-', which is the minus
sign.
Then take as many numerical digits as possible, which gets 42.
Example 3:
Input: "4193 with words"
Output: 4193
Explanation: Conversion stops at digit '3' as the next character is not a
numerical digit.
Example 4:
Input: "words and 987"
Output: 0
Explanation: The first non-whitespace character is 'w', which is not a
numerical
digit or a +/- sign. Therefore no valid conversion could be performed.
Example 5:
Input: "-91283472332"
Output: -2147483648
Explanation: The number "-91283472332" is out of the range of a 32-bit
signed integer.
Thefore INT_MIN (−2^31) is returned.
Solution
class Solution {
public int myAtoi(String str) {
int res = 0;
int sign = 1;
int i = 0;
while (i < str.length() && str.charAt(i) == ' ') i++;
if (i == str.length()) return 0;
if (str.charAt(i) == '-' || str.charAt(i) == '+') {
if (str.charAt(i) == '-') sign = -1;
i++;
} else if (str.charAt(i) - '0' < 0 || str.charAt(i) - '0' > 9) {
return 0;
}
while (i < str.length() && str.charAt(i) - '0' >= 0 && str.charAt(i) - '0' <= 9) {
int digit = str.charAt(i) - '0';
if (sign > 0 && (res > Integer.MAX_VALUE / 10 || res == Integer.MAX_VALUE / 10 && digit > Integer.MAX_VALUE % 10)) return Integer.MAX_VALUE;
if (sign < 0 && (-res < Integer.MIN_VALUE / 10 || -res == Integer.MIN_VALUE / 10 && -digit < Integer.MIN_VALUE % 10)) return Integer.MIN_VALUE;
res = res * 10 + digit;
i++;
}
return res * sign;
}
}
Palindrome Number - 9
Easy
Palindrome
Problem
Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward.
Example 1:
Input: 121
Output: true
Example 2:
Input: -121
Output: false
Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.
Example 3:
Input: 10
Output: false
Explanation: Reads 01 from right to left. Therefore it is not a palindrome.
Follow up:
Coud you solve it without converting the integer to a string?
Solution
My first thought, instead of convert it into string, it’s to compare the first and last digit continuously.
The problem is, how could I know I’ve compared half of the digits?
Thanks to the official solution, they have a very similar approach by reverse the left half of the number, and when the right half is no longer larger than the right half, we know it is the end.
class Solution {
public boolean isPalindrome(int x) {
// take care of edge cases
if (x < 0 || x % 10 == 0 && x != 0) return false;
int left = x;
int right = 0;
// = only in case of x = 0
while (left >= right) {
right = right * 10 + left % 10;
left /= 10;
if (right == left || right / 10 == left) return true;
}
return false;
}
}
Valid Parentheses - 20
Easy
Stack
Problem
Given a string 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.
Note that an empty string is also considered valid.
Example 1:
Input: "()"
Output: true
Example 2:
Input: "()[]{}"
Output: true
Example 3:
Input: "(]"
Output: false
Example 4:
Input: "([)]"
Output: false
Example 5:
Input: "{[]}"
Output: true
Solution
public class Solution {
public boolean isValid(String s) {
Map<Character, Character> map = new HashMap<>();
map.put('(', ')');
map.put('{', '}');
map.put('[', ']');
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char cur = s.charAt(i);
if (map.containsKey(cur)) {
stack.push(cur);
} else if (stack.isEmpty() || map.get(stack.pop()) != cur) {
return false;
}
}
return stack.isEmpty();
}
}
Group Anagrams - 49
Medium
Problem
Given an array of strings, group anagrams together.
Example:
Input: ["eat", "tea", "tan", "ate", "nat", "bat"],
Output:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
Note:
All inputs will be in lowercase.
The order of your output does not matter.
Solution
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> res = new ArrayList<>();
Map<String, List<String>> map = new HashMap<>();
for (String s : strs) {
char[] cur = s.toCharArray();
Arrays.sort(cur);
String key = Arrays.toString(cur);
List<String> list = map.getOrDefault(key, new ArrayList<>());
list.add(s);
map.put(key, list);
}
return new ArrayList(map.values()); // <--
}
}
Basic Calculator - 224
Hard
Problem
Implement a basic calculator to evaluate a simple expression string.
The expression string may contain open ( and closing parentheses ), the plus
+ or minus sign -, non-negative integers and empty spaces .
Example 1:
Input: "1 + 1"
Output: 2
Example 2:
Input: " 2-1 + 2 "
Output: 3
Example 3:
Input: "(1+(4+5+2)-3)+(6+8)"
Output: 23
Note:
You may assume that the given expression is always valid.
Do not use the eval built-in library function.
Solution
class Solution {
public int calculate(String s) {
int res = 0;
int cur = 0;
int sign = 1;
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == ' ') continue;
if (c >= '0' && c <= '9') {
cur = cur * 10 + c - '0';
} else if (c == '+' || c == '-') {
res += cur * sign;
cur = 0;
sign = c == '+' ? 1 : -1;
} else if (c == '(') {
stack.push(res);
stack.push(sign);
res = 0;
// cur = 0; // cur already marked as 0 before (
sign = 1;
} else if (c == ')') {
res += cur * sign;
res = res * stack.pop() + stack.pop();
cur = 0;
// sign = 1; // next char will be sign
}
}
if (cur != 0) res += cur * sign;
return res;
}
}
Backspace String Compare - 844
Easy
Two Pointer
Problem
Given two strings S and T, return if they are equal when both are typed into
empty text editors. # means a backspace character.
Example 1:
Input: S = "ab#c", T = "ad#c"
Output: true
Explanation: Both S and T become "ac".
Example 2:
Input: S = "ab##", T = "c#d#"
Output: true
Explanation: Both S and T become "".
Example 3:
Input: S = "a##c", T = "#a#c"
Output: true
Explanation: Both S and T become "c".
Example 4:
Input: S = "a#c", T = "b"
Output: false
Explanation: S becomes "c" while T becomes "b".
Note:
1 <= S.length <= 200
1 <= T.length <= 200
S and T only contain lowercase letters and '#' characters.
Follow up:
Can you solve it in O(N) time and O(1) space?
Solution
class Solution {
public boolean backspaceCompare(String S, String T) {
int i = S.length() - 1, j = T.length() - 1;
while (i >= 0 || j >= 0) {
int back = 0;
while (i >= 0 && (S.charAt(i) == '#' || back > 0)) {
back += S.charAt(i) == '#' ? 1 : -1;
i--;
}
back = 0;
while (j >= 0 && (T.charAt(j) == '#' || back > 0)) {
back += T.charAt(j) == '#' ? 1 : -1;
j--;
}
if (i >= 0 && j >= 0 && S.charAt(i) == T.charAt(j)) {
i--;
j--;
} else {
break;
}
}
return i == -1 && j == -1;
}
}
Array
Two Sum - 1
Easy
N Sum
Problem
Given an array of integers, return indices of the two numbers such that they
add up to a specific target.
You may assume that each input would have exactly one solution, and you may
not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
Solution
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int req = target - nums[i];
if (map.containsKey(req)) {
return new int[] {map.get(req), i};
}
else {
map.put(nums[i], i);
}
}
return new int[]{};
}
}
Three Sum - 15
Medium
N Sum
Problem
Given an array nums of n integers, are there elements a, b, c in nums such
that a + b + c = 0? Find all unique triplets in the array which gives the
sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
Solution
A very classical problem.
Basic idea is to sort the array and using two pointer
to find the complementary two sum.
public class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (i == 0 || nums[i] != nums[i - 1]) {
int lo = i + 1, hi = nums.length - 1;
// dont need to check the length of input array, if its length < 3, lo < hi will not stand
while (lo < hi) {
int sum = nums[i] + nums[lo] + nums[hi];
if (sum == 0) {
// hey!
res.add(Arrays.asList(nums[i], nums[lo], nums[hi]));
while (lo < hi && nums[lo] == nums[lo + 1]) lo++;
while (lo < hi && nums[hi] == nums[hi - 1]) hi--;
lo++;
hi--;
} else if (sum < 0) {
lo++;
} else {
hi--;
}
}
}
}
return res;
}
}
Majority Element - 169
Easy
Problem
Given an array of size n, find the majority element. The majority element is
the element that appears more than ⌊ n/2 ⌋ times.
You may assume that the array is non-empty and the majority element always
exist in the array.
Example 1:
Input: [3,2,3]
Output: 3
Example 2:
Input: [2,2,1,1,1,2,2]
Output: 2
Solution
class Solution {
public int majorityElement(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for (int i : nums) {
int count = map.getOrDefault(i, 0);
if (count + 1 > nums.length / 2) return i;
map.put(i, count + 1);
}
return -1;
}
}
class Solution {
public int majorityElement(int[] nums) {
int count = 0;
Integer candidate = null;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
Binary Search - 704
Easy
Binary Search
Problem
Given a sorted (in ascending order) integer array nums of n elements and a
target value, write a function to search target in nums. If target exists,
then return its index, otherwise return -1.
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
Note:
You may assume that all elements in nums are unique.
n will be in the range [1, 10000].
The value of each element in nums will be in the range [-9999, 9999].
Solution
What is log2(8) mean?
It means that, my base is 2, what should I power 2 by, to get 8?
The answer is 3 = log2(8)
log2(8) => 2^3 = 8
log10(100) => 10^2 = 100
8 -> 4 -> 2 -> 1
Why is the complexity log(n)?
Assume there are n elements in total, each time we cut the array in half.
How many times in total do we need to cut the array?
So we need to know, what should I power 2 by, to get n. The answer is log(n)
class Solution {
public int search(int[] nums, int target) {
int lo = 0, hi = nums.length - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;
}
}
Recursively
class Solution {
public int search(int[] nums, int target) {
return binarySearch(nums, target, 0, nums.length - 1);
}
public int binarySearch(int[] nums, int target, int lo, int hi) {
if (lo > hi) return -1;
int mid = (lo + hi) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] < target) {
return binarySearch(nums, target, mid + 1, hi);
} else {
return binarySearch(nums, target, lo, mid - 1);
}
}
}
Search in Rotated Sorted Array - 33
Medium
Binary Search
Problem
Suppose an array sorted in ascending order is rotated at some pivot unknown
to you beforehand.
(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).
You are given a target value to search. If found in the array return its
index, otherwise return -1.
You may assume no duplicate exists in the array.
Your algorithm's runtime complexity must be in the order of O(log n).
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
Solution
class Solution {
public int search(int[] nums, int target) {
int lo = 0, hi = nums.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2; // prevent overflow if lo + hi is too big
if (nums[mid] == target) return mid;
if (nums[lo] <= nums[mid]) { // left is sorted
if (target < nums[mid] && target >= nums[lo]) {
hi = mid - 1;
} else {
lo = mid + 1;
}
} else { // right is sorted
if (target > nums[mid] && target <= nums[hi]) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
}
return -1;
}
}
Rotate Array - 189
Easy
Geometry
Problem
Given an array, rotate the array to the right by k steps, where k is
non-negative.
Example 1:
Input: [1,2,3,4,5,6,7] and k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]
Example 2:
Input: [-1,-100,3,99] and k = 2
Output: [3,99,-1,-100]
Explanation:
rotate 1 steps to the right: [99,-1,-100,3]
rotate 2 steps to the right: [3,99,-1,-100]
Note:
Try to come up as many solutions as you can, there are at least 3 different
ways to solve this problem.
Could you do it in-place with O(1) extra space?
Solution
Naive O(n) space solution
class Solution {
public void rotate(int[] nums, int k) {
int[] tmp = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
tmp[(i + k) % nums.length] = nums[i];
}
for (int i = 0; i < nums.length; i++) {
nums[i] = tmp[i];
}
}
}
Genius O(1) sapce solution
nums = "----->-->" // k = 3
result = "-->----->"
reverse "----->-->" we can get "<--<-----"
reverse "<--" we can get "--><-----"
reverse "<-----" we can get "-->----->"
class Solution {
public void rotate(int[] nums, int k) {
k %= nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
public void reverse(int[] arr, int i, int j) {
while (i < j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
}
Product of Array Except Self - 238
Medium
Prefix Sum
Problem
Given an array nums of n integers where n > 1, return an array output such
that output[i] is equal to the product of all the elements of nums except
nums[i].
Example:
Input: [1,2,3,4]
Output: [24,12,8,6]
Note: Please solve it without division and in O(n).
Follow up:
Could you solve it with constant space complexity? (The output array does
not count as extra space for the purpose of space complexity analysis.)
Solution
Prefix product with extra space
class Solution {
public int[] productExceptSelf(int[] nums) {
if (nums.length <= 1) return nums;
int[] preLeft = new int[nums.length];
preLeft[0] = 1;
for (int i = 1; i < nums.length; i++) {
preLeft[i] = preLeft[i - 1] * nums[i - 1];
}
int[] preRight = new int[nums.length];
preRight[nums.length - 1] = 1;
for (int i = nums.length - 2; i >= 0; i--) {
preRight[i] = preRight[i + 1] * nums[i + 1];
}
int[] res = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
res[i] = preLeft[i] * preRight[i];
}
return res;
}
}
So-called O(1) solution, calc the prefix right on the fly
class Solution {
public int[] productExceptSelf(int[] nums) {
if (nums.length <= 1) return nums;
int[] res = new int[nums.length];
res[0] = 1;
for (int i = 1; i < nums.length; i++) {
res[i] = res[i - 1] * nums[i - 1];
}
int right = 1;
for (int i = nums.length - 1; i >= 0; i--) {
res[i] *= right;
right *= nums[i];
}
return res;
}
}
Bonus, real O(1) space w/ division
class Solution {
public int[] productExceptSelf(int[] nums) {
int prod = 1;
int zero = 0;
for (int i : nums) {
if (i != 0) prod *= i;
else zero++;
}
for (int i = 0; i < nums.length; i++) {
if (zero == 0) {
nums[i] = prod / nums[i];
} else if (zero == 1 && nums[i] == 0) { // this is that zero
nums[i] = prod;
} else { // if more than one zero, then prod except itself will always be 0
nums[i] = 0;
}
}
return nums;
}
}
Contiguous Array - 525
Medium
Prefix Sum
Problem
Given a binary array, find the maximum length of a contiguous subarray with
equal number of 0 and 1.
Example 1:
Input: [0,1]
Output: 2
Explanation: [0, 1] is the longest contiguous subarray with equal number of
0 and 1.
Example 2:
Input: [0,1,0]
Output: 2
Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal
number of 0 and 1.
Note:
The length of the given binary array will not exceed 50,000.
Solution
sum++ when meet 1, sum– when meet 0
if two index have same sum, the number of 0 and 1 between these two index must be equal
consider [0, 0, 0, 1, 1, 0], prefix sum = [-1, -2, -3, -2, -1, -2]
longest length is between two -1, length = 4 - 0 = 4
class Solution {
public int findMaxLength(int[] nums) {
int res = 0;
int sum = 0;
Map<Integer, Integer> map = new HashMap<>();
map.put(0, -1);
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
sum--;
} else {
sum++;
}
if (map.containsKey(sum)) {
res = Math.max(res, i - map.get(sum));
} else {
map.put(sum, i);
}
}
return res;
}
}
Subarray Sum Equals K - 560
Medium
Prefix Sum
N Sum
Problem
Given an array of integers and an integer k, you need to find the total
number of continuous subarrays whose sum equals to k.
Example 1:
Input:nums = [1,1,1], k = 2
Output: 2
Note:
The length of the array is in range [1, 20,000].
The range of numbers in the array is [-1000, 1000] and the range of the
integer k is [-1e7, 1e7].
Solution
When subarray occured, we can think about prefix sum sum(0, i)
Find out that k = sum(0, j) - sum(0, i)
, isn’t this familiar? It’s two sum!
We can solve this problem with prefix sum + two sum
class Solution {
public int subarraySum(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>(); // prefix sum -> freq
map.put(0, 1); // handle nums[i] == k
int sum = 0;
int res = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i]; // prefix sum
if (map.containsKey(sum - k)) { // two sum
res += map.get(sum - k);
}
map.put(sum, map.getOrDefault(sum, 0) + 1); // update prefix sum later, in case k = 0 (don't want to count sum itself)
}
return res;
}
}
Intersection of Two Arrays - 349
Easy
Intersect
Two Pointer
Problem
Given two arrays, write a function to compute their intersection.
Example 1:
Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2]
Example 2:
Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
Output: [9,4]
Note:
Each element in the result must be unique.
The result can be in any order.
Solution
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set = new HashSet<>();
for (int i : nums1) {
set.add(i);
}
int[] res = new int[set.size()];
int idx = 0;
for (int i : nums2) {
if (set.contains(i)) {
res[idx++] = i;
set.remove(i);
}
}
return Arrays.copyOf(res, idx);
}
}
Intersection of Two Arrays II - 350
Easy
Intersect
Two Pointer
Problem
Given two arrays, write a function to compute their intersection.
Example 1:
Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2,2]
Example 2:
Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
Output: [4,9]
Note:
Each element in the result should appear as many times as it shows in both
arrays.
The result can be in any order.
Follow up:
What if the given array is already sorted? How would you optimize your
algorithm?
What if nums1's size is small compared to nums2's size? Which algorithm is
better?
What if elements of nums2 are stored on disk, and the memory is limited such
that you cannot load all elements into the memory at once?
Solution
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Map<Integer, Integer> map = new HashMap<>();
for (int i : nums1) {
map.put(i, map.getOrDefault(i, 0) + 1);
}
List<Integer> list = new ArrayList<>();
for (int i : nums2) {
if (map.getOrDefault(i, 0) > 0) {
list.add(i);
map.put(i, map.get(i) - 1);
}
}
int[] res = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
res[i] = list.get(i);
}
return res;
}
}
If sorted
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
List<Integer> list = new ArrayList<>();
int i1 = 0, i2 = 0;
while (i1 < nums1.length && i2 < nums2.length) {
if (nums1[i1] == nums2[i2]) {
list.add(nums1[i1]);
i1++;
i2++;
} else if (nums1[i1] < nums2[i2]) {
i1++;
} else {
i2++;
}
}
int[] res = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
res[i] = list.get(i);
}
return res;
}
}
Intersection of Three Sorted Arrays - 1213
Easy
Intersect
Two Pointer
Problem
Given three integer arrays arr1, arr2 and arr3 sorted in strictly
increasing order, return a sorted array of only the integers that
appeared in all three arrays.
Example 1:
Input: arr1 = [1,2,3,4,5], arr2 = [1,2,5,7,9], arr3 = [1,3,4,5,8]
Output: [1,5]
Explanation: Only 1 and 5 appeared in the three arrays.
Constraints:
1 <= arr1.length, arr2.length, arr3.length <= 1000
1 <= arr1[i], arr2[i], arr3[i] <= 2000
Solution
class Solution {
public List<Integer> arraysIntersection(int[] arr1, int[] arr2, int[] arr3) {
List<Integer> res = new LinkedList<>();
int idx1 = 0, idx2 = 0, idx3 = 0;
while (idx1 < arr1.length && idx2 < arr2.length && idx3 < arr3.length) {
if (arr1[idx1] == arr2[idx2] && arr1[idx1] == arr3[idx3]) {
res.add(arr1[idx1]);
while (idx1 + 1 < arr1.length && arr1[idx1] == arr1[idx1 + 1]) idx1++;
while (idx2 + 1 < arr2.length && arr2[idx2] == arr2[idx2 + 1]) idx2++;
while (idx3 + 1 < arr3.length && arr3[idx3] == arr3[idx3 + 1]) idx3++;
idx1++;
idx2++;
idx3++;
} else {
if (arr1[idx1] == arr2[idx2]) {
if (arr1[idx1] > arr3[idx3]) idx3++;
else {
idx1++;
idx2++;
}
} else if (arr1[idx1] < arr2[idx2]) {
idx1++;
if (arr3[idx3] < arr2[idx2]) idx3++;
} else {
idx2++;
if (arr3[idx3] < arr1[idx1]) idx3++;
}
}
}
return res;
}
}
Shuffle an Array - 384
Medium
Random
Problem
Shuffle a set of numbers without duplicates.
Example:
// Init an array with set 1, 2, and 3.
int[] nums = {1,2,3};
Solution solution = new Solution(nums);
// Shuffle the array [1,2,3] and return its result. Any permutation of
[1,2,3] must equally likely to be returned.
solution.shuffle();
// Resets the array back to its original configuration [1,2,3].
solution.reset();
// Returns the random shuffling of array [1,2,3].
solution.shuffle();
Solution
Simple solution is to copy all elements into an arraylist, each time remove a random element from the arraylist, and add it to the output array.
We can prove that the overally possibility of an element to be picked at each point is equal. (just like the order of lottery drawing doesn’t matter)
Due of the shift of arraylist, overally complexity will be O(n2)
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(nums);
* int[] param_1 = obj.reset();
* int[] param_2 = obj.shuffle();
*/
class Solution {
private Random random = new Random();
private int[] original;
private int[] nums;
public Solution(int[] nums) {
this.original = nums.clone();
this.nums = nums;
}
/** Resets the array to its original configuration and return it. */
public int[] reset() {
nums = original.clone();
return nums;
}
/** Returns a random shuffling of the array. */
public int[] shuffle() {
List<Integer> list = getArrayCopy();
for (int i = 0; i < nums.length; i++) {
int idx = random.nextInt(list.size());
nums[i] = list.get(idx);
list.remove(idx);
}
return nums;
}
private List<Integer> getArrayCopy() {
List<Integer> list = new ArrayList<>();
for (int i : nums) {
list.add(i);
}
return list;
}
}
Fisher-Yates Algorithm
Similar idea, instead of pick number from another list, during the iteration, pick number from itself, and swap it with current idx. Complexity O(n)
class Solution {
private Random random = new Random();
private int[] original;
private int[] nums;
public Solution(int[] nums) {
this.original = nums.clone();
this.nums = nums;
}
/** Resets the array to its original configuration and return it. */
public int[] reset() {
nums = original.clone();
return nums;
}
/** Returns a random shuffling of the array. */
public int[] shuffle() {
for (int i = 0; i < nums.length; i++) {
swap(nums, i, i + random.nextInt(nums.length - i));
}
return nums;
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
Best Time to Buy and Sell Stock - 121
Easy
Sell Stock
Problem
Say you have an array for which the i^th element is the price of a given
stock on day i.
If you were only permitted to complete at most one transaction (i.e., buy
one and sell one share of the stock), design an algorithm to find the
maximum profit.
Note that you cannot sell a stock before you buy one.
Example 1:
Input: [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.
Not 7-1 = 6, as selling price needs to be larger than buying price.
Example 2:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
Solution
class Solution {
public int maxProfit(int[] prices) {
int res = 0;
Integer min = null;
for (int i : prices) {
if (min == null || i < min) min = i;
res = Math.max(res, i - min);
}
return res;
}
}
Best Time to Buy and Sell Stock II - 122
Easy
Sell Stock
Problem
Say you have an array for which the i^th element is the price of a given
stock on day i.
Design an algorithm to find the maximum profit. You may complete as many
transactions as you like (i.e., buy one and sell one share of the stock
multiple times).
Note: You may not engage in multiple transactions at the same time (i.e.,
you must sell the stock before you buy again).
Example 1:
Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit
= 5-1 = 4.
Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 =
3.
Example 2:
Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit
= 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you
are
engaging multiple transactions at the same time. You must sell before buying
again.
Example 3:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
Solution
Imagine peak and valley, valley is the point to buy, and peak is the point to sell.
class Solution {
public int maxProfit(int[] prices) {
int res = 0;
int valley = 0;
int peak = 0;
for (int i = 0; i < prices.length; i++) {
while (i < prices.length - 1 && prices[i] >= prices[i + 1]) i++;
valley = prices[i];
while (i < prices.length - 1 && prices[i] <= prices[i + 1]) i++;
peak = prices[i];
res += peak - valley;
}
return res;
}
}
Actually we don’t even need to keep track of the peak & valley.
class Solution {
public int maxProfit(int[] prices) {
int res = 0;
for (int i = 0; i < prices.length - 1; i++) {
if (prices[i + 1] > prices[i]) {
res += prices[i + 1] - prices[i];
}
}
return res;
}
}
LinkedList
Add Two Numbers - 2
Medium
Problem
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
contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the
number 0 itself.
Example:
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode cur = res, cur1 = l1, cur2 = l2;
int payload = 0;
while (cur1 != null || cur2 != null) {
int sum = payload;
if (cur1 != null) {
sum += cur1.val;
cur1 = cur1.next;
}
if (cur2 != null) {
sum += cur2.val;
cur2 = cur2.next;
}
cur.next = new ListNode(sum % 10);
payload = sum / 10;
cur = cur.next;
}
if (payload != 0) {
cur.next = new ListNode(payload);
}
return res.next;
}
}
Reverse Linked List - 206
Easy
Problem
Reverse a singly linked list.
Example:
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
Follow up:
A linked list can be reversed either iteratively or recursively.
Could you implement both?
Solution
Iteratively
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head, pre = null;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
Recursively
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head; // head == null handle null input
ListNode res = reverseList(head.next); // go straight to the tail
head.next.next = head;
head.next = null;
return res;
}
}
Middle of the Linked List - 876
Easy
Problem
Given a non-empty, singly linked list with head node head, return a middle
node of linked list.
If there are two middle nodes, return the second middle node.
Example 1:
Input: [1,2,3,4,5]
Output: Node 3 from this list (Serialization: [3,4,5])
The returned node has value 3. (The judge's serialization of this node is
[3,4,5]).
Note that we returned a ListNode object ans, such that:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next
= NULL.
Example 2:
Input: [1,2,3,4,5,6]
Output: Node 4 from this list (Serialization: [4,5,6])
Since the list has two middle nodes with values 3 and 4, we return the
second one.
Note:
The number of nodes in the given list will be between 1 and 100.
Solution
class Solution {
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
Tree
Binary Tree Inorder Traversal - 94
Medium
Traversal
Problem
Given a binary tree, return the inorder traversal of its nodes' values.
Example:
Input: [1,null,2,3]
1
\
2
/
3
Output: [1,3,2]
Follow up: Recursive solution is trivial, could you do it iteratively?
Solution
First start with the recursive approach.
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
trav(root, res);
return res;
}
private void trav(TreeNode node, List<Integer> res) {
if (node != null) {
trav(node.left, res);
res.add(node.val);
trav(node.right, res);
}
}
}
Since recursive solution is trivial :), let’s do it iteratively!
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
res.add(cur.val);
cur = cur.right;
}
return res;
}
}
Binary Tree Preorder Traversal - 144
Medium
Traversal
Problem
Given a binary tree, return the preorder traversal of its nodes' values.
Example:
Input: [1,null,2,3]
1
\
2
/
3
Output: [1,2,3]
Follow up: Recursive solution is trivial, could you do it iteratively?
Solution
Again, start with the trivial recursive approach.
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
trav(root, res);
return res;
}
private void trav(TreeNode node, List<Integer> res) {
if (node != null) {
res.add(node.val);
trav(node.left, res);
trav(node.right, res);
}
}
}
Then the iterative approach.
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
res.add(cur.val);
if (cur.right != null) stack.push(cur.right);
if (cur.left != null) stack.push(cur.left);
}
return res;
}
}
Binary Tree Postorder Traversal - 145
Medium
Traversal
Problem
Given a binary tree, return the postorder traversal of its nodes' values.
Example:
Input: [1,null,2,3]
1
\
2
/
3
Output: [3,2,1]
Follow up: Recursive solution is trivial, could you do it iteratively?
Solution
Finally comes the postorder, let’s straightforwardly go to the iterative approach.
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> res = new LinkedList<>(); // <-
if (root == null) return res;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
res.addFirst(cur.val); // <-
if (cur.left != null) stack.push(cur.left);
if (cur.right != null) stack.push(cur.right);
}
return res;
}
}
Maximum Depth of Binary Tree - 104
Easy
Problem
Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the
root node down to the farthest leaf node.
Note: A leaf is a node with no children.
Example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its depth = 3.
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 Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
Binary Tree Maximum Path Sum - 124
Hard
Problem
Given a non-empty binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some
starting node to any node in the tree along the parent-child connections.
The path must contain at least one node and does not need to go through the
root.
Example 1:
Input: [1,2,3]
1
/ \
2 3
Output: 6
Example 2:
Input: [-10,9,20,null,null,15,7]
-10
/ \
9 20
/ \
15 7
Output: 42
Solution
The tricky part is, when returning current value, return value of current node + max(left, right)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int max = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
pathSum(root);
return max;
}
private int pathSum(TreeNode node) {
if (node == null) return 0;
int maxLeft = Math.max(pathSum(node.left), 0);
int maxRight = Math.max(pathSum(node.right), 0);
max = Math.max(max, node.val + maxLeft + maxRight);
return node.val + Math.max(maxLeft, maxRight);
}
}
Maximum Width of Binary Tree - 662
Medium
Problem
Given a binary tree, write a function to get the maximum width of the given
tree. The width of a tree is the maximum width among all levels. The binary
tree has the same structure as a full binary tree, but some nodes are null.
The width of one level is defined as the length between the end-nodes (the
leftmost and right most non-null nodes in the level, where the null nodes
between the end-nodes are also counted into the length calculation.
Example 1:
Input:
1
/ \
3 2
/ \ \
5 3 9
Output: 4
Explanation: The maximum width existing in the third level with the length 4
(5,3,null,9).
Example 2:
Input:
1
/
3
/ \
5 3
Output: 2
Explanation: The maximum width existing in the third level with the length 2
(5,3).
Example 3:
Input:
1
/ \
3 2
/
5
Output: 2
Explanation: The maximum width existing in the second level with the length
2 (3,2).
Example 4:
Input:
1
/ \
3 2
/ \
5 9
/ \
6 7
Output: 8
Explanation:The maximum width existing in the fourth level with the length 8
(6,null,null,null,null,null,null,7).
Note: Answer will in the range of 32-bit signed integer.
Solution
Use a HashMap to keep track of index of each node
If index of current node is n, index of left child is 2n, index of right child is 2n + 1
/**
* 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 widthOfBinaryTree(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
Map<TreeNode, Integer> map = new HashMap<>();
queue.offer(root);
map.put(root, 1);
int maxWidth = 0;
while (!queue.isEmpty()) {
int curSize = queue.size();
int curMin = 0, curMax = 0;
for (int i = 0; i < curSize; i++) {
TreeNode curNode = queue.poll();
int curIdx = map.get(curNode);
if (curNode.left != null) {
map.put(curNode.left, curIdx * 2);
queue.offer(curNode.left);
}
if (curNode.right != null) {
map.put(curNode.right, curIdx * 2 + 1);
queue.offer(curNode.right);
}
if (i == 0) curMin = curIdx;
if (i == curSize - 1) curMax = curIdx;
}
maxWidth = Math.max(maxWidth, curMax - curMin + 1);
}
return maxWidth;
}
}
Invert Binary Tree - 226
Easy
Problem
Invert a binary tree.
Example:
Input:
4
/ \
2 7
/ \ / \
1 3 6 9
Output:
4
/ \
7 2
/ \ / \
9 6 3 1
Trivia:
This problem was inspired by this original tweet by Max Howell:
Google: 90% of our engineers use the software you wrote (Homebrew),
but you can’t invert a binary tree on a whiteboard so f*** off.
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) {
invert(root);
return root;
}
private void invert(TreeNode node) {
if (node == null) return;
TreeNode left = node.left;
node.left = node.right;
node.right = left;
invert(node.left);
invert(node.right);
}
}
Lowest Common Ancestor of a Binary Search Tree - 235
Easy
LCA
Problem
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).”
Given binary search tree: root = [6,2,8,0,4,7,9,null,null,3,5]
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.
Solution
Since it’s a BST, the LCA is the split point of the two nodes, we could easily find it with this property.
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
TreeNode cur = root;
while (cur != null) {
if (cur.val < p.val && cur.val < q.val) {
cur = cur.right;
} else if (cur.val > p.val && cur.val > q.val) {
cur = cur.left;
} else {
return cur;
}
}
return null;
}
}
Lowest Common Ancestor of a Binary Tree - 236
Medium
LCA
Problem
Given a binary tree, find the lowest common ancestor (LCA) of two given
nodes in the tree.
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).”
Given the following binary tree: root = [3,5,1,6,2,0,8,null,null,7,4]
Example 1:
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
Output: 3
Explanation: The LCA of nodes 5 and 1 is 3.
Example 2:
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
Output: 5
Explanation: The LCA of nodes 5 and 4 is 5, since a node can be a descendant
of itself according to the LCA definition.
Note:
All of the nodes' values will be unique.
p and q are different and both values will exist in the binary tree.
Solution
Without the property of BST, we cannot easily find the LCA by a split point.
The idea here is to use a map to point each nodes to their parent nodes.
- Iterate down through the root node until p and q was found, then we have all the ancestor nodes of p & q.
- Add all ancestors of p into a set, and finally iterate up through ancestors of q.
Once a ancestor of q was found in the set, it is the LCA.
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) return null;
Map<TreeNode, TreeNode> map = new HashMap<>();
Stack<TreeNode> stack = new Stack<>();
map.put(root, null);
stack.push(root);
while (!stack.isEmpty()) {
if (map.containsKey(p) && map.containsKey(q)) break;
TreeNode cur = stack.pop();
if (cur.right != null) {
map.put(cur.right, cur);
stack.push(cur.right);
}
if (cur.left != null) {
map.put(cur.left, cur);
stack.push(cur.left);
}
}
Set<TreeNode> set = new HashSet<>();
while (p != null) {
set.add(p);
p = map.get(p);
}
while (q != null) {
if (set.contains(q)) return q;
q = map.get(q);
}
return null;
}
}
Serialize and Deserialize Binary Tree - 297
Hard
Serialization
Problem
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.
Example:
You may serialize the following tree:
1
/ \
2 3
/ \
4 5
as "[1,2,3,null,null,4,5]"
Clarification: The above 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.
Note: Do not use class member/global/static variables to store states. Your
serialize and deserialize algorithms should be stateless.
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 res = new StringBuilder();
seHelper(root, res);
res.deleteCharAt(res.length() - 1);
return res.toString();
}
private void seHelper(TreeNode root, StringBuilder res) {
if (root == null) {
res.append("null,");
return;
}
res.append(root.val).append(",");
seHelper(root.left, res);
seHelper(root.right, res);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
List<String> nodes = Arrays.asList(data.split(","));
Iterator<String> iter = nodes.iterator();
return desHelper(iter);
}
private TreeNode desHelper(Iterator<String> iter) {
String cur = iter.next();
if (cur.equals("null")) return null;
TreeNode node = new TreeNode(Integer.valueOf(cur));
node.left = desHelper(iter);
node.right = desHelper(iter);
return node;
}
}
Construct Binary Search Tree from Preorder Traversal - 1008
Medium
Serialization
Problem
Return the root node of a binary search tree that matches the given preorder
traversal.
(Recall that a binary search tree is a binary tree where for every node, any
descendant of node.left has a value < node.val, and any descendant of
node.right has a value > node.val. Also recall that a preorder traversal
displays the value of the node first, then traverses node.left, then
traverses node.right.)
Example 1:
Input: [8,5,1,7,10,12]
Output: [8,5,10,1,7,null,12]
Note:
1 <= preorder.length <= 100
The values of preorder are distinct.
Solution
The tricky part is to define the upper bound for subtrees
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private int idx = 0;
public TreeNode bstFromPreorder(int[] preorder) {
return helper(preorder, Integer.MAX_VALUE);
}
private TreeNode helper(int[] preorder, int upperBound) {
if (idx >= preorder.length) return null;
TreeNode cur = new TreeNode(preorder[idx++]);
if (idx < preorder.length && preorder[idx] < cur.val) {
cur.left = helper(preorder, cur.val);
}
if (idx < preorder.length && preorder[idx] > cur.val && preorder[idx] < upperBound) {
cur.right = helper(preorder, upperBound);
}
return cur;
}
/* equals to */
// private TreeNode helper(int[] preorder, int upperBound) {
// if (idx >= preorder.length || preorder[idx] > upperBound) return null;
// TreeNode cur = new TreeNode(preorder[idx++]);
// cur.left = helper(preorder, cur.val);
// cur.right = helper(preorder, upperBound);
// return cur;
// }
}
Sort
Merge Intervals - 56
Medium
Interval
Problem
* Given a collection of intervals, merge all overlapping intervals.
*
* Example 1:
* Input: [[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: [[1,4],[4,5]]
* Output: [[1,5]]
* Explanation: Intervals [1,4] and [4,5] are considered overlapping.
Solution
class Solution {
public int[][] merge(int[][] intervals) {
if (intervals.length == 0) return intervals;
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
// Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
// Arrays.sort(intervals, new Comparator<int[]>() {
// public int compare(int[] a, int[] b) {
// return a[0] - b[0];
// }
// });
int[][] res = new int[intervals.length][2];
res[0] = intervals[0];
int idx = 0;
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] <= res[idx][1]) {
res[idx][1] = Math.max(res[idx][1], intervals[i][1]);
} else {
res[++idx] = intervals[i];
}
}
return Arrays.copyOf(res, idx + 1);
}
}
Meeting Rooms - 252
Easy
Interval
Problem
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.
Example 1:
Input: [[0,30],[5,10],[15,20]]
Output: false
Example 2:
Input: [[7,10],[2,4]]
Output: true
Solution
class Solution {
public boolean canAttendMeetings(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] < intervals[i - 1][1]) return false;
}
return true;
}
}
Meeting Rooms II - 253
Medium
Interval
Priority Queue
Problem
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.
Example 1:
Input: [[0, 30],[5, 10],[15, 20]]
Output: 2
Example 2:
Input: [[7,10],[2,4]]
Output: 1
Solution
It’s a good one.
original:
-----
-----
-----
-----
sort by start time:
-----
-----
-----
-----
we want to know if next interval’s start is bigger than someone before, if it’s we can reuse that room.
The point is how to find the room that earlest ending room.
class Solution {
public int minMeetingRooms(int[][] intervals) {
if (intervals.length == 0) return 0;
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
PriorityQueue<Integer[]> minQ = new PriorityQueue<>((a, b) -> a[1] - b[1]);
for (int[] i : intervals) {
// if current interval's start time > smallest end time in the minQ, then we know we can use that room
// otherwise, we need a new room
if (!minQ.isEmpty() && i[0] >= minQ.peek()[1]) minQ.poll();
minQ.offer(new Integer[]{i[0], i[1]});
}
return minQ.size();
}
}
Sort an Array - 912
Medium
Quick Sort
Problem
Given an array of integers nums, sort the array in ascending order.
Example 1:
Input: [5,2,3,1]
Output: [1,2,3,5]
Example 2:
Input: [5,1,1,2,0,0]
Output: [0,0,1,1,2,5]
Note:
1 <= A.length <= 10000
-50000 <= A[i] <= 50000
Solution
class Solution {
public int[] sortArray(int[] nums) {
quickSort(nums, 0, nums.length - 1);
return nums;
}
public void quickSort(int[] nums, int lo, int hi) {
if (lo < hi) {
int pivotIdx = partition(nums, lo, hi);
quickSort(nums, lo, pivotIdx - 1);
quickSort(nums, pivotIdx + 1, hi);
}
}
private int partition(int[] nums, int lo, int hi) {
int pivot = nums[hi];
int idx = lo; // idx that element < pivot
for (int i = lo; i < hi; i++) {
if (nums[i] <= pivot) {
swap(nums, i, idx++);
}
}
swap(nums, hi, idx);
return idx;
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
Kth Largest Element in an Array - 215
Medium
Priority Queue
Quick Select
Problem
Find the kth largest element in an unsorted array. Note that it is the kth
largest element in the sorted order, not the kth distinct element.
Example 1:
Input: [3,2,1,5,6,4] and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
Solution
Naive approach, simply sort the array. Time complexity: O(nlogn)
class Solution {
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);
return nums[nums.length - k];
}
}
Priority queue approach, use a min-heap to keep the first k largest element. Time complexity: O(nlogk)
Note: max-heap comparator: (n1, n2) -> n2 - n1
or Collections.reverseOrder()
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> minQ = new PriorityQueue<>();
for (int i : nums) {
minQ.offer(i);
if (minQ.size() > k) {
minQ.poll();
}
}
return minQ.poll();
}
}
And…Python Hack!
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
return heapq.nlargest(k, nums)[-1]
Quick Select, Time complexity: O(n)
class Solution {
// Quick Select Solution
public int findKthLargest(int[] nums, int k) {
return quickSelect(nums, nums.length - k, 0, nums.length - 1);
}
public int quickSelect(int[] nums, int kth, int lo, int hi) {
if (lo < hi) {
int pi = partition(nums, lo, hi);
if (pi == kth) {
return nums[pi];
} else if (pi < kth) {
return quickSelect(nums, kth, pi + 1, hi);
} else {
return quickSelect(nums, kth, lo, pi - 1);
}
}
return nums[kth];
}
private int partition(int[] nums, int lo, int hi) {
// // Simple Pivot
// int pivot = nums[hi];
// Randomized pivot
Random random = new Random();
int pivotIndex = lo + random.nextInt(hi - lo);
int pivot = nums[pivotIndex];
swap(nums, pivotIndex, hi);
int i = lo - 1;
for (int j = lo; j < hi; j++) {
if (pivot > nums[j]) {
swap(nums, ++i, j);
}
}
swap(nums, i + 1, hi);
return i + 1;
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
K Closest Points to Origin - 973
Medium
Priority Queue
Quick Select
Problem
We have a list of points on the plane. Find the K closest points to the
origin (0, 0).
(Here, the distance between two points on a plane is the Euclidean
distance.)
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]]
(The answer [[-2,4],[3,3]] would also be accepted.)
Note:
1 <= K <= points.length <= 10000
-10000 < points[i][0] < 10000
-10000 < points[i][1] < 10000
Solution
这个题真的写的要被java气死了
arraylist里存int[]都研究好半天,List<Integer[]>还不行,只能写List<int[]>
这就算了,总算是存进去了,以为就这样结束了
结果List<int[]> 转成int[][]又出现了问题: )
行,建个array一个个读了再存进去行吧
结果这个k closest和上面那个kth还不一样,这个是前k个
那现在array初始化又有问题
太气了,这题还是用python写了
class Solution {
public int[][] kClosest(int[][] points, int K) {
Map<Integer, List<int[]>> map = new HashMap<>();
for (int[] point : points) {
int dist = point[0] * point[0] + point[1] * point[1];
List<int[]> tmp = map.getOrDefault(dist, new ArrayList<int[]>());
tmp.add(point);
map.put(dist, tmp);
}
PriorityQueue<Integer> pq = new PriorityQueue<>((n1, n2) -> n2 - n1);
for (int i : map.keySet()) {
pq.add(i);
if (pq.size() > K) {
pq.poll();
}
}
// 辣鸡java 这个arraylist转int[][]写的我想砸电脑
// int res[][] = new int[points.length][2]; // 这样写是答案是不对的,会多出好多[0, 0]
// 哦我以为相同距离是不算的,大半夜脑子不清醒了,那直接初始化长度k就行了
int res[][] = new int[K][2];
int index = 0;
while (!pq.isEmpty()) {
List<int[]> list = map.get(pq.poll());
for (int i = 0; index < K && i < list.size(); i++) {
res[index] = list.get(i);
}
index++;
}
return res;
}
}
python用这个思路写不要再简单
不过写到这里也发现按照上面kth那题的思路写确实有问题
用map存的话取前k个只能粗暴的根据res的长度来判断要不要继续往res里加
这样的话多了这么多麻烦确实还不如答案里的直接sort,复杂度也就是nlogn比上nlogk,省好多事呢
class Solution:
def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
map = {}
for point in points:
dist = point[0]**2 + point[1]**2
tmp = map.get(dist, [])
tmp.append(point)
map[dist] = tmp
res = []
for i in heapq.nsmallest(K, list(map.keys())):
# python没有flatten也有点难受
for j in map[i]:
if len(res) < K:
res.append(j)
else:
return res
return res
最后发现这个题做这么难受是题目的理解有问题
最开始以为k closest是第k个,结果是前k个
前k个的话就导致一个很严重的问题,我是按照第k个来想的,所以才用了hashmap存了所有点的距离
如果是前k个的话,那pq在这里的作用就和上面kth的完全一样了,最后得到第k个点的距离就行
下面是正确理解题意后的pq解法
class Solution {
public int[][] kClosest(int[][] points, int K) {
PriorityQueue<Integer> pq = new PriorityQueue<>((n1, n2) -> n2 - n1);
for (int[] point : points) {
int dist = point[0] * point[0] + point[1] * point[1];
pq.add(dist);
if (pq.size() > K) {
pq.poll();
}
}
int kthDist = pq.poll();
int[][] res = new int[K][2];
int x = 0;
for (int i = 0; i < points.length; i++) {
int[] point = points[i];
if (point[0] * point[0] + point[1] * point[1] <= kthDist) {
res[x++] = point;
}
}
return res;
}
}
接下来的是java的sort的写法的非常的dry的code
class Solution {
public int[][] kClosest(int[][] points, int K) {
int[] dists = new int[points.length];
for (int i = 0; i < points.length; i++) {
int[] point = points[i];
dists[i] = point[0] * point[0] + point[1] * point[1];
}
Arrays.sort(dists);
int kthDist = dists[K - 1];
int[][] res = new int[K][2];
int x = 0;
for (int i = 0; i < points.length; i++) {
int[] point = points[i];
if (point[0] * point[0] + point[1] * point[1] <= kthDist) {
res[x++] = point;
}
}
return res;
}
}
然而lc里跑起来sort的解法比pq快?而且还快不少:)
damn..看了一眼讨论区,还能这么干的吗..
class Solution {
public int[][] kClosest(int[][] points, int K) {
Arrays.sort(points, (p1, p2) -> p1[0] * p1[0] + p1[1] * p1[1] - p2[0] * p2[0] - p2[1] * p2[1]);
return Arrays.copyOfRange(points, 0, K);
}
}
java对不起
Quick Select
class Solution {
/* Quick Select */
public int[][] kClosest(int[][] points, int K) {
quickSelect(points, K, 0, points.length - 1);
return Arrays.copyOf(points, K);
}
public void quickSelect(int[][] nums, int k, int lo, int hi) {
if (lo < hi) {
int pi = partition(nums, lo, hi);
if (pi == k - 1) return;
if (pi < k - 1) quickSelect(nums, k, pi + 1, hi);
else quickSelect(nums, k, lo, pi - 1);
}
}
private int partition(int[][] nums, int lo, int hi) {
int[] pivot = nums[hi];
int idx = lo;
for (int i = lo; i < hi; i++) {
if (distance(nums[i]) <= distance(pivot)) {
swap(nums, i, idx);
idx++;
}
}
swap(nums, idx, hi);
return idx;
}
private void swap(int[][] nums, int i, int j) {
int[] tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
private int distance(int[] point) {
return point[0] * point[0] + point[1] * point[1];
}
}
Backtrack
Generate Parentheses - 22
Medium
Problem
Given n pairs of parentheses, write a function to generate all combinations
of well-formed parentheses.
For example, given n = 3, a solution set is:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
Solution
class Solution {
/* runtime / space : O(4^n / sqrt(n)) */
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
helper(n, 0, new StringBuilder(), res);
return res;
}
private void helper(int l, int r, StringBuilder cur, List<String> res) {
if (l == 0 && r == 0) {
res.add(cur.toString());
return;
}
if (l > 0) {
cur.append("(");
helper(l - 1, r + 1, cur, res);
cur.deleteCharAt(cur.length() - 1);
}
if (r > 0) {
cur.append(")");
helper(l, r - 1, cur, res);
cur.deleteCharAt(cur.length() - 1);
}
}
}
Combination Sum - 39
Medium
Problem
Given a set of candidate numbers (candidates) (without duplicates) and a
target number (target), find all unique combinations in candidates where the
candidate numbers sums to target.
The same repeated number may be chosen from candidates unlimited number of
times.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [2,3,6,7], target = 7,
A solution set is:
[
[7],
[2,2,3]
]
Example 2:
Input: candidates = [2,3,5], target = 8,
A solution set is:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
Solution
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new LinkedList<>();
find(candidates, target, 0, new ArrayList<>(), res);
return res;
}
private void find(int[] candidates, int target, int index, List<Integer> comb, List<List<Integer>> res) {
if (target < 0) return;
if (target == 0) {
res.add(new ArrayList<>(comb));
return;
}
for (int i = index; i < candidates.length; i++) {
comb.add(candidates[i]);
find(candidates, target - candidates[i], i, comb, res);
comb.remove(comb.size() - 1);
}
}
}
Combination Sum II - 40
Medium
Problem
Given a collection of candidate numbers (candidates) and a target number
(target), find all unique combinations in candidates where the candidate
numbers sums to target.
Each number in candidates may only be used once in the combination.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
Example 2:
Input: candidates = [2,5,2,1,2], target = 5,
A solution set is:
[
[1,2,2],
[5]
]
Solution
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
List<List<Integer>> res = new LinkedList<>();
find(candidates, target, 0, new ArrayList<>(), res);
return res;
}
private void find(int[] candidates, int target, int index, List<Integer> comb, List<List<Integer>> res) {
if (target < 0) return;
if (target == 0) {
res.add(new ArrayList<>(comb));
return;
}
for (int i = index; i < candidates.length; i++) {
if (i > index && candidates[i] == candidates[i - 1]) continue; // tql
comb.add(candidates[i]);
find(candidates, target - candidates[i], i + 1, comb, res);
comb.remove(comb.size() - 1);
}
}
}
Permutations - 46
Medium
Problem
Given a collection of distinct integers, return all possible permutations.
Example:
Input: [1,2,3]
Output:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
Solition
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
helper(nums, 0, res);
return res;
}
private void helper(int[] nums, int idx, List<List<Integer>> res) {
if (idx == nums.length - 1) {
List<Integer> cur = new ArrayList<>();
for (int i : nums) {
cur.add(i);
}
res.add(cur);
return;
}
for (int i = idx; i < nums.length; i++) {
swap(nums, idx, i);
helper(nums, idx + 1, res); // <-- idx
swap(nums, idx, i);
}
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
Permutations II - 47
Medium
Problem
Given a collection of numbers that might contain duplicates, return all
possible unique permutations.
Example:
Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
Solution
Only need to make sure that the same element will not be permuted twice
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
helper(nums, 0, res);
return res;
}
private void helper(int[] nums, int idx, List<List<Integer>> res) {
if (idx == nums.length - 1) {
List<Integer> cur = new ArrayList<>();
for (int i : nums) {
cur.add(i);
}
res.add(cur);
return;
}
Set<Integer> visited = new HashSet<>(); // <--
for (int i = idx; i < nums.length; i++) {
if (visited.contains(nums[i])) continue; // <--
visited.add(nums[i]);
swap(nums, idx, i);
helper(nums, idx + 1, res);
swap(nums, idx, i);
}
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
Subsets - 78
Medium
Problem
Given a set of distinct integers, nums, return all possible subsets
(the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: nums = [1,2,3]
Output:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
Solution
class Solution {
// Recursive
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
helper(nums, 0, new ArrayList<>(), res);
return res;
}
private void helper(int[] nums, int idx, List<Integer> cur, List<List<Integer>> res) {
if (idx == nums.length) {
res.add(new ArrayList<>(cur));
return;
}
helper(nums, idx + 1, cur, res);
cur.add(nums[idx]);
helper(nums, idx + 1, cur, res);
cur.remove(cur.size() - 1);
}
}
Alternative way for the helper method, can easily be optmized for follow up (with duplicate element)
private void helper(int[] nums, int idx, List<Integer> cur, List<List<Integer>> res) {
if (idx <= nums.length) { <--
res.add(new ArrayList<>(cur));
}
for (int i = idx; i < nums.length; i++) {
// if (i > idx && nums[i] == nums[i - 1]) continue; // rm dup
cur.add(nums[i]);
helper(nums, i + 1, cur, res);
cur.remove(cur.size() - 1);
}
}
Subsets II - 90
Medium
Problem
Given a collection of integers that might contain duplicates, nums, return
all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: [1,2,2]
Output:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
Solution
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
helper(nums, 0, new ArrayList<>(), res);
return res;
}
private void helper(int[] nums, int idx, List<Integer> cur, List<List<Integer>> res) {
if (idx <= nums.length) {
res.add(new ArrayList<>(cur));
}
for (int i = idx; i < nums.length; i++) {
if (i > idx && nums[i] == nums[i - 1]) continue; // rm dup
cur.add(nums[i]);
helper(nums, i + 1, cur, res);
cur.remove(cur.size() - 1);
}
}
}
Word Break - 139
Medium
Memoization
Problem
Given a non-empty string s and a dictionary wordDict containing a list of
non-empty words, determine if s can be segmented into a space-separated
sequence of one or more dictionary words.
Note:
The same word in the dictionary may be reused multiple times in the
segmentation.
You may assume the dictionary does not contain duplicate words.
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
Solution
class Solution {
Map<String, Boolean> memo; // memoization
public boolean wordBreak(String s, List<String> wordDict) {
this.memo = new HashMap<>();
return find(s, new HashSet<>(wordDict));
}
private boolean find(String s, Set<String> dict) {
if (s.equals("")) return true;
if (memo.containsKey(s)) return memo.get(s); // memoized
for (int i = 1; i <= s.length(); i++) { // be careful about the index
if (dict.contains(s.substring(0, i)) && find(s.substring(i), dict)) {
memo.put(s, true); // memoize
return true;
}
}
memo.put(s, false); // memoize
return false;
}
}
Recursion stack
/*
"catsandog"
["cats", "dog", "sand", "and", "cat"]
>>> c
>>> ca
>>> cat
>>> s
>>> sa
>>> san
>>> sand
>>> o
>>> og
og <-- false
>>> sando
>>> sandog
sandog <-- false
>>> cats
>>> a
>>> an
>>> and
og -memo-> false
>>> ando
>>> andog
andog <-- false
>>> catsa
>>> catsan
>>> catsand
>>> catsando
>>> catsandog
catsandog <-- false
*/
/*
"aaaaab"
["a","aa","aaa"]
>>> a
>>> a
>>> a
>>> a
>>> a
>>> b
b <-- false
>>> ab
ab <-- false
>>> aa
b -memo-> false
>>> aab
aab <-- false
>>> aa
ab -memo-> false
>>> aaa
b -memo-> false
>>> aaab
aaab <-- false
>>> aa
aab -memo-> false
>>> aaa
ab -memo-> false
>>> aaaa
>>> aaaab
aaaab <-- false
>>> aa
aaab -memo-> false
>>> aaa
aab -memo-> false
>>> aaaa
>>> aaaaa
>>> aaaaab
aaaaab <-- false
*/
Word Break II - 140
Hard
Problem
Given a non-empty string s and a dictionary wordDict containing a list of
non-empty words, add spaces in s to construct a sentence where each word is
a valid dictionary word. Return all such possible sentences.
Note:
The same word in the dictionary may be reused multiple times in the
segmentation.
You may assume the dictionary does not contain duplicate words.
Example 1:
Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
"cats and dog",
"cat sand dog"
]
Example 2:
Input:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
"pine apple pen apple",
"pineapple pen apple",
"pine applepen apple"
]
Explanation: Note that you are allowed to reuse a dictionary word.
Example 3:
Input:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]
Solution
TODO
BFS / DFS
Minimum Path Sum - 64
Medium
DFS
Memoization
Problem
Given a m x n grid filled with non-negative numbers, find a path from top
left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
Example:
Input:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.
Solution
Since for each step, we can only choose to go down or right, the grapth itself can be treat as a kind of binary tree.
The difference with regular tree traversal is that, we cannot simply return at a random null node, because we just want the bottom right node.
So at the null node, instead of return 0, we return Integer.MAX_VALUE, this will ensure the the returnd route will always pass the bottom right node.
class Solution {
public int minPathSum(int[][] grid) {
if (grid.length == 0) return 0;
return dfs(grid, 0, 0);
}
// it's a kind of binary tree
// how to ensure reach the bottom right note?
private int dfs(int[][] grid, int r, int c) {
if (r >= grid.length || c >= grid[0].length) return Integer.MAX_VALUE;
if (r == grid.length - 1 && c == grid[0].length - 1) {
return grid[r][c];
}
return Math.min(dfs(grid, r + 1, c), dfs(grid, r, c + 1)) + grid[r][c];
}
}
With can avoid access a node twice with memoization
class Solution {
private int[][] memo;
public int minPathSum(int[][] grid) {
if (grid.length == 0) return 0;
memo = new int[grid.length][grid[0].length];
return dfs(grid, 0, 0);
}
private int dfs(int[][] grid, int r, int c) {
if (r >= grid.length || c >= grid[0].length) {
return Integer.MAX_VALUE;
}
if (r == grid.length - 1 && c == grid[0].length - 1) {
return grid[r][c];
}
if (memo[r][c] != 0) {
return memo[r][c];
}
memo[r][c] = Math.min(dfs(grid, r + 1, c), dfs(grid, r, c + 1)) + grid[r][c];
return memo[r][c];
}
}
Word Ladder - 127
Medium
BFS
Problem
Given two words (beginWord and endWord), and a dictionary's word list, find
the length of shortest transformation sequence from beginWord to endWord,
such that:
Only one letter can be changed at a time.
Each transformed word must exist in the word list. Note that beginWord is
not a transformed word.
Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" ->
"dog" -> "cog",
return its length 5.
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible
transformation.
Solution
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Map<String, List<String>> wildDict = new HashMap<>();
Map<String, Integer> discoverMap = new HashMap<>();
Queue<String> q = new LinkedList<>();
// construct wildcard dict
for (String word : wordList) {
for (int i = 0; i < word.length(); i++) {
String w = word.substring(0, i) + "*" + word.substring(i + 1);
List<String> list = wildDict.getOrDefault(w, new ArrayList<>());
list.add(word);
wildDict.put(w, list);
}
}
// bfs
discoverMap.put(beginWord, 1);
q.offer(beginWord);
while (!q.isEmpty()) {
String word = q.poll();
for (int i = 0; i < word.length(); i++) {
// find all adjacent words
String wildWord = word.substring(0, i) + "*" + word.substring(i + 1);
if (wildDict.containsKey(wildWord)) {
for (String adjWord : wildDict.get(wildWord)) {
int level = discoverMap.get(word) + 1;
if (adjWord.equals(endWord)) {
return level;
}
// if not discovered yet, add to discover map
if (!discoverMap.containsKey(adjWord)) {
discoverMap.put(adjWord, level);
q.offer(adjWord);
}
}
}
}
}
return 0;
}
}
Word Ladder II - 126
Hard
BFS
Problem
Given two words (beginWord and endWord), and a dictionary's word list, find
all shortest transformation sequence(s) from beginWord to endWord, such
that:
Only one letter can be changed at a time
Each transformed word must exist in the word list. Note that beginWord is
not a transformed word.
Note:
Return an empty list if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: []
Explanation: The endWord "cog" is not in wordList, therefore no possible
transformation.
Solution
class Solution {
/* BFS with Q */
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
List<List<String>> res = new ArrayList<>();
Set<String> wordSet = new HashSet<>(wordList);
if (!wordSet.contains(endWord)) return res;
Map<String, List<String>> wordMap = new HashMap<>();
for (String word : wordList) {
for (int i = 0; i < word.length(); i++) {
String wildCard = word.substring(0, i) + "*" + word.substring(i + 1);
List<String> list = wordMap.getOrDefault(wildCard, new ArrayList<>());
list.add(word);
wordMap.put(wildCard, list);
}
}
Queue<List<String>> q = new LinkedList<>();
q.offer(new ArrayList<>(Arrays.asList(beginWord)));
while (!q.isEmpty()) {
int size = q.size();
Set<String> curWords = new HashSet<>(); // words discovered this layer
for (int x = 0; x < size; x++) {
List<String> curLadder = q.poll();
String lastWord = curLadder.get(curLadder.size() - 1);
if (lastWord.equals(endWord)) {
res.add(curLadder);
} else {
for (int i = 0; i < lastWord.length(); i++) {
String wildCard = lastWord.substring(0, i) + "*" + lastWord.substring(i + 1);
if (wordMap.containsKey(wildCard)) {
for (String curWord : wordMap.get(wildCard)) {
if (wordSet.contains(curWord)) {
List<String> newLadder = new ArrayList<>(curLadder);
newLadder.add(curWord);
q.offer(newLadder);
curWords.add(curWord);
}
}
}
}
}
}
if (!res.isEmpty()) return res;
wordSet.removeAll(curWords);
}
return res;
}
}
class Solution {
/* BFS with Map */
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
List<List<String>> res = new ArrayList<>();
Set<String> wordSet = new HashSet<>(wordList);
if (!wordSet.contains(endWord)) return res;
Map<String, List<List<String>>> layer = new HashMap<>(); // current layer's word -> all path to word, z -> [[a,b,z],[x,y,z]]
List<List<String>> tmp = new ArrayList<>();
tmp.add(new ArrayList<>(Arrays.asList(beginWord)));
layer.put(beginWord, tmp); // beginWord -> [[beginWord]]
while (!layer.isEmpty()) {
Map<String, List<List<String>>> nextLayer = new HashMap<>();
for (String s : layer.keySet()) {
if (s.equals(endWord)) {
for (List<String> ladder : layer.get(s)) res.add(ladder);
} else {
char[] chars = s.toCharArray(); // toCharArray and avoid string concatenation
for (int i = 0; i < s.length(); i++) { // try to replace each char
char org = chars[i];
for (char c = 'a'; c <= 'z'; c++) {
chars[i] = c;
String word = new String(chars);
if (wordSet.contains(word)) {
List<List<String>> ladders = nextLayer.getOrDefault(word, new ArrayList<>());
for (List<String> ladder : layer.get(s)) {
List<String> tmpLad = new ArrayList<>(ladder);
tmpLad.add(word);
ladders.add(tmpLad);
}
nextLayer.put(word, ladders);
}
}
chars[i] = org;
}
}
}
if (!res.isEmpty()) return res;
wordSet.removeAll(nextLayer.keySet());
layer = nextLayer;
}
return res;
}
}
Number of Islands - 200
Medium
DFS
Problem
Given a 2d grid map of '1's (land) and '0's (water), count 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:
11110
11010
11000
00000
Output: 1
Example 2:
Input:
11000
11000
00100
00011
Output: 3
Solution
class Solution {
public int numIslands(char[][] grid) {
int res = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == '1') {
res++;
dfs(grid, r, c);
}
}
}
return res;
}
private void dfs(char[][] grid, int r, int c) {
if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length || grid[r][c] != '1') {
return;
}
grid[r][c] = '0';
dfs(grid, r + 1, c);
dfs(grid, r - 1, c);
dfs(grid, r, c + 1);
dfs(grid, r, c - 1);
}
}
Number of Distinct Islands - 694
Medium
DFS
Problem
Given a non-empty 2D array grid of 0's and 1's, 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.
Count the number of distinct islands. An island is considered to be the
same as another if and only if one island has the same shape as another
island (and not rotated or reflected).
Notice that:
11
1
and
1
11
are considered different island, because we do not consider
reflection / rotation.
Example 1:
Input:
[
[1,1,0,0,1],
[1,0,0,0,0],
[1,1,0,0,1],
[0,1,0,1,1]
]
Output: 3
Explanation:
11 1 1
1 11
11
1
Example 2:
Input:
[
[1,1,0,0,0],
[1,1,0,0,0],
[0,0,0,1,1],
[0,0,0,1,1]
]
Output: 1
Solution
Since we do not consider reflection / rotation, we can simply log the route of DFS.
Because DFS will always starts with the top left point of an island, number of distinct islands = number of distinct routes
public class Solution {
public int numberofDistinctIslands(int[][] grid) {
Set<String> set = new HashSet<>();
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == 1) {
StringBuilder route = new StringBuilder();
dfs(grid, r, c, route);
set.add(route.toString());
}
}
}
return set.size();
}
private void dfs(int[][] grid, int r, int c, StringBuilder route) {
if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length || grid[r][c] == 0) return;
grid[r][c] = 0;
route.append("u");
dfs(grid, r - 1, c, route);
route.append("d");
dfs(grid, r + 1, c, route);
route.append("l");
dfs(grid, r, c - 1, route);
route.append("r");
dfs(grid, r, c + 1, route);
}
}
Number of Closed Islands - 1254
Medium
DFS
Problem
Given a 2D grid consists of 0s (land) and 1s (water).
An island is a maximal 4-directionally connected group of 0s
and a closed island is an island totally (all left, top, right, bottom)
surrounded by 1s.
Return the number of closed islands.
Example 1:
Input: grid = [
[1,1,1,1,1,1,1,0],
[1,0,0,0,0,1,1,0],
[1,0,1,0,1,1,1,0],
[1,0,0,0,0,1,0,1],
[1,1,1,1,1,1,1,0]
]
Output: 2
Explanation:
Islands in gray are closed because they are completely
surrounded by water (group of 1s).
Example 2:
Input: grid = [
[0,0,1,0,0],
[0,1,0,1,0],
[0,1,1,1,0]
]
Output: 1
Example 3:
Input: grid = [
[1,1,1,1,1,1,1],
[1,0,0,0,0,0,1],
[1,0,1,1,1,0,1],
[1,0,1,0,1,0,1],
[1,0,1,1,1,0,1],
[1,0,0,0,0,0,1],
[1,1,1,1,1,1,1]
]
Output: 2
Constraints:
1 <= grid.length, grid[0].length <= 100
0 <= grid[i][j] <=1
Solution
Exclude connected group of 0s on the corners because they are not closed island.
Return number of connected component of 0s on the grid.
class Solution {
public int closedIsland(int[][] grid) {
int res = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == 0 && isClose(grid, r, c)) {
res++;
}
}
}
return res;
}
private boolean isClose(int[][] grid, int r, int c) {
if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length) return false;
if (grid[r][c] == 1) return true;
grid[r][c] = 1;
// & instead of && !!!
// "&" will evaluate both side even the left part is false
// "&&" will ignore the right part if the left part is false
return isClose(grid, r + 1, c) & isClose(grid, r - 1, c) & isClose(grid, r, c + 1) & isClose(grid, r, c - 1);
}
}
Course Schedule - 207
Medium
Topological Sort
Problem
There are a total of n courses you have to take, labeled from 0 to n-1.
Some courses may have prerequisites, for example to take course 0 you have
to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it
possible for you to finish all courses?
Example 1:
Input: 2, [[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: 2, [[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.
Note:
The input prerequisites is a graph represented by a list of edges, not
adjacency matrices. Read more about how a graph is represented.
You may assume that there are no duplicate edges in the input
prerequisites.
Solution
class Solution {
/* Topological Sort Based on DFS Circle Detection */
public boolean canFinish(int numCourses, int[][] prerequisites) {
Map<Integer, Set<Integer>> adjMap = new HashMap<>(); // course -> preqs
Map<Integer, Integer> courses = new HashMap<>(); // 0: not visited, 1: visiting, 2: visited
for (int[] pre : prerequisites) {
// assume all pres are pair of 2
Set<Integer> set = adjMap.getOrDefault(pre[0], new HashSet<>());
set.add(pre[1]);
adjMap.put(pre[0], set);
courses.put(pre[0], 0);
courses.put(pre[1], 0);
}
for (Integer course : courses.keySet()) {
if (hasCircle(adjMap, courses, course)) return false;
}
return true;
}
private boolean hasCircle(Map<Integer, Set<Integer>> adjMap, Map<Integer, Integer> vertices, Integer vertice) {
if (vertices.get(vertice) == 2) return false;
if (vertices.get(vertice) == 1) return true;
vertices.put(vertice, 1); // mark as visiting
if (adjMap.containsKey(vertice)) { // if this vertice has forward edges (has preqs)
for (Integer preq : adjMap.get(vertice)) {
if (hasCircle(adjMap, vertices, preq)) return true;
}
}
vertices.put(vertice, 2); // mark as visited
return false;
}
}
class Solution {
/* yet another solution */
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses <= 0) return true;
Queue<Integer> queue = new LinkedList<>();
int[] todo = new int[numCourses];
for (int i = 0; i < prerequisites.length; i++) {
todo[prerequisites[i][0]]++;
}
for (int i = 0; i < numCourses; i++) {
if (todo[i] == 0) {
queue.add(i);
}
}
while (!queue.isEmpty()) {
int pre = queue.remove();
for (int i = 0; i < prerequisites.length; i++) {
if (pre == prerequisites[i][1]) {
if (--todo[prerequisites[i][0]] == 0) {
queue.add(prerequisites[i][0]);
}
}
}
}
for (int i = 0; i < numCourses; i++) {
if (todo[i] != 0) return false;
}
return true;
}
}
Course Schedule II - 210
Medium
Topological Sort
Problem
There are a total of n courses you have to take, labeled from 0 to n-1.
Some courses may have prerequisites, for example to take course 0 you have
to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, return
the ordering of courses you should take to finish all courses.
There may be multiple correct orders, you just need to return one of them.
If it is impossible to finish all courses, return an empty array.
Example 1:
Input: 2, [[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 is [0,1] .
Example 2:
Input: 4, [[1,0],[2,0],[3,1],[3,2]]
Output: [0,1,2,3] or [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 and 2 should be taken after you
finished course 0.
So one correct course order is [0,1,2,3]. Another correct ordering is
[0,2,1,3] .
Note:
The input prerequisites is a graph represented by a list of edges, not
adjacency matrices. Read more about how a graph is represented.
You may assume that there are no duplicate edges in the input
prerequisites.
Solution
class Solution {
int[] order;
int idx;
public int[] findOrder(int numCourses, int[][] prerequisites) {
order = new int[numCourses];
idx = 0;
Map<Integer, Set<Integer>> adjMap = new HashMap<>(); // course -> preqs
Map<Integer, Integer> courses = new HashMap<>(); // 0: not visited, 1: visiting, 2: visited
for (int[] pre : prerequisites) {
// assume all pres are pair of 2
Set<Integer> set = adjMap.getOrDefault(pre[0], new HashSet<>());
set.add(pre[1]);
adjMap.put(pre[0], set);
courses.put(pre[0], 0);
courses.put(pre[1], 0);
}
for (Integer course : courses.keySet()) {
if (hasCircle(adjMap, courses, course)) return new int[]{};
}
for (int i = 0; idx < numCourses && i < numCourses; i++) { // handle courses that dont have preq
if (!courses.containsKey(i)) order[idx++] = i;
}
return order;
}
private boolean hasCircle(Map<Integer, Set<Integer>> adjMap, Map<Integer, Integer> vertices, Integer vertice) {
if (vertices.get(vertice) == 2) return false;
if (vertices.get(vertice) == 1) return true;
vertices.put(vertice, 1); // mark as visiting
if (adjMap.containsKey(vertice)) { // if this vertice has forward edges (has preqs)
for (Integer preq : adjMap.get(vertice)) {
if (hasCircle(adjMap, vertices, preq)) return true;
}
}
vertices.put(vertice, 2); // mark as visited
order[idx++] = vertice; // add course to order
return false;
}
}
Dynamic Programming
Trapping Rain Water - 42
Hard
Problem
Given n non-negative integers representing an elevation map where the width
of each bar is 1, compute how much water it is able to trap after raining.
The above elevation map 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. Thanks
Marcos for contributing this image!
Example:
Input: [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Solution
class Solution {
/* DP */
public int trap(int[] height) {
// for each index, amount of rain it can trap = Min(max height of left, max height of right) - height of itself
// so we want to know, for each index, max height of left so far, and max height of right so far
int[] maxLeft = new int[height.length];
int[] maxRight = new int[height.length];
for (int i = 0; i < height.length; i++) {
if (i == 0) maxLeft[i] = height[i];
else maxLeft[i] = Math.max(height[i], maxLeft[i - 1]);
}
for (int i = height.length - 1; i >= 0; i--) {
if (i == height.length - 1) maxRight[i] = height[i];
else maxRight[i] = Math.max(height[i], maxRight[i + 1]);
}
int res = 0;
for (int i = 0; i < height.length; i++) {
res += Math.min(maxLeft[i], maxRight[i]) - height[i];
}
return res;
}
}
class Solution {
/* Two Pointer */
public int trap(int[] height) {
int res = 0;
int maxLeft = 0, maxRight = 0;
int le = 0, ri = height.length - 1;
while (le <= ri) {
if (maxLeft <= maxRight) { // if upper bound constrained by left side
if (height[le] >= maxLeft) maxLeft = height[le];
else res += maxLeft - height[le];
le++;
} else { // else if upper bound constrained by right side
if (height[ri] >= maxRight) maxRight = height[ri];
else res += maxRight - height[ri];
ri--;
}
}
return res;
}
}
Jump Game - 55
Medium
Greedy
Problem
Given an array of non-negative integers, you are initially positioned at the
first index of the array.
Each element in the array represents your maximum jump length at that
position.
Determine if you are able to reach the last index.
Example 1:
Input: [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: [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 last index.
Solution
class Solution {
public boolean canJump(int[] nums) {
if (nums.length == 0) return true;
int canDo = nums[0];
int i = 0;
while (canDo > 0 && i < nums.length - 1) {
canDo = Math.max(--canDo, nums[++i]);
}
return i == nums.length - 1;
}
}
Climbing Stairs - 70
Easy
Problem
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can
you climb to the top?
Note: Given n will be a positive integer.
Example 1:
Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps
Example 2:
Input: 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
Solution
class Solution {
/* Recursive Solution */
public int climbStairs(int n) {
if (n == 0 || n == 1) return 1;
return climbStairs(n - 1) + climbStairs(n - 2);
}
/* Recursive Solutoin with Memo */
private Map<Integer, Integer> memo = new HashMap<>();
public int climbStairs(int n) {
if (n == 0 || n == 1) return 1;
if (memo.containsKey(n)) return memo.get(n);
int stairs = climbStairs(n - 1) + climbStairs(n - 2);
memo.put(n, stairs);
return stairs;
}
}
class Solution {
/* Bottom Up Solution */
public int climbStairs(int n) {
if (n == 1) return 1;
if (n == 2) return 2;
int n_2 = 1;
int n_1 = 2;
for (int i = 3; i <= n; i++) {
int curWays = n_1 + n_2;
n_2 = n_1;
n_1 = curWays;
}
return n_1;
}
}
Maximal Square - 221
Medium
Problem
Given a 2D binary matrix filled with 0's and 1's, find the largest square
containing only 1's and return its area.
Example:
Input:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Output: 4
Solution
dp(i, j) = min(dp(i − 1, j), dp(i − 1, j − 1), dp(i, j − 1)) + 1
Use dp[r][c] to represent the state of matrix[r - 1][c - 1] could save us from handling boundaries
class Solution {
public int maximalSquare(char[][] matrix) {
if (matrix.length == 0) return 0;
int[][] dp = new int[matrix.length + 1][matrix[0].length + 1];
int max = 0;
for (int r = 1; r <= matrix.length; r++) {
for (int c = 1; c <= matrix[0].length; c++) {
if (matrix[r - 1][c - 1] == '1') {
dp[r][c] = Math.min(Math.min(dp[r - 1][c], dp[r][c - 1]), dp[r - 1][c - 1]) + 1;
max = Math.max(max, dp[r][c]);
}
}
}
return max * max;
}
}
Ugly Number - 263
Easy
Problem
Write a program to check whether a given number is an ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5.
Example 1:
Input: 6
Output: true
Explanation: 6 = 2 × 3
Example 2:
Input: 8
Output: true
Explanation: 8 = 2 × 2 × 2
Example 3:
Input: 14
Output: false
Explanation: 14 is not ugly since it includes another prime factor 7.
Note:
1 is typically treated as an ugly number.
Input is within the 32-bit signed integer range: [−2^31, 2^31 − 1].
Solution
class Solution {
public boolean isUgly(int num) {
if (num <= 0) return false;
while (num != 1) {
if (num % 2 == 0) {
num /= 2;
} else if (num % 3 == 0) {
num /= 3;
} else if (num % 5 == 0) {
num /= 5;
} else {
return false;
}
}
return true;
}
}
Ugly Number II - 264
Medium
Problem
Write a program to find the n-th ugly number.
Ugly numbers are positive numbers whose prime factors *only* include 2, 3, 5.
Example:
Input: n = 10
Output: 12
Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.
Note:
1 is typically treated as an ugly number.
n does not exceed 1690.
Solution
The ugly-number sequence is 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, …
Because every number can only be divided by 2, 3, 5
We can get three subsequences by multiply the ugly-sequence itself (1, 2, 3, 5, … ) with 2, 3, 5
Then we use similar merge method as merge sort, to get every ugly number from the three subsequences
class Solution {
public int nthUglyNumber(int n) {
int[] ugly = new int[n];
ugly[0] = 1;
int idx2 = 0, idx3 = 0, idx5 = 0;
int num2 = 2, num3 = 3, num5 = 5;
for (int i = 1; i < n; i++) {
int min = Math.min(Math.min(num2, num3), num5);
ugly[i] = min;
// use if to avoid duplicate num
if (min == num2) {
num2 = ugly[++idx2] * 2;
}
if (min == num3) {
num3 = ugly[++idx3] * 3;
}
if (min == num5) {
num5 = ugly[++idx5] * 5;
}
}
return ugly[n - 1];
}
}
Longest Increasing Subsequence - 300
Medium
Subsequence
Problem
Given an unsorted array of integers, find the length of longest increasing
subsequence.
Example:
Input: [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.
Note:
There may be more than one LIS combination, it is only necessary for you to
return the length.
Your algorithm should run in O(n^2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?
Solution
class Solution {
public int lengthOfLIS(int[] nums) {
int[] maxTill = new int[nums.length];
int max = 0;
for (int i = 0; i < nums.length; i++) {
maxTill[i] = 1; // sequence with only itself
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j] && maxTill[i] < maxTill[j] + 1) { // if num @i > num @j && length of seq @j + 1 (1 is i itself) > seq @i
maxTill[i] = maxTill[j] + 1;
}
}
max = Math.max(max, maxTill[i]);
}
return max;
}
}
Longest Palindromic Subsequence - 516
Medium
Memoization
Subsequence
Problem
Given a string s, find the longest palindromic subsequence's length in s.
You may assume that the maximum length of s is 1000.
Example 1:
Input:
"bbbab"
Output:
4
One possible longest palindromic subsequence is "bbbb".
Example 2:
Input:
"cbbd"
Output:
2
One possible longest palindromic subsequence is "bb".
Solution
public class Solution {
public int longestPalindromeSubseq(String s) {
int[][] dp = new int[s.length()][s.length()];
for (int i = s.length() - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i+1; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = Math.max(dp[i+1][j-1] + 2, Math.max(dp[i+1][j], dp[i][j-1]));
} else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
}
}
return dp[0][s.length()-1];
}
}
Longest Common Subsequence - 1143
Medium
Memoization
Subsequence
Problem
Given two strings text1 and text2, return the length of their longest common
subsequence.
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. (eg, "ace" is a subsequence of "abcde" while "aec" is
not). A common subsequence of two strings is a subsequence that is common to
both strings.
If there is no common subsequence, return 0.
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 <= 1000
1 <= text2.length <= 1000
The input strings consist of lowercase English characters only.
Solution
Found out that the assignment operator in Java returns the assigned value
..
class Solution {
private Integer[][] memo;
public int longestCommonSubsequence(String text1, String text2) {
this.memo = new Integer[text1.length()][text2.length()];
return helper(text1, text2, 0, 0);
}
private int helper(String text1, String text2, int i, int j) {
if (i == text1.length() || j == text2.length()) return 0;
if (memo[i][j] != null) return memo[i][j];
if (text1.charAt(i) == text2.charAt(j)) {
return memo[i][j] = helper(text1, text2, i + 1, j + 1) + 1;
}
return memo[i][j] = Math.max(helper(text1, text2, i + 1, j), helper(text1, text2, i, j + 1));
}
}
Design
LRU Cache - 146
Medium
Problem
Design and implement a data structure for Least Recently Used (LRU) cache.
It should support the following operations: get and put.
get(key) - Get the value (will always be po$$sitive) of the key if the key
exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present.
When the cache reached its capacity, it should invalidate the least recently
used item before inserting a new item.
The cache is initialized with a positive capacity.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache(2); // capacity
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4
Solution
/**
* 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);
*/
class LRUCache {
class Node {
Integer key;
Integer val;
Node prev;
Node next;
public Node(Integer key, Integer val) {
this.key = key;
this.val = val;
}
public Node() {
this(null, null);
}
}
private Map<Integer, Node> map;
private Node head, tail;
private int capacity;
public LRUCache(int capacity) {
this.map = new HashMap<>();
this.capacity = capacity;
this.head = new Node();
this.tail = new Node();
this.head.next = this.tail;
this.tail.prev = this.head;
}
public int get(int key) {
if (map.containsKey(key)) {
Node node = map.get(key);
moveToHead(node);
return node.val;
}
return -1;
}
public void put(int key, int value) {
if (map.containsKey(key)) {
Node node = map.get(key);
node.val = value;
map.put(key, node);
moveToHead(node);
} else {
Node node = new Node(key, value);
map.put(key, node);
addToHead(node);
if (map.size() > capacity) {
Node rm = tail.prev;
map.remove(rm.key);
remove(rm);
}
}
}
private void addToHead(Node node) {
node.prev = head;
node.next = head.next;
head.next = node;
node.next.prev = node;
}
private void remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = null;
node.next = null;
}
private void moveToHead(Node node) {
remove(node);
addToHead(node);
}
}
Min Stack - 155
Easy
Problem
Design a stack that supports push, pop, top, and retrieving the minimum
element in constant time.
push(x) -- Push element x onto stack.
pop() -- Removes the element on top of the stack.
top() -- Get the top element.
getMin() -- Retrieve the minimum element in the stack.
Example:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> Returns -3.
minStack.pop();
minStack.top(); --> Returns 0.
minStack.getMin(); --> Returns -2.
Solution
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
class MinStack {
private Stack<Integer> stack;
private Integer min;
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<>();
}
public void push(int x) {
if (min == null || x <= min) {
stack.push(min);
min = x;
}
stack.push(x);
}
public void pop() {
// the JVM is caching Integer values
// == only works for numbers between -128 and 127
if (stack.pop().equals(min)) {
min = stack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return min;
}
}
Yet another solution
class MinStack {
private class Node {
int val;
int min;
Node next;
private Node(int val, int min, Node next) {
this.val = val;
this.min = min;
this.next = next;
}
}
private Node head;
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
if (head == null) {
head = new Node(x, x, null);
} else {
head = new Node(x, Math.min(x, head.min), head);
}
}
public void pop() {
head = head.next;
}
public int top() {
return head.val;
}
public int getMin() {
return head.min;
}
}
Math
Count Primes - 204
Easy
Problem
Count the number of prime numbers less than a non-negative number, n.
Example:
Input: 10
Output: 4
Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7.
Solution
Prime is a number only divisable by 1 and itself (2 is the smallest prime).
So if the number is a multiple of a number smaller than itself, then it’s not a prime number.
Follow this idea, we can calculate non-primes on the fly during iterations.
For example, when iterate to 2, mark all multiples of 2 as non-prime, then mark multiples of 3 as non-prime ..
This is called Sieve of Eratosthenes
class Solution {
/* nlog(log(n)) */
public int countPrimes(int n) {
int count = 0;
boolean[] notPrime = new boolean[n];
for (int i = 2; i < n; i++) { // O(n)
if (!notPrime[i]) {
count++;
// all multiples of i are not prime
for (int j = 2; i * j < n; j++) { // O(log(log(n))) => O(n/2 + n/3 + n/5 + ..)
notPrime[i * j] = true;
}
// Or we can mark multiples of i starting at i
// because i × (num < i) was already marked off by multiple of (num < i)
// for (int j = i; j <= (n - 1) / i; j++) {
// notPrime[i * j] = true;
// }
}
}
return count;
}
}
We can cut the space in half by storing only odd numbers
class Solution {
public int countPrimes(int n) {
if (n <= 2) return 0; // we want prime less than n
int count = 1; // 2
boolean[] notPrime = new boolean[n / 2]; // store odd numbers starting from 3
for (int i = 3; i < n; i += 2) {
if (!notPrime[i / 2 - 1]) {
count++;
for (int j = 3; i * j < n; j += 2) {
notPrime[i * j / 2 - 1] = true;
}
}
}
return count;
}
}
Bit Manipulation
Single Number - 136
Easy
Problem
Given a non-empty array of integers, every element appears twice except for
one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement
it without using extra memory?
Example 1:
Input: [2,2,1]
Output: 1
Example 2:
Input: [4,1,2,1,2]
Output: 4
Solution
Use bitwise XOR to solve this problem
0 ^ N = N
N ^ N = 0
So….. if N is the single number
N1 ^ N1 ^ N2 ^ N2 ^…………..^ Nx ^ Nx ^ N
= (N1^N1) ^ (N2^N2) ^…………..^ (Nx^Nx) ^ N
= 0 ^ 0 ^ ……….^ 0 ^ N
= N
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for (int i : nums) {
res ^= i;
}
return res;
}
}
Bitwise AND of Numbers Range - 201
Medium
Problem
Given a range [m, n] where 0 <= m <= n <= 2147483647,
return the bitwise AND of all numbers in this range, inclusive.
Example 1:
Input: [5,7]
Output: 4
Example 2:
Input: [0,1]
Output: 0
Solution
First let’s think what does bitwise AND do to two numbers, for example ( 0b means base 2)
4 & 7 = 0b100 & 0b111 = 0b100
5 & 7 = 0b101 & 0b111 = 0b101
5 & 6 = 0b101 & 0b110 = 0b100
The operator & is keeping those bits which is set in both number.
For several numbers, the operator & is keeping those bits which is 1 in every number.
In one word, this problem is asking us to find the common prefix of m and n ‘s binary code.
class Solution {
public int rangeBitwiseAnd(int m, int n) {
int i = 0;
while (m != n) {
m >>= 1;
n >>= 1;
i++;
}
return m << i;
}
}
Prime Number of Set Bits in Binary Representation - 762
Easy
Problem
Given two integers L and R, find the count of numbers in the range [L, R]
(inclusive) having a prime number of set bits in their binary
representation.
(Recall that the number of set bits an integer has is the number of 1s
present when written in binary. For example, 21 written in binary is 10101
which has 3 set bits. Also, 1 is not a prime.)
Example 1:
Input: L = 6, R = 10
Output: 4
Explanation:
6 -> 110 (2 set bits, 2 is prime)
7 -> 111 (3 set bits, 3 is prime)
9 -> 1001 (2 set bits , 2 is prime)
10->1010 (2 set bits , 2 is prime)
Example 2:
Input: L = 10, R = 15
Output: 5
Explanation:
10 -> 1010 (2 set bits, 2 is prime)
11 -> 1011 (3 set bits, 3 is prime)
12 -> 1100 (2 set bits, 2 is prime)
13 -> 1101 (3 set bits, 3 is prime)
14 -> 1110 (3 set bits, 3 is prime)
15 -> 1111 (4 set bits, 4 is not prime)
Note:
L, R will be integers L in the range [1, 10^6].
R - L will be at most 10000.
Solution
class Solution {
public int countPrimeSetBits(int L, int R) {
int res = 0;
for (int i = L; i <= R; i++) {
if (isPrime(getSetBits(i))) res++;
}
return res;
}
public int getSetBits(int num) {
int res = 0;
while (num > 0) {
if ((num & 1) == 1) res++;
num >>= 1;
}
return res;
}
public boolean isPrime(int num) {
if (num < 2) return false;
for (int i = 2; i < num; i++) {
if (num % i == 0) return false;
}
return true;
}
}