昨天騰訊一面上來就給我整的這道 easy 難度的題,然後我太緊張了還想了一會兒,差點炸裂。
題目鏈接
LeetCode 88. 合併兩個有序數組[1]
題目描述
給你兩個有序整數數組 nums1
和 nums2
,請你將 nums2
合併到 nums1
中,使 nums1
成爲一個有序數組。
說明:
- 初始化
nums1
和nums2
的元素數量分別爲m
和n
。 - 你可以假設
nums1
有足夠的空間(空間大小大於或等於m + n
)來保存nums2
中的元素。
示例1
輸入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
輸出:
[1,2,2,3,5,6]
解釋:
題解
看到這道題,腦海裏應該第一時間想到的是歸併排序,但是歸併排序需要一個額外的數組用來保存排序後的數組,這裏不允許使用額外空間。
那麼我們還是用歸併排序的思路來做,試一下兩個指針 i = 0
和 j = 0
,初始的時候分別指着 nums1[0]
和 nums2[0]
。然後比較 nums1[i]
和 nums2[j]
大小,如果 nums1[i]
更小,那麼就放在原位不動它,然後 i += 1
。如果 nums2[j]
更小,那麼就交換 nums1[i]
和 nums2[j]
,然後還是 i += 1
。這麼看貌似可行哦?但是最終一定會先遍歷完 nums1
,然後 j
還是停留在 0
,然後你會發現 nums2
中的數字還是亂序的,根本沒法處理。
那麼怎麼利用上 nums1
後面多出的那麼多空位呢?我們可以換個思路,從最大的開始遍歷。兩個指針初始的時候 i = m-1
和 j = n-1
,然後將較大值填充到 nums1
的最後面。最後如果 nums2
中還有剩餘,就依次填充到 nums1
最前面就行了。
這樣爲什麼就可以了呢?因爲如果從小到大遍歷的話,元素會覆蓋掉 nums1
中還沒遍歷的元素。但是從大到小是填充到尾部,就不會產生覆蓋。就算極限情況下 nums2
中元素全部大於 nums1
中元素,也不會覆蓋到 nums1
的最後一個元素。
面試官最後還會問你有啥優化,我當時圖省事,最後還把 nums1
中剩下元素填充到 nums1
最前面了,其實完全沒有必要,本來就是有序的,等於沒有做事。
代碼
c++
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m-1, j = n-1, k = m+n-1;
while (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) nums1[k--] = nums1[i--];
else nums1[k--] = nums2[j--];
}
while (j >= 0) nums1[k--] = nums2[j--];
}
};
python
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
i, j = m-1, n-1
while i >= 0 and j >= 0:
if nums1[i] > nums2[j]:
nums1[i+j+1] = nums1[i]
i -= 1
else:
nums1[i+j+1] = nums2[j]
j -= 1
while j >= 0:
nums1[j] = nums2[j]
j -= 1
參考資料
[1]
LeetCode 88. 合併兩個有序數組: https://leetcode-cn.com/problems/merge-sorted-array/