--- tags: info2022 --- # 2022 年「[資訊科技產業專案設計](https://hackmd.io/@sysprog/info2022)」作業 1 > 胖達-Panda > 🐶:interviewer > 🐼:interviewee > [模擬面試錄影(漢)](https://youtu.be/-eGgh2GsOFc) > [模擬面試錄影(漢)](https://youtu.be/RC5Wn5U6bgM) > [模擬面試錄影(英)](https://youtu.be/Aeqlc4_77NA) ## [15. 3sum](https://leetcode.com/problems/3sum/) ### 模擬面試過程 🐶:題目會給你一組數值,希望你把所有的組合找出來,組合有都是三個數值所組成,三個數值的總和為 $0$。請注意你的答案不能有重複的組合。 🐶:輸入最少有 $3$ 個數值,最多有 $3000$ 個。數值的值大小最小為$-10^5$,最大為$10^5$。 🐶:請問你對題目有疑問嗎? 🐼:有,我想請問輸入的數值有經過排序嗎? 🐶:沒有,題目預設輸入的數值沒有經過排序。 🐼:好的,爲了確認我對題目的理解,我想用一個例子説明 ```cpp 輸入: [-1,0,1,-1,2,-5] 輸出: {[-1,0,1],[-1,-1,2]} ``` 🐼:請問我有理解錯題目嗎? 🐶:沒有,你可以開始寫你的程式了。 🐼:在寫程式之前,我想先講一下我的想法。 🐼:最直觀的解法是用三個迴圈,把所有組合列舉出來,然後用一判斷式判斷三個數值的總和是否為$0$。 🐼:至於重複的組合,我們可以先把輸入的數值進行排序,然後用 $set$ 把答案存起來,防止加入重複組合。 ### 暴力解法(3個迴圈) ```cpp vector<vector<int>> threeSum(vector<int>& nums){ sort(nums.begin(),nums.end()); const int n =nums.size(); set<vector<int>> ans; for(int i=0;i<n;++i) for(int j=i+1;j<n;++j) for(int k=j+1;k<n;++k) if(nums[i] + nums[j] + nums[k] == 0) ans.insert({nums[i] , nums[j] , nums[k]}); return vector<vector<int>>(nums.begin(),nums.end()); } ``` 🐶:你用三個迴圈把所有組合列舉出來是可以解決我們的題目。但以實務層面來説,輸入資料十分大時,程式的效能會很差。請問你有辦法解決嗎? 🐼:的確,用三個迴圈的時間複雜度是 $n^3$。我的想法是用排序過的數值所提供給我們的資訊,先用一個迴圈把第一個數值選出來,然後用雙指標把第二,三個數值給找出來。 ### 利用快慢指標走訪 ```cpp vector<vector<int>> threeSum(vector<int>& nums){ sort(nums.begin(),nums.end()); const int n =nums.size(); vector<vector<int>> ans; if(nums.front()>0||nums.back()<0) return{}; for(int i=0;i<n;++i){ if(nums[i]>0) break; if(i && nums[i]==nums[i-1]) continue; int j=i+1; int k=n-1; while(j<k) { if( nums[j]+nums[k]<-nums[i]) ++j; else if(nums[j]+nums[k]>-nums[i]) --k; else{ ans.push_back({nums[j],nums[k],nums[i]}); while(j<k && nums[j]==nums[j+1]) ++j; while(j<k && nums[k]==nums[k-1]) --k; ++j;--k; } } return ans; } ``` 🐼:這樣時間複雜度是 $O(n^2)$。 🐶:很好,那如果我們今天不能對輸入的數值做排序,你有辦法解決問題嗎? 🐼:那我們可以用Hash Table來做,但空間複雜度也會增加。 ### 初步檢討 針對 interviewer 的檢討: * 可以更深入對演算法的時間複雜度和空間複雜度做討論 * 應該對第一段的程式碼提出質問,因爲沒有測試過 針對 interviewee 的檢討: * 說話的聲量不定,會忽大忽小 (尤其打code時説話會很小聲) * 說話用語可以再準確點 (vector array 要 分清楚) * 少用助語詞(舉例哪裡說 那,啊,啦,之類比較多) * 最好用test case 來驗證程式 * 分析時間複雜度和空間複雜度時要用 $O(n^3)$ 不要只講 $n^3$ * 第一段的程式碼要測試 ### 同儕檢討 針對 interviewer 的檢討 * [0:00](https://youtu.be/-eGgh2GsOFc): 避免說「這是我們面試的題目」這樣的開場,可改說「考慮到 ___ 情境,我們有時會想要得知連續記憶體空間內的資料分布,舉例來來說,我們想知道是否有其中三個元素總和為 0 的可能,以及發生三者總和為 0 的組合有幾個」 * [1:28](https://youtu.be/-eGgh2GsOFc?t=88): 舉例時,避免頻繁使用滑鼠游標,在視訊會議中,滑鼠游標的移動可能會落後,甚至某些系統無法正確展現滑鼠游標,可改為選取標注的方式 針對 interviewee 的檢討 * [3:16](https://youtu.be/-eGgh2GsOFc?t=196): 顯然有個好用的 container / 資料結構是答題的重要切入點,但應向 interviewer 確認是否可用 C++ STL,若不可用 STL (有些 interviewer 就想看應徵者的基本功和臨場反應),當然連帶也就不可用 `vector` * 延伸閱讀: [3sum|4sum|ksum](https://leetcode.com/problems/4sum/discuss/732885/3sum4sumksum) --- ## [112. Path Sum](https://leetcode.com/problems/path-sum/) > [模擬面試錄影](https://youtu.be/vcccBHvvoJw) ### 模擬面試過程 🐶:題目會給你一個二元樹的根節點 $root$ 和 目標數 $targetSum$。希望你可以找出根節點到葉節點的路徑,葉節點為沒有子節點的節點。 🐶:請注意路徑的所通過的節點值總和為 $targetSum$ 時回傳 $true$,否則回傳 $false$。 🐶:二元樹的節點定義你可以參考這裏。 ```cpp /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ ``` 🐶:請問你對題目有疑問嗎? 🐼:目前沒有,那我想用這裏的例子來驗證我對題目的理解。 ![](https://i.imgur.com/zrkvA8U.jpg) $targetSum=22$ 🐼:我另外再自己舉例説明。 ```plain text 1 / \ 2 3 targetSum=5 false [] false ``` 🐼:請問我有理解錯題目嗎? 🐶:沒有,你可以開始寫你的程式。 🐼:在寫程式之前,我想先講一下我的想法。 🐼:我會先判斷節點是否為葉節點,如果不是的話就把目標數減掉節點的值,把減完的數值和子節點的值存到佇列。遇到葉節點時會比對減完的數值和子節點的值是否相同。佇列爲空時演算法結束。 ### 迴圈解法 ```cpp bool hasPathSum(TreeNode* root, int targetSum) { if (!root) return false; queue<pair<TreeNode*, int>> q; q.push({root,targetSum-root->val}); while (!q.empty()) { TreeNode *node=q.front().first; int sum = q.front().second;q.pop(); if (!node->left && !node-> right && (sum==0)) return true; if (node->left) q.push({node->left,sum-node->left->val}); if (node->right) q.push({node->right,sum-node->right->val}); } return false; } ``` 🐶:你這邊使用了迭代的做法,請問你可以用遞迴的方式處理嗎? 🐼:可以,其實用遞迴和迭代的慨念差不多,但程式碼會簡潔很多。 ### 遞迴解法 ```cpp bool hasPathSum(struct TreeNode* root, int sum) { if (!root) return false; if (!root->left && !root->right && root->val == sum) /* must be leaf */ return true; return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val); } //Test Case: // 1 // / \ // 2 3 targetSum=5 ``` ### 初步檢討 針對 interviewer 的檢討: * 可以更深入對演算法的時間複雜度和空間複雜度做討論 * 應該對第一段的程式碼提出質問,因爲沒有測試過 * 可以問一下有關應用場景的問題 針對 interviewee 的檢討: * 說話的聲量不定,會忽大忽小 (尤其打code時説話會很小聲) * 第一段的程式碼要測試 * 要討論一下BFS和DFS的應用 ### 同儕檢討 程式碼的調整: ```c bool hasPathSum(struct TreeNode *root, int sum) { if (!root) return false; sum -= root->val; if (!root->left && !root->right) return (sum == 0); return hasPathSum(root->left, sum) || hasPathSum(root->right, sum); } ``` 針對 interviewee 的檢討: * 應理解 DFS 和 BFS,並在 REACTO 早期就該聲明你的策略 延伸閲讀: * [DFS](https://alrightchiu.github.io/SecondRound/graph-depth-first-searchdfsshen-du-you-xian-sou-xun.html) * [BFS](https://alrightchiu.github.io/SecondRound/graph-breadth-first-searchbfsguang-du-you-xian-sou-xun.html) --- ## [9. Palindrome Number](https://leetcode.com/problems/palindrome-number/) >模擬面試連結: [9. Palindrome Number(英)](?) ### 模擬面試過程 🐶:In this question, you are given an integer x, return true if x is a palindrome integer. 🐶:An integer is a palindrome when it reads the same backward as forward. 🐶:For example, 121 is a palindrome while 123 is not. 🐶:Do you have any questions about this question? 🐼:I think no. I want to use one example to check my understanding about this question ```cpp 10 false 123321 true ``` 🐼:Am I correct about this quetion? 🐶:Yeah, I think you are right. You can start codding now. 🐼:Before I start my coding, I want to explain my appoach to this problem. 🐼:I will first change the input number into string format.Then, I will reverse the string and check if it is same. ### 轉換成字串 ```cpp bool isPalindrome(int x) { string s= to_string(x); return s==string(rbegin(s), rend(s)); } ``` 🐶:So your first appoach is to change the integer to string format. Can you solve the problem by not changing it to string? 🐼:Yes, we can check the number digit by digit. ```cpp 121 % 10 =1 sum=1 // 1st iter 12 % 10 =2 sum=1*10+2=12 // 2nd iter 1 %10 =1 sum=12*10+1=121 // 3rd iter ``` ### Digit by Digit ```cpp bool isPalindrome(int x){ int temp = x; long sum =0 ; while(x){ sum=sum*10+ x%10; x=x/10; } return sum==temp; } //Test Case: //-121 % 10 =-1 sum = -1 //-12 % 10 =-2 sum = -1*10-2=-12 //-1 %10=-1 sum=-12*10-1=-121 ``` 🐼:Seems that I fail to deal with negative numbers. ```cpp bool isPalindrome(int x){ int temp = x; long sum =0 ; if(x<0) return false; while(x){ sum=sum*10+ x%10; x=x/10; } return sum==temp; } ``` ### 初步檢討 針對 interviewer 的檢討: * 沒有對演算法的時間複雜度和空間複雜度做討論 * 應該對第一段的程式碼提出質問,負數輸入會有問題 * 可以問一下有關應用場景的問題 針對 interviewee 的檢討: * 說話的聲量不定,會忽大忽小 (尤其打code時説話會很小聲) * 第一段的程式碼要測試,負數輸入會有問題自己沒發現 * 第二段程式碼的測試發現有問題時沒有改第一段程式碼 相關閲讀材料: * [Palindrome 演算法筆記](https://web.ntnu.edu.tw/~algo/Palindrome.html) * [Palindrome 應用](https://www.itm-conferences.org/articles/itmconf/pdf/2020/02/itmconf_icacc2020_03006.pdf) * [2021 年模擬面試](https://hackmd.io/@sysprog/S18FUpCBK)