---
# System prepended metadata

title: info2024-homework2

---

# 2024 年「[資訊科技產業專案設計](https://hackmd.io/@sysprog/info2024/https%3A%2F%2Fhackmd.io%2F%40sysprog%2FH1-4KbanC)」課程作業 2
> 貢獻者: 菜就多練-Rookie 
> 面試夥伴：迷因柴犬-Doge
### 學員面試檢討：
1. [五空-Monkey](https://hackmd.io/w7Iz6haNQkODLzs0htcQJA?view#%E4%BB%96%E8%A9%9502) 他評02
2. [卡普空-Capcom](https://hackmd.io/IbLz_05lQWmp8qJ2eMhCvg?view#%E5%90%8C%E5%84%95%E6%AA%A2%E8%A8%8E02)同儕檢討02
3. [魯智深-NatureLover](https://hackmd.io/Fa1bSAKeR6G2Eq3p8m52pA?view#%E4%BB%96%E8%A9%9502) 他評02
4. [溫兜絲奉-Wendowsform](https://hackmd.io/ZH8OCJPJSheKJjAg47mmQQ?view#%E4%BB%96%E8%A9%953) 他評3
5. [Amanda-艾曼達](https://hackmd.io/OSagT9xVQAq9U2cgdcP-5w?view#%E4%BB%96%E8%A9%9502) 他評02

## [3. Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/description/)
> [面試影片](https://www.youtube.com/watch?v=rc-TyLWEZvA)
> 💁‍♂：Interviewer(我)
> 🙋：Interviewee

### 面試過程
💁‍♂：Doge同學好，歡迎你參加今天面試，我們今天會給你一道經典題目：Longest Substring Without Repeating Characters，我們會給你一個字串，要你找出這字串中不含重複字元的最長字串，並返回長度。這樣你有理解題目嗎？
🙋：我有理解。
💁‍♂：那麼你打算怎麼處理這題呢？
🙋：我想用for迴圈檢查字串長度，從第一個字元開始檢查到最後一個字元，看有沒有重複，沒有重複的話，我就把他記到最大長度(變數)裡面，因為題目要回傳長度，因此用迴圈把答案記到最大長度(變數)裡面，就是我們要的結果。
💁‍♂：聽起來有道理，那你可以試著做做看。
🙋：(面試者做題中)
```python=
def lengthOfLongestSubstring(s: str) -> int:
    maxLength = 0
    for i in range(len(s)):
        for j in range(i + 1, len(s) + 1):
            substr = s[i:j]
            if len(set(substr)) === len(substr):
                maxLength = max(maxLength, len(substr))
    return maxLength
```
💁‍♂：同學你第6行的"="是不是多了一個
🙋：三個"="是同型別比較，因為python一開始給變數的時候不會定義型別，所以當要比叫型別時，會用三個"="，我這裡用是比較龜毛的寫法。
💁‍♂：OK，很棒的回答。那請問這個方法的時間複雜度是多少？
🙋：因為包了兩個迴圈，所以時間複雜度是$O(N^2)$，效率不好
💁‍♂：那請問你有別的方法可以improve效率嗎？
🙋：可以用滑動窗口的方式優化，若後面字串找到跟裡面有一樣的，就把前面一樣的縮短，這樣就能確保他是不重複且長度最大的。這樣可能就能用一個迴圈完成工作。
🙋：那我現在就來實做程式碼。
💁‍♂：OK
🙋：(面試者做題中)
```python=
def lengthOfLongestSubstring(s: str) -> int:  
    seen = set()    
    left = 0    
    maxLength = 0    
    for right in range(len(s)):        
        while s[right] in seen: 
            seen.remove(s[left])
            left += 1        
        seen.add(s[right])        
        maxLength = max(maxLength, right - left + 1)    
    return maxLength
```
💁‍♂：請問這樣時間複雜度多少？
🙋：因為while只會進去一次，所以數目是常數，就不把他當複雜度影響的判斷，所以複雜度是$O(N)$
💁‍♂：等於說是只有外面的for會影響複雜度，裡面的while...
🙋：只會跑常數次，因為後面有重複他就會被排除掉，所以我覺得while可以寫成if，但怕"aa"這種連續的情況，還是寫while比較安心。
💁‍♂：OK，那這解法還有什麼情況要考慮。
🙋：字串是空的時候，可以直接for迴圈跳出來，然後還有，若整個字串都沒有重複，可以直接回傳字串長度。
💁‍♂：OK，你提出不錯的方法來改善暴力解了，所以我們面試到這，謝謝你。


### 面試過程檢討(作為Interviewer)
#### 優點
- 題目講解明確，有特別強調是要會傳字串長度，而非整個字串。
#### 缺點
- 有疑問不及時發問，[00:40](https://www.youtube.com/watch?v=rc-TyLWEZvA&t=40s) 這裡面試者說把字串長度逐一檢查，但面試官一開始沒聽懂，應確認面試者講的字串是否為題目給的字串裡面的各種"子字串"。
- [01:00](https://www.youtube.com/watch?v=rc-TyLWEZvA&t=1m0s) 問題同上，面試者說記到最大長度裡面，應該是要講開一個變數叫最大長度，把它存進去，這裡我也沒很確定，應該要立刻詢問面試者意思。
- [04:25](https://www.youtube.com/watch?v=rc-TyLWEZvA&t=4m25s) 不熟悉python，面試官問這種基本問題，顯得很不專業。
- [10:00](https://www.youtube.com/watch?v=rc-TyLWEZvA&t=10m0s) 一樣沒聽懂沒問，面試者說這麼做可以避免邊界情況，但面試官當下沒聽懂，應追問面試者，請他再講詳細一些。
- [11:24](https://www.youtube.com/watch?v=rc-TyLWEZvA&t=11m24s) 面試官沒搞懂為什麼裡面的while不會計算進時間複雜度，雖然有繼續追問是對的，但更應該要讓自己強一點，不要看不懂別人的code。
- [12:42](https://www.youtube.com/watch?v=rc-TyLWEZvA&t=12m42s) 面試者提出了優化的想法，面試官應該要請他用code實做看看，而非草草結束。

## [2095. Delete the Middle Node of a Linked List](https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/description/)
> [面試影片](https://youtu.be/elbknnEbBCI)
> 💁‍♂：Interviewer
> 🙋：Interviewee(我)

### 面試過程
💁‍♂：我們這邊提供一道題目，題目叫Delete the Middle Node of a Linked List，會給你一個linked list，然後會希望你刪除中間節點再回傳。比如說linked list是`1 3 4 7 1 2 6`，會希望你把中間的節點刪掉，然後回傳head。對於題目有沒有其他問題？
🙋：請問意思是，給我`1 3 4 7 1 2 6`，我就要刪掉中間的7，然後再回傳head給你這樣嗎？
💁‍♂：對
🙋：那我們刪掉`7`後，linked list會變成`1 3 4 1 2 6`，那我們還要刪中間的話，要刪掉`4`還是`1`呢？
💁‍♂：刪掉`1`，刪右邊的node
🙋：所以會變`1 3 4 2 6`這樣？
💁‍♂：對
🙋：我有個想法：先用while迴圈traverse linked list來得知有幾個node，再把node數除以2，即可知道誰要被刪除。像是`1 3 4 7 1 2 6`這條linked list有7個node，因此7/2取整數等於3，我們就把index為3的node(也就是7)給刪掉。這個方法可以嗎？
💁‍♂：可以，那就依你的想法把code實作出來。
#### *解法一：暴力法*
直接先traverse整條linked list，得知總共有幾個node，接著除以2得出要刪除的node，接著把`now`指標指回開頭，再走一次linked list，走到被刪node的前一個node，透過改線做到刪除的效果。
```c++=
class Solution {
public:
    ListNode* deleteMiddle(ListNode* head) {
        int length = 0, delete_idx = 0, now_idx = 0;
        ListNode *now = head;
        if (now->next == nullptr) return NULL;
        while (now) {
            length++;
            now = now->next;
        }        
        delete_idx = length / 2;
        now = head;
        while (now) {
            if (now_idx == delete_idx-1) {
                now->next = now->next->next;
            }
            now_idx++;
            now = now->next;
        }
        return head;
    }
};
```
💁‍♂：這樣看起來有達到功能需求，請問這份code有沒有需要改善的地方，或者他的空間&時間複雜度是多少？
🙋：空間複雜度是$O(1)$，因為這邊只有開這幾個變數。
💁‍♂：恩，對
🙋：時間複雜度的話，因我們要看兩輪linked list，每次看都需要花$O(N)$，$N$為linked list長度，所以整體是$O(N)$
💁‍♂：恩，對
🙋：至於改善方法，我想可以用快慢指標，每次while迴圈中，快指標動兩步，慢指標動一步，這樣快指標到結尾時，慢指標就會走到中間的點，而我們題目就是要刪掉中間的點。我覺得可以用這個想法做做看。
💁‍♂：聽起來是個蠻特別也不錯的想法，那你就實作看看。
#### *解法二：快慢指標*
由於題目要求刪除中間的點，因此可以利用快慢指標的特性，快指標一次走兩步，慢指標一次走一步，那麼當快指標走到底時，慢指標就會剛好走到中間，也就是我們要刪除的點。
想法很美好，但在實務上，由於快指針一次動兩步，所以在快指標traverse到結尾時，可能會遇到兩種情況，分別為：
`fast->next`為NULL；
`fast->next`不為NULL，但`fast->next`為NULL
此時即可討論這兩種情況下，慢指標的狀況：
+ `fast->next`為NULL
    此case中，慢指標直接指向要被刪除的node，因此，我們可以事先建立`pre`指標紀錄慢指標的前一個node，若走到此case，則讓`pre->next = slow->next`即可。
+ `fast->next`不為NULL，但`fast->next`為NULL
    此case中，慢指標指向要背刪除的node的前一個node，所以直接讓`slow->next = slow->next->next`，即透過改線刪除middle node。此外，由於已經經過前面的`if`判斷，因此這裡不用再判斷`fast->next`是否為NULL，直接判斷`fast->next->next`即可。
```c++=
class Solution {
public:
    ListNode* deleteMiddle(ListNode* head) {
        if (!head->next) return NULL;
        ListNode *fast = head, *slow = head, *pre = head;
        while (1) {
            if (fast->next == NULL){
                // 慢指針要刪
                pre->next = slow->next;
                break;
            }
            if (fast->next->next == NULL) {
                // 慢指針的下一個要刪
                slow->next = slow->next->next;
                break;
            }
            fast = fast->next->next;
            pre = slow;
            slow = slow->next;
        }
        return head;
    }
};
```
💁‍♂：看起來這份code有達到要求，那請問這份code時間複雜度是多少？
🙋：老實說沒快多少，快了兩倍(但還是O$(N)$)。因為前一份code看linked list兩次，但這份code只要看一次。因為我們從頭走到尾的瞬間，就能順便處裡中間點的刪除動作，所以只要看一次。
💁‍♂：這是一個不錯的做法，可以同時在跑一次迴圈的情況下就把所有東西檢查完，提高效率，那我們這次面試就到這裡，謝謝你的參與。
🙋：謝謝

### 面試過程檢討(作為Interviewee)
#### 優點
+ 快慢指標解釋&舉例恰當。
+ 有解釋時、空間複雜度原因。
#### 缺點
+ 應確認網路、視訊、打code媒介正常，由於上述問題，[01:30](https://youtu.be/elbknnEbBCI&t=1m30s) 這裡之前我打的example沒有出現，反而之後瞬間出現，可能會被懷疑複製貼上。
+ 沒有遵守REACTO，code寫完後沒有Test。
+ [06:40](https://youtu.be/elbknnEbBCI&t=6m40s) 這裡語言沒組織好，註解要註解的完善，不要打一打覺得不對勁又刪掉，可以想好怎麼打後再把註解打回來。
+ [16:20](https://youtu.be/elbknnEbBCI&t=16m20s) 這裡沒說清楚，應該講明確說是當快指標走到Linked list結尾的瞬間。
+ 應該和面試官討論刪除linked list中間node這件事，可能在什麼情境下會發生。

## 檢討自己至今的表現
我覺得我對於這堂課的投入算是蠻認真的，雖然在擔任面試官or面試者時，都還有不小的進步空間，但在態度這一塊，我自認是蠻用心的。都會準時把作業做完，也會認真寫leetcode準備面試題目。
在上課時，我總是認真聽講，每次在課堂上的面試模擬，雖然上台模擬面試的不是我，但我也會在台下想這個題目該怎麼解，要怎麼遵循REACTO流程回答，以及一開始想到的方法時空間複雜度如何，有沒有其他更好的方法等等。每次老師抓人模擬面試都讓我有許多思考。
而學長演講的環節，我也是聽得很認真，上次Champ學長演講也是讓我獲益匪淺，也讓我知道堅持不一定是美德，有彈性的調整才是正確的這種核心理念。
基於以上幾點，我認為我對於這堂課的投入和心態都是蠻正向的。但依然是有許多需要改善的部分：
- 首先是自身實力，由於我基礎不好，有時會看不懂別人寫的code(像這次面試隊友的那題)，這點需要多刷題來加強。
- 接著是積極度問題，老師在課上問誰要自願模擬面試時，我從不舉手，因為我內心是有的怕上台的，但其實上台是一個難能可貴的練習機會，還能順便賺些小錢，不該這麼排斥才對。
- 最後是太不敢問，這次面試隊友的過程中，有數次沒聽懂他表達的意思，但我大多選擇草草帶過，這樣不求甚解是不好的，應該問到清楚理解才對。

以上這些缺點，希望能在日後的課程中逐步改進，讓我能做好準備，面對未來真正的企業面試。