---
tags: info2023-homework1
---
# 2023 年「[資訊科技產業專案設計](https://hackmd.io/@sysprog/info2023)」作業 1
> 貢獻者: 甲魚-baspis
> 🧔:interviewer 👶:interviewee
## [136. Single Number](https://leetcode.com/problems/single-number/description/)
> ==[錄影](https://www.youtube.com/watch?v=mfDstZAhoDs)==
### 面試過程
🧔:同學你好,歡迎你參加第一次的線上面試,在這次的面試中,我們希望可以了解你對於程式開發的風格與想法,所以我們會提出三個問題,希望你能夠提出你的看法並且試著將他們實作出來。
🧔:首先第一個問題,給予一個非空的整數陣列,其中除了一整數X外,都是兩兩成對的,請問你要如何判斷並且找出X呢?
👶:我想先請問,陣列的長度以及數值的上下界分別是多少呢?然後我想要舉個例子來看看我對於題目的要求是否正確。
🧔:沒錯你的舉例是正確的,那假設陣列長度在1到30000之間,而數值介於-30000到30000之間。
👶:我們要找到此陣列中唯一不成對的整數X,初步的想法是建立一個整數陣列,考慮到有負數,因此我想要建立一個大小為60002的陣列arr,使用for迴圈跑完nums,每次在arr中把每個元素+30000的位置+1,最後再用for迴圈檢查arr中哪個位置的值為1,將其-30000即為所求。
```
設nums為[2,2,1],則
arr[2+30000]=2
arr[1+30000]=1
因為arr[30001]值為1,因此可得X=30001-30000=1。
```
🧔:所以你是想要用位移的方式,讓負數儲存在arr中0~30000之間,聽起來可行,那麼麻煩你開始coding吧。
#### Method1:
```cpp
# Time: O(n)
# Space: O(n)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int arr[60002]={0};
for(auto i:nums){
arr[i+30000]++;
}
for(int i=0;i<60002;i++){
if(arr[i]==1){
return i-30000;
}
}
return -1;
}
};
```
👶:因為for迴圈跑過整個陣列,因此時間複雜度為$O(n)$,而空間複雜度為$O(n)$。
🧔:事先建立好陣列雖然是個很直接有效的辦法,但是並不是每個空間都會被使用到的,能請問你有沒有辦法只存有用到的數字呢?
👶:可以用C++的unordered_map紀錄出現的數字(key)以及出現的次數(value),最後再用for迴圈判斷map中value=1的元素即為所求X。
```
設nums為[2 2 1],則
map[1]=1
map[2]=2
因為map[1]值為1,因此可以知道X就是1。
```
#### Method2:
```cpp
# Time: O(n)
# Space: O(n)
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int,int> map;
for(auto i:nums){
map[i]++;
}
for(auto a:map){
if(a.second==1){
return a.first;
}
}
return -1;
}
};
```
👶:以上是我的演算法,程式的時間複雜度為$O(n)$,空間複雜度為$O(n)$。
🧔:其實這道題目有個技巧性的解法,利用基本邏輯閘就能不額外使用矩陣或是unordered_map,可以請你思考是利用哪個邏輯閘並且實作看看嗎?
👶:因為題目只有X一個是單獨存在的,所以應該要思考如何消去其他成對的數字並且只保留X,那麼我們應該要使用XOR這個邏輯閘,因為XOR可以讓相同的數字運算後結果變0,而0和任何數字XOR都等於數字自己,那麼在這題當中,我只需要利用for迴圈將所有數字作XOR,最後就會剩下X了。
```
設nums為[2,2,1]
2 XOR 2 XOR 1 XOR
=0 XOR 1
=1
可以得X=1
```
🧔:那麼請你將程式實作出來吧
#### Method3:
```cpp
# Time: O(n)
# Space: O(1)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans=0;
for(auto x:nums)
ans^=x;
return ans;
}
};
```
👶:以上是我的演算法,程式的時間複雜度為$O(n)$,空間複雜度為$O(1)$。
🧔:大致上都沒問題,那我們就繼續下一道題目了。
## [69. Sqrt(x)](https://leetcode.com/problems/sqrtx/description/)
> ==[錄影](https://www.youtube.com/watch?v=mfDstZAhoDs)==
### 面試過程
🧔:在這題我們想請你不要利用程式本身的函數庫,實作出整數開根號,小數點無條件捨去,而且不需要表示負數的答案,整數範圍介於$0$~$2^{31} -1$。
👶:那麼我假設題目的整數為X,令一個整數變數$temp$從0開始,判斷$temp^2$是否等於X,如果等於則回傳$temp$,如果$temp^2$小於X,而且$(temp+1)^2$大於X,則也回傳$temp$,每次$temp$都加1。
```
設X=4,因為2^2=4,所以2即為所求
設X=10,因為3^2=9<10,且4^2=16>10,所以3即為所求
```
🧔:所以你想要用乘法慢慢逼近正確答案,那就請你開始coding吧。
```cpp
# Time: O(n)
# Space: O(1)
class Solution {
public:
int mySqrt(int x) {
long int temp=0;
while(temp<=x){
//case1:
if(temp*temp==x){
return temp;
}
//case2:
else if(temp*temp<x&&(temp+1)*(temp+1)>x){
return temp;
}
//case3:
else{
temp++;
}
}
return -1;
}
};
```
👶:考慮到temp從0跑到X,所以時間複雜度為$O(n)$,空間複雜度為$O(1)$。
🧔:雖然你有考慮到可能因為數字很大的關係,必須要用long int才能夠防止變數因為平方的關係overflow,但請問你有沒有辦法調整其他程式碼,一樣解決overflow問題呢?
👶:如果不能夠用long int的話,在判斷式我們可以把temp移到X那側變成除法,以解決因為乘法導致數值過大的問題,並且因為除法的關係,需要預先考慮X=0的狀況。
```
設X=4,因為2=4/2,所以2即為所求
設X=10,因為3<10/3,且4>10/4,所以3即為所求
設X=0,因為數學上不能除以0所以要預先考慮
```
🧔:那麼請你把程式乘法的部分改用除法吧。
```cpp
# Time: O(n)
# Space: O(1)
class Solution {
public:
int mySqrt(int x) {
int temp=2;
if(x==0){
return 0;
}
if(x==1){
return 1;
}
while(temp<=x){
if(temp==(x/temp)){
return temp;
}
else if(temp<(x/temp)&&(temp+1)>(x/(temp+1))){
return temp;
}
else{
temp++;
}
}
return -1;
```
👶:以上是我的演算法,程式的時間複雜度為$O(n)$,空間複雜度為$O(1)$。
🧔:假設數字很大,那麼從0開始一個一個判斷顯然是不夠有效率的,請問你有沒有辦法改進甚至優化平均的時間複雜度呢?
👶:我們可以使用binary search,從X的一半(mid)開始判斷,如果滿足條件就回傳mid,如果太小就令頭為mid+1重新計算新的mid值,反之就令尾巴為mid-1計算新的mid值,如果最後first>last就直接回傳last。
```
設X=10,
first=2, last=10, mid=6, 因為6>10/6
first=2, last=5, mid=3, 滿足3=10/3
因此答案為3
```
🧔:這樣的確可以每次減少判斷一半的數,那請你開始coding吧
```cpp
# Time: O(logn)
# Space: O(1)
class Solution {
public:
int mySqrt(int x) {
if(x==0){
return 0;
}
if(x==1){
return 1;
}
int first=2,last=x,mid;
while(first<=last){
mid=first+(last-first)/2;
if(mid==x/mid){
return mid;
}
else if(mid>x/mid){
last=mid-1;
}
else{
first=mid+1;
}
}
return last;
}
};
```
👶:利用binary search時間複雜度變為$O(logn)$,空間複雜度為$O(1)$。
🧔:很好,我們可以進行下一道題目了。
## [2295. Replace Elements in an Array](https://leetcode.com/problems/replace-elements-in-an-array/description/)
> ==[錄影](https://www.youtube.com/watch?v=cOfrze2ppGI)==
### 面試過程
🧔:In this question, we would like you to make a function that can replace elements in an array. Given you an array called $nums$, and an array of size $N*2$ called $operations$. In $operations$, the fist postion contains the number going to be replaced in $nums$, and the second position contains the new number going to insert in $nums$.
👶:For example, if nums contains [1 2 4 6] and one row of the opearion contains [1,3], which means replace 1 with 3. Therefore, the nums becomes [3 2 4 6]. Am I correct? And I would like to ask if the numbers in nums are unique.
🧔:Yes, your example is correct. all the numbers in $nums$ are unique, and it is guaranteed that all operations can be executed.
👶:My thought is if we want to replace the elements in $nums$, we could use for loop to get two elements from $operations$ in each iteration, one representing the old number and the other representing new number. Then, we could use another for loop searching the old number and replace it.
```
nums[1 2 4 6]
operations{[1 3]}
->replace 1 with 3
nums[3 2 4 6]
```
🧔:sounds reasonable, could you translate your idea into code?
```cpp
# Time: O(n^2)
# Space: O(1)
class Solution {
public:
vector<int> arrayChange(vector<int>& nums, vector<vector<int>>& operations) {
for(int i=0;i<operations.size();i++){
int new_num=operations[i][1];
int old_num=operations[i][0];
for(int j=0;j<nums.size();j++){
if(nums[j]==old_num){
nums[j]=new_num;
break;
}
}
}
return nums;
}
};
```
👶:Here is my code. The time complexity is $O(n^2)$ because of the double for loop, and the space complexity is $O(1)$.
🧔:For the second for loop, it's obviously not efficient enough when dealing with huge amount of data. Could you improve the speed searching for the position of the old_num?
👶:We can use unordered_map to store the numbers and its' positions since the time complexity searching for the element in unordered_map is $O(1)$.
```
nums[1 2]
map[1]=0
map[2]=1
operations{[1 3]}
search map[1]->0
replace num[0] with 3
update map
```
🧔:The example sounds clear, could you revise your code?
```cpp
# Time: O(n)
# Space: O(1)
class Solution {
public:
vector<int> arrayChange(vector<int>& nums, vector<vector<int>>& operations) {
unordered_map<int,int>map;
for(int i=0;i<nums.size();i++){
map[nums[i]]=i;
}
for(int i=0;i<operations.size();i++){
int old_num=operations[i][0];
int new_num=operations[i][1];
int location=map[old_num];
nums[location]=new_num;
map[new_num]=location;
map.erase(old_num);
}
return nums;
}
};
```
👶:Here is my code,the time complexity becomes $O(n)$ and the space complexity is $O(1)$
🧔:There are no significant issues, but I've notice that there are some variables that are unnecessary. Could you please streamline the code?
👶:No problem, the variables could be replaced by map and operations.
```cpp
# Time: O(n)
# Space: O(1)
class Solution {
public:
vector<int> arrayChange(vector<int>& nums, vector<vector<int>>& operations) {
unordered_map<int,int>map;
for(int i=0;i<nums.size();i++){
map[nums[i]]=i;
}
for(int i=0;i<operations.size();i++){
nums[map[operations[i][0]]]=operations[i][1];
map[operations[i][1]]=map[operations[i][0]];
map.erase(operations[i][0]);
}
return nums;
}
};
```
👶:I've done. The code has been more streamlined.
🧔:It looks good,I have no more questions, so the interview is finished. Thanks!
## 自評-interviewer
- 在出題時應該要包裝題目,避免面試者用背題的方式回答問題。
- 互動性有點低,感覺可以多引導讓面試者不要一開始就直接暴力解。
- 針對改進程式碼的部分,也應該用一些實際的舉例包裝
- 在面試者coding結束時,應該要驗證程式的正確性。
- 英文口說需要再多練習,增加流暢度和清晰度。
## 自評-interviewee
- 在舉例題目時,可以把例子和條件寫出來而且寫清楚。
- 說明想法時,感覺可以把SOP列出來。
- 寫程式碼時,應該要適時地用一些註解讓面試官知道寫到哪裡了。
- 寫完程式時,應該要去測試程式的正確性。
- 滿容易打錯字的。
---
## 第二次作業-他評
### Interviewer
- 應該把題目寫出來,而不是只是口頭講,會比較好。
- 事先或許可以主動給出example,再由interviewee考慮特殊input來補充。
- 都有做後續討論以及說明時間複雜度和空間複雜度。
- [7:54](https://www.youtube.com/watch?v=cOfrze2ppGI&t=7m54s): Thanks不夠正式,可以講Thank you for your participation之類的。
### Interviewee
- 最後的Test或許可以直接給example input,Run code或者人體compiler。
- [1:30](https://youtu.be/mfDstZAhoDs?si=dR-DmO618zMDdD9f&t=1m30s): 沒有提到初始化所有陣列的值為0。
- [1:35](https://www.youtube.com/watch?v=cOfrze2ppGI&t=1m30s): 感覺這句打了和沒打差不多,可以跑一次範例比較清楚。
## 第二次作業-他評02
### Interviewer:
* 在介紹題目的時候也許可以有一些圖文可能會讓interviewee更能理解題目。
* interviewer有試圖想要一步一步引導的感覺。不過有些引導感覺過於直白。引導的部分可以再給一些故事性,像是平常會遇到哪些運用,所以可以怎麼思考之類的。
* 感覺可以再多增加題目對應真實應用的討論
* 在interviewee解釋可能的作法後,可以不用太快讓interviewee直接開始coding,可多討論些不同做法或此做法可能存在的問題,就不會讓面試者一開始就都是暴力解。
### Interviewee:
* 有考量到Constraints的部分,很棒!
* 有分析空間和時間複雜度。
* 邊打程式碼的時候同時有做說明,不會讓人感覺冷場的感覺。
* 缺少reacto的test部分,有點可惜。
## 第二次作業-他評03
### interviewer
- 有明確指出Interviewee的問題,並進行有效溝通
### interviewee
- 在開始coding之前,有舉例並確認題意
### 可改進之處
- Interviewer應避免開頭直接說出有多少問題
- 在和interviewer溝通時,一口氣將問題問完,等待有回覆再繼續詢問可能會更好一點,也可以避免在實際溝通中讓對方感到混亂
## 第二次作業-他評04
### interviewer
- [ ] 優點
* 直接丟問題讓interviewee自己尋找路綫,適當時候有給hints拉回來
- [ ] 可改進之處
* 一般系統下long int 與 int 其實都是一樣 -2^32 ~ 2^32 - 1, 只有long long才是2^64
### interviewee
- [ ] 優點
* code是實作的時候有解釋清楚
- [ ] 可改進之處
* 一般系統下long int 與 int 其實都是一樣 -2^32 ~ 2^32 - 1, 只有long long才是2^64