--- tags: info2022-homework1 --- > 貢獻者: 鞋墊-Shoe > 🧔:interviewer 👶:interviewee > [模擬面試錄影(漢)](https://youtu.be/FBT4lqwDxB4) > [模擬面試錄影(漢)](https://youtu.be/q6PqC0Kv_NY) > [模擬面試錄影(英)](https://youtu.be/O9-SUY2eu20) ## [53. Maximum Subarray](https://leetcode.com/problems/maximum-subarray/) ### 面試過程 🧔:一個array中,有很多個subarray,要你找出最大總和的subarray並return出該subarray的總和 👶:那想先請問這個array的長度範圍,有沒有可能是空的,以及array的element取值範圍 🧔:array長度界在1 ~ 之間,大小界在 -10^4 ~ 10^4 👶:那我會用python去實作想先請問一下,我是否能假設function的輸入為一個list 🧔:可以這樣假設,沒問題 👶:這邊他希望去找出一個total sum最大的subarray,我們想要快速知道這個subarray的"總和"(影片口誤)是多少,我會想到用preSum的方式先去紀錄0~i的總和,就能藉由下式知道subarray的total sum,就能夠將問題轉成找preSum[j] - preSum[i] (j > i)的最大值,這樣就能在固定preSum[j]的情況下去找一個最小的preSum[i] (curMin),這樣我只需要去loop preSum這個array,並且將每個值當作preSum[j],在扣掉j前面的最小element "curMin",就能找出以j結尾的subarray中之最大total sum,接著再將每個位置結尾的total sum取max就能得到我們的答案 ``` preSum[j] - preSum[i] (j > i) => find max arr = [1, 2, 3] preSum = [0, 1, 3, 6] curMin ``` 👶:那整體的時間複雜度為O(n),空間複雜度為O(n),以上為我的演算法,請問有甚麼問題嗎 ? 🧔:聽起來是沒甚麼問題,你可以開始實作了 #### Method: preSum ```python # Time: O(n) # Space: O(n) class Solution: def maxSubArray(self, arr: List[int]) -> int: n = len(arr) preSum = [0] * (n + 1) for i in range(1, n + 1): preSum[i] = preSum[i - 1] + arr[i - 1] ans = -float("inf") curMin = 0 for j in range(1, n + 1): ans = max(ans, preSum[j] - curMin) curMin = min(curMin, preSum[j]) return ans ``` 👶:以上為我的演算法實作後的code,請問有甚麼問題嗎 ? 🧔:希望你可以在進一步的優化空間,讓空間複雜度優化到O(1) 👶:基本上我每次loop只會用到preSum[j],所以可將兩個loop合併就不需要使用到preSum的空間 #### Method: preSum ```python # Time: O(n) # Space: O(1) class Solution: def maxSubArray(self, arr: List[int]) -> int: n = len(arr) ans = -float("inf") curMin = 0 _sum = 0 for j in range(1, n + 1): _sum += arr[j - 1] ans = max(ans, _sum - curMin) curMin = min(curMin, _sum) return ans ``` 👶:以上為我的演算法實作後的code,請問這樣有符合你的要求嗎 ? 🧔:大致上是ok,那這邊還有一點時間,給一個follow up,如果用divide and conquer去實作你會怎麼處理這個問題,當然這邊時間複雜度就不一定在O(n)以內 👶:divide and conquer就是把大問題化成子問題,也就是說在index範圍i ~ j的arr中,可以將問題轉成找,i ~ k和k+1 ~ j的maxSubarray以及橫跨k的maxSubarray,那怎麼找這個橫跨k的maxSubarray呢 ? 我只需以k開頭向左邊加總找出一個最大值,在以k+1開頭向右邊加總找出一個最大值,兩值相加最會是橫跨k的maxSubarray,最後在與i ~ k和k+1 ~ j的maxSubarray比較找出最大值,就能夠找出i ~ j的maxSubarray 👶:那整體時間複雜度為O(nlogn),空間複雜度為O(logn),以上為我的演算法,請問有甚麼問題嗎 ? 🧔:聽起來是沒甚麼問題,你可以開始實作了 #### Method: divide and conquer ```python # Time: O(nlogn) # Space: O(logn) class Solution: def maxSubArray(self, arr: List[int]) -> int: def recur(i, j): if i == j: return arr[i] k = i + (j - i) // 2 maxi = recur(i, k) maxj = recur(k + 1, j) maxL = -float("inf") _sum = 0 for idx in range(k, i - 1, -1): _sum += arr[idx] maxL = max(maxL , _sum ) maxR = -float("inf") _sum = 0 for idx in range(k + 1, j + 1): _sum += arr[idx] maxR = max(maxR , _sum ) maxk = maxL + maxR return max(maxi, maxj, maxk) return recur(0, len(arr) - 1) ``` 👶:以上為我的演算法實作後的code,請問這樣有符合你的要求嗎 ? 🧔:大致上是ok,今天的面試就到此結束謝謝 ### 同儕檢討 針對 interviewer 的檢討: * [0:00](https://youtu.be/FBT4lqwDxB4): 不該自稱是「面試官」,這有「上對下」的隱含意思,大家日後可能會是同事,而且說不定應徵者一開始的職等就比自己高^台灣很小,你永遠很難想像記仇會多久^,interview 是得知「相互」的「觀點」的途徑,不是讓你施展官威的場合。可改說「感謝你的時間,接下來由我主持這場面試」或「你好,從我手上這份簡歷,我感受到您對程式設計的熱情和專業,但在我們談及工作內容前,我想幫公司同仁先認識你在程式開發的想法和風格」 * [0:05](https://youtu.be/FBT4lqwDxB4?t=5): 避免說「有幾個問題要你回答」,可改說「由於敝公司的產品開發中,不免會用到若干經典演算法,同仁想知道你的想法,因此我嘗試彙整過去的開發經驗,提出一兩個問題,請你稍候討論並在 Google Docs 撰寫程式碼」 * [0:10](https://youtu.be/FBT4lqwDxB4?t=10): 這問題太經典,可以直接說「考慮 [Maximum subarray problem](https://en.wikipedia.org/wiki/Maximum_subarray_problem),給定 ...(程式開發條件)」,當然,若應徵者一臉茫然,再從頭解釋題目,順便觀察臨場反應 * [0:16](https://youtu.be/FBT4lqwDxB4?t=16): 用字要精準,不是「sub-array 的總和」,而是「sub-[array 元素](https://terms.naer.edu.tw/detail/1272416/)的數值總和」,減少非必要的中英夾雜,尤其在視訊會議中,可能會不容易識別 * [0:41](https://youtu.be/FBT4lqwDxB4?t=41): 避免說「10 的 4 次方」,可改說「一萬」,這樣對應徵者更友善,否則一直講「10 的多少次方」,可能會造成非必要的焦慮感。數值範圍很關鍵,在 REACTO 的 Optimization 或變化題階段,就可變更範圍,觀察應徵者如何思考和回應 * [13:52](https://youtu.be/FBT4lqwDxB4?t=832): 避免說「嗯!聽起來是還可以」,這樣可能讓人誤會你根本沒檢查程式碼是否正確,應該回顧程式碼,看其中哪裡可調整,或者針對程式碼流程/變數命名提出改進 * [17:51](https://youtu.be/FBT4lqwDxB4?t=1071): 避免說「聽起來是沒有問題」這樣的話,會讓人覺得你很敷衍,將心比心,你若你一間知名的科技公司面試,結果 interviewer 跟你沒什麼互動和討論,你會怎麼想?其實最簡單的互動,就是簡述應徵者提出的方法,然後詢問如何驗證結果、程式碼是否可更精簡、特定的邊界條件是否考慮等等。follow-up 的問題不見得要讓應徵者把完整程式碼做出來,關鍵是「認識一個人的技能和面對問題的態度」 針對 interviewee 的檢討: * [1:55](https://youtu.be/FBT4lqwDxB4?t=115): 突然提到 "pre-sum",對於溝通沒有太大幫助,應該依循 REACTO 法則,先充分 Repeat,然後解釋 "pre-sum" 的想法和益處 * [4:11](https://youtu.be/FBT4lqwDxB4?t=251): 避免頻繁的移動滑鼠游標,不僅在視訊會議可能無法完整展現,也會讓對方感到焦慮,從而影響專注程度 * [06:21](https://youtu.be/FBT4lqwDxB4?t=381): 面試不是講課,不用把「請問有任何問題嗎?」掛在嘴邊,可適度停頓,等 interviwer 的同時,順便整理 REACTO 的 Approach 內容。也要確認是否可用 Python 撰寫程式碼 * [06:45](https://youtu.be/FBT4lqwDxB4?t=405): 避免頻繁的低頭,或許你在打字,但可能會有人懷疑你在跟槍手交流,儘量用一致的儀態作答 * [07:57](https://youtu.be/FBT4lqwDxB4?t=477): 「並且找到它的 `curMin` 」聽起來像是「找 [Kermit](http://www.columbia.edu/kermit/)」,反而造成誤會。直接說「更新目前的最小值」 * [08:38](https://youtu.be/FBT4lqwDxB4?t=518): 為何用到 `float`? * [11:30](https://youtu.be/FBT4lqwDxB4?t=690): 既然要討論到空間的使用改進 (即 `preSum` 佔用的空間),應當跟 interviewer 確認,並討論這樣改善的效益何在 * [13:44](https://youtu.be/FBT4lqwDxB4?t=824): 避免說「請問這樣是否符合你的需求?」,這樣不會顯得你有禮貌,反而會讓人誤解是「你剛背完答案,等老師批改」,可改說「我剛才從直觀的想法開始,在實作階段想到可調整空間效率」這類回顧的話語,特別在英語作答時,可引導 interviwer 提出更深入問題並彰顯自己的付出 * [14:30](https://youtu.be/FBT4lqwDxB4?t=870): Divide-and-Conquer 應展現在 REACTO 的 Approach 中,可回頭看之前分析的過程,而非急著看程式碼,畢竟人家對你有興趣才會 follow-up。如何清楚展現自己的想法,才是面試的關鍵 * 在定義function的spec時,也應該注意fuction name的意義,不應該只是用"f"代替 * [Kadane's algorithm](https://en.wikipedia.org/wiki/Maximum_subarray_problem) (一種動態規劃手法) 可以很好的計算maxSubarray #### Method: [kadane's algorithm](https://www.geeksforgeeks.org/largest-sum-contiguous-subarray/) ```python # Time: O(n) # Space: O(1) class Solution: def maxSubArray(self, nums: List[int]) -> int: ''' kadane's algorithm 當 sum_ = nums[i: j + 1] < 0 時,會想要從前面的 i 再往前縮,不過不管怎麼縮 sum_ 都會是負的 (可以把sum_想成利潤,既然到目前為止錢都被敗光了,那前面的 i 再往前縮,利潤仍然是負的) ''' ans = -float("inf") sum_ = 0 for num in nums: sum_ += num ans = max(ans, sum_) if sum_ < 0: sum_ = 0 return ans ``` :::success [How to pronounce Kadane?](https://www.howtopronounce.com/kadane) ::: 延伸閱讀 * [Maximum subarray problem](https://en.wikipedia.org/wiki/Maximum_subarray_problem) * [Kadane's algorithm](https://zhuanlan.zhihu.com/p/96014673) --- ## [50. Pow(x, n)](https://leetcode.com/problems/powx-n/) > 影片 ### 面試過程 🧔:在數學上經常會計算到 x^n,希望你能夠實作出一個程式計算 x^n,並計算後的回傳數值 👶:那想先請問x、n的範圍,n有沒有可能是負的或是小數之類的 🧔:x的範圍界在-100 ~ 100之間,n的範圍界在-2^31 ~ 2^31-1之間,且n都是整數 👶:還有一個問題是我輸入0^0,會希望我的系統回傳甚麼 🧔:如果是0^0回傳1就可以了 👶:就值觀來說,解這個題目會想將n分成兩種情況處理,n>=0計算x^n,n<0計算1 / x^(-n),這樣就只需要在乎n>=0的情況來實作我們的演算法,就值觀來說就是乘x n次,也就是loop n次,這樣就能計算x^n 的數值,但是這個n的範圍大於2^31 -1,也就是已經大於10^9,數值非常大複雜度為O(n),也就是直接loop會耗費很多時間,那這樣子的話... 我想到一個方式就是累乘的方式進行,這樣x的次數會以指數的方式增長,假設需要乘t次,這樣t=2^n,t=logn,時間複雜度大約就會是logn ``` n >= 0 => x ** n n < 0 => 1 / x ** (-n) s = x s = s * s => x ** 2 s = s * s => x ** 4 s = s * s => x ** 8 …. s = s * s 2 ** t = n => t = logn ``` 👶:可是n不一定是2的冪(power of 2),所以必須計算$2^k$ 將近n的數值,所以利用recursion計算剩餘的數值,再用相同的方式找到一個2^k將近次數的數值來計算以此類推直到n=0就回傳1 ``` x ** 15 = x ** n x ** 8 = s x ** 7 => recur(x, n - 8) = recur(x, 7) return s * recur(x, n - 8) x ** 4 x ** 3 => recur(x, 7 - 4) = recur(x, 3) return s * recur(x, 7 - 4) ``` 👶:那整體時間複雜度為O(logn),空間複雜度因為recursion需要cache address,所以為O(logn) 👶:那這邊假設function input為浮點數x和整數n,最後回傳x^n的數值,請問這樣假設ok嗎 ? 🧔:這樣子去定義function是可以的 👶:那目前我的演算法大致上長這樣,請問有甚麼問題嗎 ? 🧔:聽起來是沒甚麼問題,你可以開始實作了 #### Method: recursion ```python= # Time: O(logn) # Space: O(logn) class Solution: def myPow(self, x: float, n: int) -> float: # n >= 0 def pow(x, n): if n == 0: return 1 s = x times = 1 while times * 2 <= n: s = s * s times *= 2 return s * pow(x, n - times) if n >= 0: return pow(x, n) else: return 1 / pow(x, -n) ``` 👶:以上為我的演算法實作後的code,請問這樣有符合你的要求嗎 ? 🧔:大致上是ok,今天的面試就到此結束謝謝 ### 檢討面試 1. 在定義function的spec時,也應該注意fuction name的意義,不應該只是用"f"代替 2. 可以利用iteration的方式進行,降低空間複雜度 #### Method: binary ```python= # Time: O(logn) # Space: O(logn) class Solution: def myPow(self, x: float, n: int) -> float: # 2**14 = 2**8 x 2**4 x 2**2 (14 = 0b1110) def pow2(x, n): if n == 0: return 1 elif n == 1: return x ans = x count = 1 while count * 2 <= n: ans *= ans count *= 2 return ans ans = 1 digitVal = 1 n_ = abs(n) while n_ > 0: if n_ % 2 == 1: ans *= pow2(x, digitVal) n_ //= 2 digitVal *= 2 return ans if n >= 0 else 1 / ans ``` ### 同儕檢討 - Interviewer - [1:00](https://youtu.be/q6PqC0Kv_NY?t=60) 覺得在聽到範圍後將數字打出來很好,因為可以給interviewer確認,也可以記錄下來 - 整體檢討:在開始寫code前,做了非常詳細的說明,而且有一邊打字,很好地展現了面試者的思路。 --- ## [695. Max Area of Island](https://leetcode.com/problems/max-area-of-island/) > 影片 ### 面試過程 🧔:Hello, I am your interviewer. The first question is we given a grid and value of the grid is one become island, zero become water. An island is a group of 1's connected 4-directionally. And you need to find the max area of an island in the grid. 👶:Ok! We have 2D grid. "m" is grid of height and "n" is grid of width. I want to ask the m、n range. It will become empty or not ? 🧔:The m、n range will become 1~50. 👶:I think I will use the depth-first search to solve the this question. If the grid will become like this one (left grid), we will loop all the position of the grid, and use depth-first search to search the island and calculate the island area. If we start at this position ((i, j) = (1, 1)), we will search this position left, right, up and down, and search connected 4-directionally island's area. And the island area will become 5 (left grid). After we search this area, we will change grid value become zeros (right grid). And if next time loop the position again, we will not depth-first search again the island. 👶:I think I will use for loop to loop all the grid. if we find the grid value is one, we will use the depth-first search to search all area of island, and let island's value become zero like this (right grid). ``` m = len(grid) n = len(grid[0]) 1 <= m, n <= 50 0 0 0 0 0 0 0 0 0 1 0 1 => 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 area = 5 ``` 👶:I think I define the function like this, input is a 2D grid. Could I define the function like this ? 🧔:Ok! You can define function like this 👶:This is my algorithm, could you have any concern ? 🧔:I think I do not have any concern. You can start to implement. 👶:Ok! #### Method: depth-first search ```python= # Time: O(mn) # Space: O(log(mn)) class Solution: def maxAreaOfIsland(self, grid: List[List[int]]) -> int: m = len(grid) n = len(grid[0]) def dfs(i, j): if not (0 <= i < m and 0 <= j < n) or grid[i][j] == 0: return 0 grid[i][j] = 0 return dfs(i - 1, j) + dfs(i + 1, j) + dfs(i, j - 1) + dfs(i, j + 1) + 1 ans = 0 for i in range(m): for j in range(n): if grid[i][j] == 1: ans = max(ans, dfs(i, j)) return ans ``` 👶:This is my program. Could you have any concern ? 🧔:I think this is the good method, and thank for your repeat. ### 檢討面試 1. 在定義function的spec時,也應該注意fuction name的意義,不應該只是用"f"代替 2. 雖然可以溝通,不過英文語法糟糕