51. N-Queens_s.split()_s.strip().split(‘ ‘)_list(*zip(mat))_Unique Paths_jump_Spiral_75. Sort Colors

51. N-Queens

The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively.

Example 1:

Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above

Example 2:

Input: n = 1
Output: [["Q"]]

 

题意的要求简单来说就是在一个8*8的棋盘上摆上8个棋子,使它们不在同一行、同一列、同一斜线上。这题是一个很经典的问题。难点在问题的描述。这里我们使用一个数组board,board中元素下标代表第几行,元素值board[i]代表第i行Queen摆放的位置。下面来看这三个约束条件:

1.不同行:这个自然满足,数组board下标不同

2不同列:这个只要使board中各元素值不相同就行了。即board[i]!=board[j]

3.不同斜线:这个只要使board中元素相应的横纵坐标差的绝对值不等就行了。即|board[i]-board[j]|!=|i-j|

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        board_list = []
        
        # place n queens on an n x n chessboard
        def dfs( each_row_queen_position, diagonal_asc, diagonal_desc ):
            #  number of queens               
            if len( each_row_queen_position ) == n: # the number of queens is equal to n
                board = [ '.' * q_idx + 'Q' + '.' * (n-1-q_idx)
                          for q_idx in each_row_queen_position
                        ]
                board_list.append( board )
                return
            
            # else the number of queens is not enough(<n)
            # Layout row by row
            cur_row_idx = len( each_row_queen_position ) # The row currently to be laid out
            
            # Note, only can put 1 Queen on diagonal ascending, '/'
            #                                   cur_row_idx + col_idx == n-1 since idx start from 0 to n-1
            #                    1 Queen on diagonal descending, '\'
            #                                   cur_row_idx - col_idx == 0
            for col_idx in range(n):
                if col_idx in each_row_queen_position or cur_row_idx + col_idx in diagonal_asc or cur_row_idx - col_idx in diagonal_desc:
                    continue
                else:
                    dfs( each_row_queen_position + [col_idx], 
                         diagonal_asc + [cur_row_idx + col_idx],
                         diagonal_desc + [cur_row_idx - col_idx],
                       )
        
        dfs( [], [], [] )
        
        return board_list

52. N-Queens II

The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.

Given an integer n, return the number of distinct solutions to the n-queens puzzle.

Example 1:

Input: n = 4
Output: 2
Explanation: There are two distinct solutions to the 4-queens puzzle as shown.

Example 2:

Input: n = 1
Output: 1
class Solution:
    def totalNQueens(self, n: int) -> int:
        self.number_of_solutions = 0
        
        # place n queens on an n x n chessboard
        def dfs( each_row_queen_position, diagonal_asc, diagonal_desc ):
            
            if len( each_row_queen_position ) == n: # the number of queens is equal to n
                self.number_of_solutions +=1
                return
            
            # else the number of queens is not enough(<n)
            # Layout row by row
            cur_row_idx = len( each_row_queen_position )
            
            # Note, only can put 1 Queen on diagonal ascending, '/'
            #                                   cur_row_idx + col_idx == n-1 since idx start from 0 to n-1
            #                    1 Queen on diagonal descending, '\'
            #                                   cur_row_idx - col_idx == 0
            for col_idx in range(n):
                if col_idx in each_row_queen_position or cur_row_idx+col_idx in diagonal_asc or cur_row_idx-col_idx in diagonal_desc:
                    continue
                else:
                    dfs( each_row_queen_position+[col_idx], diagonal_asc+[cur_row_idx+col_idx], diagonal_desc+[cur_row_idx-col_idx] )
                    
        dfs([],[],[])
        return self.number_of_solutions

53. Maximum Subarray

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example 1:

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Example 2:

Input: nums = [1]
Output: 1

Example 3:

Input: nums = [5,4,-1,7,8]
Output: 23
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        # As long as the previous number is positive, add it to the current value
        for i in range( 1, len(nums) ):
            if nums[i-1] > 0:
                nums[i] += nums[i-1]
        return max(nums)

54. Spiral Matrix螺旋矩阵

Given an m x n matrix, return all elements of the matrix in spiral order.

Example 1:

Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]
Output: [1,2,3,6,9,8,7,4,5]

while loop:

  1. # step1. matrix.pop(0) ==> [1, 2, 3]
  2. # step2. *matrix ==> [4, 5, 6] [7, 8, 9] # two lists
  3. # step3. list( zip(*matrix)  ) ==> [ (4, 7), (5, 8), (6, 9) ]
  4. # step4. list( zip(*matrix) )[::-1] ==> [(6, 9), (5, 8), (4, 7)]

Example 2:

Input: matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:

        result = []
        while matrix:
            result += matrix.pop(0) # ==> [1, 2, 3]
            # matrix = list( zip(*matrix) )[::-1]
            matrix = [ *zip(*matrix) ][::-1]
        return result

OR

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        # matrix: [[1,2,3],[4,5,6],[7,8,9]]
        # step1. matrix.pop(0) ==> [1, 2, 3]
        
        # step2. *matrix ==> [4, 5, 6] [7, 8, 9] # two lists
        # step3. list( zip(*matrix)  ) ==> [ (4, 7), (5, 8), (6, 9) ]
        #        for i in map( list, zip(*matrix)  ):
        #           print(i)
        #        output:
        #        [4, 7]
        #        [5, 8]
        #        [6, 9]
        # why use list?
        # since zip(*matrix)[::-1] and the 'zip' object( (4, 7) (5, 8) (6, 9) ) is not subscriptable        
        # step4. list( zip(*matrix) )[::-1] ==> [(6, 9), (5, 8), (4, 7)]
        #        OR [ *zip(*matrix) ][::-1]
        
        # step5. self.spiralOrder( list(zip(*matrix))[::-1] )
        #           step1. matrix.pop(0) ==> (6,9) so [*matrix.pop(0)]
        
        #  while matrix is not None       
        # return matrix and [ *matrix.pop(0) ] + self.spiralOrder( [*zip(*matrix)][::-1] )
        # OR
        return matrix and [*matrix.pop(0)]+ self.spiralOrder( list( zip(*matrix) )[::-1] )

55. Jump Game

(45. Jump Game II
https://blog.csdn.net/Linli522362242/article/details/118349075)

Given an array of non-negative integers nums, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1:

Input: nums = [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: nums = [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.
class Solution:
    def canJump(self, nums: List[int]) -> bool:
        # 1 <= nums.length <= 10^4
        if len(nums) ==1:
            return True # we don't need to jump since it is the last index
        

        # nums[i]: maximum jump length at the position index i
        # i+nums[i]: current postion index + maximum jump length at the position index i == > current_farthest_postion_index
        
        # Assuming the previous farthest postion index = -1
        # the search range should be 0 = -1 + 1
        left_index = 0          # index of the current position starting from the left
        current_farthest_position_index= nums[0] # = left_index + nums[left_index] = 0+nums[0]
        # steps = 1
        
        while current_farthest_position_index < len(nums)-1:
            # Breadth First Search 
            next_farthest_postion_index = max( i+nums[i] for i in range(left_index, 
                                                                        current_farthest_position_index +1)
                                             )
            # steps+=1 
            left_index = current_farthest_position_index+1
            current_farthest_position_index = next_farthest_postion_index
            
            if left_index > current_farthest_position_index: # added
                return False
            
        return True # return steps

56. Merge Intervals

Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.

Example 1:

Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].

Example 2:

Input: intervals = [[1,4],[4,5]]
Output: [[1,5]]
Explanation: Intervals [1,4] and [4,5] are considered overlapping.
class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        # less memory
        # 1 <= intervals.length <= 10^4
        if len(intervals) ==1:
            return intervals
        
        i=1
        intervals.sort()
        while i< len(intervals):
            if intervals[i-1][-1] >= intervals[i][0]:
                intervals[i-1][-1] = max( intervals[i-1][-1] , intervals.pop(i)[-1] )# intervals[i][-1] )
                # del intervals[i]
                continue
            else:
                i+=1
        return intervals

OR

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        # less memory
#         # 1 <= intervals.length <= 10^4
#         if len(intervals) ==1:
#             return intervals
        
#         i=1
#         intervals.sort()
#         while i< len(intervals):
#             if intervals[i-1][-1] >= intervals[i][0]:
#                 intervals[i-1][-1] = max( intervals[i-1][-1] , intervals.pop(i)[-1] )# intervals[i][-1] )
#                 # del intervals[i]
#                 continue
#             else:
#                 i+=1
#         return intervals

        # faster
        merged_intervals=[]
        for interval in sorted( intervals ):
            if merged_intervals and merged_intervals[-1][-1] >= interval[0]:
                # why use max? since [[1,4],[2,3]] expects [[1,4]]
                merged_intervals[-1][-1] = max(merged_intervals[-1][-1], interval[-1])
            else:
                merged_intervals.append( interval )
        return merged_intervals

57. Insert Interval

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).

You may assume that the intervals were initially sorted according to their start times.

Example 1:

Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]

Example 2:

Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Output: [[1,2],[3,10],[12,16]]
Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

Example 3:

Input: intervals = [], newInterval = [5,7]
Output: [[5,7]]

Example 4:

Input: intervals = [[1,5]], newInterval = [2,3]
Output: [[1,5]]

Example 5:

Input: intervals = [[1,5]], newInterval = [2,7]
Output: [[1,7]]

class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        # 0 <= intervals.length <= 10^4
        left_part = []
        right_part = []
        
        for interval in intervals:
            if interval[-1]< newInterval[0]:
                # left_part+=[interval]
                left_part.append(interval)
            elif newInterval[-1]<interval[0]:
                # right_part+=[interval]
                right_part.append(interval)
            else:
                newInterval[0] = min(interval[0], newInterval[0])
                newInterval[-1] = max(interval[-1], newInterval[-1])
                
               
        return left_part + [newInterval] + right_part

58. Length of Last Word

Given a string s consists of some words separated by spaces, return the length of the last word in the string. If the last word does not exist, return 0.

word is a maximal substring consisting of non-space characters only.

Example 1:

Input: s = "Hello World"
Output: 5

Example 2:

Input: s = " "
Output: 0
class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        # " " ==> s.split() ==> []
        t = s.split() # Delimiter default: spaces, newlines (\n), tabs (\t), etc.
        
        if len(t)==0:
            return 0
        else:
            return len( t[-1] )

OR

class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        # " " ==> s.split() ==> []
#         t = s.split() # Delimiter default: spaces, newlines (\n), tabs (\t), etc.
        
#         if len(t)==0:
#             return 0
#         else:
#             return len( t[-1] )
        
        # less space
        # " " ==> s.split(' ') ==> ['', '']
        # " " ==> len( s.split(' ')[-1] ) ==> 0
        # note: 'a ' ==> s.split(' ') ==> ['a', ''] but expected return 1
        
        # so we have to use s.strip() first
        return len( s.strip().split(' ')[-1] )

59. Spiral Matrix II

Given a positive integer n, generate an n x n matrix filled with elements from 1 to n^2 in spiral order.

Example 1:

 <==range(1,4)=
                                  +list( zip(*mat[::-1]) ) and mat=[[4, 5], (9, 6), (8, 7)]

        # [[9]]
        # [[8]]+[(9,)]                                ==> [[8], (9,)]
        # [[6, 7]] + [(9, 8)]                       ==> [[6, 7], (9, 8)]
        # [[4, 5]] + [(9, 6), (8, 7)]             ==> [[4, 5], (9, 6), (8, 7)]
        # [[1, 2, 3]] + [(8, 9, 4), (7, 6, 5)] ==> [[1, 2, 3], (8, 9, 4), (7, 6, 5)]

Input: n = 3
Output: [[1,2,3],[8,9,4],[7,6,5]]

Example 2:

Input: n = 1
Output: [[1]]
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        mat = [[n*n]]
        lo = n*n # [0, n*n+1)
        
        while lo>1:
            # print( mat )
            lo, hi = lo-len(mat), lo

            # list() can generate a range to a list of number, or convert tuple to a list
            #                     print(*mat)
            # e.g. [[8], (9,)] ==> *mat       ==> [8] (9,)
            #                  ==> *mat[::-1] ==> (9,) [8]
            #                  ==> list( zip( *mat[::-1] ) ) ==> [(9,8)]
            mat = [ list( range(lo, hi) ) ] + list( zip(*mat[::-1]) )
            # OR                    # * to unpack a tuple then put its elements in a list
            # mat = [ list( range(lo, hi) ) ] + [ *zip( *mat[::-1] )  ]
        # [[9]]
        # [[8]]+[(9,)]                         ==> [[8], (9,)]
        # [[6, 7]] + [(9, 8)]                  ==> [[6, 7], (9, 8)]
        # [[4, 5]] + [(9, 6), (8, 7)]          ==> [[4, 5], (9, 6), (8, 7)]
        # [[1, 2, 3]] + [(8, 9, 4), (7, 6, 5)] ==> [[1, 2, 3], (8, 9, 4), (7, 6, 5)]
        # print(mat)
        return mat  

OR faster 

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        mat = [[n*n]]
        lo = n*n # [0, n*n+1)
        
        while lo>1:
            # print( mat )
            lo, hi = lo-len(mat), lo

            # list() can generate a range to a list of number, or convert tuple to a list
            #                     print(*mat)
            # e.g. [[8], (9,)] ==> *mat       ==> [8] (9,)
            #                  ==> *mat[::-1] ==> (9,) [8]
            #                  ==> list( zip( *mat[::-1] ) ) ==> [(9,8)]
            # mat = [ list( range(lo, hi) ) ] + list( zip(*mat[::-1]) )
            mat = [ range(lo, hi) ] + list( zip(*mat[::-1]) )
            # [[9]]
            # [range(8, 9), (9,)]
            # [range(6, 8), (9, 8)]
            # [range(4, 6), (9, 6), (8, 7)]
            # [range(1, 4), (8, 9, 4), (7, 6, 5)]
        # print(mat)
        return mat

60. Permutation Sequence

The set [1, 2, 3, ..., n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order, we get the following sequence for n = 3:

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

Given n and k, return the kth permutation sequence.
 

Example 1:

Input: n = 3, k = 3
Output: "213"

Example 2:

Input: n = 4, k = 9
Output: "2314"

Example 3:

Input: n = 3, k = 1
Output: "123"

class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        
        seq = [_ for _ in range(1, n+1)] # if n=4, seq=[1,2,3,4]
        k = k-1                  # if k=9, then k-1=8
        res = []
        while len(seq) > 1:
            
            # k_i = k/(n-1)!
            fac = 1
            for i in range(1,n): # if n=4, then factorial = (4-1)!= 3x2x1=6
                fac *= i         # if n=3, then factorial = (3-1)!= 2x1=2
                                 # if n=2, then factorial = (2-1)!= 1
                
            k_i = k//fac         # k_i = 8//6 = 1 ~ seq[k_i] = 2 # seq=[1,2,3,4]
                                 # k_i = 2//2 = 1 ~ seq[k_i] = 3 # seq=[1,3,4]
                                 # k_i = 0//2 = 0 ~ seq[k_i] = 1 # seq=[1,4]
            res.append( str( seq.pop(k_i) ) ) # ==> seq=[1,3,4] ==> seq=[1,4] ==>[4]
            
            # k = k%(n-1)!
            k = k % fac          # k = 8%6 = 2  # k = 2%2 = 0 # k = 0%2 = 0
            n = n-1              # n = 4-1=3    # n = 3-1 =2  # n = 2-1
            
        res.append( str( seq[0] ) )
        return ''.join( res )
  class Solution:
    def getPermutation(self, n: int, k: int) -> str:       
        import math
        seq = [_ for _ in range(1, n+1)]
        k = k-1
        res = []
        while len(seq) > 1:
            n = n-1
            # k_i = k/(n-1)!
            fac = math.factorial(n)
            k_i = k//fac
            res.append( str( seq.pop(k_i) ) )
            
            # k = k%(n-1)!
            k = k % fac
            
        res.append( str( seq[0] ) )
        return ''.join( res )

 OR

class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        import itertools as it
        
        # seq = [_ for _ in range(1, n+1)]
        # seq = list( it.permutations(seq) )[k-1]    
        # print( "".join([str(_) for _ in seq])  )
        
        # seq = list( it.permutations( seq ) )[k-1]
        # seq = list( it.permutations( range(1,n+1) ) )[k-1]
        # return "".join([str(_) for _ in seq])
        
        return "".join([ str(_) for _ in list( 
                                                it.permutations( range(1,n+1) ) 
                                             )[k-1]
                       ])

 61. Rotate List

Given the head of a linked list, rotate the list to the right by k places.

Example 1:

 

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

Example 2:

Input: head = [0,1,2], k = 4
Output: [2,0,1]
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def rotateRight(self, head: ListNode, k: int) -> ListNode:
        # The number of nodes in the list is in the range [0, 500]
        if head is None:
            return None
        
        # Count the length of the linked list
        move_to_back = head
        length = 1
        while move_to_back.next:
            move_to_back = move_to_back.next
            length +=1
        #now length == 5 if head = [1,2,3,4,5]
        move_to_back.next = head # Note: forms a cycle
        
        find_new_head = head
        k = k%length          # -1: point to --> new_head
        for _ in range( length-k-1 ):
            find_new_head = find_new_head.next
        
        head = find_new_head.next # new head
        find_new_head.next = None # cut off the link for processing the cyble

        return head

62. Unique Paths

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?

Example 1:

Input: m = 3, n = 7
Output: 28

Example 2:

Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Down -> Down
2. Down -> Down -> Right
3. Down -> Right -> Down

Example 3:

Input: m = 7, n = 3
Output: 28

Example 4:

Input: m = 3, n = 3
Output: 6

     The combination of time steps to go down from the number of steps m+n-2=m-1+n-1 (as long as you choose which time to go down, the time step to go right is determined). 在总数为m + n - 2中的数目中挑选n - 1个位置放竖着的走
  

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
              
#         steps = m + n - 2 # m-1 + n-1
#         down_steps = m-1 # number of steps that need to go down
        
#         def combination( n, r ):
            
#             A = 1
#             for i in range(n, n-r,-1):
#                 A *= i
                
#             m_fac=1 # factorial of m
#             for i in range( r, 0, -1 ):
#                 m_fac *= i
            
#             return int(A/m_fac)
#         return combination( steps, min(down_steps, steps-down_steps) )
        
        C=1.
        steps = m+n-2
        down_steps = m-1 # number of steps that need to go down
        r = min( down_steps, steps-down_steps )
        
        # e.g. C_(2,8) = 8x7 // 1x2 = (8-1+1)*(8-2+1)//(1x2)
        for i in range( 1,r+1):
            C = C* (steps-i+1.)/i # False: C *= (steps-i+1.)/i since divided by 2
  
        return int(C)

OR  DP

https://www.youtube.com/watch?v=fEcyKrdIkho

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:

        dp = [ [0]* n for _ in range(m) ]
        for i in range(m):
            for j in range(n):
                if i==0 or j==0: # slower if i=j=0 for only set dp[0][0] =0
                    dp[i][j]=1
                    continue
                 # hidden: [-1][j]=0 and dp[i][-1]=0 since dp = [ [0]*n for _ in range(m) ]
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
                
        return dp[m-1][n-1]

OR
suppose 2 robost walk from bottom-right corner to top-left corner(start)

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        # suppose 2 robost walk from bottom-right corner(start) to top or left corner(end)
        def dfs( m, n, memory ):
            if m==0 or n==0:
                return 1
            
            if memory[m][n]:
                return memory[m][n]
            
            walk_up = dfs(m-1, n, memory)
            walk_left = dfs(m, n-1, memory)
            memory[m][n] = walk_up + walk_left
            
            return memory[m][n]

        memory = [ [None]*n for _ in range(m) ] # mxn
        # accumulate steps
        return dfs( m-1, n-1, memory)

 63. Unique Paths II

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and space is marked as 1 and 0 respectively in the grid.

Example 1:

Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
Output: 2
Explanation: There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right

Example 2:

Input: obstacleGrid = [[0,1],[0,0]]
Output: 1
class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        # The following line contains two assignment statements 
        # and must be separated by ‘,’ and cannot be written in two lines 
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        # why?
        # m = len(obstacleGrid)
        # print(m) # (3,) # tuple
        
        dp = [ [0]*n for _ in range(m) ]
        
        for i in range(m):
            for j in range(n):
                if obstacleGrid[i][j] == 0:
                    if i==j==0: # dp[0][0]=1
                        dp[i][j] = 1
                        continue
                    else:         # hidden: [-1][j]=0 and dp[i][-1]=0 since dp = [ [0]*n for _ in range(m) ]
                        dp[i][j] = dp[i-1][j] + dp[i][j-1]
                
        return dp[m-1][n-1] 

64. Minimum Path Sum

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right, which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

Example 1:

Input: grid = [[1,3,1],[1,5,1],[4,2,1]]
Output: 7
Explanation: Because the path 1 → 3 → 1 → 1 → 1 minimizes the sum.

Example 2:

Input: grid = [[1,2,3],[4,5,6]]
Output: 12
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        # 1 <= m, n <= 200
        m, n = len(grid), len(grid[0])
        
        for i in range( m ):
            for j in range( n ):
                if i==0 and j==0:
                    continue
                elif i == 0: # and j>0
                    grid[i][j] += grid[i][j-1]
                    continue
                elif j == 0: # and i>0
                    grid[i][j] += grid[i-1][j]
                    continue
                else: # i>0 and j>0
                    grid[i][j] += min( grid[i-1][j], grid[i][j-1] )
        return grid[m-1][n-1]

65. Valid Number

valid number can be split up into these components (in order):

  1. decimal number or an integer.
  2. (Optional) An 'e' or 'E', followed by an integer.

decimal number can be split up into these components (in order):

  1. (Optional) A sign character (either '+' or '-').
  2. One of the following formats:
    1. One or more digits, followed by a dot '.'.
    2. One or more digits, followed by a dot '.', followed by one or more digits.
    3. A dot '.', followed by one or more digits.

An integer can be split up into these components (in order):

  1. (Optional) A sign character (either '+' or '-').
  2. One or more digits.

For example, all the following are valid numbers: ["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"], while the following are not valid numbers: ["abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"].

Given a string s, return true if s is a valid number.

Example 1:

Input: s = "0"
Output: true

Example 2:

Input: s = "e"
Output: false

Example 3:

Input: s = "."
Output: false

Example 4:

Input: s = ".1"
Output: true

Constraints:

  • 1 <= s.length <= 20

class Solution:
    def isNumber(self, s: str) -> bool:
        # # wrong: "inf" expect False but return True
        # try:
        #     float(s)
        #     return True
        # except:
        #     return False
        
        s=s.strip()
        if len(s)==0:
            return False
        
        transition = [
              { 'sign': 1, 'digit':2, '.':3 }, # [0] valid start character:specified dict
              { 'digit':2, '.':3 },            # [1] after sign
              { 'digit':2, '.':4, 'e':5 },     # [2] after digit
              { 'digit':4 },                   # [3]
              { 'digit':4, 'e':5 },            # [4]
              { 'sign':6, 'digit':7},          # [5]
              { 'digit':7 },                   # [6]
              { 'digit':7 },                   # [7]          
        ]
        
        current_state = 0
        for c in s:
            if c>='0' and c<='9':
                c = 'digit'
            if c in ['+', '-']:
                c = 'sign'
            if c.lower() =='e': # "1E9" # Expected:true
                c = 'e'
            if c not in transition[current_state].keys():
                return False
            current_state = transition[current_state][c]
            
        if current_state not in [2,4,7]: # final state_list
            return False
        return True

66. Plus One

Given a non-empty array of decimal digits representing a non-negative integer, increment one to the integer.

The digits are stored such that the most significant digit is at the head of the list, and each element in the array contains a single digit.

You may assume the integer does not contain any leading zero, except the number 0 itself.

Given a non-empty array of decimal digits representing a non-negative integer, increment one to the integer.

The digits are stored such that the most significant digit is at the head of the list, and each element in the array contains a single digit.

You may assume the integer does not contain any leading zero, except the number 0 itself.

Example 1:

Input: digits = [1,2,3]
Output: [1,2,4]
Explanation: The array represents the integer 123. and 123+1=124

Example 2:

Input: digits = [4,3,2,1]
Output: [4,3,2,2]
Explanation: The array represents the integer 4321. and 4321+1=4322

Example 3:

Input: digits = [0]
Output: [1] # 0 + 1=1
class Solution:
    def plusOne(self, digits: List[int]) -> List[int]:
        carry = 1
        for i in range( len(digits)-1, -1, -1): # (-1, len(digit)]
            if digits[i] + carry == 10:
                digits[i] = 0
                continue
            else:
                digits[i] = digits[i] + carry
                carry = 0
                break
                
        if carry==1:
            digits.insert(0,carry)
        return digits

67. Add Binary

Given two binary strings a and b, return their sum as a binary string.

Example 1:

Input: a = "11", b = "1"
Output: "100"

Example 2:

Input: a = "1010", b = "1011"
Output: "10101"
class Solution:
    def addBinary(self, a: str, b: str) -> str:
        a = list(a)
        b = list(b)
        
        carry = 0
        i = len(a)-1
        
        while i>-1 or b or carry==1:
            if b:
                carry += int( b.pop() ) # == a.pop(-1)
                
            if i>-1: # Traverse list a 
                carry += int( a[i] )
                a[i] = str( carry % 2 )
                i -= 1
            else:
                a.insert(0, str(carry%2) )
                
            carry //= 2
            
        return ''.join(a)  

68. Text Justification

Given an array of strings words and a width maxWidth, format the text such that each line has exactly maxWidth characters and is fully (left and right) justified.

You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces ' ' when necessary so that each line has exactly maxWidth characters.

Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line does not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right except the last line.

For the last line of text, it should be left-justified and only one extra space is inserted between words.(Correct the description of the original title: For the last line of text, it should be left-justified and no extra space is inserted between words.)

Note:

  • A word is defined as a character sequence consisting of non-space characters only.
  • Each word's length is guaranteed to be greater than 0 and not exceed maxWidth.
  • The input array words contains at least one word.

Example 1:

Input: words = ["justification.","This", "is", "an", "example", "of", "text"], 
maxWidth = 16
Output:
[ "justification.  ",
  "This    is    an",
  "example of text "
]
Input: words = ["This", "is", "an", "example", "of", "text", "justification."], maxWidth = 16
Output:
[
   "This    is    an",
   "example  of text",
   "justification.  "
]

Example 2:

Input: words = ["What","must","be","acknowledgment","shall","be"], maxWidth = 16
Output:
[
  "What   must   be",
  "acknowledgment  ",
  "shall be        "
]
Explanation: Note that the last line is "shall be    " instead of "shall     be", because the last line must be left-justified instead of fully-justified.
Note that the second line is also left-justified becase it contains only one word.

Example 3:

Input: words = ["Science","is","what","we","understand","well","enough","to","explain","to","a","computer.","Art","is","everything","else","we","do"], maxWidth = 20
Output:
[
  "Science  is  what we",
  "understand      well",
  "enough to explain to",
  "a  computer.  Art is",
  "everything  else  we",
  "do                  "
]
class Solution:
    def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
        
        cur, n_chars_in_cur, res = [], 0, []
        for w in words:
               # word + space + new_word
            if n_chars_in_cur + len(cur)  + len(w) > maxWidth:
                             # number of spaces need to be appened to each word 
                             # in cur except last one
                for i in range( maxWidth - n_chars_in_cur):
                    # loaction: i % (number of words in cur-1)
                                                        # 0%1 for only word
                    cur[ i % (len(cur)-1 if len(cur)>1 else 1) ] += ' '
                    
                res.append( ''.join(cur) )
                cur, n_chars_in_cur = [], 0
                
            cur.append(w)
            n_chars_in_cur += len(w)
        # For the last line of text, it should be left-justified and no extra
        # space is inserted between words.
        # str.ljust(width[, fillchar]) #default fillchar is space
        return res + [ ' '.join(cur).ljust(maxWidth) ]

69. Sqrt(x)

Given a non-negative integer x, compute and return the square root of x.

Since the return type is an integer, the decimal digits are truncated, and only the integer part of the result is returned.

Note: You are not allowed to use any built-in exponent function or operator, such as pow(x, 0.5) or x ** 0.5.

Example 1:

Input: x = 4
Output: 2

Example 2:

Input: x = 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since the decimal part is truncated, 2 is returned.
class Solution:
    def mySqrt(self, x: int) -> int:
        x_next = x
        while x_next*x_next > x:
            # Newton's iterative formula for f(x)=x*2: x_1 = x_0 - f(x_0)/f'(x_0)
            #                                           x_1 = x_0 - x_0*x_0 / (2*x_0)
            
            #                                           x_2 = x_1 - f(x_1)/f'(x_1)
            #                                           x_2 = x_1 - x_1*x_1 / (2*x_1)
            
            #                                           x_3 = x_2 - f(x_2)/f'(x_2)
            #                                           x_3 = x_2 - x_2*x_2 / (2*x_2)
  
            # ... ...
            #                                        x_cur_next = x_cur - (x_cur*x_cur-x_initial) / (2*x_cur)
            
            # x_next = x_next - (x_next*x_next - x)/(2*x_next)
            # x_next = x_next/2 + x/(x_next*2)
            x_next = (x_next + x/x_next)//2
        return int(x_next)    

class Solution:
    def mySqrt(self, x: int) -> int:
        x_i = x
        while x_i*x_i > x:
            # x_i = x_i - (x_i*x_i - x)/(2*x_i)
            # x_i = x_i/2 + x/(x_i*2)
            x_i = (x_i + x/x_i)//2
        return int(x_i)  

 OR

class Solution:
    def mySqrt(self, x: int) -> int:
        # x_i = x
        # while x_i*x_i > x:
        #     # x_i = x_i - (x_i*x_i - x)/(2*x_i)
        #     # x_i = x_i/2 + x/(x_i*2)
        #     x_i = (x_i + x/x_i)//2
        # return int(x_i)
                
        l, r = 0, x
        
        while l<=r:
            mid = (l+r)//2
            if mid*mid <= x < (mid+1)*(mid+1): # e.g. x=8, root=2
                return mid
            elif x < mid*mid:
                r = mid-1
                continue
            else: #elif x>= (mid+1)*(mid+1):
                l = mid+1

70. Climbing Stairs

You are climbing a staircase. It takes n steps to reach the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Example 1:

Input: n = 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

Example 2:

Input: n = 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step
class Solution:
    def climbStairs(self, n: int) -> int:
        # n = 1 : output = 1
        # n = 2 : output = 2
        # n = 3 : output = 3
        # n = 4 : output = 5
        # n = 5 : output = 8
        if n<=3:
            return n
        way_list = [1,2,3]
        
        for i in range(3,n):
            way_list.append( way_list[i-1] + way_list[i-2] )
        return way_list[n-1] # n-1 since index start from 0

71. Simplify Path

Given a string path, which is an absolute path (starting with a slash '/') to a file or directory in a Unix-style file system, convert it to the simplified canonical path.

In a Unix-style file system,

  • a period '.' refers to the current directory,
  • a double period '..' refers to the directory up a level, and
  • any multiple consecutive slashes (i.e. '//') are treated as a single slash '/'.
  • For this problem, any other format of periods such as '...' are treated as file/directory names.

The canonical path should have the following format:

  • The path starts with a single slash '/'.
  • Any two directories are separated by a single slash '/'.
  • The path does not end with a trailing '/'.
  • The path only contains the directories on the path from the root directory to the target file or directory (i.e., no period '.' or double period '..')

Return the simplified canonical path.

Example 1:

Input: path = "/home/"
Output: "/home"
Explanation: Note that there is no trailing slash after the last directory name.

Example 2:

Input: path = "/../"
Output: "/"
Explanation: Going one level up from the root directory is a no-op, as the root level is the highest level you can go.

Example 3:

Input: path = "/home//foo/"
Output: "/home/foo"
Explanation: In the canonical path, multiple consecutive slashes are replaced by a single one.

Example 4:

Input: path = "/a/./b/../../c/"
Output: "/c"

path = "/a/./b/../../c/", =>"/c"

整个过程:

l  “/”根目录

l  “a”进入子目录a,目前处于“/a”

l  “.”当前目录,不操作仍处于“/a”

l  “/b”进入子目录b,目前处于“/a/b”

l  “..”返回上级目录,处于“/a”

l  “..”返回上级目录,处于根目录“/”

l  “c”进入子目录c,目前处于“/c”

class Solution:
    def simplifyPath(self, path: str) -> str:
        # a period '.' refers to the current directory
        # dirs = [ p for p in path.split('/') if p!='' and p!='.']
        # # ['home']
        # # ['..']
        # # ['home', 'foo']
        # # ['a', 'b', '..', '..', 'c']
        
        stack = []
        
        for p in path.split('/'):
            if p == '..':
                if stack: # len(stack) > 0
                    stack.pop()
            elif p!='' and p!='.':# '.' refers to the current directory
                stack.append(p)  # first step
        return '/' + '/'.join(stack)

72. Edit Distance

Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2.

You have the following three operations permitted on a word:

  • Insert a character
  • Delete a character
  • Replace a character

Example 1:

Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation: 
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')

Example 2:

Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation: 
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        rows, cols = len(word1)+1, len(word2)+1
        dp = [ [ 0 for c in range(cols) ]
               for r in range(rows) 
             ]
        
        for c in range(cols):
            dp[0][c] = c
        
        for r in range(rows):
            dp[r][0] = r
        
        # r,c means advance one position (r-1 or c-1)in the original string
        for r in range( 1, rows ):
            for c in range( 1, cols ):
                if word1[r-1] == word2[c-1]:
                    dp[r][c] = dp[r-1][c-1]     # + 0 operation
                else:
                    # Choose the shortest path, or the least operation
                    dp[r][c] = min( dp[r][c-1],  # + 1 insert operation
                                    dp[r-1][c],  # + 1 remove operation
                                    dp[r-1][c-1] # + 1 replace operation
                                  )+1
        print(dp)            
        return dp[rows-1][cols-1]

73. Set Matrix Zeroes

Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0's, and return the matrix.

You must do it in place.

Example 1:

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

Example 2:

Input: matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
Output: [[0,0,0,0],[0,4,5,0],[0,3,1,0]]
class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
    
        rows = []
        cols = []
        for r in range( len(matrix) ):
            for c in range( len(matrix[0]) ):
                if matrix[r][c] == 0:
                    rows.append(r)
                    cols.append(c)
        
        for r in rows:
            matrix[r] = [0]*len(matrix[0]) # [0,0,...]
        
        for c in cols:
            for r in range( len(matrix) ):
                matrix[r][c] = 0
        
        return matrix

74. Search a 2D Matrix

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

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

Example 1:

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

Example 2:

Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
Output: false
class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        for r in matrix:
            if target>r[-1]:
                continue
            return target in r
        return False

OR

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        # for r in matrix:
        #     if target>r[-1]:
        #         continue
        #     return target in r
        # return False
    
        for r in matrix:
            if target>r[-1]:
                continue
            
            left, right =0, len(r)-1
            while left<=right:
                mid = (left + right)//2
                    
                if r[mid] == target:
                    return True
                elif r[mid] < target:
                    left = mid+1
                elif target < r[mid]:
                        right = mid-1
            return False 

75. Sort Colors

Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue.

We will use the integers 01, and 2 to represent the color red, white, and blue, respectively.

You must solve this problem without using the library's sort function.

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        # 1 <= nums.length <= 300
        start_idx, end_idx, cur_idx = 0, len(nums)-1, 0
        
        while start_idx < end_idx and nums[start_idx]<1:
            start_idx+=1
            cur_idx+=1   
            
        while end_idx>0 and nums[end_idx]>1:
            end_idx -= 1      
        
        while cur_idx<=end_idx:
            if nums[cur_idx] > 1:
                nums[cur_idx], nums[end_idx] = nums[end_idx], nums[cur_idx]
                end_idx -= 1
                continue
            elif nums[cur_idx] < 1:
                nums[start_idx], nums[cur_idx] = nums[cur_idx], nums[start_idx]
                start_idx += 1
                cur_idx += 1
                continue
            else:
                cur_idx += 1  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值