# 2023年資訊科技產業專案設計 作業一 [影片](https://youtu.be/jAHPmNvnk-w?t=0) 👹 : interviewer 👾 : interviewee ## 21. Merge Two Sorted Lists > [影片](https://youtu.be/jAHPmNvnk-w?t=2) ### 測驗說明與問答 👹 : 屎地芬森先生你好,給你兩個linked list, 節點的順序都依照節點裡面的值排序好了, 請把它們合併然後回傳給我合併後的linked list head, 可以闡述你打算使用的解答做法, 然後實作 👾 : 我們先預設這個題目是升序排列的, 我的工作會是需要把`list1, list2`給合併成`mergeList`並且回傳list head 首先我的相法認為我們可以利用merge sort當中merge的做法來處理 一開始我會先定義一個結構體`struct node`如下 ```c struct node { int val; struct node *next; } ``` 👾 : 這裡使用的linked list是singly linked list 👾 : 接著考慮兩種特例也就是`list1`或`list2`其中一個為空, 或者兩者都為空的時候, 直接回傳即可 👾 : 接下來處理一般情況可以遞迴的處理, 每次挑出要加入`mergeHead`的node後, 把剩下的linked list繼續傳入`merge`當中處理 ```c struct node *merge(struct node *list1, struct node *list2) { struct node *mergeHead; if (!list1) return list2; if (!list2) return list1; struct node *cur_node1, *cur_node2; cur_node1 = list1; cur_node2 = list2; if (cur_node1->val > cur_node2->val) { mergeHead = cur_node2; mergeHead->next = merge(list1, list2->next); } else { mergeHead = cur_node1; mergeHead->next = merge(list1->next, list2); } return mergeHead; } ``` 👹 : `cur_node1, cur_node2`是否有存在的必要?另外可以分析時間複雜度嗎? 👾 : 面試官說的沒錯, 確實不需要使用`cur_node1, cur_node2`就可以完成此算法, 改寫如下 ```c struct node *merge(struct node *list1, struct node *list2) { struct node *mergeHead; if (!list1) return list2; if (!list2) return list1; if (list1->val > list2->val) { mergeHead = list2; mergeHead->next = merge(list1, list2->next); } else { mergeHead = list1; mergeHead->next = merge(list1->next, list2); } return mergeHead; } ``` 👾 : 計算時間複雜度首先可以先列出遞迴式, 假設這個`merge`function的input size是$n$, 時間複雜度可以表示$T(n)$, 進行recursive call的時間複雜度會是$T(n-1)$, 其他部分的操作都是$O(1)$, 所以遞迴式可以表示成$T(n) = T(n-1) + O(1)$, 解這個遞迴式可以得到$T(n)=O(n)$ 👹 : 可以優化此算法嗎? 👾 : 可以, 原本我沒有使用到編譯器的tail recursion特性導致遞迴呼叫之後會讓function stack不斷疊加, 我可以嘗試把程式改寫成non-recursive ```c struct node *merge(struct node *list1, struct node *list2) { struct node *mergeHead; if (!list1) return list2; if (!list2) return list1; struct node *cur_node; if (list1->val > list2->val) { mergeHead = list2; list2 = list2->next; } else { mergeHead = list1; list1 = list1->next; } cur_node = mergeHead; while (list1 && list2) { if (list1->val > list2->val) { cur_node->next = list2; list2 = list2->next; } else { cur_node->next = list1; list1 = list1->next; } cur_node = cur_node->next; } if (list1) cur_node->next = list1; if (list2) cur_node->next = list2; return mergeHead; } ``` 👾 : (使用測資測試...) 👹 : 屎地芬森先生謝謝你的作答, 答的不錯謝謝你 ## 24. Swap Nodes in Pairs > [影片](https://youtu.be/jAHPmNvnk-w?t=1717) ### 測驗說明與問答 👹 : 屎地芬森先生您好, 歡迎來到我們公司的面試, 我們給你一個問題, 給你一個linked list, 當中的值是隨機的, 希望你把節點兩兩交換, 並且不能直接修改node當中的值, 請你闡述你的解法之後再開始實作 👾 : 謝謝面試官,這個題目會提供給我一個linked list,之後把節點兩兩交換並且不能直接改變節點中的值 👾 : 首先我會先定義linked list的節點結構 ```c struct node { int val; struct node *next; } ``` 👾 : 再來思考兩個特別情況, 如果給我的linked list為空, 或者只有一個節點,那也沒有節點可以互換, 直接回傳即可 👾 : 再來處理一般情況, 使用`cnode`和`nnode`, `nnode`是`cnode`的下一個節點, 每次互換的時候把`cnode->next`指向原本的`nnode->next`, 然後把`nnode->next`指向`cnode` ```c struct node *SwapPairs(sturct node *head) { if (!head || !head->next) return head; struct node *cnode, *nnode; cnode = head; while (cnode && cnode->next) { nnode = cnode->next; cnode->next = nnode->next; nnode->next = cnode; cnode = cnode->next; } return head->next; } ``` 👹 : 可以使用一些測資確認正確性嗎? 👾 : (測試...) 不好意思面試官, 有發現錯誤的部分, 請給我一點時間思考並修正 👹 : 沒問題 👾 : 謝謝面試官給我時間思考並修正, 我想可以利用一個指標來記錄前一組pairs的`cnode`, 因為錯誤的原因是`cnode->next`原本應該指向下一組的`nnode`, 但在我原本的做法中卻只會指向下一組的`cnode`, 所以我利用`pnode`來記錄上一組的`cnode`並把`pnode->next`指向當前的`nnode` ```c struct node *SwapPairs(sturct node *head) { if (!head || !head->next) return head; struct node *cnode, *nnode, *pnode; pnode = NULL; cnode = head; while (cnode && cnode->next) { nnode = cnode->next; if (pnode) pnode->next = nnode; pnode = cnode; cnode->next = nnode->next; nnode->next = cnode; cnode = cnode->next; } return head->next; } ``` 👹 : 可以請你計算時間複雜度嗎? 👾 : 因為while loop在linked list上面走訪的時候不會往回走, 每個節點都只會走訪一次, 如果給定的linked list長度是$n$, 則時間複雜度就會是$O(n)$ 👹 : 屎地芬森先生謝謝你的作答 ## 83. Remove Duplicates from Sorted List > [影片](https://youtu.be/jAHPmNvnk-w?t=3103) ### 測驗說明與問答 👹 : Hello Mr.Stevenson, welcome to our company's interview. We have a question for you, you can describe your solution first and solve it. We're going to give you a sorted linked list with some duplicate values in it, please remove the duplicate nodes and make sure every value exists only once 👾 : Hello interviewer, I'm happy to be interviewed by your company and you. You're going to give me a head of a sorted linked list, and I'll have to remove all the duplicates, I want to make sure that I can assume the sorted order is ascending order right? and I still need remain the duplicate value in one node, not remove all of it right? 👹 : Yes your assumption is right, you can continue your answer 👾 : First I have to define a structure of the linked list node ```c struct node { int val; struct node *next; } ``` 👾 : I have to deal with speical case first, if you give me an empty linked list or a linked list with a single node. In both cases I'll just return the head back to you 👾 : For common cases, I'll use two pointer to `struct node`, `cnode, nnode`, and I'll compare the value of them like the following. Since we'll never remove the `head` node, the original `head` is still going to be the final `head` so we can just return `head` ```c struct node *RemoveDup(struct node *head) { if (!head || !head->next) return head; struct node *cnode, *nnode; cnode = head; nnode = head->next; while (nnode) { if (cnode->val == nnode->val) { cnode->next = nnode->next; } else { cnode = nnode; } nnode = nnode->next; } return head; } ``` 👹 : Can you calculate the time complexity of your algorithm? and I wonder that if you can use only one pointer to solve this problem? 👾 : I'll change my code using just one pointer first, becauze `nnode` is always `cnode->next`, so we can replace `nnode` ```c struct node *RemoveDup(struct node *head) { if (!head || !head->next) return head; struct node *cnode; cnode = head; while (cnode->next) { if (cnode->val == cnode->next->val) { cnode->next = cnode->next->next; } else { cnode = cnode->next; } } return head; } ``` 👾 : For the time complexity, assume the given linked list size is $n$, for each iteration in the while loop, the time complexity is $O(1)$, and we'll traverse each node exactly once, so the total time complexity is $O(n)$ 👹 : Thank you Mr. Stevenson. I've noticed that you remove the duplicate node by changing `cnode->next`, but the duplicate nodes are still alive in the system, can you try to delete it? 👾 : I know I didn't actually free the memory space of the duplicates node, I'll call these nodes as garbage node, and delete it like the following ```c struct node *RemoveDup(struct node *head) { if (!head || !head->next) return head; struct node *cnode; cnode = head; while (cnode->next) { if (cnode->val == cnode->next->val) { struct node *tmp = cnode->next; cnode->next = cnode->next->next; free(tmp); } else { cnode = cnode->next; } } return head; } ``` 👹 : Thank you very much Mr. Steveson.