# [21. Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/description/)
面試官: 地精-Dee-jing
面試者: 尼莫魚魚-nemo
面試官: 你好我是今天負責面試你的人,假設你正在參與一個資料處理系統的開發,這個系統需要處理大量已排序的數據。系統的任務之一是合併不同來源的數據,並且這些數據已經各自進行過排序。為了簡化並提高系統效率,我們希望在維持排序的情況下合併這些列表。現在我們希望你來設計一個函數,將兩個排序好的 list 合併成一個有序的 list 。每個節點的值範圍在 -100 到 100 之間。
面試者: 所以我會拿到兩個節點值都為整數的 list,然後我要將它們合併成一個有序的list,例如我拿到 list1 = [1,2] 、 list2 = [2,3],我要回傳[1,2,2,3]對嗎
面試官: 沒錯,你的理解沒有問題
面試者: 那我想請問一下若其中一個 list 為空,是直接回傳另一個 list 還是需要另外做處理呢
面試官: 若有其中一個 list 為空,只須回傳另一個 list 即可
面試者: 了解,那我這裡先稍微說明一下我解題的想法,首先用遞迴比較兩個 list 的第一個節點,選擇較小的節點接入合併的 list,並將該 list 的下一個節點和另一個 list 進行下一次遞迴。例如,給定 list1 = [1, 2, 4] 和 list2 = [1, 3, 5],我會先選擇 list1 的第一個節點 1,接著讓 list1[2, 4] 和 list2[1, 3, 5] 繼續遞迴,直到合併完成。
面試官: 聽起來沒什麼問題,那你可以開始實作了
面試者: 好的那我選用的語言是 c++,我這裡會先定義 linked list 的結構....
```
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(list1 == NULL) return list2;
if(list2 == NULL) return list1;
if(list1->val <= list2->val){
list1->next = mergeTwoLists(list1->next,list2);
return list1;
}
else{
list2->next = mergeTwoLists(list1,list2->next);
return list2;
}
}
};
```
面試官: 那我想請問一下,由於你本題是使用到遞迴,會浪費到不少空間,請問你可以提出其他方法來減少空間的使用嗎
面試者: 先前方法確實造成了空間的浪費,那我這裡想改採用 iteration 的方式來修改,避免函式的重複呼叫,應該可以減少空間的使用
面試官: 好,那你試看看
面試者: 好 ...(邊coding邊解釋)
```
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(!list1) return list2;
if(!list2) return list1;
ListNode* head = list1;
if(list1->val <= list2->val){
list1 = list1->next;
}
else{
head = list2;
list2 = list2->next;
}
ListNode* curr = head;
while(list1 && list2){
if(list1->val <= list2->val){
curr->next = list1;
list1 = list1->next;
}
else{
curr->next = list2;
list2 = list2->next;
}
curr = curr->next;
}
if(list1) curr->next = list1;
if(list2) curr->next = list2;
return head;
}
};
```
面試者: 由於合併後的 list 是透過指針在原本的 list 上進行操作,不會像遞迴每次調用都會建立新的stack,因此可以讓空間複雜度降低到O(1)
面試官: 好,明白了,今天面試就到這,感謝你的參與
## 自評
- 講話會有點卡詞
- 在介紹程式碼時,對於linked list描述較不熟悉,因此敘述方面會有點不通順或者誤用詞的情況
- 說話可以在大聲一點
# [88 Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array/description/)
面試官: 尼莫魚魚-nemo
面試者: 地精-Dee-jing
面試官:你好,今天我們會討論 LeetCode 的第88題「合併兩個有序數組」。你可以先簡單描述一下這道題的要求嗎?
面試者🧔:好的,這道題要求將兩個已經排序的數組合併成一個有序數組,並且結果需要儲存在 nums1 中。這在數據合併、排序等應用中經常會用到。題目給的條件是,nums1 有足夠的空間容納合併後的結果,但其中只有 m 個有效元素,而 nums2 中有 n 個元素。我們需要將 nums2 中的所有元素合併到 nums1 中,保持兩者的排序,並且不使用額外空間。
面試官:不錯,那你會如何著手解決這道題呢?
面試者🧔:我的解法是從兩個數組的末尾開始進行比較和合併,這樣可以避免覆蓋掉 nums1 中的有效數據。具體來說,我會用到三個指針變量:
i 指向 nums1 中最後一個有效元素的位置。
j 指向 nums2 中的最後一個元素。
k 指向 nums1 最後一個位置,這將是合併後放置結果的位置。
然後,我會從後往前比較 nums1[i] 和 nums2[j],將較大的值放在 nums1[k],並移動相應的指針。這樣可以確保在不覆蓋有效數據的前提下進行合併。
面試官:那能展示一下具體的程式碼嗎?
```
#include <stdio.h>
void merge(int* nums1, int m, int* nums2, int n) {
int i = m - 1; // 指向 nums1 的最後一個有效元素
int j = n - 1; // 指向 nums2 的最後一個元素
int k = m + n - 1; // 最終合併位置
while (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) {
nums1[k--] = nums1[i--];
} else {
nums1[k--] = nums2[j--];
}
}
// 將剩餘的 nums2 元素拷貝到 nums1 中
while (j >= 0) {
nums1[k--] = nums2[j--];
}
}
```
面試官:很好,請解釋一下這段程式碼的邏輯吧。
面試者🧔:當然。在這裡,i 和 j 分別指向 nums1 和 nums2 的最後一個有效元素位置,k 是合併結果儲存的目標位置。接下來的流程是:
比較 nums1[i] 和 nums2[j] 的大小,將較大的元素放到 nums1[k],然後向前移動指針 k。
重複這個過程直到其中一個數組被合併完。
如果 nums2 中還有剩餘元素,繼續將它們複製到 nums1 開頭,這樣可以確保最終結果有序。
這個方法能避免覆蓋 nums1 中的有效元素,同時實現從後往前合併的排序過程。
面試官:你對這段程式碼的時間和空間複雜度有什麼看法?
面試者🧔:這段程式碼的時間複雜度是 O(m + n),因為需要遍歷 nums1 和 nums2 中的所有元素才能完成合併。在空間複雜度方面,這個方法是原地合併的,直接在 nums1 上操作,因此空間複雜度是 O(1),沒有額外的空間需求。
面試官:很好,這是最優解嗎?還有其他可以優化的地方嗎?
面試者🧔:在這道題的條件下,這已經是最優解法。由於需要從後往前合併來避免覆蓋,所以在不使用額外空間的情況下,我認為這是最理想的方式。時間複雜度 O(m + n) 也是最小的,因為我們至少需要檢查所有元素。
面試官:很好的解釋。最後,你能總結一下這道題的關鍵點嗎?
面試者🧔:好的。這道題的關鍵在於利用數組已經排序的特性,從後往前合併,這樣可以避免覆蓋掉 nums1 中的有效數據。使用三個指針從末尾進行比較,確保 O(m + n) 的時間複雜度,並且保持 O(1) 的空間複雜度。
面試官:非常好,今天的表現很不錯,謝謝你!
面試者🧔:謝謝您的指導!
## 自評
- 未將題目包裝成實際可應用的例子,不應該直接說他是leetcode的哪道題
- 對話擬稿有點不自然
- 應該是由面試官出題,而不是反而讓面試者去解釋題目