leetcode Flashcards

1
Q

STATEMENT
Given an array of integers, return indices of the two numbers such that they add up to a specific target.

EXAMPLES
[2, 7, 11, 15], 9 -> [0,1]

A

CLARIFICATIONS

  • What happens when there is no solution? Assume solution exists.
  • Can the list be empty? No.
  • Is the list sorted? Not necessarily.

COMMENTS
- We can ‘remember’ what we have seen so far in O(n) space complexity using a set.
- Iterate the list and at every step, we can look back and see there is a possible candidate for complement of the current item.
- O(n) time complexity and O(n) space complexity.
- To do without the space complexity, we may have to move to O(n log n) time complexity, by sorting the list
and then using two pointers from start and end.
“””

def twoSum(nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        hash_dict = {}
        for i in range(len(nums)):
            compl = target-nums[i]
            if compl in hash_dict:
                return [hash_dict[compl], i]
            hash_dict[nums[i]] = i
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

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

CLARIFICATIONS
- Do I return tuple of elements of their indexes? Elements.
- The solution may not exist, right? Yes.
- By unique, you mean, each tuple should be considered as set and then unique set of sets? Yes.
EXAMPLES
[-1, 0, 1, 2, -1, -4] ->
[
[-1, 0, 1],
[-1, -1, 2]
]

COMMENTS
- We can first try to solve the problem for two elements that add up to a given target.
- Then, we can use that function to solve this for each element in the array.
- We have to be extra careful to make the tuples unique.
- First, we would assume the two_sum function exists already and write that later.
- When we find a valid tuple, we need to sort them to ensure the uniqueness.
- We’d accumulate the tuples in a set, but since lists are mutable, they can’t be in a set.
We can make the valid tuple a string joined by a delimiter, say comma.

A
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        ans = []
        nums.sort()
        for i in range(len(nums)-2):
        if i>0 and nums[i]==nums[i-1]:
            continue
            l,r = i+1,len(nums)-1
            while l0:
                    r = r-1
                else:
                    ans.append((nums[i], nums[l], nums[r]))
                    while l < r and nums[l] == nums[l+1]:
                        l += 1
                    while l < r and nums[r] == nums[r-1]:
                        r -= 1
                    l = l+1
                    r = r-1
        return ans

time - O(n*n)
space- O(n)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Given an m x n matrix. If an element is 0, set its entire row and column to 0. Do it in-place.

Follow up:

A straight forward solution using O(mn) space is probably a bad idea.
A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?

Example 1:

Input: matrix = [[1,1,1],[1,0,1],[1,1,1]]
Output: [[1,0,1],[0,0,0],[1,0,1]]

A

class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
“””
Do not return anything, modify matrix in-place instead.
“””
is_col = False
R = len(matrix)
C = len(matrix[0])
for i in range(R):
# Since first cell for both first row and first column is the same i.e. matrix[0][0]
# We can use an additional variable for either the first row/column.
# For this solution we are using an additional variable for the first column
# and using matrix[0][0] for the first row.
if matrix[i][0] == 0:
is_col = True
for j in range(1, C):
# If an element is zero, we set the first element of the corresponding row and column to 0
if matrix[i][j] == 0:
matrix[0][j] = 0
matrix[i][0] = 0

    # Iterate over the array once again and using the first row and first column, update the elements.
    for i in range(1, R):
        for j in range(1, C):
            if not matrix[i][0] or not matrix[0][j]:
                matrix[i][j] = 0

    # See if the first row needs to be set to zero as well
    if matrix[0][0] == 0:
        for j in range(C):
            matrix[0][j] = 0

    # See if the first column needs to be set to zero as well        
    if is_col:
        for i in range(R):
            matrix[i][0] = 0

Time Complexity : O(M \times N)O(M×N)
Space Complexity : O(1)O(1)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

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

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

Example 1:

Input: strs = [“eat”,”tea”,”tan”,”ate”,”nat”,”bat”]
Output: [[“bat”],[“nat”,”tan”],[“ate”,”eat”,”tea”]]

A

class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = collections.defaultdict(list)
for s in strs:
ans[tuple(sorted(s))].append(s)
return ans.values()

Time Complexity: O(NK \log K)O(NKlogK), where NN is the length of strs, and KK is the maximum length of a string in strs. The outer loop has complexity O(N)O(N) as we iterate through each string. Then, we sort each string in O(K \log K)O(KlogK) time.

Space Complexity: O(NK)O(NK), the total information content stored in ans.

class Solution:
    def groupAnagrams(strs):
        ans = collections.defaultdict(list)
        for s in strs:
            count = [0] * 26
            for c in s:
                count[ord(c) - ord('a')] += 1
            ans[tuple(count)].append(s)
        return ans.values()

Time Complexity: O(NK)O(NK), where NN is the length of strs, and KK is the maximum length of a string in strs. Counting each string is linear in the size of the string, and we count every string.

Space Complexity: O(NK)O(NK), the total information content stored in ans.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

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

Example 1:

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

A
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        dict = {}
        start = maxl = 0
        for i,c in enumerate(s):
            if c in dict and start <= dict[c]:
                start = dict[c]+1
            else:
                maxl = max(maxl,  i - start +1)
            dict[c] = i
        return maxl

Time complexity : O(2n) = O(n)O(2n)=O(n). In the worst case each character will be visited twice by ii and jj.

Space complexity : O(min(m, n))O(min(m,n)). Same as the previous approach. We need O(k)O(k) space for the sliding window, where kk is the size of the Set. The size of the Set is upper bounded by the size of the string nn and the size of the charset/alphabet mm.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Longest Palindromic Substring

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”

A
class Solution:
    def longestPalindrome(self, s: str) -> str:
        res = ""
        for i in range(len(s)):
            # odd case, like "aba"
            tmp = self.helper(s, i, i)
            if len(tmp) > len(res):
                res = tmp
            # even case, like "abba"
            tmp = self.helper(s, i, i+1)
            if len(tmp) > len(res):
                res = tmp
        return res
# get the longest palindrome, l, r are the middle indexes   
# from inner to outer
def helper(self, s, l, r):
    while l >= 0 and r < len(s) and s[l] == s[r]:
        l -= 1; r += 1
    return s[l+1:r]

time: O(n*n)
space: O(n)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Increasing Triplet Subsequence

Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the array.

Formally the function should:

Return true if there exists i, j, k
such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false.
Note: Your algorithm should run in O(n) time complexity and O(1) space complexity.

Example 1:

Input: [1,2,3,4,5]
Output: true
Example 2:

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

A
class Solution:
    def increasingTriplet(self, nums: List[int]) -> bool:
        # we can use two thresholds to divide the subsquence length
        # everything between threshold1 and threshold2 will form doublets
        # everything above threshold2 will form a triplet
        # dynamically change these two thresholds
        threshold1 = threshold2 = float("inf")
        for num in nums:
            # lower threshold1
            if num <= threshold1:
                threshold1 = num
            # lower threshold2
            elif num <= threshold2:
                threshold2 = num
            # if greater than both thresholds (note equal doesn't count)
            else:
                return True
        return False

O(n)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Add Two Numbers

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.

A
# Definition for singly-linked list.
# class ListNode:
#     def \_\_init\_\_(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        result = ListNode(0)
        result_tail = result
        c = 0
        while l1 or l2 or c:
            lv1 = l1.val if l1 else 0
            lv2 = l2.val if l2 else 0
            c,o = divmod(lv1+lv2+c,10)
        result_tail.next = ListNode(o)
        result_tail = result_tail.next                      

        l1 = (l1.next if l1 else None)
        l2 = (l2.next if l2 else None)

    return result.next

O(n)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Odd Even Linked List

Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes.

You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity.

Example 1:

Input: 1->2->3->4->5->NULL
Output: 1->3->5->2->4->NULL
Example 2:

Input: 2->1->3->5->6->4->7->NULL
Output: 2->3->6->7->1->5->4->NULL

A
# Definition for singly-linked list.
# class ListNode:
#     def \_\_init\_\_(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head:
            return head
    odd = head # Both of them point at the first node of the target linked list
    even = head.next # doesn't matter even there's only one node in the linked list (even will become None)
    eHead = even # We have to keep where the even-node list starts
        while even and even.next: # won't get in the loop at first if there's only one node in the linked list
            # both even and even.next are necessary condition because even might point to None, which has no attribute 'next'
            # AND, why these two, small discussion by myself as below
            odd.next = odd.next.next
            even.next = even.next.next
            # After these two ops, odd/even still points at its original place
            # Therefore, we move them to the next node repectively
            odd = odd.next
            even = even.next
    odd.next = eHead # the odd pointer currently points at the last node of the odd-node list

    return head # We keep the start of the odd-node list in the first of our code
time O(n)
space O(1)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Intersection of Two Linked Lists

Write a program to find the node at which the intersection of two singly linked lists begins.

A
# Definition for singly-linked list.
# class ListNode:
#     def \_\_init\_\_(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
    ta = headA
    tb = headB
    al = bl = 0

    while ta:
        al += 1
        ta = ta.next

    while tb:
        bl += 1
        tb = tb.next

    ta = headA
    tb = headB
        if al > bl:
            t = al - bl
            while t:
                ta = ta.next
                t -= 1
        elif bl > al:
            t = bl - al
            while t:
                tb = tb.next
                t -= 1
        while ta and tb:
                if ta == tb:
                    return ta
                ta = ta.next
                tb = tb.next
    return None

O(m+n)
O(1)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Binary Tree Inorder Traversal

Given the root of a binary tree, return the inorder traversal of its nodes’ values.

Follow up: Recursive solution is trivial, could you do it iteratively?

A
# Definition for a binary tree node.
# class TreeNode:
#     def \_\_init\_\_(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    # recursively
    def rinorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        self.helper(root, res)
        return res
def helper(self, root, res):
        if root:
            self.helper(root.left, res)
            res.append(root.val)
            self.helper(root.right, res)    
    #iterative
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res, stack = [], []
        while True:
            while root:
                stack.append(root)
                root = root.left
            if not stack:
                return res
            node = stack.pop()
            res.append(node.val)
            root = node.right
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Binary Tree Zigzag Level Order Traversal

Given a binary tree, return the zigzag level order traversal of its nodes’ values. (ie, from left to right, then right to left for the next level and alternate between).

A
if not root: return []
    res, temp, stack, flag=[], [], [root], 1
    while stack:
        for i in xrange(len(stack)):
            node=stack.pop(0)
            temp+=[node.val]
            if node.left: stack+=[node.left]
            if node.right: stack+=[node.right]
        res+=[temp[::flag]]
        temp=[]
        flag*=-1
    return res
# Definition for a binary tree node.
# class TreeNode:
#     def \_\_init\_\_(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        ans = []
        stack = []
        queue = []
        queue.append(root)
        while queue:
            t = []
            while queue:
                i = queue.pop()
                t.append(i.val)
                if i.left:
                    stack.append(i.left)
                if i.right:
                    stack.append(i.right)
            if t:
                ans.append(t)
            t = []
            while stack:   
                i = stack.pop()
                if i.right:
                    queue.append(i.right)
                if i.left:
                    queue.append(i.left)
                t.append(i.val)
            if t:
                ans.append(t)
    return ans
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Construct Binary Tree from Preorder and Inorder Traversal

Given preorder and inorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

For example, given

preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
Return the following binary tree:

    3
   / \
  9  20
    /  \
   15   7
A
# Definition for a binary tree node.
# class TreeNode:
#     def \_\_init\_\_(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if inorder:
            i = inorder.index(preorder.pop(0))
            root = TreeNode(inorder[i])
            root.left = self.buildTree(preorder, inorder[0:i])
            root.right = self.buildTree(preorder, inorder[i+1:])
            return root
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Populating Next Right Pointers in Each Node

Solution
You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has the following definition:

Input: root = [1,2,3,4,5,6,7]
Output: [1,#,2,3,#,4,5,6,7,#]

A

Since we are manipulating tree nodes on the same level, it’s easy to come up with
a very standard BFS solution using queue. But because of next pointer, we actually
don’t need a queue to store the order of tree nodes at each level, we just use a next
pointer like it’s a link list at each level; In addition, we can borrow the idea used in
the Binary Tree level order traversal problem, which use cur and next pointer to store
first node at each level; we exchange cur and next every time when cur is the last node
at each level.

class Solution:
    def connect(self, root: 'Node') -> 'Node':
        if not root:
            return None
        cur  = root
        next = root.left
        while cur.left :
            cur.left.next = cur.right
            if cur.next:
                cur.right.next = cur.next.left
                cur = cur.next
            else:
                cur = next
                next = cur.left
        return root
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Kth Smallest Element in a BST

Solution
Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.

Example 1:

Input: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
Output: 1
A
# Definition for a binary tree node.
# class TreeNode:
#     def \_\_init\_\_(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def kthSmallest(self, root: TreeNode, k: int) -> int:
        stack = []
        while True:
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            k -= 1
            if not k:
                return root.val
            root = root.right

Time complexity: \mathcal{O}(H + k)O(H+k), where HH is a tree height. This complexity is defined by the stack, which contains at least H + kH+k elements, since before starting to pop out one has to go down to a leaf. This results in \mathcal{O}(\log N + k)O(logN+k) for the balanced tree and \mathcal{O}(N + k)O(N+k) for completely unbalanced tree with all the nodes in the left subtree.
Space complexity: \mathcal{O}(H)O(H) to keep the stack, where HH is a tree height. That makes \mathcal{O}(N)O(N) in the worst case of the skewed tree, and \mathcal{O}(\log N)O(logN) in the average case of the balanced tree.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Number of Islands

Solution
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: grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
Output: 1
A
class Solution:
    def numIslands(self, grid):
        if not grid:
            return 0
        count = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '1':
                    self.dfs(grid, i, j)
                    count += 1
        return count
    def dfs(self, grid, i, j):
        if i<0 or j<0 or i>=len(grid) or j>=len(grid[0]) or grid[i][j] != '1':
            return
        grid[i][j] = '#'
        self.dfs(grid, i+1, j)
        self.dfs(grid, i-1, j)
        self.dfs(grid, i, j+1)
        self.dfs(grid, i, j-1)