├── 501 └── 506-relative-ranks.py ├── 001-050 ├── 001-two-sum.py ├── 002-add-two-numbers.py ├── 003-longest-substring-without-repeating.py ├── 005-longest-palindromic-substring.py ├── 006-zigzag-conversion.py ├── 007-reverse-integer.py ├── 008-string-to-nteger-(atoi).py ├── 011-contains-most-water.py ├── 014-longest-common-prefix.py ├── 015-3Sum.py ├── 018-4sum.py └── 029-divide-two-integers.py ├── 051-100 ├── 054-spiral-matrix.py ├── 056-merge-intervals.py ├── 061-rotate-list.py ├── 068-text-justification.py ├── 071-simplify-path.py └── 098-validate-bst.py ├── 101-150 ├── 101-symmetric-tree.py ├── 102-binary-tree-level-order-reversal.py ├── 103-binary-tree-zigzag-order-reversal.py ├── 104-maximum-depth-binary-tree.py ├── 105-binary-tree-pre-in.py ├── 106-binary-tree-in-post.py ├── 107-binary-tree-level-traversal-II.py ├── 108-sorted-array-bst.py ├── 109-sorted-linked-list-bst.py ├── 110-balanced-binary-tree.py ├── 111-min-depth-binary-tree.py ├── 112-path-sum.py ├── 113-path-sum-II.py ├── 114-flattern-list.py ├── 118-pascal-triangle.py ├── 119-pascal-triangle-II.py ├── 120-triangle.py ├── 121-buy-sell-stock.py ├── 122-buy-sell-stock-II.py ├── 125-valid-palindrome.py ├── 127-word-ladder.py ├── 128-longest-consecutive-sequence.py ├── 129-sum-root-to-leaf.py └── 130-surrounded-regions.py ├── 151-200 ├── 151-reverse-words-in-string.py ├── 152-max-product-subarray.py └── 179-largest-number.py ├── 201-250 └── 222-count-complete-tree-node.py ├── 301-350 └── 307-range-sum-query-mutable.py ├── 451-500 ├── 475-heaters.py ├── 476-number-complement.py ├── 477-total-hamming-distance.py ├── 481-magical-string.py ├── 482-license-key-formatting.py └── 485-max-consecutive-ones.py ├── LICENSE └── README.md /001-050/001-two-sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an array of integers, return indices of the two numbers such that they add up to a specific target. 4 | 5 | CLARIFICATIONS 6 | - What happens when there is no solution? Assume solution exists. 7 | - Can the list be empty? No. 8 | - Is the list sorted? Not necessarily. 9 | 10 | EXAMPLES 11 | [2, 7, 11, 15], 9 -> [0,1] 12 | 13 | COMMENTS 14 | - We can 'remember' what we have seen so far in O(n) space complexity using a set. 15 | - Iterate the list and at every step, we can look back and see there is a possible candidate for complement of the current item. 16 | - O(n) time complexity and O(n) space complexity. 17 | - To do without the space complexity, we may have to move to O(n log n) time complexity, by sorting the list 18 | and then using two pointers from start and end. 19 | """ 20 | 21 | 22 | def twoSum(nums, target): 23 | """ 24 | :type nums: List[int] 25 | :type target: int 26 | :rtype: List[int] 27 | """ 28 | hash_dict = {} 29 | for i in range(len(nums)): 30 | compl = target-nums[i] 31 | if compl in hash_dict: 32 | return [hash_dict[compl], i] 33 | hash_dict[nums[i]] = i 34 | -------------------------------------------------------------------------------- /001-050/002-add-two-numbers.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | You are given two non-empty linked lists representing two non-negative integers. 4 | The digits are stored in reverse order and each of their nodes contain a single digit. 5 | Add the two numbers and return it as a linked list. 6 | 7 | CLARIFICATIONS 8 | - Can there be leading zeros? No, except the number zero. 9 | - I am confirming that the number of digits in the two numbers can be different? Yes. 10 | 11 | EXAMPLES 12 | (2 -> 4 -> 3), (5 -> 6 -> 4) -> 7 -> 0 -> 8 13 | 14 | COMMENTS 15 | - Since the numbers are given in reverse order, we only have to iterate the two linked lists and 16 | accumulate the result in a new linked list. 17 | - We should remember the carry. 18 | - If one of the numbers end early, we should add the rest of the unfinished number added to the carry. 19 | - O(m+n) time complexity and O(m+n) space complexity, where m and n are number of digits in the given numbers. 20 | """ 21 | 22 | def addTwoNumbers(l1, l2): 23 | """ 24 | :type l1: ListNode 25 | :type l2: ListNode 26 | :rtype: ListNode 27 | """ 28 | to_return, carry = ListNode(0), 0 29 | to_return_head = to_return 30 | num1_p, num2_p = l1, l2 31 | while (num1_p is not None) and (num2_p is not None): 32 | to_add_val = num1_p.val + num2_p.val + carry 33 | if to_add_val >= 10: 34 | carry = 1 35 | to_add_val %= 10 36 | else: 37 | carry = 0 38 | to_return.next = ListNode(to_add_val) 39 | to_return = to_return.next 40 | num1_p, num2_p = num1_p.next, num2_p.next 41 | while num1_p is not None: 42 | to_add_val = num1_p.val + carry 43 | if to_add_val >= 10: 44 | carry = 1 45 | to_add_val %= 10 46 | else: 47 | carry = 0 48 | to_return.next = ListNode(to_add_val) 49 | to_return, num1_p = to_return.next, num1_p.next 50 | 51 | while num2_p is not None: 52 | to_add_val = num2_p.val + carry 53 | if to_add_val >= 10: 54 | carry = 1 55 | to_add_val %= 10 56 | else: 57 | carry = 0 58 | to_return.next = ListNode(to_add_val) 59 | to_return, num2_p = to_return.next, num2_p.next 60 | if carry: 61 | to_return.next = ListNode(1) 62 | return to_return_head.next 63 | -------------------------------------------------------------------------------- /001-050/003-longest-substring-without-repeating.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a string, find the length of the longest substring without repeating characters. 4 | 5 | CLARIFICATIONS 6 | - Contiguous substring, not subsequence, right? Yes. 7 | - Can the string be empty ? Yes. 8 | 9 | EXAMPLES 10 | "abcabcbb" -> 3 ("abc", starts at first) 11 | "bbbbb" -> 1 ("b", single element substring) 12 | "pwwkew" -> 3 ("wke", starts at middle) 13 | 14 | COMMENTS 15 | - We can keep a window of substring and grow/shrink it. 16 | - The characters in the current string have to be remembered, set is good for that. 17 | Fast lookup, decent removal in size of the substring size. 18 | - O(n) time complexity and O(n) space complexity. 19 | - To do with constant space complexity, we may have to move to O(n^2) time complexity, by naively looking at every (i,j) position 20 | to check if that is a valid substring. 21 | """ 22 | 23 | def lengthOfLongestSubstring(s): 24 | """ 25 | :type s: str 26 | :rtype: int 27 | """ 28 | start, end = 0, 0 29 | current_set = set() 30 | to_return = 0 31 | for ch in s: 32 | if ch not in current_set: 33 | current_set.add(ch) 34 | else: 35 | to_return = max(to_return, end-start) 36 | while s[start] != ch: 37 | current_set.remove(s[start]) 38 | start += 1 39 | start += 1 40 | end += 1 41 | to_return = max(to_return, end-start) 42 | return to_return 43 | -------------------------------------------------------------------------------- /001-050/005-longest-palindromic-substring.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a string s, find the longest palindromic substring in s. 4 | 5 | CLARIFICATIONS 6 | - If there is no palindrome that is more than one character, we can report 7 | any character? Yes. 8 | 9 | EXAMPLES 10 | "babad" -> "bab" 11 | "cbbd" -> "bb" 12 | 13 | COMMENTS 14 | - We can keep a cache to store the result for all possible substring. 15 | - Technically, the cache can be a list of list, but since the cache is half filled, 16 | we can keep a dictionary of dictionaries. 17 | - O(n^2) time complexity and O(n^2) space complexity. 18 | """ 19 | 20 | def longestPalindrome(s): 21 | """ 22 | :type s: str 23 | :rtype: str 24 | """ 25 | if not s: 26 | return "" 27 | cache = {} 28 | n = len(s) 29 | for i in range(n): 30 | cache[i] = {} 31 | for j in range(i,n): 32 | cache[i][j] = 0 33 | 34 | for k in range(n-1): 35 | cache[k][k] = 1 36 | if s[k] == s[k+1]: 37 | cache[k][k+1] = 1 38 | cache[n-1][n-1] = 1 39 | 40 | for gap in range(2,n): 41 | start, end = 0, gap 42 | while end < n: 43 | if s[start] == s[end] and cache[start+1][end-1]: 44 | cache[start][end] = 1 45 | start += 1 46 | end += 1 47 | 48 | for gap in range(n-1,-1,-1): 49 | start, end = 0, gap 50 | while end < n: 51 | if cache[start][end]: 52 | return s[start:end+1] 53 | start += 1 54 | end += 1 55 | -------------------------------------------------------------------------------- /001-050/006-zigzag-conversion.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a string and a number of rows, pring the string in a zigzag fashion across rows. 4 | 5 | "PAYPALISHIRING" can be written as the following in 3 rows: 6 | P A H N 7 | A P L S I I G 8 | Y I R 9 | 10 | CLARIFICATIONS 11 | - Can I assume row number to be positive? Yes. 12 | - For row number 1, it prints the identical string, right? Yes. 13 | 14 | EXAMPLES 15 | The given example looks OK to begin with. 16 | 17 | COMMENTS 18 | - We can initiate the rows as list of empty strings. 19 | - We divide the given string in number of rows, and then print them with alternating direction in the rows. 20 | - O(n) time complexity and (technically) O(n) space complexity. 21 | - Our solution may involve slicing strings, which are technically in order of size of the string, but let's not worry about that right now. 22 | Python optimizes string slicing pretty well. 23 | """ 24 | 25 | def convert(s, numRows): 26 | """ 27 | :type s: str 28 | :type numRows: int 29 | :rtype: str 30 | """ 31 | if (len(s)<=1): 32 | return s 33 | if (numRows <= 1): 34 | return s 35 | rows = ["" for i in range(numRows)] 36 | rows[0] = s[0] 37 | chunkStrings = [ s[i:i+numRows-1] for i in range(1, len(s), numRows-1) ] 38 | direction = 1 #true is forward 39 | for chunk in chunkStrings: 40 | if (direction==1): 41 | for i in range(1,numRows): 42 | if chunk: 43 | rows[i] += chunk[0] 44 | chunk = chunk[1:] 45 | else: 46 | for i in range(numRows-2, -1, -1): 47 | if chunk: 48 | rows[i] += chunk[0] 49 | chunk = chunk[1:] 50 | direction = direction*(-1) 51 | return ''.join(rows) 52 | -------------------------------------------------------------------------------- /001-050/007-reverse-integer.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Reverse digits of an integer. 4 | 5 | CLARIFICATIONS 6 | - I am assuming the number can be negative? Yes. 7 | - Do I have to handle 32-bit interger overflow? Yes, return 0 in that case. 8 | - Can I convert the interger to string first (easier to reverse)? Sure. 9 | - Are leading zeros OK? No. 10 | 11 | EXAMPLES 12 | 123 -> 321 13 | -123 -> -321 14 | 15 | COMMENTS 16 | - We can convert the absolute value of the integer to string and then reverse the string. 17 | - We have to be careful about adding the sign of the string back after conversion. 18 | - O(n) time complexity and O(n) space complexity. 19 | - Constant space complexity means doing it in-place, which is not obvious to me. 20 | - Python allows 64-bit integers, so we need to mimic 32-bit integer overflow. 21 | """ 22 | 23 | def reverse(x): 24 | """ 25 | :type x: int 26 | :rtype: int 27 | """ 28 | if (x<0): 29 | x = abs(x) 30 | neg = -1 31 | else: 32 | neg = 1 33 | 34 | rev_x = int(str(x)[::-1]) 35 | if abs(rev_x) < 2147483648: 36 | return rev_x*neg 37 | else: 38 | return 0 39 | -------------------------------------------------------------------------------- /001-050/008-string-to-nteger-(atoi).py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Implement atoi to convert a string to an integer. 4 | 5 | CLARIFICATIONS 6 | - Is there some basic conditions, such as how to 7 | handle whitespaces or non-digit characters? Ignore all whitespaces at start, 8 | then get the number and ignore the rest. 9 | - What should the output for a string that is not an integer? Return 0. 10 | - Should we assume the integer is 32 bit? Yes, for outside range, return 0. 11 | 12 | EXAMPLES 13 | "213" -> 213 14 | "-1.6" -> -2 15 | "0" -> 0 16 | "112aeqwe" -> 112 17 | 18 | COMMENTS 19 | - The number can be negative. 20 | - It can be a float, if the first digit after the point is more than or equal 5, 21 | the output would be incremented. 22 | """ 23 | 24 | def myAtoi(str): 25 | """ 26 | :type str: str 27 | :rtype: int 28 | """ 29 | to_return_val = 0 30 | str = str.strip() 31 | if not str: 32 | return 0 33 | n, sign, carry, start = len(str), 1, 0, 0 34 | if str[0] in ["-", "+"]: 35 | start = 1 36 | if str[0] == "-": 37 | sign = -1 38 | 39 | for i in range(start, n): 40 | s = str[i] 41 | if s in "0123456789": 42 | to_return_val = to_return_val*10 + ord(s)-ord('0') 43 | elif s == ".": 44 | if i == n-1: 45 | return 0 46 | else: 47 | if str[i+1] in "0123456789" and str[i+1] >= 5: 48 | to_return_val += 1 49 | break 50 | else: 51 | break 52 | to_return_val = to_return_val*sign 53 | if to_return_val > 2147483647: 54 | return 2147483647 55 | if to_return_val < -2147483648: 56 | return -2147483648 57 | return to_return_val 58 | -------------------------------------------------------------------------------- /001-050/011-contains-most-water.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). 4 | n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). 5 | Find two lines, which together with x-axis forms a container, such that the container contains the most water. 6 | 7 | CLARIFICATIONS 8 | - I am assuming there is no redundant duplicate in the list? Sure. 9 | - Can the list be empty? No. 10 | - I am assuming the list is not sorted? Sure. 11 | - Should I return the lines, or the 'area' of water? The area is fine. 12 | 13 | EXAMPLES 14 | [2, 11, 13, 9] -> 18 (9*2, for lines [11,9]) 15 | 16 | COMMENTS 17 | - We can try with the widest container and move the lines in if that gives a container with more area. 18 | - Both the minimum height among the lines and the width matter, so we can keep the current area. 19 | - O(n) time complexity and constant space complexity. 20 | - Unless the list is sorted, I don't see any way to improve the complexity. 21 | """ 22 | 23 | def maxArea(self, height): 24 | """ 25 | :type height: List[int] 26 | :rtype: int 27 | """ 28 | left, right = 0, len(height)-1 29 | area_so_far = 0 30 | while left < right: 31 | h = min(height[left], height[right]) 32 | area_so_far = max(area_so_far, (right-left)*h) 33 | while (height[left] <= h and left < right): 34 | left += 1 35 | while (height[right] <= h and left < right): 36 | right -= 1 37 | return area_so_far 38 | -------------------------------------------------------------------------------- /001-050/014-longest-common-prefix.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Write a function to find the longest common prefix string amongst an array of strings. 4 | 5 | CLARIFICATIONS 6 | - What happens when there is no solution? Return empty string. 7 | - Can the list be empty? Yes, return empty string. 8 | 9 | EXAMPLES 10 | ["ab", "a", "acd", "aaa"] -> "a" 11 | ["ab", "c", "acd"] -> "" 12 | 13 | COMMENTS 14 | - Since it is talking about prefix, we can keep a single index starting from left and move it as we see a matching prefix in all strings. 15 | - O(n) time complexity and O(n) space complexity to keep the list of characters in current index. 16 | - To do without the space complexity, we have to iterate through the given list and stop when the character in the current index 17 | does not match. It can be done, but let's do with basic space complexity. 18 | - Reducing time complexity does not seem to be possible, since the list has to be iterated at least once. 19 | """ 20 | 21 | def longestCommonPrefix(self, strs): 22 | """ 23 | :type strs: List[str] 24 | :rtype: str 25 | """ 26 | index = 0 27 | to_return = "" 28 | if not strs: 29 | return to_return 30 | while True: 31 | try: 32 | result = [str[index] for str in strs] 33 | if len(set(result)) == 1: 34 | to_return += result[0] 35 | else: 36 | return to_return 37 | index += 1 38 | except IndexError: 39 | return to_return 40 | -------------------------------------------------------------------------------- /001-050/015-3Sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all the unique triplets. 4 | 5 | CLARIFICATIONS 6 | - Do I return tuple of elements of their indexes? Elements. 7 | - The solution may not exist, right? Yes. 8 | - By unique, you mean, each tuple should be considered as set and then unique set of sets? Yes. 9 | 10 | EXAMPLES 11 | [-1, 0, 1, 2, -1, -4] -> 12 | [ 13 | [-1, 0, 1], 14 | [-1, -1, 2] 15 | ] 16 | 17 | COMMENTS 18 | - We can first try to solve the problem for two elements that add up to a given target. 19 | - Then, we can use that function to solve this for each element in the array. 20 | - We have to be extra careful to make the tuples unique. 21 | - First, we would assume the two_sum function exists already and write that later. 22 | - When we find a valid tuple, we need to sort them to ensure the uniqueness. 23 | - We'd accumulate the tuples in a set, but since lists are mutable, they can't be in a set. 24 | We can make the valid tuple a string joined by a delimiter, say comma. 25 | """ 26 | 27 | def threeSum(self, nums): 28 | """ 29 | :type nums: List[int] 30 | :rtype: List[List[int]] 31 | """ 32 | to_return = set() 33 | for i in range(len(nums)): 34 | target = 0-nums[i] 35 | start, end = i+1, len(nums)-1 36 | rest_two = self.twoSum(nums, start, end, target) 37 | if rest_two: 38 | for rt in rest_two: 39 | valid_tuple = rt+[nums[i]] 40 | valid_tuple.sort() 41 | valid_tuple = "%s,%s,%s"%(valid_tuple[0], valid_tuple[1], valid_tuple[2]) 42 | to_return.add(valid_tuple) 43 | to_return = list(to_return) 44 | return map(lambda x: self._splitInt(x), to_return) 45 | 46 | def _splitInt(self, arr): 47 | return map(lambda x: int(x), arr.split(',')) 48 | 49 | def twoSum(self, nums, start, end, target): 50 | compl_dict = set() 51 | to_return = [] 52 | for i in range(start, end+1): 53 | if nums[i] in compl_dict: 54 | to_return.append([nums[i], target-nums[i]]) 55 | else: 56 | compl_dict.add(target-nums[i]) 57 | return to_return 58 | -------------------------------------------------------------------------------- /001-050/018-4sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an array S integers and a target, find all unique quadruplets a, b, c, and d in S such that a + b + c + d = target. 4 | 5 | CLARIFICATIONS 6 | - Similar clarifications as 015-3sum. 7 | 8 | EXAMPLES 9 | [1, 0, -1, 0, -2, 2], 0 => 10 | 11 | [ 12 | [-1, 0, 0, 1], 13 | [-2, -1, 1, 2], 14 | [-2, 0, 0, 2] 15 | ] 16 | 17 | COMMENTS 18 | - We keep a dictionary of possible sum and their indices. 19 | - Then we do nested iteration to find the rest two values. 20 | - The time complexity is O(n^2), probably O(n^3), when the complementary set is in order of n. 21 | """ 22 | 23 | from collections import defaultdict 24 | def fourSum(nums, target): 25 | """ 26 | :type nums: List[int] 27 | :type target: int 28 | :rtype: List[List[int]] 29 | """ 30 | if not nums: 31 | return [] 32 | n = len(nums) 33 | to_return = set() 34 | two_dict = defaultdict(set) 35 | for i in range(n): 36 | for j in range(i+1, n): 37 | sum_val = nums[i]+nums[j] 38 | two_dict[target-sum_val].add((i,j)) 39 | 40 | for i in range(n): 41 | for j in range(i+1, n): 42 | sum_val = nums[i]+nums[j] 43 | if sum_val in two_dict: 44 | compl_set = two_dict[sum_val] 45 | for (compl_i, compl_j) in compl_set: 46 | if not (set([compl_i, compl_j]) & set([i,j])): 47 | to_add = [nums[i],nums[j],nums[compl_i],nums[compl_j]] 48 | to_add.sort() 49 | to_add = ','.join([str(x) for x in to_add]) 50 | to_return.add(to_add) 51 | return [map(lambda x: int(x), x.split(',')) for x in to_return] 52 | -------------------------------------------------------------------------------- /001-050/029-divide-two-integers.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Divide two integers without using multiplication, division and mod operator. 4 | 5 | CLARIFICATIONS 6 | - Do I have to handle 32-bit integer overflow? Yes, return the MAX_INT in that case. 7 | - Can the divisor be zero? Yes, return the MAX_INT. 8 | 9 | EXAMPLES 10 | 34/3 -> 11 11 | 12 | COMMENTS 13 | - This solution is by tusizi in Leetcode (picked up from https://discuss.leetcode.com/topic/8714/clear-python-code) 14 | """ 15 | 16 | def divide(dividend, divisor): 17 | """ 18 | :type dividend: int 19 | :type divisor: int 20 | :rtype: int 21 | """ 22 | sign = (dividend < 0) is (divisor < 0) 23 | dividend, divisor = abs(dividend), abs(divisor) 24 | INT_MIN, INT_MAX = -2147483648, 2147483647 25 | 26 | if (not divisor) or (dividend < INT_MIN and divisor == -1): 27 | return INT_MAX 28 | to_return = 0 29 | while dividend >= divisor: 30 | temp, i = divisor, 1 31 | while dividend >= temp: 32 | dividend -= temp 33 | to_return += i 34 | i <<= 1 35 | temp <<= 1 36 | if not sign: 37 | to_return = -to_return 38 | return min(max(INT_MIN, to_return), INT_MAX) 39 | -------------------------------------------------------------------------------- /051-100/054-spiral-matrix.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order. 4 | 5 | CLARIFICATIONS 6 | - So, we start from top left and go clockwise? Yes. 7 | 8 | EXAMPLES 9 | [ 10 | [ 1, 2, 3 ], 11 | [ 4, 5, 6 ], 12 | [ 7, 8, 9 ] 13 | ] => [1,2,3,6,9,8,7,4,5] 14 | 15 | COMMENTS 16 | - A spiral is determined by the corners, or where it takes right turn. We could keep the width and height too, 17 | but let's go with stops. Should be easier to maintain and sanity check. 18 | - We still have to keep track of the width and height for stopping criteria (<=1). 19 | """ 20 | 21 | def spiralOrder(self, matrix): 22 | """ 23 | :type matrix: List[List[int]] 24 | :rtype: List[int] 25 | """ 26 | m = len(matrix) 27 | if not m: 28 | return [] 29 | n = len(matrix[0]) 30 | to_return = [] 31 | 32 | start = (0,0) 33 | stop1, stop2, stop3 = (0,n-1), (m-1,n-1), (m-1,0) 34 | width, height = n, m 35 | while width > 1 and height > 1: 36 | i,j = start 37 | while (i,j) != stop1: 38 | to_return.append(matrix[i][j]) 39 | j += 1 40 | while (i,j) != stop2: 41 | to_return.append(matrix[i][j]) 42 | i += 1 43 | while (i,j) != stop3: 44 | to_return.append(matrix[i][j]) 45 | j -= 1 46 | while (i,j) != start: 47 | to_return.append(matrix[i][j]) 48 | i -= 1 49 | start = (start[0]+1, start[1]+1) 50 | stop1 = (stop1[0]+1, stop1[1]-1) 51 | stop2 = (stop2[0]-1, stop2[1]-1) 52 | stop3 = (stop3[0]-1, stop3[1]+1) 53 | width -= 2 54 | height -= 2 55 | if width == 1: 56 | i,j = start 57 | i_end = stop2[0] 58 | while i <= i_end: 59 | to_return.append(matrix[i][j]) 60 | i += 1 61 | return to_return 62 | if height == 1: 63 | i,j = start 64 | j_end = stop1[1] 65 | while j <= j_end: 66 | to_return.append(matrix[i][j]) 67 | j += 1 68 | return to_return 69 | return to_return 70 | -------------------------------------------------------------------------------- /051-100/056-merge-intervals.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a collection of intervals, merge all overlapping intervals. 4 | 5 | CLARIFICATIONS 6 | - If two intervals touch on the boundary, are they considered overlapping? Yes. 7 | - Are the intervals sorted in any way? Not necessarily. 8 | - Are the intervals given as object instantiations of some class or an array of start and end? 9 | An object, define that too. 10 | 11 | EXAMPLES 12 | [1,3],[2,6],[8,10],[15,18] -> [1,6],[8,10],[15,18]. 13 | 14 | COMMENTS 15 | - We should start with the leftmost interval possible. Let's sort the intervals by their start. 16 | - Then we would iterate the list of intervals and merge until we get an interval whose start is 17 | at right of the current interval end. 18 | - O(n log n) time complexity and O(n) space complexity, assuming in-place sort. 19 | """ 20 | 21 | class Interval(object): 22 | def __init__(self, s=0, e=0): 23 | self.start = s 24 | self.end = e 25 | 26 | def merge(intervals): 27 | """ 28 | :type intervals: List[Interval] 29 | :rtype: List[Interval] 30 | """ 31 | if not intervals: 32 | return [] 33 | intervals.sort(key=lambda x:x.start) 34 | to_return = [] 35 | current_interval = intervals[0] 36 | current_start = current_interval.start 37 | current_end = current_interval.end 38 | for i in range(1, len(intervals)): 39 | interval = intervals[i] 40 | if interval.start <= current_end: 41 | current_end = max(current_end, interval.end) 42 | else: 43 | to_add = Interval() 44 | to_add.start = current_start 45 | to_add.end = current_end 46 | to_return.append(to_add) 47 | current_start = interval.start 48 | current_end = interval.end 49 | to_add = Interval() 50 | to_add.start = current_start 51 | to_add.end = current_end 52 | to_return.append(to_add) 53 | 54 | return to_return 55 | -------------------------------------------------------------------------------- /051-100/061-rotate-list.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a list, rotate the list to the right by k places, where k is non-negative. 4 | 5 | CLARIFICATIONS 6 | - Is the list given as an array? No, a linked list. 7 | - Each node in the linked list is an object of a custom data structure, right? Yes. 8 | Define them. 9 | - Can k be more than the length of the list? Sure. 10 | - Do I return the head of the updated list? Yes. 11 | 12 | EXAMPLES 13 | 1->2->3->4->5->None, 2 => 4->5->1->2->3->NULL 14 | 15 | COMMENTS 16 | - If k is much longer than the list length, we should not update the list unneccessarily, 17 | so k should be updated to the k modulo list length. 18 | - If k is 0, return the original list. 19 | """ 20 | 21 | def rotateRight(head, k): 22 | """ 23 | :type head: ListNode 24 | :type k: int 25 | :rtype: ListNode 26 | """ 27 | if head is None: 28 | return head 29 | 30 | temp = head 31 | list_length = 1 32 | 33 | while temp.next is not None: 34 | temp = temp.next 35 | list_length += 1 36 | 37 | k = k%list_length 38 | if k == 0: 39 | return head 40 | 41 | prev, curr = head, head 42 | count = 1 43 | 44 | while count <= k: 45 | curr = curr.next 46 | count += 1 47 | 48 | while curr.next is not None: 49 | prev = prev.next 50 | curr = curr.next 51 | to_return = prev.next 52 | prev.next = None 53 | curr.next = head 54 | return to_return 55 | -------------------------------------------------------------------------------- /051-100/068-text-justification.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an array of words and a length L, format the text such that 4 | each line has exactly L characters and is fully (left and right) justified. 5 | 6 | CLARIFICATIONS 7 | - Can we pack as much words as can in a sentence starting from the left? Yes, greedy approach is fine. 8 | - Does the line last has to be taken care of separately? Words can be left-aligned in the last sentence 9 | filled with spaces. Yes, that's an edge case (leetcode OJ does this too). 10 | - Can a word length exceed the given length? No. 11 | 12 | EXAMPLES 13 | ["This", "is", "an", "example", "of", "text", "justification."] -> 14 | 15 | [ 16 | "This is an", 17 | "example of text", 18 | "justification. " 19 | ] 20 | 21 | COMMENTS 22 | - We should keep adding words till the total length of those words (and one space between each two of them) exceeds the given length L. 23 | - If there's extra space left, we should evenly distrubute them between words. 24 | - O(n) time complexity and O(n) space complexity. 25 | - Whether it's the last sentence should be noted with a boolean variable. 26 | We'd keep a number of variables like word length etc. to minimize number of operations. 27 | """ 28 | 29 | def fullJustify(words, maxWidth): 30 | """ 31 | :type words: List[str] 32 | :type maxWidth: int 33 | :rtype: List[str] 34 | """ 35 | width = maxWidth 36 | if not words: 37 | return [""] 38 | 39 | to_return = [] 40 | word_index = 0 41 | line_width = 0 42 | line_words = [] 43 | 44 | while word_index < len(words): 45 | 46 | temp = line_width + len(line_words) 47 | if temp-1 == width: 48 | to_return.append(' '.join(line_words)) 49 | line_width = 0 50 | line_words = [] 51 | continue 52 | if temp + len(words[word_index]) <= width: 53 | line_width += len(words[word_index]) 54 | line_words.append(words[word_index]) 55 | word_index += 1 56 | else: 57 | padded_line = _pad_words(line_words, width, line_width, False) 58 | to_return.append(padded_line) 59 | line_width = 0 60 | line_words = [] 61 | 62 | if len(line_words) > 0: 63 | padded_line = _pad_words(line_words, width, line_width, True) 64 | to_return.append(padded_line) 65 | 66 | return to_return 67 | 68 | def _pad_words(line_words, width, line_width, last_line): 69 | 70 | padding = width-line_width 71 | if len(line_words) == 1: 72 | return line_words[0]+(' '*padding) 73 | if last_line: 74 | return ' '.join(line_words) + ' '*(padding-len(line_words)+1) 75 | min_gap_padding = ' '*(padding/(len(line_words)-1)) 76 | extra_padding_size = padding%(len(line_words)-1) 77 | for i in range(extra_padding_size): 78 | line_words[i] += ' ' 79 | 80 | return min_gap_padding.join(line_words) 81 | -------------------------------------------------------------------------------- /051-100/071-simplify-path.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an absolute path for a file (Unix-style), simplify it. 4 | 5 | CLARIFICATIONS 6 | - Is it always given with respect to the root folder (/)? Not necessarily. 7 | - I am assuming once in the root directory, doing "../" will still be in 8 | the root directory, so will return "/"? Yes. 9 | - Should I ignore multiple consecutive "/" (except the one at the start, of course)? Yes. 10 | - Should the trailing "/" remain? No. 11 | 12 | EXAMPLES 13 | "/home/" -> "/home" 14 | "/a/./b/../../c/" -> "/c" 15 | "/../" -> "/" 16 | "/home//foo/" -> "/home/foo" 17 | 18 | COMMENTS 19 | - We will split the path by "/", ignore empty strings and consider spacial cases. 20 | - When joining, we'd ensure we keep the first "/" if it starts the path. 21 | """ 22 | 23 | def simplifyPath(path): 24 | """ 25 | :type path: str 26 | :rtype: str 27 | """ 28 | path_split = path.split("/") 29 | to_return_split = [] 30 | for chunk in path_split: 31 | if chunk == "..": 32 | try: 33 | if to_return_split[-1] != "../": 34 | temp = to_return_split.pop() 35 | else: 36 | to_return_split.append(chunk) 37 | except IndexError: 38 | continue 39 | elif chunk == ".": 40 | continue 41 | elif chunk == "": 42 | continue 43 | else: 44 | to_return_split.append(chunk) 45 | if path[0] == "/": 46 | return "/%s"%('/'.join(to_return_split)) 47 | else: 48 | if to_return: 49 | return ''.join(to_return_split) 50 | else: 51 | return './' 52 | -------------------------------------------------------------------------------- /051-100/098-validate-bst.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, determine if it is a valid binary search tree (BST). 4 | 5 | CLARIFICATIONS 6 | - For any root, the max of the left subtree is smaller than the current value and 7 | the min of the right subtree is bigger than the current value. Yes, and both the 8 | left and right subtrees are also BST. 9 | - I am assuming the tree nodes are custom data structure? Yes, define it. 10 | - Is an empty tree BST or a single element tree BST? Yes. 11 | 12 | EXAMPLES 13 | 1 14 | 2 15 | 3 16 | -> True 17 | 18 | COMMENTS 19 | - Recursion looks like a possible clean way to start. 20 | - We would assume the finding minimum and maximum in a binary is given, and then write 21 | them as helper functions. 22 | """ 23 | 24 | class TreeNode(object): 25 | def __init__(self, x): 26 | self.val = x 27 | self.left = None 28 | self.right = None 29 | 30 | def isValidBST(root): 31 | """ 32 | :type root: TreeNode 33 | :rtype: bool 34 | """ 35 | if root is None: 36 | return True 37 | 38 | if root.left is None and root.right is None: 39 | return True 40 | 41 | if root.left is None: 42 | temp = root.right 43 | return isValidBST(root.right) and (root.val < _min_value(root.right)) 44 | 45 | if root.right is None: 46 | temp = root.left 47 | while temp.left is not None: 48 | temp = temp.left 49 | return isValidBST(root.left) and (root.val > _max_value(root.left)) 50 | 51 | return isValidBST(root.left) and isValidBST(root.right) 52 | and (root.val < _min_value(root.right)) and (root.val > _max_value(root.left)) 53 | 54 | def _max_value(root): 55 | if root.left is None and root.right is None: 56 | return root.val 57 | if root.left is None: 58 | return max(root.val, _max_value(root.right)) 59 | if root.right is None: 60 | return max(root.val, _max_value(root.left)) 61 | return max(root.val, _max_value(root.left), _max_value(root.right)) 62 | 63 | def _min_value(root): 64 | if root is None: 65 | return -1 66 | if root.left is None and root.right is None: 67 | return root.val 68 | if root.left is None: 69 | return min(root.val, _min_value(root.right)) 70 | if root.right is None: 71 | return min(root.val, _min_value(root.left)) 72 | return min(root.val, _min_value(root.left), _min_value(root.right)) 73 | -------------------------------------------------------------------------------- /101-150/101-symmetric-tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). 4 | 5 | CLARIFICATIONS 6 | - Can we assume that the node values have a well-defined equals (== in python, say) operator? Yes. 7 | 8 | EXAMPLES 9 | [1,2,2,3,4,4,3] -> True 10 | [1,2,2,null,3,null,3] -> False 11 | 12 | COMMENTS 13 | - Recursively, we need to check that the left subtree is the mirror of the right subtree. 14 | - Which essentially boils down to the two root values of the subtrees are equal and the right subtree 15 | of the left is same as the left subtree of the right (and vice versa). 16 | """ 17 | 18 | def isSymmetric(root): 19 | """ 20 | :type root: TreeNode 21 | :rtype: bool 22 | """ 23 | if root is None: 24 | return True 25 | 26 | if _isMirror(root.left, root.right): 27 | return True 28 | else: 29 | return False 30 | 31 | def isMirror(tree1, tree2): 32 | if tree1 is None and tree2 is None: 33 | return True 34 | if tree1 is None or tree2 is None: 35 | return False 36 | if (tree1.val == tree2.val): 37 | return (_isMirror(tree1.left, tree2.right) and _isMirror(tree1.right, tree2.left)) 38 | return False 39 | -------------------------------------------------------------------------------- /101-150/102-binary-tree-level-order-reversal.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). 4 | 5 | CLARIFICATIONS 6 | - Do we print all 'None' values that a level may have? Yes. 7 | 8 | EXAMPLES 9 | [3,9,20,null,null,15,7] -> [[3],[9,20],[15,7]] 10 | 11 | COMMENTS 12 | - We remember the current level of nodes, the next level of nodes and the values of the current level of nodes. 13 | - Then, we update the level with the next level and keep going until the level is empty. 14 | - The time complexity is linear in the number of nodes while the space complexity is linear 15 | in the maximum size of the levels (which can essentially be the number of nodes). 16 | """ 17 | 18 | def levelOrder(root): 19 | """ 20 | :type root: TreeNode 21 | :rtype: List[List[int]] 22 | """ 23 | to_return = [] 24 | if not root: 25 | return to_return 26 | level = [root] 27 | 28 | while level: 29 | next_level, current_level_val = [], [] 30 | for node in level: 31 | if node is not None: 32 | current_level_val.append(node.val) 33 | next_level.append(node.left) 34 | next_level.append(node.right) 35 | level = next_level 36 | if current_level_val: 37 | to_return.append(current_level_val) 38 | return to_return 39 | -------------------------------------------------------------------------------- /101-150/103-binary-tree-zigzag-order-reversal.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, return the zigzag level order traversal of its nodes' values. 4 | (ie, from left to right, then right to left for the next level and alternate between). 5 | 6 | CLARIFICATIONS 7 | - Do we print all 'None' values that a level may have? Yes. 8 | - Do we start with left-to-right? Yes. 9 | 10 | EXAMPLES 11 | [3,9,20,null,null,15,7] -> [[3],[20,9],[15,7]] 12 | 13 | COMMENTS 14 | - We remember the current level of nodes, the next level of nodes and the values of the current level of nodes. 15 | - Then, we update the level with the next level and keep going until the level is empty. 16 | - The only thing is we need to reverse the direction. We can not do that when adding the next nodes, 17 | so we need to keep a variable to remember direction and then reverse the list of next nodes in 18 | alternative cases. 19 | """ 20 | 21 | def zigzagLevelOrder(root): 22 | """ 23 | :type root: TreeNode 24 | :rtype: List[List[int]] 25 | """ 26 | to_return = [] 27 | if not root: 28 | return to_return 29 | level = [root] 30 | ltor = True 31 | while level: 32 | level_val, next_level = [], [] 33 | for node in level: 34 | if node: 35 | level_val.append(node.val) 36 | next_level.append(node.left) 37 | next_level.append(node.right) 38 | if not ltor: 39 | level_val.reverse() 40 | if level_val: 41 | to_return.append(level_val) 42 | level = next_level 43 | ltor = not ltor 44 | return to_return 45 | -------------------------------------------------------------------------------- /101-150/104-maximum-depth-binary-tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, find its maximum depth. 4 | The maximum depth is the number of nodes along the longest path from the root node down 5 | to the farthest leaf node. 6 | 7 | CLARIFICATIONS 8 | - We are not counting a None value to be a node, right? Correct. 9 | 10 | EXAMPLES 11 | [3,9,20,null,null,15,7] -> 3 12 | 13 | COMMENTS 14 | - We can recursively find the max depth of the left and right subtrees and add 1. 15 | """ 16 | 17 | def maxDepth(root): 18 | """ 19 | :type root: TreeNode 20 | :rtype: int 21 | """ 22 | if not root: 23 | return 0 24 | return 1 + max(maxDepth(root.left), maxDepth(root.right)) 25 | -------------------------------------------------------------------------------- /101-150/105-binary-tree-pre-in.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given preorder and inorder traversal of a tree, construct the binary tree. 4 | 5 | CLARIFICATIONS 6 | - Can I assume that duplicates do not exist? Sure. 7 | - Can we use Python's loosely typed nature? Then we can make it recursive. Sure. 8 | 9 | EXAMPLES 10 | (needs to be drawn) 11 | 12 | COMMENTS 13 | - The base case is when both the list is empty. Then it returns None. 14 | - The first element of the preorder is the root. 15 | - We can find the root in the inorder list (using the no-duplicate policy). 16 | - Then inorder of the left (right) subtree are the left (right) part of the inorder list. 17 | - Similarly, we can find the preorder of the left and right subtree. 18 | - The time complexity is O(n^2), for heavily left-skewed trees. 19 | """ 20 | 21 | def buildTree(preorder, inorder): 22 | """ 23 | :type preorder: List[int] 24 | :type inorder: List[int] 25 | :rtype: TreeNode 26 | """ 27 | if not preorder or not inorder: 28 | return None 29 | root_val = preorder[0] 30 | root_val_index = inorder.index(root_val) 31 | root = TreeNode(root_val) 32 | pre_left = preorder[1:root_val_index+1] 33 | pre_right = preorder[root_val_index+1:] 34 | in_left = inorder[:root_val_index] 35 | in_right = inorder[root_val_index+1:] 36 | root.left = buildTree(pre_left, in_left) 37 | root.right = buildTree(pre_right, in_right) 38 | return root 39 | -------------------------------------------------------------------------------- /101-150/106-binary-tree-in-post.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given inorder and postorder traversal of a tree, construct the binary tree. 4 | 5 | CLARIFICATIONS 6 | - Can I assume that duplicates do not exist? Sure. 7 | - Can we use Python's loosely typed nature? Then we can make it recursive. Sure. 8 | 9 | EXAMPLES 10 | (needs to be drawn) 11 | 12 | COMMENTS 13 | - The base case is when both the list is empty. Then it returns None. 14 | - The last element of the postorder is the root. 15 | - We can find the root in the inorder list (using the no-duplicate policy). 16 | - Then inorder of the left (right) subtree are the left (right) part of the inorder list. 17 | - Similarly, we can find the postorder of the left and right subtree. 18 | - The time complexity is O(n^2), for heavily right-skewed trees. 19 | """ 20 | 21 | def buildTree(inorder, postorder): 22 | """ 23 | :type inorder: List[int] 24 | :type postorder: List[int] 25 | :rtype: TreeNode 26 | """ 27 | if not postorder or not inorder: 28 | return None 29 | root_val = postorder[-1] 30 | root_val_index = inorder.index(root_val) 31 | root = TreeNode(root_val) 32 | post_left = postorder[:root_val_index] 33 | post_right = postorder[root_val_index:-1] 34 | in_left = inorder[:root_val_index] 35 | in_right = inorder[root_val_index+1:] 36 | root.left = buildTree(in_left,post_left) 37 | root.right = buildTree(in_right,post_right) 38 | return root 39 | -------------------------------------------------------------------------------- /101-150/107-binary-tree-level-traversal-II.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, return the bottom-up level order traversal of its nodes' values. 4 | (ie, from left to right, level by level from leaf to root). 5 | 6 | CLARIFICATIONS 7 | - Do we print all 'None' values that a level may have? Yes. 8 | 9 | EXAMPLES 10 | [3,9,20,null,null,15,7] -> [[15,7],[9,20],[3]] 11 | 12 | COMMENTS 13 | - The usual tree level traversal would work 14 | (https://github.com/RatulSaha/leetcode/blob/master/101-150/102-binary-tree-level-order-reversal.py). 15 | - We can use deque from collections module to do appendleft instead of append. 16 | """ 17 | 18 | def levelOrderBottom(root): 19 | """ 20 | :type root: TreeNode 21 | :rtype: List[List[int]] 22 | """ 23 | to_return = [] 24 | if not root: 25 | return to_return 26 | level = [root] 27 | while level: 28 | current_level_val = [node.val for node in level] 29 | to_return = [current_level_val] + to_return 30 | next_level = [] 31 | for node in level: 32 | if node.left is not None: 33 | next_level.append(node.left) 34 | if node.right is not None: 35 | next_level.append(node.right) 36 | level = next_level 37 | return to_return 38 | -------------------------------------------------------------------------------- /101-150/108-sorted-array-bst.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an array where elements are sorted in ascending order, convert it to a height balanced BST. 4 | 5 | CLARIFICATIONS 6 | - Can we assume that there is no duplicate in the array? Yes. 7 | 8 | EXAMPLES 9 | (needs to be drawn) 10 | 11 | COMMENTS 12 | - The two base cases are empty list, which transforms into None, and single element list, which 13 | transforms into a TreeNode. 14 | - Then, recursively, the root is the node with (n/2)th value, and then we can recursively call 15 | the left and right subtree. 16 | """ 17 | 18 | def sortedArrayToBST(nums): 19 | """ 20 | :type nums: List[int] 21 | :rtype: TreeNode 22 | """ 23 | if not nums: 24 | return None 25 | n = len(nums) 26 | if n == 1: 27 | return TreeNode(nums[0]) 28 | root = TreeNode(nums[n/2]) 29 | root.left = sortedArrayToBST(nums[:n/2]) 30 | root.right = sortedArrayToBST(nums[n/2+1:]) 31 | return root 32 | -------------------------------------------------------------------------------- /101-150/109-sorted-linked-list-bst.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. 4 | 5 | CLARIFICATIONS 6 | - Can we assume that there is no duplicate element in the list? Sure. 7 | 8 | EXAMPLES 9 | (needs to be drawn) 10 | 11 | COMMENTS 12 | - We can do it similar to the array version, except we need to remember the size of the 13 | linked list under consideration. 14 | - We use a helper function that takes the head and the supposed size of the array and 15 | recursively builds the tree. 16 | - The base case is for n<2. 17 | """ 18 | 19 | def sortedListToBST(head): 20 | """ 21 | :type head: ListNode 22 | :rtype: TreeNode 23 | """ 24 | if head is None: 25 | return None 26 | curr = head 27 | n = 1 28 | while curr.next is not None: 29 | curr = curr.next 30 | n += 1 31 | return _sorted_list_BST(head,n) 32 | 33 | def _sorted_list_BST(head, n): 34 | if not n: 35 | return None 36 | if n == 1: 37 | return TreeNode(head.val) 38 | curr = head 39 | m = n/2 40 | for _ in range(m): 41 | curr = curr.next 42 | root = TreeNode(curr.val) 43 | root.left = _sorted_list_BST(head,m) 44 | if not n%2: 45 | m = n/2-1 46 | root.right = _sorted_list_BST(curr.next,m) 47 | return root 48 | -------------------------------------------------------------------------------- /101-150/110-balanced-binary-tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, determine if it is height-balanced. 4 | 5 | CLARIFICATIONS 6 | - A height-balanced binary tree is defined as a binary tree in which the depth of the 7 | two subtrees of every node never differ by more than 1. Yes. 8 | 9 | EXAMPLES 10 | (needs to be drawn) 11 | 12 | COMMENTS 13 | - Both the left and right subtree has to be height-balanced. 14 | - Also, the max depth of the left and right subtrees can not differ more than one. 15 | """ 16 | 17 | def isBalanced(root): 18 | """ 19 | :type root: TreeNode 20 | :rtype: bool 21 | """ 22 | if root is None: 23 | return True 24 | return (abs(_max_depth(root.left) - _max_depth(root.right)) <= 1) and (isBalanced(root.left)) and (isBalanced(root.right)) 25 | 26 | def _max_depth(root): 27 | if root is None: 28 | return 0 29 | return 1 + max(_max_depth(root.left), _max_depth(root.right)) 30 | -------------------------------------------------------------------------------- /101-150/111-min-depth-binary-tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, find its minimum depth. 4 | The minimum depth is the number of nodes along the shortest path 5 | from the root node down to the nearest leaf node. 6 | 7 | CLARIFICATIONS 8 | - The root is not leaf for trees with levels more than one? Yes. 9 | 10 | EXAMPLES 11 | (needs to be drawn) 12 | 13 | COMMENTS 14 | - A recursive solution checking the existence left and right subtree should work. 15 | """ 16 | 17 | def minDepth(root): 18 | """ 19 | :type root: TreeNode 20 | :rtype: int 21 | """ 22 | if not root: 23 | return 0 24 | if not root.left: 25 | return 1 + minDepth(root.right) 26 | if not root.right: 27 | return 1 + minDepth(root.left) 28 | return 1 + min(minDepth(root.left), minDepth(root.right)) 29 | -------------------------------------------------------------------------------- /101-150/112-path-sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree and a sum, determine if the tree has a root-to-leaf path 4 | such that adding up all the values along the path equals the given sum. 5 | 6 | CLARIFICATIONS 7 | - 8 | 9 | EXAMPLES 10 | (needs to be drawn) 11 | 12 | COMMENTS 13 | - A recursive solution over the left and right subtree should work. 14 | - The base cases are tricky, since we need to track both None and when the 15 | sum finishes. 16 | """ 17 | 18 | def hasPathSum(root, sum): 19 | """ 20 | :type root: TreeNode 21 | :type sum: int 22 | :rtype: bool 23 | """ 24 | if root is None and sum is 0: 25 | return False 26 | if not root: 27 | return False 28 | if (not root.left) and (not root.right) and (root.val == sum): 29 | return True 30 | return hasPathSum(root.left, sum-root.val) or hasPathSum(root.right, sum-root.val) 31 | -------------------------------------------------------------------------------- /101-150/113-path-sum-II.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. 4 | 5 | CLARIFICATIONS 6 | - Should I return a list of paths? Yes. 7 | - Does the order of the list of paths matter? No. 8 | - Will each path have to be well-ordered? Yes. 9 | 10 | EXAMPLES 11 | (needs to be drawn) 12 | 13 | COMMENTS 14 | - We can recursively find if such a path exists, but we need to remember the path so far 15 | (prefix of a possible path). 16 | - Since there is a unique way to reach a node from the root, for each node at recursion, 17 | only one path has to be remembered. 18 | - However, since a path and all its prefix has to be remembered, it is not space-efficient. 19 | - We somehow have to remember the path prefix efficiently, a parent relation using Python dictionary 20 | seems to be possible. 21 | - We have to remember the leaves where a valid path ends, so that at the end, we can simply traverse back 22 | using the parent relation and get the entire path. Python deque with appendleft would be a good idea to 23 | have that path, I can later convert it to a list. 24 | - We also have to do all the recursion in-place in the sense that this parent relation and the set of valid 25 | leaves have to be built on-the-fly. 26 | """ 27 | 28 | def pathSum(root, sum): 29 | """ 30 | :type root: TreeNode 31 | :type sum: int 32 | :rtype: List[List[int]] 33 | """ 34 | to_return = [] 35 | if root is None: 36 | return to_return 37 | parent, leaves = {}, [] 38 | _path_sum_helper(root, sum, parent, leaves) 39 | for node in leaves: 40 | path = deque() 41 | while node in parent: 42 | path.appendleft(node.val) 43 | node = parent[node] 44 | path.appendleft(node.val) 45 | to_return.append(list(path)) 46 | return to_return 47 | 48 | def _path_sum_helper(node, sum, parent, leaves): 49 | if node is None: 50 | return 51 | if (node.val == sum) and (node.left is None) and (node.right is None): 52 | leaves.append(node) 53 | return 54 | if node.left is not None: 55 | parent[node.left] = node 56 | if node.right is not None: 57 | parent[node.right] = node 58 | _path_sum_helper(node.left, sum-node.val, parent, leaves) 59 | _path_sum_helper(node.right, sum-node.val, parent, leaves) 60 | -------------------------------------------------------------------------------- /101-150/114-flattern-list.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree, flatten it to a linked list in-place. 4 | 5 | CLARIFICATIONS 6 | - Does the order in the linked list matter? Technically it should not, but 7 | leetcode OJ cares about a DFS-oriented order. 8 | 9 | EXAMPLES 10 | (needs to be drawn) 11 | 12 | COMMENTS 13 | - We run a vanilla DFS and store all the values in a list. 14 | - Then we reconstruct the tree in-place. 15 | - Both time and space complexity are linear in size of the tree. 16 | """ 17 | 18 | def flatten(root): 19 | """ 20 | :type root: TreeNode 21 | :rtype: void Do not return anything, modify root in-place instead. 22 | """ 23 | if root is None: 24 | return 25 | stack, visited = deque(), [] 26 | stack.appendleft(root.right) 27 | stack.appendleft(root.left) 28 | while stack: 29 | node = stack.popleft() 30 | if node is None: 31 | continue 32 | stack.appendleft(node.right) 33 | stack.appendleft(node.left) 34 | visited.append(node.val) 35 | root.left = None 36 | curr = root 37 | for val in visited: 38 | curr.right = TreeNode(val) 39 | curr = curr.right 40 | -------------------------------------------------------------------------------- /101-150/118-pascal-triangle.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given numRows, generate the first numRows of Pascal's triangle. 4 | 5 | CLARIFICATIONS 6 | - Can we assume numRows is positive? Yes. 7 | 8 | EXAMPLES 9 | numRows = 5 -> 10 | [ 11 | [1], 12 | [1,1], 13 | [1,2,1], 14 | [1,3,3,1], 15 | [1,4,6,4,1] 16 | ] 17 | 18 | COMMENTS 19 | - The base case is for numRows = 1 and 2. 20 | - Then each row is started and ended with 1, and the rest of the element are sum of consecutive elements 21 | in the previous row. 22 | - Technically, using a deque would be faster, but for simplicity let's use Python lists and their addition. 23 | """ 24 | 25 | def generate(numRows): 26 | """ 27 | :type numRows: int 28 | :rtype: List[List[int]] 29 | """ 30 | if not numRows: 31 | return [] 32 | if numRows == 1: 33 | return [[1]] 34 | to_return = [[1],[1,1]] 35 | for row in range(2, numRows): 36 | last_row = to_return[-1] 37 | to_return.append([1]+[last_row[i]+last_row[i+1] for i in range(len(last_row)-1)]+[1]) 38 | return to_return 39 | -------------------------------------------------------------------------------- /101-150/119-pascal-triangle-II.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an index k, return the kth row of the Pascal's triangle. 4 | 5 | CLARIFICATIONS 6 | - Can we assume numRows is positive? Yes. 7 | 8 | EXAMPLES 9 | k = 3 -> [1,3,3,1]] 10 | 11 | COMMENTS 12 | - The base case is for numRows = 1 and 2. 13 | - Then each row is started and ended with 1, and the rest of the element are sum of consecutive elements 14 | in the previous row. 15 | - Technically, using a deque would be faster, but for simplicity let's use Python lists and their addition. 16 | """ 17 | 18 | def getRow(rowIndex): 19 | """ 20 | :type rowIndex: int 21 | :rtype: List[int] 22 | """ 23 | if rowIndex == 0: 24 | return [1] 25 | if rowIndex == 1: 26 | return [1,1] 27 | current_row = [1,1] 28 | for i in range(rowIndex-1): 29 | next_row = [1]+[current_row[i]+current_row[i+1] for i in range(len(current_row)-1)]+[1] 30 | current_row = next_row 31 | return current_row 32 | -------------------------------------------------------------------------------- /101-150/120-triangle.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below. 4 | 5 | CLARIFICATIONS 6 | - Starting with a single-element row, at each row, the number of elements increase by one? Yes. 7 | - The adjacent of the jth element in the ith row are jth and (j+1)th elements in the (i+1)th row? 8 | 9 | EXAMPLES 10 | [ 11 | [2], 12 | [3,4], 13 | [6,5,7], 14 | [4,1,8,3] 15 | ] -> 16 | 11 (because: 2 + 3 + 5 + 1 = 11) 17 | 18 | COMMENTS 19 | - We recursively compute the minimum path sum from each element to the bottom. 20 | - We compute that starting from the bottom and store the results in a cache. 21 | - The cache is initiated with the last array (that's the sum from an element in the last array 22 | to reach the last array). 23 | - Then, we can use this linear space to compute for the rest of the elements. 24 | - At the end, the first element in the cache would be the result. 25 | """ 26 | 27 | def minimumTotal(triangle): 28 | """ 29 | :type triangle: List[List[int]] 30 | :rtype: int 31 | """ 32 | if not triangle: 33 | return 0 34 | n = len(triangle) 35 | current_cache = triangle[-1] 36 | 37 | for i in range(n-2, -1, -1): 38 | for j in range(i+1): 39 | current_cache[j] = triangle[i][j] + min(current_cache[j], current_cache[j+1]) 40 | 41 | return current_cache[0] 42 | -------------------------------------------------------------------------------- /101-150/121-buy-sell-stock.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an array for which the i-th element is the price of a given stock on day i. 4 | 5 | If you were only permitted to complete at most one transaction (ie, buy one and sell 6 | one share of the stock), find the maximum profit. 7 | 8 | CLARIFICATIONS 9 | - What happens when there is no solution? Assume solution exists. 10 | - Can the list be empty? No. 11 | - Is the list sorted? Not necessarily. 12 | 13 | EXAMPLES 14 | [7, 1, 5, 3, 6, 4] -> 5 (because 6-1=5) 15 | 16 | COMMENTS 17 | - The buying price starts with the first day price. 18 | - We keep store of the profit (which is zero to start with) and update it 19 | along with the buying price when I get a smaller possible buying price. 20 | - This trick is similar to Kadane's algorithm. In fact, if we convert this array 21 | to only store the differences between consecutive days, this would transform exactly 22 | to finding maximum sum subarray. 23 | """ 24 | 25 | def maxProfit(prices): 26 | """ 27 | :type prices: List[int] 28 | :rtype: int 29 | """ 30 | if not prices: 31 | return 0 32 | max_profit = 0 33 | buying_price = prices[0] 34 | for i in range(1, len(prices)): 35 | if (prices[i]-buying_price > 0): 36 | max_profit = max(max_profit, prices[i]-buying_price) 37 | else: 38 | buying_price = prices[i] 39 | return max_profit 40 | -------------------------------------------------------------------------------- /101-150/122-buy-sell-stock-II.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an array for which the ith element is the price of a given stock on day i, find the maximum profit. 4 | You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). 5 | However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). 6 | 7 | CLARIFICATIONS 8 | - The prices are positive, I assume? Yes. 9 | 10 | EXAMPLES 11 | [1,3,2,3] -> 3 12 | 13 | COMMENTS 14 | - There is no point to buying and selling at the same day, so we can assume transactions 15 | happend in different days. 16 | - Starting at the first day, we should go as low as we can to find the buying price, and then 17 | as high as we can to get the selling price. The difference is added to total profit. We continue 18 | until we finish the given list. 19 | """ 20 | 21 | def maxProfit(self, prices): 22 | """ 23 | :type prices: List[int] 24 | :rtype: int 25 | """ 26 | if not prices: 27 | return 0 28 | index, profit = 0, 0 29 | while index < len(prices)-1: 30 | try: 31 | while prices[index+1] <= prices[index]: 32 | index += 1 33 | except IndexError: 34 | break 35 | 36 | buying_price = prices[index] 37 | while index < len(prices)-1 and prices[index+1] >= prices[index]: 38 | index += 1 39 | selling_price = prices[index] 40 | 41 | profit += selling_price-buying_price 42 | index += 1 43 | return profit 44 | -------------------------------------------------------------------------------- /101-150/125-valid-palindrome.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. 4 | 5 | CLARIFICATIONS 6 | - We can assume the characters can be converted into lowercases? Yes. 7 | - is Empty string a valid palindrome? Yes. 8 | 9 | EXAMPLES 10 | "A man, a plan, a canal: Panama" -> True 11 | "race a car" -> False 12 | 13 | COMMENTS 14 | - We convert the string to a new string ignoring alphanumeric. 15 | - Then we put two pointers at start and end of the string and compare 16 | each character (lowered) till either the pointers cross path or a mismatch is found. 17 | """ 18 | 19 | def isPalindrome(s): 20 | """ 21 | :type s: str 22 | :rtype: bool 23 | """ 24 | s = ''.join(c for c in s if c.isalnum()) 25 | if not s: 26 | return True 27 | start, end = 0, len(s)-1 28 | while (abs(start-end)>1): 29 | if (s[start].lower() != s[end].lower()): 30 | return False 31 | start += 1 32 | end -= 1 33 | if (start==end): 34 | return True 35 | if ((end-start)==1): 36 | return s[start].lower()==s[end].lower() 37 | -------------------------------------------------------------------------------- /101-150/127-word-ladder.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given two words (beginWord and endWord), and a dictionary's word list, 4 | find the length of shortest transformation sequence from beginWord to endWord, such that: 5 | (i) Only one letter can be changed at a time, and (ii) each transformed word must exist in 6 | the word list. Note that beginWord is not a transformed word. 7 | 8 | CLARIFICATIONS 9 | - Are all words of the same length? You can assume that. 10 | - What should the return value be if there is no such transformation sequence? Return 0. 11 | - Can there be duplicate in the word list? No. 12 | - Are the words only of lowercase letter? Yes. 13 | 14 | EXAMPLES 15 | beginWord = "hit" 16 | endWord = "cog" 17 | wordList = ["hot","dot","dog","lot","log","cog"] 18 | -> 5 (because "hit" -> "hot" -> "dot" -> "dog" -> "cog") 19 | 20 | COMMENTS 21 | - We can do a BFS using a queue and remembering the path so far, and the first path returned 22 | would be the shortest path. 23 | - The next set of words in the transformation can be generated from tweaking the word one character 24 | at a time and checking if that is in the word list. 25 | - Such a BFS, while works, may have room for improvement. The following code times out in leetcode OJ after 21 testcases. 26 | - A clever technique is to do a two-way BFS, that is not presented here 27 | (https://discuss.leetcode.com/topic/19721/share-my-two-python-solutions-a-very-concise-one-12-lines-160ms-and-an-optimized-solution-100ms). 28 | """ 29 | 30 | def ladderLength(beginWord, endWord, wordList): 31 | """ 32 | :type beginWord: str 33 | :type endWord: str 34 | :type wordList: Set[str] 35 | :rtype: int 36 | """ 37 | 38 | queue = deque() 39 | queue.append((beginWord, [beginWord])) 40 | while queue: 41 | node, path = queue.popleft() 42 | for next in self.next_nodes(node, wordList) - set(path): 43 | if next == endWord: 44 | return len(path) + 1 45 | else: 46 | queue.append((next, path + [next])) 47 | return 0 48 | 49 | def next_nodes(word, word_list): 50 | to_return = set() 51 | for w in word_list: 52 | mismatch_count, w_length = 0, len(w) 53 | for i in range(w_length): 54 | if w[i] != word[i]: 55 | mismatch_count += 1 56 | if mismatch_count == 1: 57 | to_return.add(w) 58 | return to_return 59 | -------------------------------------------------------------------------------- /101-150/128-longest-consecutive-sequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an unsorted array of integers, find the length of the longest consecutive elements sequence. 4 | 5 | CLARIFICATIONS 6 | - Does longest consecutive mean the order of the elements can be altered? Yes. 7 | - For empty array, we should return 0? Yes. 8 | 9 | EXAMPLES 10 | [100, 4, 200, 1, 3, 2] -> 4 (because [1, 2, 3, 4]) 11 | 12 | COMMENTS 13 | - We can sort the array and use two pointers to determine the longest sequence of consecutive elements. 14 | It will be O(nlogn) time complexity. 15 | - A clever O(n) time and O(n) space complexity solution 16 | (thanks to https://discuss.leetcode.com/topic/15383/simple-o-n-with-explanation-just-walk-each-streak) 17 | is to convert the array into set and for each element in the set, check if it is the start of 18 | a possible sequence of consecutive elements. 19 | 20 | """ 21 | 22 | def longestConsecutive(nums): 23 | """ 24 | :type nums: List[int] 25 | :rtype: int 26 | """ 27 | arr_set = set(nums) 28 | to_return = 0 29 | for elem in arr_set: 30 | if elem-1 in arr_set: 31 | continue 32 | curr_val, max_streak = elem, 0 33 | while curr_val in arr_set: 34 | max_streak += 1 35 | curr_val += 1 36 | to_return = max(to_return, max_streak) 37 | return to_return 38 | -------------------------------------------------------------------------------- /101-150/129-sum-root-to-leaf.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number. 4 | Find the total sum of all root-to-leaf numbers. 5 | 6 | CLARIFICATIONS 7 | - The binary tree can be of an arbitrary structure? Yes. 8 | - Should we return 0 in case the tree is empty? Yes. 9 | - Do I consider when the sum may overflow (less concern in Python, though)? No. 10 | 11 | EXAMPLES 12 | A tree with root value 1 and two leaves 2 and 3 -> 25 (because 12+13 = 25). 13 | 14 | COMMENTS 15 | - One way would be to traverse the tree with something like DFS and get all possible root-to-leaf paths and add the 16 | corresponding numbers. However, same nodes will be stored over and over (for example, the root will be stored 17 | for each path). 18 | - Instead, we can traverse the tree and build a parent relation, while noting down the leaves. The values in leaves 19 | are the unit's place in the corresponding numbers. 20 | - We can then traverse back using the parent relation and add to the sum similar to the way one calculates sum of a 21 | number of digits by hand, starting from the unit's digit. 22 | """ 23 | 24 | 25 | def sumNumbers(root): 26 | """ 27 | :type root: TreeNode 28 | :rtype: int 29 | """ 30 | if root is None: return 0 31 | parent, leaves = {}, [] 32 | stack = [root] 33 | while stack: 34 | node = stack.pop() 35 | if node.left is None and node.right is None: 36 | leaves.append(node) 37 | continue 38 | if node.left is not None: 39 | parent[node.left] = node 40 | stack.append(node.left) 41 | if node.right is not None: 42 | parent[node.right] = node 43 | stack.append(node.right) 44 | to_return, carry, i = 0, 0, 0 45 | while leaves: 46 | to_add = sum([node.val for node in leaves]) + carry 47 | carry, digit = divmod(to_add, 10) 48 | to_return += digit*(10**i) 49 | leaves = [parent[node] for node in leaves if node in parent] 50 | i += 1 51 | to_return += carry*(10**i) 52 | return to_return 53 | -------------------------------------------------------------------------------- /101-150/130-surrounded-regions.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'. 4 | A region is captured by flipping all 'O's into 'X's in that surrounded region. 5 | 6 | CLARIFICATIONS 7 | - Should we update the board in-place? Yes. 8 | 9 | EXAMPLES 10 | X X X X 11 | X O O X 12 | X X O X 13 | X O X X 14 | -> 15 | X X X X 16 | X X X X 17 | X X X X 18 | X O X X 19 | 20 | COMMENTS 21 | - A possible region is not surrounded if it "leaks" to the boundary. 22 | - Hence, we start with the boundary "O" values, and then traverse the board using 23 | DFS to find all "O" values that are not surrounded. 24 | - Then, in one pass, we convert all "O"s that are surrounded, using the previously 25 | built set of "O"s that should not be converted. 26 | """ 27 | 28 | def solve(board): 29 | """ 30 | :type board: List[List[str]] 31 | :rtype: void Do not return anything, modify board in-place instead. 32 | """ 33 | if not board: 34 | return 35 | m, n = len(board), len(board[0]) 36 | if m < 3 or n < 3: 37 | return 38 | boundary_bad = set() 39 | for i in range(m): 40 | if board[i][0] == 'O': 41 | boundary_bad.add((i,0)) 42 | if board[i][n-1] == 'O': 43 | boundary_bad.add((i,n-1)) 44 | 45 | for j in range(n): 46 | if board[0][j] == 'O': 47 | boundary_bad.add((0,j)) 48 | if board[m-1][j] == 'O': 49 | boundary_bad.add((m-1,j)) 50 | 51 | insider_bad = set() 52 | for boundary_bad_point in boundary_bad: 53 | visited, stack = set(), [boundary_bad_point] 54 | while stack: 55 | point = stack.pop(0) 56 | for neigh_point in _neighbors(point, board, m, n): 57 | if neigh_point not in visited and neigh_point not in insider_bad: 58 | stack.append(neigh_point) 59 | insider_bad.add(neigh_point) 60 | visited.add(neigh_point) 61 | 62 | for i in range(1, m-1): 63 | for j in range(1, n-1): 64 | if (i,j) not in insider_bad: 65 | board[i][j] = 'X' 66 | 67 | def _neighbors((i,j), board, m, n): 68 | possible_neighbors = [(i+1,j), (i-1,j), (i,j-1), (i,j+1)] 69 | return [(i,j) for (i,j) in possible_neighbors if i >= 0 and j >= 0 and i < m and j < n and board[i][j] == 'O'] 70 | -------------------------------------------------------------------------------- /151-200/151-reverse-words-in-string.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an input string, reverse the string word by word. 4 | 5 | CLARIFICATIONS 6 | - Are spaces at the start allowed? No. 7 | - Can words have extra spaces between? Yes, remove them. 8 | - Can we use a ready, in-place reverse() in python? Sure. 9 | 10 | EXAMPLES 11 | "the sky is blue" -> "blue is sky the" 12 | 13 | COMMENTS 14 | - We can split the list by single spaces, then strip extra spaces at start and end of each word and then join back. 15 | """ 16 | 17 | if not s: 18 | return s 19 | s = map(lambda x: x.strip(), s.split(' ')) 20 | s = [word for word in s if word] 21 | s.reverse() 22 | return ' '.join(s) 23 | -------------------------------------------------------------------------------- /151-200/152-max-product-subarray.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Find the contiguous subarray within an array (containing at least one number) which has the largest product. 4 | 5 | CLARIFICATIONS 6 | - Do I return the subarray or the maximum product? Maximum product is fine. 7 | - Can the list be empty? No. 8 | 9 | EXAMPLES 10 | [2,3,-2,4] -> 6 ([2,3]) 11 | 12 | COMMENTS 13 | - This is similar to the Kadane's algorithm for maximum sum subarray. 14 | - In this case, we need to keep two local variables, min and max. 15 | - O(n) time complexity and constant space complexity. 16 | """ 17 | 18 | def maxProduct(self, nums): 19 | """ 20 | :type nums: List[int] 21 | :rtype: int 22 | """ 23 | max_so_far=big=small=nums[0] 24 | len_nums = len(nums) 25 | for i in range(1, len_nums): 26 | n = nums[i] 27 | big,small = max(n,n*big,n*small), min(n,n*big,n*small) 28 | max_so_far = max(big,max_so_far) 29 | return max_so_far 30 | -------------------------------------------------------------------------------- /151-200/179-largest-number.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a list of non negative integers, arrange them such that they form the largest number. 4 | 5 | CLARIFICATIONS 6 | - Do I return the integer? No, return it as a string, the interger can be very large. 7 | 8 | EXAMPLES 9 | [3, 30, 34, 5, 9] -> "9534330" 10 | 11 | COMMENTS 12 | - We can pass the list and insert each element in the to-be-returned list (can be joined later) 13 | at appropriate place. 14 | - O(n^2) time complexity and O(n) space complexity. 15 | """ 16 | 17 | def largestNumber(nums): 18 | if not nums: 19 | return "" 20 | to_return = [str(nums[0])] 21 | for i in range(1, len(nums)): 22 | to_insert = str(nums[i]) 23 | for j in range(len(to_return)): 24 | n = to_return[j] 25 | # return int(to_insert+n), 26 | if int(to_insert+n) >= int(n+to_insert): 27 | to_return = to_return[:j]+[to_insert]+to_return[j:] 28 | break 29 | else: 30 | to_return.append(to_insert) 31 | return ''.join(to_return).lstrip('0') or '0' 32 | -------------------------------------------------------------------------------- /201-250/222-count-complete-tree-node.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a complete binary tree, count the number of nodes. 4 | 5 | CLARIFICATIONS 6 | - So, I can assume the tree is complete, or have to check for that? You can assume that. 7 | - To reiterate, a complete binary tree only has the last level not filled. The last 8 | level is filled from the left, if any. 9 | 10 | EXAMPLES 11 | (not drawn) 12 | 13 | COMMENTS 14 | - We first have to figure out the height h of the tree. We can do that going as far left 15 | down as we can. 16 | - Then, the leaves can be counted separately, given the height. 17 | """ 18 | 19 | def countNodes(root): 20 | """ 21 | :type root: TreeNode 22 | :rtype: int 23 | """ 24 | if not root: 25 | return 0 26 | level = root 27 | height = 0 28 | while level.left != None: 29 | height += 1 30 | level = level.left 31 | if not height: 32 | return 1 33 | return (2**(height))-1 + _countLeaves(root, height) 34 | 35 | def _countLeaves(root, height): 36 | if height == 0: 37 | return 0 38 | 39 | h, level = height, root 40 | while level.left != None: 41 | h -= 1 42 | level = level.left 43 | if h: 44 | return 0 45 | 46 | h, level = height, root 47 | while level.right != None: 48 | h -= 1 49 | level = level.right 50 | if not h: 51 | return 2**height 52 | 53 | level, h = root.left, height-1 54 | if level == None: 55 | return 1 56 | while level.right != None: 57 | h -= 1 58 | level = level.right 59 | if not h: 60 | return 2**(height-1) + _countLeaves(root.right, height-1) 61 | else: 62 | return _countLeaves(root.left, height-1) 63 | -------------------------------------------------------------------------------- /301-350/307-range-sum-query-mutable.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. 4 | The update(i, val) function modifies nums by updating the element at index i to val. 5 | 6 | CLARIFICATIONS 7 | - Are these two the only functions available to access/modify the array? Yes. 8 | - How many times would the update and finding sum be called? They can be called the same number of times. 9 | - Is the list sorted? Not necessarily. 10 | 11 | EXAMPLES 12 | [1, 3, 5] 13 | sumRange(0, 2) -> 9 14 | update(1, 2) 15 | sumRange(0, 2) -> 8 16 | 17 | COMMENTS 18 | - The usual array has constant time update but O(n) to find the sum. 19 | - We can store another array to indicate the sum till i-th point, then the time complexity 20 | to update would be linear, but finding sum would be constant. We'd need additional O(n) 21 | space complexity array. 22 | - We can use binary index tree to do both the operations in O(log n). We'd need additional 23 | O(n) array, though. 24 | """ 25 | 26 | class NumArray(object): 27 | def __init__(self, nums): 28 | """ 29 | initialize your data structure here. 30 | :type nums: List[int] 31 | """ 32 | self.arr = nums 33 | self.size = len(nums) 34 | self.btree = [0 for _ in range(len(nums)+1)] 35 | for j in range(len(nums)): 36 | val = nums[j] 37 | i = j +1 38 | while i <= self.size: 39 | self.btree[i] += val 40 | i += i&(-i) 41 | 42 | def update(self, i, val): 43 | """ 44 | :type i: int 45 | :type val: int 46 | :rtype: int 47 | """ 48 | self.arr[i], diff = val, val- self.arr[i] 49 | i += 1 50 | while i <= self.size: 51 | self.btree[i] += diff 52 | i += i&(-i) 53 | 54 | def sumRange(self, i, j): 55 | """ 56 | sum of elements nums[i..j], inclusive. 57 | :type i: int 58 | :type j: int 59 | :rtype: int 60 | """ 61 | return self.sumTill(j)-self.sumTill(i-1) 62 | 63 | 64 | def sumTill(self, i): 65 | i += 1 66 | to_return = 0 67 | while i > 0: 68 | to_return += self.btree[i] 69 | i -= i&(-i) 70 | return to_return 71 | -------------------------------------------------------------------------------- /451-500/475-heaters.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given positions of houses and heaters on a horizontal line, 4 | find out minimum radius of heaters so that all houses could be covered by those heaters. 5 | 6 | CLARIFICATIONS 7 | - The radius that needs to be found would be universal for all heaters? Yes. 8 | - Is there a bound on range of houses/heaters? The number will be less than 25000 and the positions below 10^9. 9 | - Is the list of positions sorted? Let's start with that they are not sorted. 10 | 11 | EXAMPLES 12 | (house list, heater list) 13 | [1,2,3], [2] -> 1 14 | [1,2,3,4], [1,4] -> 1 15 | 16 | COMMENTS 17 | - We should start with sorting the list of heaters. 18 | - For each house, we can find the closest heater by binary search on the sorted list of heaters. 19 | - We'd keep a minimum radius so far, and the current radius for the heater. 20 | - The global minimum radius has to be definite small, so we can do float("-inf") or if the positions are non-negative, -1. 21 | - The time complexity is O (n log n + n log m) in terms of size of the heater list (n) and house list (m). 22 | """ 23 | 24 | def findRadius(houses, heaters): 25 | """ 26 | :type houses: List[int] 27 | :type heaters: List[int] 28 | :rtype: int 29 | """ 30 | if not heaters or not houses: 31 | return 0 32 | heaters.sort() 33 | min_radius = -1 34 | 35 | for house in houses: 36 | radius_so_far = abs(heaters[0]-house) 37 | start, end = 0, len(heaters)-1 38 | while start <= end: 39 | mid = (start+end)/2 40 | current_radius = abs(heaters[mid]-house) 41 | if current_radius < radius_so_far: 42 | radius_so_far = current_radius 43 | if not current_radius: 44 | break 45 | else: 46 | if house < heaters[mid]: 47 | end = mid-1 48 | else: 49 | start = mid+1 50 | if min_radius < radius_so_far: 51 | min_radius = radius_so_far 52 | return min_radius 53 | -------------------------------------------------------------------------------- /451-500/476-number-complement.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a positive integer, output its complement number. 4 | The complement strategy is to flip the bits of its binary representation. 5 | 6 | CLARIFICATIONS 7 | - The numbers are 32-bit binary number? Yes. 8 | - No leading zeros in the binary representation? Yes. 9 | 10 | EXAMPLES 11 | 5 (101) -> 2 (010) 12 | 13 | COMMENTS 14 | - We can do string manipulation with linear space and time complexity. 15 | - Bit manipulation is the expected way, I guess using left shift. 16 | """ 17 | 18 | def findComplement(num): 19 | """ 20 | :type num: int 21 | :rtype: int 22 | """ 23 | bin_num = bin(num)[2:] 24 | to_return = "" 25 | for c in bin_num: 26 | to_return += str(abs(int(c)-1)) 27 | return int(to_return,2) 28 | -------------------------------------------------------------------------------- /451-500/477-total-hamming-distance.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | The Hamming distance between two integers is the number of positions 4 | at which the corresponding bits are different. Find the total 5 | Hamming distance between all pairs of the given numbers. 6 | 7 | CLARIFICATIONS 8 | - The numbers are given as positive integers? Yes. 9 | 10 | EXAMPLES 11 | [4, 14, 2] -> 6 12 | 13 | COMMENTS 14 | - The naive solution is to do n choose 2 XOR operations. 15 | - A better solution is to keep a cache of XOR operations of the first element 16 | and the rest of the elements. The idea is to use the result 17 | XOR(XOR(a,b), XOR(b,c)) = XOR(a,c). The space complexity is O(n) and time 18 | complexity is O(n^2) 19 | - An even better solution is to use the very nifty trick that i-th position in 20 | the result is the combinations of ways to choose two values from the i-th 21 | positions in all the values (constant space complexity and linear time 22 | complexity). 23 | """ 24 | 25 | def totalHammingDistance(nums): 26 | """ 27 | :type nums: List[int] 28 | :rtype: int 29 | """ 30 | nums = map(lambda x: bin(x)[2:].zfill(32), nums) 31 | to_return = 0 32 | for i in range(32): 33 | zero_count, one_count = 0, 0 34 | for num in nums: 35 | if num[i] == '0': 36 | zero_count += 1 37 | else: 38 | one_count += 1 39 | to_return += zero_count*one_count 40 | return to_return 41 | 42 | def totalHammingDistance(nums): 43 | """ 44 | :type nums: List[int] 45 | :rtype: int 46 | """ 47 | 48 | if not nums: 49 | return 0 50 | n, to_return = len(nums), 0 51 | cache = [0 for _ in range(n)] 52 | 53 | for i in range(n): 54 | cache[i] = nums[i]^nums[0] 55 | to_return += bin(cache[i]).count("1") 56 | 57 | for i in range(1,n): 58 | for j in range(i+1,n): 59 | to_return += bin(cache[i]^cache[j]).count("1") 60 | return to_return 61 | -------------------------------------------------------------------------------- /451-500/481-magical-string.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | The string S consisting of only '1' and '2' is magical because concatenating the number of 4 | contiguous occurrences of characters '1' and '2' generates the string S itself. 5 | The first few elements of string S is the following: S = "1221121221221121122..." 6 | Given an integer N as input, return the number of '1's in the first N number in the magical string S. 7 | 8 | CLARIFICATIONS 9 | - Do we assume that the string S starts as given? Yes. 10 | 11 | EXAMPLES 12 | 6 (S=12211) => 3 13 | 14 | COMMENTS 15 | - We should keep generating the string S till length n, and keep an index to keep 16 | track of the item that represents the subarray at the end of S currently. 17 | - Since we may end up generating a little more than n, we should count the number of 18 | 1s carefully. 19 | """ 20 | 21 | def magicalString(self, n): 22 | """ 23 | :type n: int 24 | :rtype: int 25 | """ 26 | if not n: 27 | return 0 28 | if n <= 3: 29 | return 1 30 | s = "122" 31 | len_so_far, index = 3, 2 32 | while len_so_far <= n: 33 | curr_val = int(s[index]) 34 | if s[-1] == "2": 35 | s += "1"*curr_val 36 | else: 37 | s += "2"*curr_val 38 | index += 1 39 | len_so_far += curr_val 40 | 41 | to_return = 0 42 | for i in range(n): 43 | if s[i] == '1': 44 | to_return += 1 45 | return to_return 46 | -------------------------------------------------------------------------------- /451-500/482-license-key-formatting.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given an string (represents a software license key) consisting of alphanumeric and dashes, 4 | where the dashes are possibly misplaced, rewrite the string as group of K characters divided 5 | by new dashes. 6 | 7 | CLARIFICATIONS 8 | - Is the number of alphanumeric characters multiple of K? No, the first group may contain 9 | less than K, but not zero. 10 | - Can the list be empty? No. 11 | - Any lower/uppercase restriction? Assume you have to convert all lowercase to uppercase. 12 | 13 | EXAMPLES 14 | S = "2-4A0r7-4k", K = 4 -> "24A0-R74K" 15 | S = "2-4A0r7-4k", K = 3 -> "24-A0R-74K" 16 | 17 | COMMENTS 18 | - I should minimize string concatnation, so will use a queue/deque and then join it before returning. 19 | - Iterate the list from end, ignore the dashes, put a fresh dash after each K items. 20 | - We can use the in-built upper() on the whole string, or we can use upper() per character basis, checking 21 | if they are lower characters using ord(). 22 | """ 23 | 24 | from collections import deque 25 | 26 | def licenseKeyFormatting(self, S, K): 27 | """ 28 | :type S: str 29 | :type K: int 30 | :rtype: str 31 | """ 32 | to_return = deque() 33 | chunk_size = 0 34 | for i in range(len(S)-1, -1, -1): 35 | c = S[i] 36 | if c == '-': 37 | continue 38 | else: 39 | if chunk_size < K: 40 | to_return.appendleft(self._uppercase(c)) 41 | chunk_size += 1 42 | else: 43 | to_return.appendleft("-") 44 | to_return.appendleft(self._uppercase(c)) 45 | chunk_size = 1 46 | return ''.join(to_return) 47 | 48 | def _uppercase(self, c): 49 | if ord('a') <= ord(c) and ord(c) <= ord('z'): 50 | return c.upper() 51 | else: 52 | return c 53 | -------------------------------------------------------------------------------- /451-500/485-max-consecutive-ones.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given a binary array, find the maximum number of consecutive 1s in this array. 4 | 5 | CLARIFICATIONS 6 | - Reiterating, the items in the array can only be 0 and 1? Yes. 7 | 8 | EXAMPLES 9 | [1,1,0,1,1,1] -> 3 10 | 11 | COMMENTS 12 | - This is similar to maximum sum subarray problem, and a variation of Kadane's algorithm can be used. 13 | - We can keep a global max and a local max and reset the local max each time we hit a zero. 14 | """ 15 | 16 | def findMaxConsecutiveOnes(self, nums): 17 | """ 18 | :type nums: List[int] 19 | :rtype: int 20 | """ 21 | if not nums: 22 | return 0 23 | local_max, global_max = 0, 0 24 | for num in nums: 25 | if num == 0: 26 | global_max = max(global_max, local_max) 27 | local_max = 0 28 | else: 29 | local_max += 1 30 | return max(global_max, local_max) 31 | -------------------------------------------------------------------------------- /501/506-relative-ranks.py: -------------------------------------------------------------------------------- 1 | """ 2 | STATEMENT 3 | Given scores of N athletes, find their relative ranks and the people with the top three highest scores, 4 | who will be awarded medals: "Gold Medal", "Silver Medal" and "Bronze Medal". 5 | 6 | CLARIFICATIONS 7 | - Are the scores unique? Yes. 8 | - Are the scores bounded by some value? Not necessarily. 9 | - Do I need to output the rank of those from 4th position onwards? Yes. 10 | 11 | EXAMPLES 12 | [5, 4, 3, 2, 1] -> ["Gold Medal", "Silver Medal", "Bronze Medal", "4", "5"] 13 | 14 | COMMENTS 15 | - Since the scores are not bounded by any upper limit, it'd be hard, if not impossible, 16 | to improve time complexity than O(n log n). 17 | - If space complexity is not an issue, 18 | we can store an array of (score, index), and then sort that array by score. 19 | - Then we can build the output array based on the sorted by score array, but pulling out 20 | the elements by the index. 21 | - We can "try" to put the medal information for top three. If the size of the list is less than three, 22 | it will raise an IndexError, in which case we can simply pass. 23 | """ 24 | 25 | def findRelativeRanks(nums): 26 | """ 27 | :type nums: List[int] 28 | :rtype: List[str] 29 | """ 30 | n = len(nums) 31 | if not n: 32 | return nums 33 | indexed_arr = [(nums[i],i) for i in range(n)] 34 | indexed_arr.sort(reverse=True,key=lambda x:x[0]) 35 | to_return = [0 for _ in range(n)] 36 | for i in range(n): 37 | (score,index) = indexed_arr[i] 38 | to_return[index] = str(i+1) 39 | try: 40 | to_return[indexed_arr[0][1]] = "Gold Medal" 41 | to_return[indexed_arr[1][1]] = "Silver Medal" 42 | to_return[indexed_arr[2][1]] = "Bronze Medal" 43 | except IndexError: 44 | pass 45 | return to_return 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ratul Saha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # leetcode 2 | This repository contains solutions to a set of problems from https://leetcode.com (highly recommended for interview preperation). 3 | 4 | There are quite a few repositories available already that are more exhaustive, plus the discuss forum in leetcode is excellent too (go check them out!). This particular repository **serves a different purpose** -- it takes each problem as if posed by an interviewer, and very briefly chalks out the possible questions the interviewee should ask, the early comments one may make about their approach, and a very mediocre solution. Mostly easy and medium level problems are addressed. Please note that this should provide an interview prep suggestions, not the cool or even the most efficient solution. 5 | 6 | ## Assumptions 7 | Some basic data structure initiations may be pressumed and is listed below for references. 8 | 9 | A binary tree node is initiated as follows: 10 | 11 | ``` 12 | class TreeNode(object): 13 | def __init__(self, x): 14 | self.val = x 15 | self.left = None 16 | self.right = None 17 | ``` 18 | 19 | A linked list node is initiated as follows: 20 | 21 | ``` 22 | class ListNode(object): 23 | def __init__(self, x): 24 | self.val = x 25 | self.next = None 26 | ``` 27 | 28 | ## Issues 29 | - A small number of solutions may give a timeout in leetcode online judge. 30 | - Some solutions may have misplaced ```self```, because the solutions were checked by the leetcode OJ first. 31 | 32 | ## Disclaimers 33 | - A small number of problems may be influenced by solutions from elsewhere (such as discuss forum), but due credit is given in such case. 34 | - This list of problems is neither exclusive nor exhaustive. 35 | - This repository does not contain another important part of tech interviews, system design problems. 36 | --------------------------------------------------------------------------------