skip to content
logo Yu's Blog

Leetcode note

/

Two pointer

  • 34. Find First and Last Position of Element in Sorted Array
class Solution:
    def bs(self, arr: list[int], target: int, first: bool) -> int:
        l, r = 0, len(arr) - 1
        idx = -1
        while l <= r:
            mid = (l + r) // 2
            if arr[mid] == target:
                idx = mid
                if first: # find first
                    r = mid - 1
                else: # fird last
                    l = mid + 1
            elif arr[mid] > target:
                r = mid - 1
            else:
                l = mid + 1
        return idx
        
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        i1 = self.bs(nums, target, True)
        if i1 == -1:
            return [-1, -1]
        i2 = self.bs(nums, target, False)
        return [i1, i2]
  • 153. Find Minimum in Rotated Sorted Array, (fissible function)
    def findMin(self, nums: List[int]) -> int:
        l, r, idx = 0, len(nums) -1, -1
        while l <=r :
            mid = (l +r) //2
            if(nums[mid] <= nums[-1]): # may equal
                idx = mid
                r = mid -1
            else:
                l = mid +1
        return nums[idx]
  • 852. Peak Index in a Mountain Array. [0,2,1,0] return idx 1
    def peakIndexInMountainArray(self, arr: List[int]) -> int:
        l, r, idx = 0, len(arr) - 1, -1
        while l <= r:
            mid = (l + r) //2
            if mid == len(arr) -1 or arr[mid] > arr[mid + 1]:
              # mid = len(arr) -1, is consider true
              # to fullfill the monotonous
                idx = mid
                r = mid - 1 
            else:
                l = mid + 1
        return idx
  ## or
        while l <= r:
            mid = (l + r) //2
            if mid ==0 or arr[mid - 1] < arr[mid]:
                idx = mid
                l = mid + 1 
            else:
                r = mid - 1
        return idx
  • 2395. subarray with equal sum
    def findSubarrays(self, nums: List[int]) -> bool:
        res = set()
        for i in range(1, len(nums)):
            cur_sum = nums[i] + nums[i-1]
            if cur_sum in res:
                return True
            res.add(cur_sum)
        return False
  • 170. Two Sum III - Data structure design
class TwoSum {
    arr: Map<number, number>
    constructor() {
        this.arr = new Map<number, number>()
    }
    add(number: number): void {
        this.arr.set(number, (this.arr.get(number) || 0) + 1)
    }
    find(value: number): boolean {
        for (const [key, freq] of this.arr) {
            const diff = value - key
            if (diff === key) {
                if (freq > 1) return true
                // return freq >1 incorrect, pre-return
            } else {
                if (this.arr.has(diff)) return true
            }
        }
        return false
    }

window

  • 346. Given a stream of integers and a window size, calculate the moving average. fixed window
class MovingAverage:
    def __init__(self, size: int):
        self.size = size
        self.que = collections.deque()
        self.sum = 0

    def next(self, val: int) -> float:
        if len(self.que) < self.size:
            # self.que.append(val)
            self.sum += val
        else:
            self.sum += val - self.que.popleft()
            # self.que.append(val)
        self.que.append(val)
        return self.sum / len(self.que)

# movingAverage = new MovingAverage(3);
# movingAverage.next(1); // return 1.0 = 1 / 1
# movingAverage.next(10); // return 5.5 = (1 + 10) / 2

— since we are not care about the order within the window, only need to keep track the last item. use single pointer.

class MovingAverage:
    def __init__(self, size: int):
        self.size = size
        self.que = [0] * size # need initial full size, or 
        self.sum = 0
        self.count = 0

    def next(self, val: int) -> float:
        idx = self.count % self.size ## last item 
        self.sum += val - self.que[idx]
        self.que[idx] = val ## not push, require preset length
        self.count += 1
        return self.sum / min(self.count, self.size)
  • 300. Longest Increasing Subsequence
    construct the growing window, maintain the first larger element.
class Solution:
    def first_large(self, arr: list[int], target: int) ->int:
        l, r, idx = 0, len(arr) -1, -1
        while l <= r:
            mid = ( l + r) //2
            if arr[mid] >= target:
                idx = mid
                r = mid -1
            else:
                l = mid +1 
        return idx

    def lengthOfLIS(self, nums: List[int]) -> int:
        res = []
        for i in nums:
            idx = self.first_large(res, i)
            if idx == -1:
                res.append(i)
            else:
                res[idx] = i
        return len(res)
def lengthOfLIS(self, nums: List[int]) -> int:
    n = len(nums)
    dp = [1] * n
    for right in range(1, n):
        for left in range(right):
            if nums[right] > nums[left]:
                if dp[right] < dp[left] + 1:
                    dp[right] = dp[left] + 1
# no dp[right] += 1 or dp[left] += 1
# use dp[left] + 1 to update dp[right]
# if right > left, then choose one of dp[left] to add one(to get larger res)
    return max(dp)
  • 674. Longest Continuous Increasing Subsequence
    def findLengthOfLCIS(self, nums: List[int]) -> int:
        # res = 1
        # cur_length = 1
        # for right in range(1, len(nums)):
        #     if nums[right] > nums[right-1]:
        #         cur_length  += 1
        #         res = max(res, cur_length)
        #     else:
        #         cur_length = 1
        # return res

        dp = [1] * len(nums)
        for right in range(1, len(nums)):
            if nums[right] > nums[right-1]:
                dp[right] =  dp[right-1] + 1
            #     res = max(res, cur_length)
            # else:
            #     cur_length = 1
        return max(dp)
  • 2099. find a subsequence of nums of length k that has the largest sum.
class Solution:
   def maxSubsequence(self, nums: List[int], k: int) -> List[int]:
       idxs = list(range(len(nums)))
       idxs.sort(key=lambda i: nums[i])
       sub_idxs = idxs[-k:]
       sub_idxs.sort()
       return [nums[i] for i in sub_idxs]

— or greedy, keep the min idx for each round.

    def maxSubsequence(self, nums: List[int], k: int) -> List[int]:
        res = []
        for val in nums:
            if len(res) < k:
                res.append(val)
                continue
            min_idx, min_v = 0, res[0]
            for i, v in enumerate(res):
                if v < min_v:
                    min_idx, min_v = i, v

            if val > min_v:
                res[min_idx] = v
        final = []
        res = Counter(res)
        for i in nums:
            # if i in res:j
                # final.append(i)
                # res.remove(i)
            # for j, v in enumerate(res):
            #     if i == v:
            #         final.append(i)
            #         res[j] = None
            #         break ## need break
            if res[i] > 0:
                final.append(i)
                res[i] -= 1

        return final

prefix sum

  • 523. Continuous Subarray Sum, the sum of the elements of the subarray is a multiple of k.
    def checkSubarraySum(self, nums: List[int], k: int) -> bool:
        pre = 0
        two_sum = {0: -1} ## 
        for i, v in enumerate(nums):
            pre += v
            rem  = pre % k
            if rem in two_sum:
                if i - two_sum[rem] >= 2:
                    return True
            else: ## need else to avoid overwrite first occurance
                two_sum[rem] = i
        return False
  • 560. Subarray Sum Equals K, the total number of continuous subarrays whose sum equals to k.
    def subarraySum(self, nums: List[int], k: int) -> int:
        pre = 0
        dic = {0: 1}
        res = 0
        for v in nums:
            pre += v
            diff = pre -k
            count += dic.get(diff, 0)
            dic[pre] = dic.get(pre, 0) + 1
        return count
# or
            if diff in dic:
                count += dic[diff]
            if pre in dic:
                dic[pre] += 1
            else:
                dic[pre] = 1

interval

interval function

def overlap(a, b):
    return not (b[1] < a[0] or a[1] < b[0]) 
const overlap = (a, b) => !(b[1] < a[0] || a[1] < b[0]);
  • 252. Meeting Rooms I
    def canAttendMeetings(self, intervals: List[List[int]) -> bool:
        intervals.sort()
        for i in range(1, len(intervals)):
            if intervals[i][0] < intervals[i-1][1]:
                return False
        return True
  • 253. Meeting Rooms II
    def minMeetingRooms(self, intervals: List[List[int]]) -> int:
        up_down = []
        for start, end in intervals:
            up_down.append((start,  1))
            up_down.append((end, -1))
        up_down.sort()
        res, cur = 0
        for _, n in up_down:
            cur += n
            res = max(cur, res)
        return res 
function minMeetingRooms(intervals: number[][]): number {
    const upDown: [number, number][] = []
    for (const [start, end] of intervals) {
        upDown.push([start, 1])
        upDown.push([end, -1])
    }
    upDown.sort((a, b) => a[0] - b[0] || a[1] - b[1])
    let [max, cur] = [0, 0]
    for (const [_, n] of upDown) {
        cur += n
        max = Math.max(cur, max)
    }
    return max

}
  • 236. Lowest Common Ancestor of a Binary Tree
def lowestCommonAncestor(self, root, p, q):
    if root is None or root is p or root is q:
        return root
    left = self.lowestCommonAncestor(root.left, p, q)
    right = self.lowestCommonAncestor(root.right, p, q)
    if left and right:
        return root
    return left or right
function lowestCommonAncestor(root: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null {
    if(root === null || root === p || root === q) return root
    const l = lowestCommonAncestor(root.left, p, q)
    const r = lowestCommonAncestor(root.right, p, q)
    if(l === null) return r
    if(r === null) return l
    return root
}
  • 98. Validate Binary Search Tree
def isValidBST(self, root: Optional[TreeNode]) -> bool:
    def dfs(node, l , r):
        if node is None:
            return True
        if not l < node.val < r: # start with negative condition
            return False
        return dfs(node.left, l, node.val) and dfs(node.right, node.val, r)
    return dfs(root, -inf, inf)
function isValidBST(root: TreeNode | null): boolean {
    function dfs(node: TreeNode | null, l: number, r: number){
        if(node === null) return true
        if(!( node.val > l && node.val < r)) return false
        return dfs(node.left, l, node.val) && dfs(node.right, node.val, r)
    }
    return dfs(root, -Infinity, Infinity)    
}
  • 701. Insert into a Binary Search Tree
function insertIntoBST(root: TreeNode | null, val: number): TreeNode | null {
    if(!root) return new TreeNode(val)
    if( val > root.val){
        root.right = insertIntoBST(root.right, val)
    }else{
        root.left = insertIntoBST(root.left, val)
    }
    return root
}

linked list

  • 203. Remove Linked List Elements
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        dummy = ListNode(0, head)
        pre, cur = dummy, head
        while cur:
            if cur.val == val:
                pre.next = cur.next
            else:
                pre = cur
            cur = cur.next
        return dummy.next
        ## or
        pre = dummy
        while pre.next: 
## no need check pre && pre.next. pre will never be None. 
## while condition is checked first. dummy garantee pre.next exist 
            if pre.next.val == val:
                pre.next = pre.next.next
            else:
                pre = pre.next
        return dummy.next

recursion

  • 257. Binary Tree Paths
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        res = []
        def dfs(node, path):
            if node is None:
                return
            path += str(node.val)
            if node.left is None and node.right is None:
                res.append(path)
                return
            path += '->'
            dfs(node.left, path)
            dfs(node.right, path)
        dfs(root, '')
        return res
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        res = []

        def dfs(node, path):
            if node is None:
                return
            path.append(str(node.val))

            if node.left is None and node.right is None:
                res.append("->".join(path))
                # return
            choices = []
            if node.left:
                choices.append(node.left)
            if node.right:
                choices.append(node.right)
            for choice in choices:
                dfs(choice, path)
                path.pop()
            #or pop here but need to delete return in the append
            path.pop()

        dfs(root, [])
        return res
  • 297. Serialize and Deserialize Binary Tree
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Codec:
    def serialize(self, root):
        res = []
        def dfs(node: TreeNode):
            if node is None:
                res.append('x')
                return
            res.append(str(node.val))
            dfs(node.left)
            dfs(node.right)
        dfs(root)
        return ','.join(res)

    def deserialize(self, data):
        res = iter(data.split(','))
        def dfs(res):
            cur = next(res)
            if(cur == 'x'):
                return None
            node = TreeNode(int(cur))
            node.left = dfs(res)
            node.right = dfs(res)
            return node
        return dfs(res)
 class TreeNode {
     val: number
     left: TreeNode | null
     right: TreeNode | null
     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
         this.val = (val===undefined ? 0 : val)
         this.left = (left===undefined ? null : left)
         this.right = (right===undefined ? null : right)
}
  
function serialize(root: TreeNode | null): string {
    function dfs(node: TreeNode | null) {
        if (!node) return 'x'
        return node.val.toString() + ',' + dfs(node.left) + ',' + dfs(node.right)
    }
    return dfs(root)
};

function deserialize(data: string): TreeNode | null {
    const res = data.split(',')[Symbol.iterator]()
    function dfs(res: IterableIterator<string>) {
        const cur = res.next().value
        if (cur === 'x') return null
        const node = new TreeNode(parseInt(cur))
        node.left = dfs(res)
        node.right = dfs(res)
        return node
    }

    return dfs(res)
}

backtracking

  • 22. Generate Parentheses
class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        res = []
        def dfs(s, l, r):
            if len(s) == 2 * n:
                res.append(s)
                return
            if l < n:
                dfs(s + '(', l + 1, r)
            if r < l:
                dfs(s + ')', l, r + 1)
        dfs('', 0, 0)
        return res
function generateParenthesis(n: number): string[] {
    const res: string[] = []
    function dfs(path: string[], l: number, r: number) {
        if (path.length == 2 * n) {
            res.push(path.join(''))
            return
        }
        if (l < n) {
            path.push('(')
            dfs(path, l + 1, r)
            path.pop()
        }
        if (r < l) {
            path.push(')')
            dfs(path, l, r + 1)
            path.pop()
        }
    }
    dfs([], 0, 0)
    return res
}
  • 698. Partition to K Equal Sum Subsets
class Solution:
    def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
        def dfs(i):
            if i == len(nums):
                return True
            for j in range(k):
                if j > 0 and cur[j] == cur[j - 1]:
                    continue
                cur[j] += nums[i]
                if cur[j] <= s and dfs(i + 1):
                    return True
                cur[j] -= nums[i]
            return False

        s, mod = divmod(sum(nums), k)
        if mod:
            return False
        cur = [0] * k
        nums.sort(reverse=True)
        return dfs(0)
function canPartitionKSubsets(nums: number[], k: number): boolean {
    const tot = nums.reduce((acc, cur) => acc + cur, 0)
    if (tot % k != 0) return false
    nums.sort((a, b) => a - b)
    const n = nums.length;
    const t = tot / k

    function dfs(idx: number, cur: number, cnt: number, vis: boolean[]): boolean {
        if (cnt == k) return true
        if (cur == t) return dfs(n - 1, 0, cnt + 1, vis)
        for (let i = idx; i >= 0; i--) {
            if (vis[i] || cur + nums[i] > t) continue
            vis[i] = true
            if (dfs(i - 1, cur + nums[i], cnt, vis)) return true
            vis[i] = false
            if (cur == 0) return false
        }
        return false
    }
    return dfs(n - 1, 0, 0, new Array<boolean>(n).fill(false))
};
  • 89. Gray Code
    def grayCode(self, n: int) -> List[int]:
        length, visited = 1 << n , [False] * length
        path = [0]
        visited[0] = True

        def dfs(code):
            if len(path) == length:
                return True
            for i in range(n):
                new_code = code ^ (1 << i)

                if visited[new_code]:
                    continue
                path.append(new_code)
                visited[new_code] = True
                if dfs(new_code):
                    break ## or return True
                visited[new_code] = False
                path.pop()
            return True ## no need if pre return
         
        dfs(0)
        return path

backtracking + dp

  • 124. Binary Tree Maximum Path Sum
    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        res = -inf
        def dfs(node):
            nonlocal res
            if node is None:
                return 0
            left = max(dfs(node.left), 0)
            right = max(dfs(node.right), 0)
            res = max(res, node.val + left + right)
            return node.val + max(left, right)
        dfs(root)
        return res
        # or 
        def dfs(node):
            if node is None:
                return 0
            l = dfs(node.left)
            r = dfs(node.right)
            nonlocal ans
            ans = max(ans, l + r + node.val)
            return max(max(l, r) + node.val, 0)
        dfs(root)
        return ans
  • 139. Word Break
function wordBreak(s: string, wordDict: string[]): boolean {
    const dict = new Set(wordDict);
    const dp = new Array(s.length + 1).fill(false);
    dp[0] = true; // Base case: empty string
    // string dp. 
    for (let j = 0; j <= s.length; j++) { 
        for (const item of wordDict) {
            if( item.length > j) continue
            if (dp[j - item.length] && dict.has(s.slice(j - item.length, j))) {
                dp[j] = true; 
                break
            }
        }
    }
    return dp[s.length]; 
}
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        words = set(wordDict)
        dp = [False for i in range(len(s) + 1)]
        dp[0] = True
        for i in range(len(s) + 1):
            for word in words:
                if i >= len(word):
                    if dp[i - len(word)] and s[i - len(word) : i] in words:
                        dp[i] = True
                        break
        return dp[len(s)
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
  dic = set(wordDict)
  @cache
  def dfs(idx):
      if idx == len(s):
          return True
      res = false  
      for item in dic:
          if s[idx:].startswith(item):
              if dfs(idx + len(item)):
                  res = True
                  break
              res = dfs(idx + len(item)) # or
              if res:
                  break
      return res
  return dfs(0)
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
    dic = set(wordDict)
    @cache
    def dfs(idx):
        if idx == len(s):
            return True
        res = False
        for item in dic:
            pre = s[idx : idx + len(item)]
            if pre in dic:
                if dfs(idx + len(item)):
                    res = True
                    break
                res = dfs(idx + len(item))
                if res:
                    break
        return res
    return dfs(0)
  • 91. Decode Ways
function numDecodings(s) {
    const dict = Array.from(Array(26).keys(), idx => (idx +1).toString())
    // const set = new Set(dict)
    const map = new Map()
    function dfs(idx) {
        if(idx > s.length) return 0 
        if(idx === s.length) return 1
        if(map.has(idx)) return map.get(idx)
        let res = 0
        for (let item of dict) {
            if((idx + item.length) > s.length) continue
             const pre = s.slice(idx, idx + item.length)
            
            if( set.has(pre)){ // !!!
                res += dfs(idx + item.length);
            }
            if (pre === item) { // not set.has(pre)
                res += dfs(idx + item.length);
            }
        }
        map.set(idx, res)
        return res
    }
    return dfs(0)
}
  • 322. Coin Change
function coinChange(coins: number[], amount: number): number {
    const dp = new Array(amount + 1).fill(-1)
    dp[0] = 0
    for(const item of coins){
        for( let j = 0; j <= amount; j++){
            if(  j >= item && dp[j - item] !== -1 ) {
                if(dp[j] !== -1){
                    dp[j] = Math.min(dp[j], dp[j - item] + 1)  
                } else{
                    dp[j] = dp[j - item ] +1
                }
            }
        }
    }
    return dp[amount] 
}
def coinChange(self, coins: List[int], amount: int) -> int:
    # dp = [0] + [inf] * amount
    dp = [0] + [amount + 1] * amount
    for item in coins:
        for j in range(item, amount + 1):
            if dp[j] > dp[j - item] + 1:
                dp[j] = dp[j - item] + 1
    if dp[amount] == amount + 1:
        return -1
    return dp[amount]
function coinChange(coins: number[], amount: number): number {
    const dp = new Array(amount + 1).fill(amount + 1)
    dp[0] = 0
    for (const item of coins) {
        for (let j = item; j <= amount; j++) {
          // dp[j] could be uninitial or a larger one
            if (dp[j] > dp[j - item] + 1) { 
                dp[j] = dp[j - item] + 1
            }
        }
    }
    if (dp[amount] === amount + 1) return -1
    return dp[amount]
}
function coinChange(coins: number[], amount: number): number {
    const memo = new Map<number,number>()

    function dfs(depth: number, total: number){
        if(memo.has(total)) return memo.get(total)
        if(total === amount) {
            return depth // can not memo here 
        } 
        if(total > amount){
            return -1
        }
        let ans = Infinity
        for(const item of coins){
            const res = dfs(depth + 1, total + item)
            if( res !== -1 && ans > res){
                ans = res
            }
        }
        memo.set(total,ans) 
        // memo is give current total, additional coins need
        // not mini coins need to get current total.
        return ans
    }
    const ans = dfs(0,0)

    return ans ===Infinity ? -1: ans
    
};
  • 494. Target Sum
function findTargetSumWays(nums: number[], target: number): number {
    const total = nums.reduce( (acc, cur) => acc + cur, 0)
    if( Math.abs(total) < Math.abs(target)) return 0
    if( (total + target) %2 !== 0) return 0
    const expected = ( total + target) / 2
    const dp = new Array(expected +1).fill(0)
    dp[0] = 1
    for( const item of nums) {
        for( let j = expected; j >= item; j--){ // >= not =>
            dp[j] = dp[j] + dp[j - item]
        }
    }
    return dp[expected];
}
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        total = sum(nums)
        if abs(target) > abs(total) :
            return 0
        if( (target + total) %2 == 1):
            return 0
        expected =  (target + total) // 2

        dp = [1] + [0] * expected
        for item in nums:
            for c in range( expected, item -1, -1):
                dp[c] += dp[ c - item]
        return dp[expected]
  • 300. Longest Increasing Subsequence
function lengthOfLIS(nums: number[]): number {
    if(nums.length === 0) return 0
    const dp = new Array(nums.length).fill(1)
    for(let i= 0; i < nums.length; i++){
        const lastItem = nums[i]
        for(let j = 0; j < i; j++){
            if(lastItem > nums[j]){
                if(dp[j] + 1 > dp[i]){
                    dp[i] = dp[j] + 1
                }
                // dp[i] = Math.max(dp[i], dp[j] + 1) // j always less then i, or i-1 
            }
        }
    }
    return Math.max(...dp)
}
  • 368. Largest Divisible Subset
function largestDivisibleSubset(nums: number[]): number[] {
    nums.sort((a,b) => a - b)
    const dp = new Array(nums.length).fill(1)
    const prev = new Array(nums.length).fill(-1)
    for (let i = 0; i < nums.length; i++) {
        const lastItem = nums[i]
        for (let j = 0; j < i; j++) {
            if (lastItem % nums[j] === 0) {
                if (dp[j] + 1 > dp[i]) {
                    dp[i] = dp[j] + 1
                    prev[i] = j //bookkeeping
                }
            }
        }
    }
    let maxIdx = dp.indexOf(Math.max(...dp))
    const res = []
    while( maxIdx !== -1){
        res.push(nums[maxIdx])
        maxIdx = prev[maxIdx]
    }
    return res
}
  • 62. Unique Paths
var uniquePaths = function (m, n) {
    const dp = Array.from({ length: m }, () => new Array(n).fill(0))
    for (let i = 0; i < m; i++) {
        dp[i][0] = 1
    }
    for (let j = 0; j < n; j++) {
        dp[0][j] = 1
    }
    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        }
    }
    return dp[m - 1][n - 1]
}
func uniquePaths(m int, n int) int {
	dp := make([][]int, m)
	for i := range(dp) {
		dp[i] = make([]int, n)
	}
	for i := range(dp) {
		for j := range(dp[0]) {
			if i == 0 || j == 0 {
				dp[i][j] = 1
				continue
			}
			dp[i][j] = dp[i-1][j] + dp[i][j-1]
		}
	}
	return dp[m-1][n-1]
}
  • 63. Unique Paths II
func uniquePathsWithObstacles(obstacleGrid [][]int) int {
	m, n := len(obstacleGrid), len(obstacleGrid[0])
	dp := make([][]int, m)
	for i := range dp {
		dp[i] = make([]int, n)
	}

	for i := range dp {
		if obstacleGrid[i][0] == 1 {
			break
		}
		dp[i][0] = 1
	}
	for j := range dp[0] {
		if obstacleGrid[0][j] == 1 {
			break
		}
		dp[0][j] = 1
	}

	for i := 1; i < m; i++ {
		for j := 1; j < n; j++ {
			if obstacleGrid[i][j] == 0 {
				dp[i][j] = dp[i-1][j] + dp[i][j-1]
			}
		}
	}
	return dp[m-1][n-1]
}
  • 64. Minimum Path Sum
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        dp = [[0] * n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if i == 0 or j == 0:
                    dp[i][j] = grid[i][j]

        for i in range(m):
            for j in range(n):
                if i == 0 and j == 0:
                    continue  # need to avoid update [0][0] item
                if i == 0 and j > 0:
                    dp[i][j] += dp[i][j - 1]
                elif j == 0 and i > 0:
                    dp[i][j] += dp[i - 1][j]
                else:
                    dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
        return dp[-1][-1]
# or 
    def minPathSum(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        dp = [[0] * n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if i == 0 and j == 0:
                    dp[i][j] = grid[i][j]
                elif i == 0:
                    dp[i][j] = grid[i][j] + dp[i][j - 1]  ## set and overwrite same time
                elif j == 0:
                    dp[i][j] = grid[i][j] + dp[i - 1][j] 
                else:
                    dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
        return dp[-1][-1]
  • 198. House Robber
    def rob(self, nums: List[int]) -> int:
        dp = [0] + [0] * len(nums)
        dp[1] = nums[0] # the nums and dp has offset 1
        for i in range(2, len(nums) + 1):
            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1])
        return dp[len(nums)]

stack

  • 227. Basic Calculator II
    def calculate(self, s: str) -> int:
        stack, sign, num = [], "+", 0
        for i in range(len(s) + 1):
            if i < len(s):
                c = s[i]
                if c == " ":
                    continue
                if c.isdigit():
                    num = 10 * num + int(c)

            if (not c.isdigit()) or i == len(s):
                match sign:
                    case "+":
                        stack.append(num)
                    case "-":
                        stack.append(-num)
                    case "*":
                        stack[-1] = stack[-1] * num
                    case "/":
                        stack[-1] = int(stack[-1] / num)
                num = 0
                sign = c
        return sum(stack)
  • 239. Sliding Window Maximum
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        res = []
        que = deque()
        for i, v in enumerate(nums):
            while que and nums[que[-1]] < v:
                que.pop()
            que.append(i)
            if i - que[0] >= k:
                que.popleft()
            if i >= k - 1:
                res.append(nums[que[0]])
        return res
function maxSlidingWindow(nums: number[], k: number): number[] {
    const ans: number[] = []
    const queue: number[] = []
    let j = 0
    for(let i =0; i< nums.length; i++){
        while( queue.length >j && nums[i] > queue[queue.length-1]  ){
            queue.pop()
        }
        queue.push(nums[i])
        if( i >= k-1){
            ans.push(queue[j])
            if(nums[i - k +1] === queue[j]){
                j++  // queue.shift()
            }
        }
    }
   return ans 
}
  • 416. Partition Equal Subset Sum
function canPartition(nums: number[]): boolean {
    const total = nums.reduce((a, b) => a + b, 0)
    if (total % 2 === 1) return false
    const target = total >> 1
    const dp = new Array(target + 1).fill(false)
    dp[0] = true
    for (const item of nums) {
        for (let j = target; j >= item; j--) {
            if (dp[j - item] ) {
                dp[j] = true
            }
            // or 
            if (dp[j - item] + item > dp[j]) {
                dp[j] = dp[j - item] + item
            }
            //or 
            dp[j] = Math.max(dp[j] , dp[j- item] + item)
        }
    }
    return dp[target] 
}
def canPartition(self, nums: List[int]) -> bool:
    @cache
    def dfs(start, cum):
        if cum == target:
            return True
        for i in range(start, len(nums)):
            cur = cum + nums[i]
            if cur <= target and dfs(i + 1, cur):
                return True
        return False
    return dfs(0, 0)
    def canPartition(self, nums: List[int]) -> bool:
        total = sum(nums)
        if total % 2:
            return False
        target = total >> 1
        dp = [True] + [False] * target
        for item in nums:
            for j in range(target, item - 1, -1):
                if dp[j - item]:
                    dp[j] = True
        return dp[target]

bfs

  • 199. Binary Tree Right Side View
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        res = []
        que = deque([root])
        while que:
            size = len(que)
            for i in range(size):
                node = que.popleft()
                if i == size - 1:
                    res.append(node.val)
                if node.left:
                    que.append(node.left)
                if node.right:
                    que.append(node.right)
        ## or
        while queue:
            res.append(queue[0].val)
            for _ in range(len(queue)):
                node = queue.popleft()
                if node.right:
                    queue.append(node.right)
                if node.left:
                    queue.append(node.left)
        return res
function rightSideView(root: TreeNode | null): number[] {
    const res : number[] = []
    function dfs(node: TreeNode | null, depth: number){
        if(!node) return
        if(depth === res.length){
            res.push(node.val)
        }
        dfs(node.right, depth + 1)
        dfs(node.left, depth + 1) // res is mutated. when go to left, res is increased.
    }
    dfs(root, 0)
    return res
}
  • 111. Minimum Depth of Binary Tree
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        que, depth = deque([root]), 0
        while que:
            depth += 1
            for _ in range(len(que)):
                node = que.popleft()
                if not node.left and not node.right:
                    return depth
                if node.left:
                    que.append(node.left)
                if node.right:
                    que.append(node.right)
        # return depth
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        if root.left and root.right:
            return 1 + min(self.minDepth(root.left), self.minDepth(root.right))
        elif root.left:
            return 1 + self.minDepth(root.left)
        elif root.right:
            return 1 + self.minDepth(root.right)
        else:
            return 
var minDepth = function (root) {
    if (!root) return 0
    const l = minDepth(root.left)
    const r = minDepth(root.right)
    // if (l === 0) return minDepth(root.right)
    // if (r === 0) return minDepth(root.left)
    if( l === 0 && r === 0 ){// must check this first
        return 1
    }else if(r === 0){
        return l + 1
    }else if(l === 0){
       return r + 1
    }else{
         return Math.min(l, r) + 1
    }
}
var minDepth = function (root) {
    if (!root) return 0
    const l = minDepth(root.left)
    const r = minDepth(root.right)
    if (l > 0 && r > 0) {
        return Math.min(l, r) + 1
    } else if (r === 0) {
        return l + 1
    } else if (l === 0) {
        return r + 1
    } else {
        return 1 // base case to build up, interact with 0
    }
}

var minDepth = function (root) {
    let res = Infinity
    function dfs(node, depth) {
        if (!node) return
        depth++
        if (!node.left && !node.right) {
            res = Math.min(res, depth )
        }
        dfs(node.left, depth )
        dfs(node.right, depth )
    }
    dfs(root, 0)
    return res === Infinity ? 0: res
}

##graph

  • 133. Clone Graph
    def cloneGraph(self, node: 'Node') -> 'Node':
        if not node:
            return None
        visited = {}
        def dfs(node):
            if node in visited:
                return visited[node]
            clone = Node(node.val, [])
            visited[node] = clone
            for n in node.neighbors:
                clone.neighbors.append(dfs(n))
            return clone
        return dfs(node)
  • 1197. Minimum Knight Moves
function minKnightMoves(x: number, y: number): number {
    let res = 0
    const seen = new Set<string>('0,0')
    let queue = new Array<[number, number]>([0 ,0])
    while( queue.length > 0){
        const q = []
        let len = queue.length
        while(len--){
            const [r, c] = queue.pop()
            if (r === x && c === y) return res
            for( const [dx, dy] of move){
                const item : [number, number] = [r + dx, c + dy]
                const id = `${r + dx},${c + dy}`
               if(seen.has(id)) continue 
               seen.add(id)
               q.push(item)
            }
        }
        queue = q
        res++
    }
    return res
  • 286. Walls and Gates
    def wallsAndGates(self, rooms: List[List[int]]) -> None:
        if not rooms:
            return
        m, n = len(rooms), len(rooms[0])
        que = deque()
        for i in range(m):
            for j in range(n):
                if rooms[i][j] == 0:
                    que.append((i, j))
        while que:
            x, y = que.popleft()
            for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
                nx, ny = x + dx, y + dy
                if 0 <= nx < m and 0 <= ny < n and rooms[nx][ny] == 2147483647:
                    rooms[nx][ny] = rooms[x][y] + 1
                    que.append((nx, ny))
function wallsAndGates(rooms: number[][]): void {
    const m = rooms.length
    const n = rooms[0].length
    const que: number[][] = []
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (rooms[i][j] === 0) {
                que.push([i, j])
            }
        }
    }
    const dir = [[0, 1],[1, 0], [0, -1], [-1, 0]]
    while (que.length) {
        let len = que.length  // not necessary
        while (len--) { // not necessary
            const [r, c] = que.shift()
            for (const [dx, dy] of dir) {
                const newX = r + dx
                const newY = c + dy
                if (newX < 0 || newX > m - 1 || newY < 0 || newY > n - 1) continue
                if (rooms[newX][newY] === 2147483647) {
                    rooms[newX][newY] = rooms[r][c] + 1
                    que.push([newX, newY])
                }
            }
        }
    }
};