Leetcode 2021-12-07

341. Flatten Nested List Iterator (Medium)

You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may also be integers or other lists. Implement an iterator to flatten it.

#MY ANSWER
class NestedIterator:
    def __init__(self, nestedList: [NestedInteger]):
        self.res = []
        def helper(nli):
            for e in nli:
                if e.isInteger():
                    self.res.append(e.getInteger())
                else:
                    helper(e.getList())
        helper(nestedList)
            
    def next(self) -> int:
        return self.res.pop(0)
         
    def hasNext(self) -> bool:
        return bool(self.res)
         
#USING STACK
class NestedIterator(object):
    def __init__(self, nestedList):
        self.stack = nestedList[::-1]
        
    def next(self):
        return self.stack.pop().getInteger()
        
    def hasNext(self):
        while self.stack:
            top = self.stack[-1]
            if top.isInteger():
                return True
            self.stack = self.stack[:-1] + top.getList()[::-1]
        return False

#USING GENERTOR
class NestedIterator:

    def __init__(self, nestedList: [NestedInteger]):
        # Get a generator object from the generator function, passing in
        # nestedList as the parameter.
        self._generator = self._int_generator(nestedList)
        # All values are placed here before being returned.
        self._peeked = None

    # This is the generator function. It can be used to create generator
    # objects.
    def _int_generator(self, nested_list) -> "Generator[int]":
        # This code is the same as Approach 1. It's a recursive DFS.
        for nested in nested_list:
            if nested.isInteger():
                yield nested.getInteger()
            else:
                # We always use "yield from" on recursive generator calls.
                yield from self._int_generator(nested.getList())
        # Will automatically raise a StopIteration.
    
    def next(self) -> int:
        # Check there are integers left, and if so, then this will
        # also put one into self._peeked.
        if not self.hasNext(): return None
        # Return the value of self._peeked, also clearing it.
        next_integer, self._peeked = self._peeked, None
        return next_integer
        
    def hasNext(self) -> bool:
        if self._peeked is not None: return True
        try: # Get another integer out of the generator.
            self._peeked = next(self._generator)
            return True
        except: # The generator is finished so raised StopIteration.
            return False

generator 解法没见过, 普通解法,要么初始化时候分解,要么用stack倒序,保证栈顶是integer。

342. Power of Four (Easy)

Given an integer n, return true if it is a power of four. Otherwise, return false.
An integer n is a power of four, if there exists an integer x such that n == 4^x.

class Solution:
    def isPowerOfFour(self, n: int) -> bool:
        if n==0: return False 
        while n:
            if n==1: return True
            if n%4!=0:
                return False
            n//=4
        return True

class Solution(object):
    def isPowerOfFour(self, n):
        if n == 0:
            return False
        while n % 4 == 0:
            n /= 4
        return n == 1

343. Integer Break (Medium)

Given an integer n, break it into the sum of k positive integers, where k >= 2, and maximize the product of those integers.
Return the maximum product you can get.

class Solution:
    def integerBreak(self, n: int) -> int:
        dp = [0]*(n+1)
        dp[1]=1
        for i in range(2,n+1):
            for j in range(1,i):
                dp[i]=max(dp[i],max(dp[j],j)*max(i-j,dp[i-j]))
        return dp[n]
  
class Solution:
    def integerBreak(self, n: int) -> int:
        if n==2: return 1
        if n==3: return 2
        product = 1
        while n>4:
            product*=3;
            n-=3
        product*=n;
        return product

#
class Solution:
    def integerBreak(self, n: int) -> int:
        if n == 2: 
            return 1 
        elif n == 3:
            return 2
        elif n%3 == 0:
            return 3**(n//3)
        elif n%3 == 1:
            return 4* 3**((n - 4) // 3)
        else: 
            #n%3==2
            return 2 * (3** (n//3))


#MY SOLUTION
class Solution:
    def integerBreak(self, n: int) -> int:
        # 2(1+1)   1
        # 3(1+2)   2
        # 4(2+2,1+3) 4
        # 5(2+3,1+4) 6
        # 6(2+4)     9
        # 7 (2+5,3+4)   12
        # 8             16
        # 9             20
        # 10             36

        # dp[i] = maximum product by breaking i
        # max( i-2*2 , 2*dp[i-2] , 3*dp[i-3]...)

        dp = [0]*(n+1)
        dp[2]=1

        for i in range(3,n+1):
            for j in range(2,n+1):
                if i-j>0:
                    dp[i] = max(dp[i], (i-j)*j)
                    dp[i] = max(dp[i], j*dp[i-j])
                else:
                    break
        
        #print(dp)
        return dp[n]

没思路,答案一,dp,dp【i】保存i可以被分解的最大乘集,假设i被分解成 a和b的和, 那么a或者b可以选中集训分解dp【a/b】或者不分解保留原始值a/b. 思路2: 用3 as many as possible. 比如 6, 3 乘 3大于2 乘 2 乘 2. 因此答案不会有超过3个的2. 尽力用3. 答案3: 有了思路2 ,和容易写出答案3.但dp这种方法应该想出来。。。

344. Reverse String (Easy)

Write a function that reverses a string. The input string is given as an array of characters s.
You must do this by modifying the input array in-place with O(1) extra memory.

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        l=0
        r=len(s)-1
        while l<r:
            s[l],s[r]=s[r],s[l]
            l+=1
            r-=1

345. Reverse Vowels of a String (Easy)

Given a string s, reverse only all the vowels in the string and return it.
The vowels are 'a', 'e', 'i', 'o', and 'u', and they can appear in both cases.

class Solution:
    def reverseVowels(self, s: str) -> str:
        s=[e for e in s]
        l=0
        r=len(s)-1
        vowels={'a','e','i','o','u'}
        while l<r:
            while l<r and s[l].lower() not in vowels:
                l+=1
            while l<r and s[r].lower() not in vowels:
                r-=1
            
            s[l],s[r]=s[r],s[l]
            r-=1
            l+=1
        
        return ''.join(s)

346. Moving Average from Data Stream (Easy)

Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window.
Implement the MovingAverage class:
MovingAverage(int size) Initializes the object with the size of the window size.
double next(int val) Returns the moving average of the last size values of the stream.

class MovingAverage:

    def __init__(self, size: int):
        self.size=size
        self.q=[]
    def next(self, val: int) -> float:
        if len(self.q)<self.size:
            self.q.append(val)
            return sum(self.q)/len(self.q)
        else:
            self.q.pop(0)
            self.q.append(val)
            return sum(self.q)/len(self.q)
        
from collections import deque
class MovingAverage:
    def __init__(self, size: int):
        self.size = size
        self.queue = deque()
        # number of elements seen so far
        self.window_sum = 0
        self.count = 0

    def next(self, val: int) -> float:
        self.count += 1
        # calculate the new sum by shifting the window
        self.queue.append(val)
        tail = self.queue.popleft() if self.count > self.size else 0

        self.window_sum = self.window_sum - tail + val

        return self.window_sum / min(self.size, self.count)

class MovingAverage:
    def __init__(self, size: int):
        self.size = size
        self.queue = [0] * self.size
        self.head = self.window_sum = 0
        # number of elements seen so far
        self.count = 0

    def next(self, val: int) -> float:
        self.count += 1
        # calculate the new sum by shifting the window
        tail = (self.head + 1) % self.size
        self.window_sum = self.window_sum - self.queue[tail] + val
        # move on to the next head
        self.head = (self.head + 1) % self.size
        self.queue[self.head] = val
        return self.window_sum / min(self.size, self.count)

用queue 和 Circular Queue 都是好想法。

347. Top K Frequent Elements (Medium)

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.
Your algorithm's time complexity must be better than O(n log n), where n is the array's size

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        dic_c = collections.Counter(nums)
        return sorted(dic_c.keys(),key=lambda x: dic_c[x],reverse=True)[:k]

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:        
        from heapq import heappush, heappop
        c = Counter(nums)
        res = []
        for kk,vv in c.items():
            heappush(res, (-vv,kk) )
        r = []
        for i in range(k):
            r.append( heappop(res)[1])
        return r


#ANSWER
from collections import Counter
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        count = Counter(nums)
        unique = list(count.keys())
        
        def partition(left, right, pivot_index) -> int:
            pivot_frequency = count[unique[pivot_index]]
            # 1. move pivot to end
            unique[pivot_index], unique[right] = unique[right], unique[pivot_index]  
            
            # 2. move all less frequent elements to the left
            store_index = left
            for i in range(left, right):
                if count[unique[i]] < pivot_frequency:
                    unique[store_index], unique[i] = unique[i], unique[store_index]
                    store_index += 1

            # 3. move pivot to its final place
            unique[right], unique[store_index] = unique[store_index], unique[right]  
            
            return store_index
        
        def quickselect(left, right, k_smallest) -> None:
            """
            Sort a list within left..right till kth less frequent element
            takes its place. 
            """
            # base case: the list contains only one element
            if left == right: 
                return
            
            # select a random pivot_index
            pivot_index = random.randint(left, right)     
                            
            # find the pivot position in a sorted list   
            pivot_index = partition(left, right, pivot_index)
            
            # if the pivot is in its final sorted position
            if k_smallest == pivot_index:
                 return 
            # go left
            elif k_smallest < pivot_index:
                quickselect(left, pivot_index - 1, k_smallest)
            # go right
            else:
                quickselect(pivot_index + 1, right, k_smallest)
         
        n = len(unique) 
        # kth top frequent element is (n - k)th less frequent.
        # Do a partial sort: from less frequent to the most frequent, till
        # (n - k)th less frequent element takes its place (n - k) in a sorted array. 
        # All element on the left are less frequent.
        # All the elements on the right are more frequent.  
        quickselect(0, n - 1, n - k)
        # Return top k frequent elements
        return unique[n - k:]

#ANSWER
from collections import Counter
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        
        bucket = [[] for _ in range(len(nums) + 1)]
        Count = Counter(nums).items()  
        for num, freq in Count: 
            bucket[freq].append(num) 
        flat_list = [item for sublist in bucket for item in sublist]
        return flat_list[::-1][:k]

答案给出了quick selelct的解法O(n)。但bucket 方法更好。 也是O(n)。

348. Design Tic-Tac-Toe (Medium)

Assume the following rules are for the tic-tac-toe game on an n x n board between two players:
A move is guaranteed to be valid and is placed on an empty block.
Once a winning condition is reached, no more moves are allowed.
A player who succeeds in placing n of their marks in a horizontal, vertical, or diagonal row wins the game.
Implement the TicTacToe class:
TicTacToe(int n) Initializes the object the size of the board n.
int move(int row, int col, int player) Indicates that the player with id player plays at the cell (row, col) of the board. The move is guaranteed to be a valid move.

class TicTacToe:

    def __init__(self, n: int):
        self.n=n
        self.rows_p1 = [0]*n
        self.rows_p2 = [0]*n
        self.cols_p1 = [0]*n
        self.cols_p2 = [0]*n
        self.i_plus_j_n_1_p1=0
        self.i_plus_j_n_1_p2=0
        self.i_equal_j_p1=0
        self.i_equal_j_p2=0
                
    def move(self, row: int, col: int, player: int) -> int:
        if player==1:
            self.rows_p1[row]+=1
            if self.rows_p1[row]==self.n: return player
            self.cols_p1[col]+=1
            if self.cols_p1[col]==self.n: return player
            if row+col==self.n-1:
                self.i_plus_j_n_1_p1+=1
                if self.i_plus_j_n_1_p1==self.n: return player
            if row==col:
                self.i_equal_j_p1+=1
                if self.i_equal_j_p1==self.n: return player
        else:
            self.rows_p2[row]+=1
            if self.rows_p2[row]==self.n: return player
            self.cols_p2[col]+=1
            if self.cols_p2[col]==self.n: return player
            if row+col==self.n-1:
                self.i_plus_j_n_1_p2+=1
                if self.i_plus_j_n_1_p2==self.n: return player
            if row==col:
                self.i_equal_j_p2+=1
                if self.i_equal_j_p2==self.n: return player
        
        return 0


#MY ANSWER
class TicTacToe:

    def __init__(self, n: int):
        self.n = n
        self.p1 = {'row':[0]*n, 'col':[0]*n, 'rpc':0 ,'rmc':0} 
        self.p2 = {'row':[0]*n, 'col':[0]*n, 'rpc':0 ,'rmc':0} 
    
    def check(self):
        if self._check(self.p1):
            return 1
        if self._check(self.p2):
            return 2
        return 0
    
    def _check(self, info):
        for n in info['row']:
            if n==self.n:
                return True
        for n in info['col']:
            if n==self.n:
                return True
        if info['rpc']==self.n:
            return True
        if info['rmc']==self.n:
            return True
        return False

    def move(self, row: int, col: int, player: int) -> int:
        if player==1:
            info = self.p1
        else:
            info = self.p2

        info['row'][row]+=1
        info['col'][col]+=1
        if row+col==self.n-1:
            info['rpc']+=1 
        if row-col==0:
            info['rmc']+=1
        return self.check()
        

349. Intersection of Two Arrays (Easy)

Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must be unique and you may return the result in any order.

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        return list(set(nums1)&set(nums2))

folowup, solve in O(n) time O(1) space given nums1,nums2 are sorted.

def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
    # if the lists are already sorted and you're told to solve in O(n) time and O(1) space:
    nums1.sort() # assume sorted
    nums2.sort() # assume sorted

    # iterate both nums backwards till at least 1 is empty
    # if num2[j] > num1[i], pop num2
    # if num2[j] < num1[i], pop num1
    # if equal and num not last appended to result, append to result and pop both nums
    
    result = []
            
    while nums1 and nums2:
        if nums2[-1] > nums1[-1]:
            nums2.pop()
        elif nums2[-1] < nums1[-1]:
            nums1.pop()
        else:
            # to avoid duplicates
            if not result or (result and nums1[-1] != result[-1]):
                result.append(nums1[-1])
            nums1.pop()
            nums2.pop()

    return result

350. Intersection of Two Arrays II (Easy)

Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must appear as many times as it shows in both arrays and you may return the result in any order.

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        c1=collections.Counter(nums1)
        c2=collections.Counter(nums2)
        res=[]
        for k in c1:
            if k in c2:
                res.extend([k]*min(c1[k],c2[k]))
        return res

#Bad but save space answer
class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()
        result = []
    
        while nums1 and nums2:
            if nums2[-1] > nums1[-1]:
                nums2.pop()
            elif nums2[-1] < nums1[-1]:
                nums1.pop()
            else:
                result.append(nums1[-1])
                nums1.pop()
                nums2.pop()

        return result