---
title: 2021 年資訊科技產業專案設計課程面試範例
tags: INFO2021
---
# 2021 年「[資訊科技產業專案設計](https://hackmd.io/@sysprog/info2021)」面試範例
> 貢獻者: 德米安 Demi
> [video](https://youtu.be/CeQNrkbr5Ho)
🧔:interviewer 👶:interviewee
## [226. Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/)
> 影片: [00:19](https://youtu.be/CeQNrkbr5Ho?t=19)
### 測驗說明與問答
🧔:希望Demi能夠實作一個function,輸入binary tree並回傳一個invert binary tree。
👶:好的那我們要處理invert binary tree的問題,好比如我要將:
```graphviz
digraph graphname{
node[shape=box]
{
A[label=1]
B[label=2]
C[label=3]
D[label=4]
E[label=5]
F[label=6]
G[label=NULL,shape=plaintext]
}
{
H[label=1]
I[label=3]
J[label=2]
K[label=5]
L[label=4]
M[label=NULL,shape=plaintext]
N[label=6]
}
A->{B,C}
B->{D,E}
C->{F,G}
H->{I,J}
J->{K,L}
I->{M,N}
}
```
👶:另外也必須特別考量到空集合的狀況。
👶:我剛開始想到的方法是用遞迴的方式,以postorder的方式先向下找節點,如果有子節點時即向下執行函數,直到到達樹葉,或是已訪問完自己的子節點後,即對自己的左右子節點做交換。最後直到根節點完成交換後即結束全部的遞迴函數。
🧔:這樣的時間複雜度是如何?
👶:由於需要遞迴的訪問每一個節點執行函數,故時間複雜度為$O(n)$
### Invert Binary Tree with recursion
```cpp
struct tree{
int val;
struct tree *left;
struct tree *right;
}
struct tree *invertBtree(struct tree * root) {
if(root->left) root->left = invertBtree(root->left);
if(root->right) root->right = invertBtree(root->right);
sturct tree *temp;
temp = root->left;
root->left = root->right;
root->right = temp;
}
```
🧔:想請問是否有不是 recursive 的做法呢?
👶:我的想法是可以透過一個 linked list 的 stack 對我們要做 invert 的 node 做紀錄,將搜尋到的node紀錄在stack中,再逐一從stack中的node做invert,以使用iterative達到類似recursive的做法。
### Invert Binary Tree with iterative
```cpp
struct tree{
int val;
struct tree * left;
struct tree * right;
}
struct treestack{
struct tree* cur;
struct tree* next;
}
struct tree * invertbtree(struct tree * root){
if(!root) return 0;
struct treestack* tstack;
tstack->cur = root;
while(tstack->cur){
if (tstack->cur->left || tstack->cur->right ) {
//預期做tstack紀錄的動作,提供else階段pop tstack節點
}
else{
sturct tree* temp;
temp = tstack->cur-> left;
tstack->cur-> left = tstack->cur->right;
tstack->cur->right =temp;
tstack->next = tsack->cur->next;
tstack->cur = tstack->next;
}
}
}
```
延伸閱讀: [Invert binary tree in O(1) space without stack/queue/recursion](https://hackmd.io/@fennecJ/Invert_bin_tree)
## [35. Search Insert Position](https://leetcode.com/problems/search-insert-position/)
> 影片: [16:38](https://youtu.be/CeQNrkbr5Ho?t=998)
### 測驗說明與問答
🧔:在第二題我會提供一個int array和一個int target,我希望你可以回傳給我此target在array的index,若target不在array中,則告訴我如果target插入array時,他的index是多少?我希望你可以在$O(log(n))$完成。
👶:這一題最直覺的方法是使用Sequential Search的方式一個一個找值,然而這個方法的時間複雜度為$O(log(n))$。那在此我們可以用Binary Search的方式來讓時間複雜度降在$O(log(n))$內。
👶:每次在尋找前紀錄首尾index的區間,並比較中間index的值與target的大小,若arr[index]>target,則往左側區間找值,反之往右,如果相同則回傳該index。直到區間小於一,直接做大小的比較來回傳index。
### binary search
```cpp
int bsearch(int * arr, int target){
int head = 0;
int tail = sizeof(arr)/ sizeof(int) -1;
int cur;
while(tail-head>1){
cur = head + (tail - head)/2;
if(arr[cur] > target) tail =cur-1;
else if (arr[cur] < target) head = cur+1;
else return cur;
}
if(arr[head] >= target) return head;
else if(arr[tail] >= target ) return tail;
else return tail+1;
}
```
## [50. Pow(x, n)](https://leetcode.com/problems/powx-n/)
> 影片: [28:07](https://youtu.be/CeQNrkbr5Ho?t=1687)
### 測驗說明與問答
🧔:最後希望你能實作一個power function,即是$x^n$。
👶:這一題要實作$x^n$的function。那在這一題中有幾個需要注意的部分,比如n的正負。如果n為正,那就是用加乘的方式處理,若為負則要做加除。而一些情況下可以免去計算直接回傳:比如
$x=0 , 1\ return\ 0,1\\ n=0\ \ return\ 1$
最直覺的作法是用for迴圈讓$x$乘(除)上$n$次,不過這樣的時間複雜度是$O(n)$。我認為更快的方式是使用類似餘數分解的方式。(之後解釋)
### Pow(x, n)
```cpp
double power(double x , int n){
if(x ==1) return 1;
else if (x == 0) return 0;
if(n == 0 ) return 1;
double result;
if(n/2){
result = power(x, n/2);
result *= result;
}
else result = 1;
if(n%2){
if(n>0) result *= x;
else result /= x;
}
return result;
}
```
延伸閱讀: [LeetCode 50. Pow(x, n)](https://hackmd.io/@Zero871015/LeetCode-50)
## 總體初步檢討
針對 interviewer 的檢討:
* 時間複雜度 (time complexity) 不該說「是多少」,這是「等級」,而非「數量」
* 提問時,避免只拋出「時間複雜度是什麼等級?」這樣的問題,這樣對話欠缺上下文,可能 interviewee 憑藉猜測或背誦做答,從而喪失鑑別度 (注意對話時間,儘量讓問答有效率),可改為一併要求 interviewee 解釋並簡介對應到現有程式碼該怎麼展現
* [226. Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/) 這題,interviewee 一開始說用遞迴,在程式碼作答後,應該快速檢討 (如某些極端案例是否有考慮到) 或要求 interviewee 自己說明驗證和改進的方案,而非急著問「能否改為非遞迴的方式」,畢竟後者太容易被預測
* [07:29](https://youtu.be/CeQNrkbr5Ho?t=449): interviewee 實作的程式碼不夠精簡,應該提示是否能改進。例如:
```cpp
struct TreeNode *invertTree(struct TreeNode *root) {
if (!root) return NULL;
struct TreeNode *l = invertTree(root->left), *r = invertTree(root->right);
root->left = r, root->right = l;
return root;
}
```
* [15:52](https://youtu.be/CeQNrkbr5Ho?t=952): interviewee 顯然在此處花了很多時間撰寫程式碼,卻看不出思維的特別之處,於是可適度打斷,跟 interviewee 討論可能的實作方式,給予提示,畢竟面試的重點是「認識一個人,包含專業能力及其展現」,適度略過細節,可讓雙向溝通更順暢
* 講解 [35. Search Insert Position](https://leetcode.com/problems/search-insert-position/) 題意時,過於簡短,應該給予一些程式碼期待的說明,至少舉一個案例,當然也可一開始就拿特例來探討,畢竟 interviewee 在前一個問題已有中等水準的表現
* [19:57](https://youtu.be/CeQNrkbr5Ho?t=1197): interviewee 宣稱不需要特別傳遞描述陣列空間的變數 (如 `arr_size`),應該適度打斷,討論 C 語言函式呼叫的議題,畢竟光靠一個傳入的指標,該如何知道陣列具體的空間呢?
* [28:00](https://youtu.be/CeQNrkbr5Ho?t=1680): 既然 interviewee 宣稱完成程式碼,應有必要的檢驗和討論,例如極端狀況是否考慮到、變數命名是否清晰,和後續改進
```cpp
int searchInsert(int *nums, int numsSize, int target) {
int lo = 0, hi = numsSize;
while (lo < hi) {
int m = lo + (hi - lo) / 2;
if (nums[m] < target)
lo = m + 1;
else
hi = m;
}
return lo;
}
```
* 聽到 interviewee 提出的想法,不該照單全收,可要求拆解更細的部分,或是先撰寫一部分虛擬碼,畢竟面試有明確的時間限制,若因 interviewee 寫程式花費較多時間,就意味著無從評估其具體能力
* 看到 interviewee 寫出 `int *arr; int tail = sizeof(arr) / sizeof(int) - 1;` 這樣的程式碼時,應該要打斷,畢竟 `sizeof(arr)` 存在嚴重的錯誤
* [你所不知道的 C 語言:技巧篇](https://hackmd.io/@sysprog/c-trick) 的「不夠謹慎的 `ARRAY_SIZE` 巨集」一節
* [50. Pow(x, n)](https://leetcode.com/problems/powx-n/) 之所以列為中等難度,就在於有很多討論,應該引導 interviewee 思考。
```cpp
double myPow(double x, int n) {
double value = 1;
long nn = n;
if (nn < 0) {
for (nn *= -1; nn; nn >>= 1) {
if (nn & 1)
value /= x;
x *= x;
}
return value;
}
for (; nn; nn >>= 1) {
if (nn & 1)
value *= x;
x *= x;
}
return value;
}
```
針對 interviewee 的檢討:
1. 在遠距面試的場合中,通常鏡頭會採正面且不會拍到鍵盤,有效的手勢範圍很窄,不該太依賴手勢,相反的,儘量用 REACTO 步驟的 Example,打字並說出輸入/預期輸出案例的「特性」
2. 對話中舉例的測試資料 (或某一組輸入/預期輸出) 可敲入到指定的作答內文中,如 Google Docs,這樣在用英語解說時 (畢竟不是母語,說越多,錯誤就越多),可說得少一點
3. 以 [226. Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/) 來說,既然一開始提到想用遞迴,可以先寫下 function prototype 和遞迴呼叫的本體,隨後再補上終止條件和細節,這樣程式碼和思路探討會更一致
4. 用 Google Docs 撰寫程式碼,會發現程式碼縮排不好顧及,現有影片的程式碼在視覺上過於緊湊。其實書寫程式碼時,不用嚴格依據實際程式碼讀取的順序,反而可寫下「對應到 REACTO 步驟的 Approach 的關鍵程式碼」的函式呼叫、變數指派,或者迴圈主體
5. [06:12](https://youtu.be/CeQNrkbr5Ho?t=372): 儘量撰寫簡潔的程式碼,例如 `struct btree *temp; temp = root->left;` 可改為 `struct btree *tmp = root->left;`。不要小看只是幾個字元的差距,因為在 Google Docs 中,程式碼的縮排都要自己處理,越少換行或者非必要的 tab 按鍵,都可避免說話和程式碼撰寫間的落差
6. [07:26](https://youtu.be/CeQNrkbr5Ho?t=446): 以 [226. Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/) 來說,說明用 stack 操作改寫原本遞迴程式的手法,應該要在實際撰寫程式碼之前,跟 interviewer 討論,畢竟這手段可能不符合 interviewer 的期待
7. 原本已撰寫的程式碼可適度保存,例如用複製貼上,放在 Google Docs 某處,畢竟 interviewer 可能會要求對照
8. 撰寫非遞迴版本的實作,過多的滑鼠移動和區域反白,會影響 interviewer 視覺焦點,而且途中反覆在鄰近的程式碼間切換並變更程式碼敘述,佔用可觀的時間
9. [16:41](https://youtu.be/CeQNrkbr5Ho?t=1001): "array" 的發音是 [[ə-ˈrā](https://www.merriam-webster.com/dictionary/array)],要留意。中英交錯時,如果發音不精準,容易讓 interviewer 誤會,尤其還要將變數名稱納入時,因此儘量避免非必要的中英詞彙交錯
10. [00:43](https://youtu.be/CeQNrkbr5Ho?t=43): 二元樹的用語是「子節點」,而非「兒子」,而且反轉二元樹是操作 sibling 的順序
11. [00:53](https://youtu.be/CeQNrkbr5Ho?t=53): 二元樹舉例時,應善用 tab 來區隔各節點
12. [02:08](https://youtu.be/CeQNrkbr5Ho?t=128): 程式碼撰寫儘量精簡,例如可改為 `struct TreeNode { struct TreeNode *left, *right; }` 注意 `bintree` 的命名,從而避免跟 [B-Tree](https://en.wikipedia.org/wiki/B-tree) 混淆。星號 (asterisk, 即 `*`) 作為指標的宣告一部分,應靠向變數名稱,這樣不僅可寫出更緊湊的程式碼,也能避免混淆。在 [03:36](https://youtu.be/CeQNrkbr5Ho?t=215) 誤將 binary tree 講成 B-Tree,也該改進
13. [02:20](https://youtu.be/CeQNrkbr5Ho?t=140): 手勢在演講過程有強化表現的效果,但在面試場合,由於鏡頭方位,手勢就需要調整,或者儘量改進說話的資訊量
14. [03:01](https://youtu.be/CeQNrkbr5Ho?t=181) 時間複雜度有很多表示法,可見 [Complexity:Asymptotic Notation](http://alrightchiu.github.io/SecondRound/complexityasymptotic-notationjian-jin-fu-hao.html),這裡既然是討論 big-O,那就該清楚讀出來。且,除了時間複雜度,尚有空間複雜度,特別是之前已提過要用遞迴實作,這就是該著墨之處
15. [04:15](https://youtu.be/CeQNrkbr5Ho?t=255): 之前提到打算用遞迴實作,可緊接著撰寫函式原型 (function prototype) 和遞迴呼叫的程式碼,這樣一來對話更緊湊,二來思緒和程式碼對應也更好
16. [18:18](https://youtu.be/CeQNrkbr5Ho?t=1098): 解釋 Approach 時,應該順道解釋為何可滿足題目的 $O(\log n)$
17. [23:33](https://youtu.be/CeQNrkbr5Ho?t=1413): `==` 讀作「等於」即可,也可說「判斷是否相等」,簡潔的讀法習慣有助於英語表達
18. [25:23](https://youtu.be/CeQNrkbr5Ho?t=1523): 舉例應該獨立於程式碼列表