---
# System prepended metadata

title: Leetcode 052721~

---

# Leetcode 052721~

## 052721

### #454. 4Sum II

#### 論壇解1
combined double hashmap:
不必考慮數值重複問題。符合資格的case，兩個hashmap的值相乘即可
```python=
class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        n1, n2, n3, n4 = len(nums1), len(nums2), len(nums3), len(nums4)
        tg_dict_a, tg_dict_b = {}, {}
        res = 0
        
        for i in nums1:
            for j in nums2:
                if i + j not in tg_dict_a: tg_dict_a[i+j] = 1
                else: tg_dict_a[i+j] += 1
                    
        for i in nums3:
            for j in nums4:
                if i + j not in tg_dict_b: tg_dict_b[i+j] = 1
                else: tg_dict_b[i+j] += 1
        
        for i in tg_dict_a:
            for j in tg_dict_b:
                if not i+j:
                    res += (tg_dict_a[i] * tg_dict_b[j])
        return res
```

### #13. Roman to Integer

#### first try: 
By rule, naively.

```python=
class Solution:
    def romanToInt(self, s: str) -> int:
        res = 0
        if 'CM' in s:
            s = s.replace('CM', '')
            res += 900
        if 'CD' in s:
            s = s.replace('CD', '')
            res += 400
        if 'XC' in s:
            s = s.replace('XC', '')
            res += 90
        if 'XL' in s:
            s = s.replace('XL', '')
            res += 40
        if 'IX' in s:
            s = s.replace('IX', '')
            res += 9
        if 'IV' in s:
            s = s.replace('IV', 'we')
            res += 4
        if 'D' in s:
            s = s.replace('D', '')
            res += 500
        if 'L' in s:
            s = s.replace('L', '')
            res += 50
        if 'V' in s:
            s = s.replace('V', '')
            res += 5
        res += 10 * s.count('X')
        res += 100 * s.count('C')
        res += 1000 * s.count('M')
        res += s.count('I')
        return res     
```

#### 網友解

直指問題本質的解法。

沒辦法，我就不是西方人。
```python=
class Solution:
    def romanToInt(self, s: str) -> int:
        s = s.lower()
        roman = {'i':1,'v':5,'x':10,'l':50,'c':100,'d':500,'m':1000}
        res, index = 0, 'i'
        for i in s[::-1]:
            res, index = res-roman[i] if roman[index] > roman[i] else res + roman[i], i
        return res

```


## 053021
### #19. Remove Nth Node From End of List

#### first try:
題幹提示，希望one-pass。
naive solution的問題是，因為深度(長度)需要等traverse到尾端才知道，same for the location of removal. 需要先run一次確定深度，再run一次修改目標位置的指標。
所以我的方法就是建成一個list，list自然會包含長度資訊。接下來用list indexing就知道要抓的位置。

但這有一個問題是，list indexing本身是$O(1)$還是$O(N)$呢？如果是後者，那我的方法等同於naive solution的過程。
```python=
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        lnlist = []
        res = head
        while head.next:
            lnlist.append(head)
            head = head.next
        lnlist.append(head)
        if len(lnlist) - 1 >= n:
            lnlist[-n-1].next = lnlist[-n+1] if -n + 1 < 0 else None
            lnlist[-n] = None
            return res
        else:
            return res.next
```

#### 網友解
double pointer
等速雙指標，雖然他用fast, slow命名，但差距只有一開始的位置差(fast先前進n步)，fast, slow之後一起移動。
等等，但他這樣就不算2 pass?

恩對，官方解答看來定調這樣叫做1 pass。
```python=
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        fast = slow = head
        
        for _ in range(n):
            fast = fast.next
        if not fast:
            return head.next
        
        while fast.next:
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next
        return head
```
<br>
<br>

### #26. Remove Duplicates from Sorted Array

#### first try

明顯的浪費空間，不過Leetcode結果是看不出來的。
利用倒序刪除重複元素避免了index synchronization的問題。
```python=
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        loc = [0] * len(nums)
        for i in range(len(nums)):
            if i and nums[i] == nums[i-1]:
                loc[i] = 1
        for i in range(len(loc)-1, -1, -1):
            if loc[i]: del nums[i]
        return len(nums)
```

#### 網友解：
```python=
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        tail = 0
        for i in range(1, len(nums)):
            if nums[i] == nums[tail]:
                continue
            tail += 1
            nums[tail] = nums[i]
        return tail + 1
```

<br>
<br>

## 053121

### #27. Remove Element

#### first try:

跟昨天26題一樣的解法，但其實不需要，請見網友解1
```python=
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        for i in range(len(nums)-1, -1, -1):
            if nums[i] == val:
                del nums[i]
        return len(nums)

```

#### 網友解1：
用while就不會有iterable synchronization的問題

```python=
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        i = 0
        while i < len(nums):
            if nums[i] == val:
                del nums[i]
            else:
                i += 1
        return len(nums)

```

#### 網友解2:
最後是標準的double pointer，牛刀殺雞。
```python=
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        nums.sort()
        i, j = 0, 0
        while j < len(nums):
            if nums[j] != val:
                nums[i] = nums[j]
                i, j = i + 1, j + 1
            else:
                j += 1
        return i  

```

#### Analysis
- Time: $O(N)$ for all
- Space: $O(1)$ for all; all in-place


## 060121

### #88. Merge Sorted Array
題目原意是排兩個sorted array，也就是merge sort的最後一步(或每個subroutine的最後一步)。
如下面網友解，用2 pointer 的方式比較原汁原味，但實際上簡單粗暴的方法卻比較好用:
手動pop nums1多餘的元素，再把nums2接上去。然後call sort()，這會做一個in place sorting。

重點在避免改到nums1的id。
#### first try

```python=
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        for _ in range(n):
            nums1.pop()
        for i in range(n):
            nums1.append(nums2[i])
        nums1.sort()
```

#### 網友解:
體現題目要的精神，可惜表現不如人意(56ms(PR5) vs 32ms(PR88), by first try)

```python=
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        i, j = m-1, n-1
        while j+1 and i+1:
            if nums1[i] > nums2[j]:
                nums1[i+j+1] = nums1[i]
                i -= 1
            else:
                nums1[i+j+1] = nums2[j]
                j -= 1
        while j+1:
            nums1[i+j+1] = nums2[j]
            j -= 1
        
```

#### second try:
所以同first try的道理這樣也行。只是performance就真的比較差了。(60ms, PR5)

```python=
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        nums3, i, j = [], 0, 0
        while i < m and j < n:
            if nums1[i] < nums2[j]:
                nums3.append(nums1[i])
                i += 1
            else:
                nums3.append(nums2[j])
                j += 1
        
        if i < m:
            nums3 += nums1[i:m]
        elif j < n:
            nums3 += nums2[j:n]
        
        for i in range(len(nums3)):
            nums1[i] = nums3[i]
```

### #75. Sort Colors

### first try

好這完全是來亂的。
但是performance最好。
```python=
class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        nums.sort()
```

### second try:
那就自己寫一個qsort。
performance在28ms(PR90)~56ms(PR 0)之間。leetcode的benchmark真的歹就補?



```python=
class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        def qsort(data, left, right):
            if left > right: return
            i, j = left, right
            while i != j:
                while j > i and data[j] > data[left]:
                    j -= 1
                while j > i and data[i] <= data[left]:
                    i += 1
                if j > i:
                    data[i], data[j] = data[j], data[i]
            data[i], data[left] = data[left], data[i]
            qsort(data, left, i-1)
            qsort(data, i+1, right)
        qsort(nums, 0, len(nums)-1)

```

### third try
既然都知道所有的選項了，那就用字典紀錄次數再重組就好了。
對於沒有內建hashmap的語言來說關鍵就是如何實作hash table了。

```python=
class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        ndict = {0:0, 1:0, 2:0}
        for n in nums:
            ndict[n] += 1
        for i in range(ndict[0]):
            nums[i] = 0
        for i in range(ndict[0], ndict[0] + ndict[1]):
            nums[i] = 1
        for i in range(ndict[0]+ndict[1], ndict[0]+ndict[1]+ndict[2]):
            nums[i] = 2
```

### 網友解:
原來這問題叫做Dutch national flag problem。簡單來說就是sorting的特例。
sorting的可能元素有限時，可以用$O(N)$解出而不是$O(NlogN)$。
只要拆成子問題即可。(如*)


但須考慮常數大小。常數太大，反而不如一般sorting。適用於總共數值不超過四五種的狀況。
(數值選項趨近len(data)時，其實就等同於$O(N^2)$了，超過的話甚至比bubble sorting還糟糕，所以真的是特例)
```python=
class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        w, r, b = 0, 0, len(nums)-1
        # w == 0, r == 1, b == 2
        while w <= b:
            if nums[w] == 1:
                w += 1
            elif nums[w] == 0:
                nums[w], nums[r] = nums[r], nums[w]
                w, r = w + 1, r + 1 
            elif nums[w] == 2:
                nums[w], nums[b] = nums[b], nums[w]
                b -= 1
```
<br>
<br>

\* Mauritius，模里西斯的國旗:
![](https://i.imgur.com/DjTv8dK.jpg =150x)
四色旗，和荷蘭旗問題一樣的概念。

同樣的概念理論上可以延伸到N種值。

```python=
mau = [1,3,2,2,3,0,1,2,3,0,1,2,3,1,1,1,2,2,0,3,3,3,2,2,3,1,2,3,2,2,1,0,1,0,0]
print(mau)
def fourcolor(data):
  i, j, k= 0, 0, len(data)-1
  while i <= k:
    if data[i] < 2:
      data[i], data[j] = data[j], data[i]
      i, j = i + 1, j + 1
    elif data[i] == 2:
      i += 1
    elif data[i] == 3:    
      data[i], data[k] = data[k], data[i]
      k -= 1
  i, j = 0, j-1
  while i <= j:
    if data[i] == 0:
      i += 1
    else:
      data[i], data[j] = data[j], data[i]
      j -= 1
fourcolor(mau)
print(mau)
```


## 060221

### #35. Search Insert Position
只是看了一下網友的解，修正一下reassign pointer的方式。

#### first try:
```python=
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        lo, hi = 0, len(nums) - 1
        cur = (lo + hi) // 2
        
        while lo <= hi:
            if nums[cur] == target: return cur
            if nums[cur] > target: hi = cur - 1
            elif nums[cur] < target: lo = cur + 1
            cur = (lo + hi) // 2
        
        return lo
```


#### Analysis:
基本上就是sorted binary search
直接call bisect的bisect_left是一樣的意思。
- Time: $O(logN)$
- Space:$O(1)$

## 060521
### #4. Median of Two Sorted Arrays

#### first try:
redundant solution：比naive solution還差。做了多餘的排序，反而變成需要$O(NlogN)$。
naive solution是從兩邊的頭取較小的，取到median 為止，需要花費$O(N)$。

```python=
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        index = len(nums1) + len(nums2)
        if not index % 2:
            index //= 2
            return (sorted(nums1 + nums2)[index - 1] + sorted(nums1 + nums2)[index]) / 2
        else:
            index //= 2
            return sorted(nums1 + nums2)[index]
```

#### 網友解
最佳解，使用binary search。
因為限定是median number，位置固定的情況下，nums1取數後就知道nums2該取何者。所以就在兩者之中較短者上用binary search。
如何知道找到了？
只要檢視nums1[i-1], nums1[i], nums2[j-1], nums2[j]的關係就可以知道。因為median number必然是：
```python=
max(nums1[i-1], nums2[j-1])
```

若len相加是偶數，則為：
```python=
(min(nums1[i], nums2[j]) + max(nums1[i-1 + nums2[j-1]]) ) / 2
```

這樣就可以在$O(logN)$內得到解。

比較要注意的是邊界條件：例如i==0, j==0。這是令這題變得複雜的原因之一。

## 060621

### #1486. XOR Operation in an Array

乍看之下很簡單，照著題意做一次就得到$O(N)$的解，也會過。
但觀察後發現規則，只要照規則就可以$O(1)$判斷出來。
```python=
class Solution:
    def xorOperation(self, n: int, start: int) -> int:
        if n == 1: return start
        last = start + (n - 1) * 2
        
        if start % 4 < 2:
            # 如果start % 4 < 2, 可以知道第2位一定是0(以下皆以LSB為第一位)
            # 所以如果n是4的倍數，XOR後一定為0
            # 如果n % 4 == 2，因為只有第2位會變，所以一定是bx0010
            # 那餘1和於3的情況就可以推導出來了
            if n % 4 == 0: return 0
            elif n % 4 == 1: return last
            elif n % 4 == 2: return 2
            elif n % 4 == 3: return 2 ^ last
        else:
            # 如果start % 4 > 1: 其實就是上例往前推一步，也就是說start+2就會回到上面的例子。
            # n % 4 == 3的情況，首項保留，中間4的倍數消去後，倒數第1、2位又如同上面start % 4 < 2的前兩位，所以XOR結果為2。
            if n % 4 == 1: return start
            elif n % 4 == 2: return start ^ last
            elif n % 4 == 3: return start ^ 2
            elif n % 4 == 0: return start ^ 2 ^ last
            
```

## 060721
### #53. Maximum Subarray

看起來是DP但其實不是，只不過有那個味道在。

#### 論壇解1
subarray最大和預設為首項值。一項項往後看，如果和小於當前最大值就直接丟棄，重設起點。
```python=
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        self.max_sum = nums[0]
        def _max_sub(nums, f):
            if f == 0: 
                self.max_sum = nums[0]
                return nums[0]
            local_max = max(nums[f], _max_sub(nums, f-1) + nums[f])
            self.max_sum = max(local_max, self.max_sum)
            return local_max
        _max_sub(nums, len(nums) - 1)
        return self.max_sum if len(nums) > 1 else nums[0]
    
```

#### 論壇解2
概念一樣，只是不用遞迴，用iteration
```python=
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        res, sums = nums[0], nums[0]
        for i in range(1, len(nums)):
            sums = max(nums[i] + sums, nums[i])
            if sums > res: res = sums
        return res
```

#### 論壇解3
用divide and conquer。
每個sub-procedure由中間往左右找該fragment最大值。
sub-procedure求最大值的過程看起來跟前面有點像，不同之處在於必須要跟最內側(nums[mid-1] for left part, nums[mid+1] for right part)連在一起，所以加過去一路取max就可以了。不過這作法不聰明，只是為DC而DC。


```python=
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        def dvcqr(nums, l, r):
            if l > r: return -2**32
            mid = (l + r) // 2
            
            cur, rtbest, ltbest = 0, 0, 0
            for i in range(mid + 1, r + 1):
                cur += nums[i]
                rtbest = max(rtbest, cur)
            cur = 0
            for i in range(mid - 1, l - 1, -1):
                cur += nums[i]
                ltbest = max(ltbest, cur)
            
            ctbest = rtbest + ltbest + nums[mid]
            ltonlybest = dvcqr(nums, l, mid - 1)
            rtonlybest = dvcqr(nums, mid + 1, r)
            return max(ctbest, ltonlybest, rtonlybest)
        
        return dvcqr(nums, 0, len(nums) - 1)
```

#### 論壇解4
更聰明的DC。
我沒寫過，要找時間寫一遍。
###### tags: TO DO

```python=
class Solution:
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        def divide_and_conquer(nums, i, j):
            if i == j-1:
                return nums[i],nums[i],nums[i],nums[i]
            
            # we will compute :
            # a which is max contiguous sum in nums[i:j] including the first value
            # m which is max contiguous sum in nums[i:j] anywhere 
            # b which is max contiguous sum in nums[i:j] including the last value
            # s which is the sum of all values in nums[i:j]
                
            # compute middle index to divide array in two halves
            i_mid = i+(j-i)//2
            
            # compute a, m, b, s for left half
            a1, m1, b1, s1 = divide_and_conquer(nums, i, i_mid)
            
            # compute a, m, b, s for right half
            a2, m2, b2, s2 = divide_and_conquer(nums, i_mid, j)
            
            # combine a, m, b, s values from left and right halves to form a, m, b, s for whole array (bottom up)
            a = max(a1, s1+a2)
            b = max(b2, s2+b1)
            m = max(m1,m2,b1+a2)
            s = s1+s2
            return a,m,b,s
                  
        _,m,_,_ = divide_and_conquer(nums, 0, len(nums))
        return m

```

#### Analysis:
- Time: $O(N)$, $O(N)$, $O(NlogN)$, $O(N)$

### #7. Reverse Integer

first javascript try
```javascript=
/**
 * @param {number} x
 * @return {number}
 */
var reverse = function(x) {
    if (x > 0) {
        x = x.toString()
        let y = x.split('')
        y = y.reverse()
        y = y.join('')
        if (y <= 2 ** 31 - 1) return y
        else return 0
    }
    else if (x < 0) {
        x = (-x).toString()
        let y = x.split('')
        y = y.reverse()
        y = y.join('')
        if (y <= 2 ** 31) return -y
        else return 0
    }
    else return 0
};
```


## 060821

### #121. Best Time to Buy and Sell Stock
上一題的變體。
解法觀念類似。這邊用上一題第一個解法的概念。

```python=
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) < 2: return 0
        profit = - prices[0]
        lowest = highest = prices[0]
        for i in range(1, len(prices)):
            if prices[i] < lowest: 
                lowest = prices[i]
                continue
            if prices[i] - lowest > profit: 
                profit = prices[i] - lowest
        return profit if profit > 0 else 0
```

## 061621
### #122. Best Time to Buy and Sell Stock II
題意想清楚就知道，這樣就可以了。比前一題還簡單。
最難的地方應該是了解題意的意涵。
#### first try
```python=
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) == 1: return 0
        res = 0
        for i in range(1, len(prices)):
            if prices[i] > prices[i-1]:
                res += (prices[i] - prices[i-1])
        return res
```
#### Analysis
- Time:$O(N)$

### #309. Best Time to Buy and Sell Stock with Cooldown

#### HuaHua_1
DP, naive ver.:

```python=
import sys
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) == 1: return 0
        prices = prices[0]
        lp = len(prices)
        hold, sold, rest = [0] * lp, [0] * lp, [0] * lp
        hold[0] = -sys.maxsize
        for i in range(1, lp):
            rest[i] = max(rest[i-1], sold[i-1])
            sold[i] = hold[i-1] + prices[i]
            hold[i] = max(rest[i-1] - prices[i], hold[i-1])
        
        return max(rest[-1], sold[-1])
```

#### HuaHua_2
DP, dimension-reduction ver.:

```python=
import sys
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) == 1: return 0
        sold, rest, hold = 0, 0, -sys.maxsize
        
        for p in prices:
            prev_sold = sold
            sold = hold + p
            hold = max(rest - p, hold)
            rest = max(rest, prev_sold)
        return max(sold, rest)
```
pic1.
![](https://i.imgur.com/SrGa8KS.png =250x)

pic2.
![](https://i.imgur.com/qWKqsc1.png)

了解pic1的邏輯後，畫出來就可以簡單計算出答案。
因為每個狀態都只需要前一項，所以只要maintain每個狀態的前一項，不需要整個list，這就是第二個implementation做的簡化。

#### Analysis

- Time: $O(N)$
- Space: $O(N)$ / $O(1)$


## 061721

### #123. Best Time to Buy and Sell Stock III

網友解：
[小明](https://maxming0.github.io/2020/08/16/Best-Time-to-Buy-and-Sell-Stock-III/)

一樣用動態規劃。限制只能做兩次交易，那就是分成buy1, sell1; buy2, sell2兩對、四個數組來做DP。實務上，一樣，由於今日狀態只跟前一天有關，所以縮減成maintain四個variables就可以了。
需要給定限制，在第一次交易完成前不能進行第二次交易嗎？不需要。在我們的模型裡，第一次交易完成後(包含完成當下)，第二次交易的結果會等同"當天立即買進賣出"。即假設現在為day 3，本日完成buy1賺2元；同日buy2和sell2的取值動作可以解讀為"這天立即買進並賣出"，這跟沒做是一樣的，所以不違反題意。
所以最後也不用return max(s1, s2)，return s2即可。

```python=
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) < 2: return 0
        
        b1 = b2 = -float('inf')
        s1 = s2 = 0
        for p in prices:
            b1 = max(b1, -p)
            s1 = max(b1 + p, s1)
            b2 = max(s1 - p, b2)
            s2 = max(b2 + p, s2)
        return s2
```
#### Analysis
- Time: $O(N)$


### #188. Best Time to Buy and Sell Stock IV
呼呼，系列題最終回，也是最難的。
一樣使用DP with dimension diminishing
解析待補，以及greedy解法。待我沈澱一晚。

#### 網友解：
```python=
class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        n = len(prices)
        if n < 2: return 0
        elif n <= k // 2: 
            res = 0
            for i, j in zip(prices[1:], prices[:-1]):
                if i > j: res += i - j
            return res
        
        res = [[0, 0] for _ in range(k + 1)]
        for i in range(k + 1):
            res[i][1] = -prices[0]
        for i in range(n):
            for j in range(1, k + 1):
                res[j - 1][1] = max(res[j - 1][1], res[j - 1][0] - prices[i])
                res[j][0] = max(res[j][0], res[j - 1][1] + prices[i])
        return res[k][0]
```


### #67. Add Binary
#### first try:
邊界條件注意一下。

```python=
class Solution:
    def addBinary(self, a: str, b: str) -> str:
        if len(a) < len(b):
            a, b = b, a
        b = b.zfill(len(a)) # 10011 -> 00010011
        
        a, b = a[::-1] + '0', b[::-1] + '0'
        a, b = list(a), list(b)
        # 00010011, 10011100 => 110010000, 001110010 
        # one more 0 at tail for possible carrying number
        
        
        res = ['0'] * (len(a))
        c, n1, n2 = 0, 0, 0
        for i in range(len(a)):
            if not c:
                n1, n2 = int(a[i]), int(b[i])
                if n1 + n2 == 2:
                    c = 1
                elif n1 + n2 < 2:
                    res[i] = str(n1 + n2)
            elif c == 1:
                n1, n2 = int(a[i]), int(b[i])
                if n1 + n2 == 2:
                    res[i] = '1'
                elif n1 + n2 == 1:
                    res[i] = '0'
                elif not (n1 + n2):
                    res[i] = '1'
                    c = 0
        if not int(res[-1]): res = res[:-1]
        return "".join(res[::-1])
```

#### 網友解
概念是一樣的。
不過更優良地利用了基本庫的函式，程式碼較簡潔。
```python=
class Solution:
    def addBinary(self, a: str, b: str) -> str:
        c, res = 0, ''
        a, b = list(a), list(b)
        
        while a or b or c:
            if a:
                c += int(a.pop())
            if b: 
                c += int(b.pop())
            res += str(c%2)
            c //= 2
        return res[::-1]
```


## 070621

### #1338. Reduce Array Size to The Half

#### first try
題意就是要了解arr內一樣的數字分組，需要多少會過半。只要知道每個數字有多少個就好，所以就先用字典分組。然後排序，看加到多少會過半。
```python=
class Solution:
    def minSetSize(self, arr: List[int]) -> int:
        s = len(arr)
        number_times = {}
        for i in arr:
            if i not in number_times: number_times[i] = 1
            else: number_times[i] += 1
        number_times_rank = sorted(list(number_times.values()), reverse = True)
        if len(number_times_rank) == 1: return 1
        res, aggre = 0, number_times_rank[0]
        while aggre < (len(arr) + 1) // 2:
            res += 1
            aggre += number_times_rank[res]
        return res + 1
```

#### 官方解1:
bucket sort:
對一陣列arr:
```python=
arr = [1,2,2,4,1,2,1,5,4,5,3,2,2,1]
counts = collections.Counter(arr).values() # == [4,5,2,2,1]
max_val = max(counts) # == 5

buckets = [0] * (max_val + 1)

for c in counts:
    buckets[c] += 1
# buckets這個array非常重要。它的index等於'arr裡有index個的數字的個數'
# buckets == [0,1,2,0,1,1]

```
所以在這個情況下我們不需要跑任何n(logn)排序，因為只要由buckets的尾端往回取值，就可以知道我們已掌握了arr之中的多少個數字。取到超過一半時就是答案。當然最後一步需要一點處理：
```python=
min(buckets[bucket], math.ceil(number_to_count / bucket))
```


```python=
class Solution:
    def minSetSize(self, arr: List[int]) -> int:
        counts = collections.Counter(arr).values() # e.g. Counter({1: 4, 2: 5, 3: 1, 4: 2, 5: 2})
        number_to_count = len(arr) // 2
         # e.g. [4,5,1,2,2]
        max_val = max(counts)
        buckets = [0] * (max_val + 1)
        for c in counts:
            buckets[c] += 1
        # buckets: [0,1,2,0,1,1]
        bucket = max_val
        num_set = 0
        
        while number_to_count > 0:
            if number_to_count - buckets[bucket] * bucket >= 0:
                num_set += buckets[bucket]
                number_to_count -= buckets[bucket] * bucket
                bucket -= 1
                continue
            num_set += min(buckets[bucket], math.ceil(number_to_count / bucket))
            return num_set
        return num_set
```

#### Analysis:
- Time: $O(NlogN)$ (mine) /  $O(N)$ (official better solution)


### #94. Binary Tree Inorder Traversal

### first try:
recursive method; is said to be trivial

```python=
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if not root: return []
        return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
```

### 網友解： iterative method: stack method

```python=
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res, stack = [], []
        
        while True:
            while root:
                stack.append(root)
                root = root.left
            if not stack:
                return res
            node = stack.pop()
            res.append(node.val)
            root = node.right
```

### 網友解2: Morris traversal
這是一種threaded binary tree。
其主要優點是可以在同樣$O(N)$時間複雜度的前提下，實現$O(1)$的空間複雜度，不需要使用stack等額外記錄路徑的data structure。

```python=
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        
        while root:
            if not root.left:
                res.append(root.val)
                root = root.right
            else:
                pred = self.find_pred(root)
                
                if pred.right != root:
                    pred.right = root
                    root = root.left
                else:
                    root.left = None
        return res
        
    def find_pred(self, root):
        cur = root.left
        while cur.right and cur.right != root:
            cur = cur.right
        return cur

```
[Morris Traversal](https://www.youtube.com/watch?v=wGXB9OWhPTg&ab_channel=TusharRoy-CodingMadeSimple)

#### Analysis:
- Time: $O(N)$
- Space: $O(N)$ $O(N)$ $O(1)$

### #100. Same Tree


## 070821

### #101. Symmetric Tree
和前面比較兩棵樹是否相等的作法一樣，稍微變化而已。
```python=
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        return self.is_symmetric_tree(root.left, root.right)
        
    def is_symmetric_tree(self, p, q):
        if not p and not q: return True
        if not p or not q: return False
        if p.val != q.val: return False
        return self.is_symmetric_tree(p.left, q.right) and self.is_symmetric_tree(p.right, q.left)
        
```

#### 網友解
補上iterative method
```python=
from collections import deque

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        p, q = root.left, root.right
        deq = deque([(p, q)])
        
        while deq:
            p, q = deq.popleft()
            if not self.check(p, q):
                return False
            if p:
                deq.append((p.left, q.right))
                deq.append((p.right, q.left))
        return True
    
    def check(self, p, q):
        if p == None and q == None: return True
        if p == None or q == None: return False
        if p.val != q.val: return False
        return True
```

#### Analysis:
- Time: $O(N)$
- Space: $O(N)$

### #718. Maximum Length of Repeated Subarray	
DP

#### 網友解
```python=
class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        if len(nums1) < len(nums2): 
            nums1, nums2 = nums2, nums1
        
        m, n = len(nums1), len(nums2)
        ans = 0
        for a in range(-n + 1, m + n - 1):
            cnt = 0
            for bptr in range(n):
                aptr = a + bptr
                if aptr < 0: continue
                if aptr >= m: break
                if nums1[aptr] == nums2[bptr]:
                    cnt += 1
                    if cnt > ans: ans = cnt
                else:
                    cnt = 0
        return ans
```


### #104. Maximum Depth of Binary Tree

#### first try
```python=
class Solution:
    max_depth, cur_depth = 0, 0
    def maxDepth(self, root: TreeNode) -> int:
        if not root: return 0
        self.cur_depth += 1
        self.max_depth = max(self.max_depth, self.cur_depth)
        self.maxDepth(root.left)
        self.maxDepth(root.right)
        self.cur_depth -= 1
        if self.cur_depth == 0: return self.max_depth
```

#### 網友解1
一行文
```python=
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        return 1 + max(self.maxDepth(root.right), self.maxDepth(root.left)) if root else 0
```

### #12. Integer to Roman
只是路過看到寫一下而已

```python=
class Solution:
    def intToRoman(self, num: int) -> str:
        digits = {
            1000:'M', 900:'CM', 500:'D', 400:'CD', 100:'C', \
            90:'XC', 50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1:'I' \
        }
        digits_li = list(digits.keys())
        res, ptr = '', 0
        while num and ptr < len(digits_li):
            if num / digits_li[ptr] >= 1:
                num -= digits_li[ptr]
                res += digits[digits_li[ptr]]
            else: ptr += 1
        return res
```

### #110. Balanced Binary Tree

#### first try
第12次提交。再不對，我可要哭了。

其實這樣就是DFS。
```python=
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        self.balance = True
        if not root: return self.balance
        self.br_depth(root, 1)
        return self.balance
        
    def br_depth(self, root, cur_dep):
        lt_depth = rt_depth = cur_dep
        if root.left:
            lt_depth = self.br_depth(root.left, cur_dep + 1)
        if root.right:
            rt_depth = self.br_depth(root.right, cur_dep + 1)
        if abs(rt_depth - lt_depth) > 1: self.balance = False
        return max(lt_depth, rt_depth)
```
#### 網友解
DFS, 簡潔版
```python=
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        self.balance = 1
        
        def dfs(node):
            if not node: return 0
            ld, rd = dfs(node.left), dfs(node.right)
            if abs(ld - rd) > 1: self.balance = 0
            return max(ld, rd) + 1
        
        dfs(root)
        return self.balance
```
#### Analysis:
- Time:$O(N)$
- Space: $O(N)$


## 081321

### #94. Binary Tree Inorder Traversal (revisited)

#### Morrison's traversal
    question: 
    is space complexity of this really constant?
    since we make thread to root(or current) at every step and there should be $O(logN)$ number of this thread links, the space complexity should bt $O(logN)$

    but if you allocate space for all node with their member variables no matter if they exist(i.e. if they are None or not), this traversal shall be $O(1)$ in space complexity. Since all we did is just use the already allocated space efficiently.


第一個判斷是現在的節點有沒有left subtree
如果沒有就直接印出現在的節點並移到右子樹，這邊很簡單。
第二個判斷是要找出thread，方法可以另外寫，但因為不複雜，直接寫在一起也可以。
thread是一個暫時的連結(産霊, musubi)，其起點是cur的left subtree的最後一個節點(至於怎樣是最後一個節點要看traversal方法)，終點是cur本身。

建立thread的方法是先移動到cur的left(因為我們才剛經歷過上一個條件判斷，這裡必定不會是None)，然後一直往右移動(pred = pred.right)直到以下兩個情形之一發生為止：
    1. pred.right == None
    2. pred.right = cur

1的情況表示我們找到thread的正確起點了，這時就設定thread並且將cur = cur.left

2的情況表示這個的確是thread的起點，但這條路我們已經是第二次走了，並且對cur來說，他的left subtree應該已經都被瀏覽過了，所以直接切斷：cur.left = None，然後繼續。下一個while loop就會執行第一個判斷(cur.left==None)。

這樣應該夠清楚吧......



```python=
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res, cur = [], root
        
        while cur:
            if not cur.left:
                res.append(cur.val)
                cur = cur.right
            
            else:
                pred = cur.left
                while pred.right and pred.right != cur:
                    pred = pred.right
                if not pred.right: 
                    pred.right = cur
                    cur = cur.left
                else: # equal to "if pred.right == cur:"
                    cur.left = None
        return res
```

### #73. Set Matrix Zeroes

#### first try
naive method:
```python=
class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        i_set, j_set = set(), set()
        for i in range(len(matrix)):
            for j in range(len(matrix[i])):
                if matrix[i][j] == 0:
                    i_set.add(i)
                    j_set.add(j)
        for i in range(len(matrix)):
            if i in i_set:
                matrix[i] = [0] * len(matrix[i])
        
            else:
                for j in range(len(matrix[i])):
                    if j in j_set:
                        matrix[i][j] = 0
```

slightly changed:
```python=
class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        i_set, j_set = set(), set()
        for i in range(len(matrix)):
            for j in range(len(matrix[i])):
                if matrix[i][j] == 0:
                    i_set.add(i)
                    j_set.add(j)
        for i in i_set:
            matrix[i] = [0] * len(matrix[i]) 
        for j in j_set:
            for i in range(len(matrix)):
                matrix[i][j] = 0
```



## 090221

### 



### #102. Binary Tree Level Order Traversal

#### first try:
用字典存，key是level number。只要照著左->右的順序recursion，intralevel順序不會有問題，再來就是排序key，按照key順序填入答案。

不過這其實有點麻煩，因為照我目前的技術，還必須把levels分開寫，不然會有referenced before assignment的問題。

```python=
class Solution:
    def __init__(self):
        self.levels = {}
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        output = []
        
        def _traverse(node, lv):
            if node.left:
                if lv not in self.levels:
                    self.levels[lv] = [node.left.val]
                else:
                    self.levels[lv].append(node.left.val)
                _traverse(node.left, lv + 1)
            if node.right:
                if lv not in self.levels:
                    self.levels[lv] = [node.right.val]
                else:
                    self.levels[lv].append(node.right.val)
                _traverse(node.right, lv + 1)
        
        self.levels[0] = [root.val]
        _traverse(root, 1)
        
        all_level = sorted(self.levels.keys())
        for i in all_level:
            output.append(self.levels[i])
        return output
```
#### 


#### second try:
採用BFS，好處是不需要遞迴呼叫。

```python=
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        res = [[root.val]]
        queue = [root]
        
        while queue:
            size = len(queue)
            layer = []
            while size:
                size -= 1
                s = queue[0]
                if s.left:
                    queue.append(s.left)
                    layer.append(s.left.val)
                if s.right:
                    queue.append(s.right)
                    layer.append(s.right.val)
                queue.pop(0)
            if layer:
                res.append(layer)
        return res
                
                
```

#### Analysis:
- Time:$O(N)$
- Space:$O(N)$

N:node number


## 090621

### #1629. Slowest Key
给你一个长度为 n 的字符串 keysPressed ，其中 keysPressed[i] 表示测试序列中第 i 个被按下的键。releaseTimes 是一个升序排列的列表，其中 releaseTimes[i] 表示松开第 i 个键的时间。字符串和数组的 下标都从 0 开始 。第 0 个键在时间为 0 时被按下，接下来每个键都 恰好 在前一个键松开时被按下。

测试人员想要找出按键 持续时间最长 的键。第 i 次按键的持续时间为 releaseTimes[i] - releaseTimes[i - 1] ，第 0 次按键的持续时间为 releaseTimes[0] 。

来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/slowest-key
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
#### first try:
```python=
class Solution:
    def slowestKey(self, releaseTimes: List[int], keysPressed: str) -> str:
        releaseTimes = [0] + releaseTimes
        longest, max_duration = ' ', 0
        
        for k in range(len(keysPressed)):
            duration = releaseTimes[k + 1] - releaseTimes[k]
            if duration > max_duration:
                longest = keysPressed[k]
                max_duration = duration
            elif duration == max_duration and ord(keysPressed[k]) > ord(longest):
                longest = keysPressed[k]
        return longest
```


### #562. Longest Line of Consecutive One in Matrix

33
#### 網友解1

善用python內建的data structure。

```python=
class Solution:
    def longestLine(self, mat: List[List[int]]) -> int:
        if not mat: return 0
        
        def consecutive(line):
            return max(len(list(v)) if k else 0 
                       for k, v in itertools.groupby(line))
        
        group = collections.defaultdict(list)
        
        for n, row in enumerate(mat):
            for m, col in enumerate(row):
                group[0, n] += [col]
                group[1, m] += [col]
                group[2, n - m] += [col]
                group[3, n + m] += [col]
        
        return max( map(consecutive, group.values()) )
                
```


# 網友解2

DFS
```python=
class Solution:
    def longestLine(self, mat: List[List[int]]) -> int:
        if not mat or not mat[0]:
            return 0
        seen = [set(), set(), set(), set()]
        cur_dir = ((1, 0), (0, 1), (1, 1), (1, -1))
        
        row_len, col_len = len(mat), len(mat[0])
        res = 0
        
        for r in range(row_len):
            for c in range(col_len):
                if mat[r][c]:
                    for i in range(4):
                        if (r, c) not in seen[i]:
                            _dir = cur_dir[i]
                            res = max(res, 
                                      self.dfs(r, c, _dir, i, seen, mat, row_len, col_len))
        return res
    
    def dfs(self, r, c, _dir, i, seen, mat, row_len, col_len):
        cur_res = 1
        seen[i].add((r, c))
        new_r, new_c = r + _dir[0], c + _dir[1]
        if row_len > new_r >= 0 and col_len > new_c >= 0 and mat[new_r][new_c]:
            cur_res += self.dfs(new_r, new_c, _dir, i, seen, mat, row_len, col_len)
        return cur_res

```

#### Analysis:
- Time: $O(M * N)$
- Space: $O(M * N)$


## 090721

### #206. Reverse Linked List
很好啊，這種題目一個小時寫不出來嘛~

#### 公式解1: iteration method

```python=
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        prev, cur = None, head
        while cur:
            next_temp = cur.next
            cur.next = prev
            prev = cur
            cur = next_temp
        return prev
            

```

#### 公式解2: recursive method
```python=
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
    # recursive method
        if not head or not head.next: return head
        p = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return p
```

#### Analysis:
- Time: $O(N)$ both
- Space: $O(1)$ / $O(N)$


## 091021
### #83. Remove Duplicates from Sorted List
 
first try:

```python=
class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head: return
        cur = head
        while cur.next:
            if cur.val == cur.next.val:
                while cur.val == cur.next.val:
                    cur.next = cur.next.next
                    if not cur.next: break
                
            if cur.next:
                cur = cur.next
        return head
```

#### Analysis:
- Time: $O(N)$
- Space: $O(1)$


#### 網友解：recursion
```python=
class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next: return head
        head.next = self.deleteDuplicates(head.next)
        return head if head.val != head.next.val else head.next
```
#### Analysis:
- Time: $O(N)$
- Space: $O(N)$



### #82. Remove Duplicates from Sorted List II

#### first try
解是解出來了，但是...

```python=
class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        pre, cur, _init = ListNode(float('-inf'), head), head, True
        if not head: return head
        while cur.next:
            if cur.val != cur.next.val:
                pre = cur
                cur = pre.next
                _init = False
            else:
                while cur.val == cur.next.val:
                    cur.next = cur.next.next
                    if not cur.next: break
                pre.next = cur.next
                cur = pre.next
                if _init: 
                    head = cur
                if not cur: break
        return head
```


#### 公式解
```python=
class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        sentinel = ListNode(-100, head)
        pred = sentinel
        
        while head:
            if head.next and head.val == head.next.val:
                while head.next and head.val == head.next.val:
                    head = head.next
                pred.next = head.next
            else:
                pred = pred.next
            head = head.next
        return sentinel.next
```