--- title: 2021 年資訊科技產業專案設計課程面試範例 tags: INFO2021 --- # 2021 年「[資訊科技產業專案設計](https://hackmd.io/@sysprog/info2021)」面試範例 > 貢獻者: 林宇庭-Austin > ==[video](https://youtu.be/zokVE4RUmmU)== # Homework 2 ## (E) [167. Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) Given an array of integers `numbers` that is already ***sorted in non-decreasing order*** Find two numbers such that they add up to a specific `target` number. Return *the indices of the two numbers (**1-indexed**) as an integer array `answer`* * The tests are generated such that there is **exactly one solution** ### Solution 1: Brute Forece ```cpp= class Solution { public: vector<int> twoSum(vector<int>& numbers, int target) { vector<int> answer(2, 0); for (int i = 0; i < (numbers.size()-1); i++) { for (int j = (i+1); j < numbers.size(); j++) { if (numbers[i] + numbers[j] == target) { answer[0] = (i+1); answer[1] = (j+1); return answer; } } } return {}; } }; ``` Time complexity: $O(n^2)$ :::danger Time Limit Exceeded ::: ### Solution 2: Two Pointers ```cpp= class Solution { public: vector<int> twoSum(vector<int>& numbers, int target) { vector<int> answer(2, 0); int left = 0; int right = numbers.size() - 1; while (left < right) { int sum = numbers[left] + numbers[right]; if (sum == target) { answer[0] = (left + 1); answer[1] = (right + 1); break; } else if (sum > target) { right--; } else { // sum < target left++; } } return answer; } }; ``` Time complexity: $O(n)$ 【想法】 左右夾擠 ## (E) [69 Sqrt(x)](https://leetcode.com/problems/sqrtx/) Given a non-negative integer `x`, compute and return the square root of `x`. ### Solution 1: Brute force ```cpp= class Solution { public: int mySqrt(int x) { if (x == 0 || x == 1) return x; for (long root = 0; root <= x; root++) { if (root*root > x) { return (root - 1); } } return -1; } }; ``` Time complexity: $O(\sqrt{x})$ ### Solution 2: Binary search ```cpp= class Solution { public: int mySqrt(int x) { long begin = 0; long end = static_cast<long>(x); while (begin <= end) { long middle = (begin + end)/2; if (middle*middle > x) { end = middle - 1; } else { begin = middle + 1; } } return (begin - 1); } }; ``` Time complexity: $O(logx)$ 【想法】 1. binary search: 找出最小的正整數 $c$,使得 $c^2 > x$ 2. 則 $\sqrt{x} = (c - 1)$ ## (E) [141. Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/) Given `head`, the head of a linked list, determine if the linked list has a cycle in it. ### Solution1: HashTable (`unordered_set`) ```cpp= class Solution { public: bool hasCycle(ListNode *head) { unordered_set<ListNode*> record; ListNode* current = head; while (current) { if (record.count(current)) { return true; } record.insert(current); current = current->next; } return false; } }; ``` Time complexity: $O(n)$ Space complexity: $O(n)$ 【想法】 看過的建築物又再看到一次,肯定是繞圈了 ### Solution2: Fast + Slow pointers ```cpp= class Solution { public: bool hasCycle(ListNode *head) { ListNode* fast = head; ListNode* slow = head; while (fast) { if (!fast->next) return false; fast = fast->next->next; slow = slow->next; if (fast == slow) return true; } return false; } }; ``` Time complexity: $O(n)$ Space complexity: $O(1)$ 【想法】 跑操場才有倒追,否則跑比較快的會先到終點 ## (M) [102. Binary Tree Level Order Traversal](https://leetcode.com/problems/binary-tree-level-order-traversal/) Given the `root` of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level). ### Solution 1: BFS ```cpp= class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { if (!root) return {}; vector<vector<int>> levelOrder; vector<TreeNode* > currentLevel, nextLevel; currentLevel.push_back(root); while (!currentLevel.empty()) { levelOrder.push_back({}); for (TreeNode* node : currentLevel) { levelOrder.back().push_back(node->val); if (node->left) nextLevel.push_back(node->left); if (node->right) nextLevel.push_back(node->right); } currentLevel = nextLevel; nextLevel.clear(); } return levelOrder; } }; ``` Time complexity: $O(n)$ ### Solution 2: DFS ```cpp= class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { DFS (root, 0); return result; } private: vector<vector<int>> result; void DFS (TreeNode* root, int level) { if (!root) return; while (result.size() <= level) result.push_back({}); result[level].push_back(root->val); DFS (root->left, level + 1); DFS (root->right, level + 1); } }; ``` Time complexity: $O(n)$ # 第三次作業-他評01(林宇庭-Austin) **Interviewer** - 優點 1. 有提出進階的問題 - 缺點 1. 和interviewee的對話偏少 2. 說明題目時,不知為何聽者好像沒辦法一次瞭解題目的意思 **Interviewee** - 優點 1. 有執行REACTO的步驟 2. 第一題第一段的Repeat,Example和Approach所佔的時間很少,大概30秒就進入打code 3. 第一題第二段思考改進方法時,依然有記得按照REACTO的步驟 4. 邊打code也有邊解釋 5. 第二題第一段有解釋定義變數值域的原因 6. 第二題有考慮到邊界值的問題 - 缺點 1. (個人觀點)不喜歡看到簡體字:) 2. "應該是"的口頭禪聽起來不太肯定 3. 第一題沒有考慮到輸入為空的情況 4. 第一題第二段作改進時都是口頭解釋作法,可以在電腦上舉例說明會讓人更容易了解改進的方法 5. 第二題第一段講解Approach時可以在電腦上舉例說明會讓人更容易了解方法 6. 第二題第一段考慮邊界值的問題時有點不太清楚 7. 第一題第二段思考改進方法時,講解的不太清楚,如果使用REACTO會比較清楚 # 第三次作業-他評02 **Interviewer** - 優點 1. 有follow up 問題給interviewee - 缺點 1. 題目規範可以模糊一點,供interviewee藉由提問方式去清楚定義問題 2. coding 過程中未針對interviewee未講解清楚部分做提問 3. 缺少不同方法的比較及優缺點問題 **Interviewee** - 優點 1. 有執行REACTO的步驟 2. Code 編寫能力流暢 - 缺點 1. 程式碼可以寫得簡潔些,多餘的變數不須額外定義 2. 第二題題目並未解釋清楚為何使用long 3. Repeat, Example 步驟未提供清楚的例子進行講解 4. 與interviewer 互動太少 # 第三次作業-他評03 + **寫 Code 的部分非常流暢,也幾乎沒有完全安靜的空間,非常方便 Interviewer 理解 Intervieree 想做什麼。** + 通篇來說,Interviewer 幾乎只有朗誦問題的用處,說實話就算刪掉感覺也不構成大礙,下面會補上我認為可以問受試者的問題。 + 我覺得頗有趣的是,在中國因為翻譯作的足夠完善,(雖然不是在程式界的經驗)我通常不會聽到名詞轉用英文的情形。但是這裡出現了中國用詞『數組』等等,卻又不用中國單詞的『索引』之類的。 + Interviewer 的朗讀感非常重。我認為即使是 Interviewer 也應該會用比較口語的方式去敘述一個題目,又考慮到在敘述題目時應該 Interviewee 也該能看到文字說明,倘若還是複述的話其實根本沒必要。 + Interviewee 與 Interviewer 有相同的問題,REACTO 裡的 "R" 應該指的是整理題目後清晰的說明自己的理解,而不是照著重念一次。 + Interviewee 舉例的時候,應該要打在右方程式碼區域,而不是用滑鼠指。錄成影片都有點難跟了,如果是 LIVE 一定更困難。 ## 第一題 Sorted Two Sum ### Interviewer + 『可以讓這個程式變得更快嗎』我覺得實在太明顯,或者說 $O(n^2)$ 本來就根本不可能是最佳解。一開始就限定解答的時間複雜度可以節省時間去問更有意義的問題,或讓 Interviewee 不『裝弱』。 + 假設這個面試是指定使用 C++ 作答,我認為 Interviewer 應該同時會想知道 Interviewee 對 C++ STL 的理解,因此在撰寫 `for` 迴圈的時候我認為可以要求受試者使用 iterator 作答(殘酷一點順便叫他說明 iterator 的好處壞處)等等,看看 interviewee 到底有沒有對 C++ STL 有深入的理解。 + 最後的統計上,使用的記憶體空間只贏過 35% 的使用者;可以問 Interviewee 到底輸給 65% 的使用者的原因為何? ### Interviewee + 1:38, 這裡又變成 "vector",不是『數組』了XD + 超時的理由如果可以自己提出更好一點。 + 6:00 左右說明時,『右邊的 pointer 往中間靠』『左邊的 pointer 往中間靠』不太精準,比如說 `[1, 2, 3, 5, 6], target = 3` 的狀況,答案的左右 pointer 都會存在『中間』的左側。應該說『往左/右靠』。 ## 第二題 sqrt(q) ### Interviewer + 『請在不使用內建的函數或運算符號的情況下,撰寫一個求平方根整數部分的函數』就行了。 + 這題的 $O(n)$ 到 $O(\log n)$ 的優化實在太明顯,和上一題有一樣的問題。 + 在計算平方根的時候,乘法計算其實也會花掉許多時間。或許可以問問 Interviewee 『如果可以維持 O(n)的時間複雜度,你可以不用乘法,也不用迴圈模擬乘法,一樣解決問題嗎?』來考考 Interviewee 有沒有從 $n^2$ 求 $(n+1)^2$ 的數學能力。 + `static_cast` 都出來了,當然可以問問『`const_cast` `dynamic_cast` `reinterpret_cast` `static_cast` 的差異』在哪裡之類的問題,一樣考驗 Interviewee 對 C++ 的認知。 ### Interviewee + 更嚴重,說明的時候連滑鼠都不動了。假設 Interviewer 像我一樣聽人說話無法專心,但是看影像專心到不行,Interviewee 直接完蛋。 + 很多『應該』,自信一點。 + 11:05,`long` 的使用原因很奇怪。`long` 就是那個在各個 CPU Architecture 跟 OS 上會有不同大小的奇葩人,在 『Windows』 和 『IA-32 的 Linux』 上是 4 Bytes, 在 『Intel-64 的 Linux 和 max OS』 上是 8 Bytes,僅僅說『因為我們要把 int 平方所以我們用 long』反而會讓人暴露自己不太懂的東西。 - Standard 只規定 `int` 需 $\geq 16$ bit,`long` 需 $\geq 32$ bit,但 `long` 不一定比 `int` 大。