15. 三數之和
16. 最接近的三數之和
18. 四數之和
26. 刪除排序數組中的重複項
27. 移除元素
75. 顏色分類
88. 合併兩個有序數組
21. 合併兩個有序鏈表
劍指 Offer 21. 調整數組順序使奇數位於偶數前面python
給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出全部知足條件且不重複的三元組。算法
注意:答案中不能夠包含重複的三元組。數組
示例:app
給定數組 nums = [-1, 0, 1, 2, -1, -4],知足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]函數
咱們依然使用與兩數之和相似的想法, 將三數之和轉換爲兩數之和, 即首先須要固定第一個數, 而後去移動其他兩個數, 而且爲了方便, 首先將數組進行排序.指針
具體思路以下rest
按照上述的思路, 能夠寫出以下的代碼code
def ThreeSum(nums): if not sums or len(sums) < 3: return [] nums.sort() n = len(nums) res = [] for i in range(n-2): p1 = i + 1 p2 = n - 1 while p1 < p2: if nums[p1] + nums[p2] < -nums[i]: p1 += 1 elif nums[p1] + nums[p2] > -nums[i]: p2 -= 1 else: res.append([nums[i], nums[p1], nums[p2]]) p1 += 1 p2 -= 1 return res
額, 看上去代碼並無什麼問題, 結構也還算清楚, 但其實存在一個較大的問題, 上述代碼會獲得重複的結果排序
好比一個排序數組[-4, -1, -1, 0, 1, 2],上述代碼會獲得
[[-1,-1,2],[-1,0,1],[-1,0,1]]的結果, 所以咱們須要跳過第二個及之後的重複元素.three
def ThreeSum(nums): if not sums or len(sums) < 3: return [] nums.sort() n = len(nums) res = [] """ 若是第一個元素就大於0或者最後一個元素還小於0直接返回. """ if nums[0] > 0 or nums[-1] < 0: return res for i in range(n-2): p1 = i + 1 p2 = n - 1 """ 去重 """ if i > 0 and nums[i] == nums[i-1]: continue while p1 < p2: if nums[p1] + nums[p2] < -nums[i]: p1 += 1 elif nums[p1] + nums[p2] > -nums[i]: p2 -= 1 else: res.append([nums[i], nums[p1], nums[p2]]) """ 去重 """ while p1 < p2 and nums[p1] == nums[p1 + 1]: p1 += 1 while p1 < p2 and nums[p2] == nums[p2 - 1]: p2 -= 1 p1 += 1 p2 -= 1 return res
時間複雜度爲$O(n^2)$, 空間複雜度爲$O(1)$
給定一個包括 n 個整數的數組 nums 和 一個目標值 target。找出 nums 中的三個整數,使得它們的和與 target 最接近。返回這三個數的和。假定每組輸入只存在惟一答案。
示例:
輸入:nums = [-1,2,1,-4], target = 1
輸出:2
解釋:與 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
這題的思路和上一題的思路相似,一樣是固定第一個數, 移動其他兩個數, 代碼很是一致. 而且還不用去重.
def threeSumClosest(nums, target): if not nums or len(nums) < 3: return [] n = len(nums) # 排序 nums.sort() res = [] nearest_sum = sum(nums[:3]) if nums[0] >= target: return sum(nums[:3]) if nums[-1] <= target: return sum(nums[-3:]) for i in range(n-2): p1 = i + 1 p2 = n - 1 while p1 < p2: temp = nums[i] + nums[p1] + nus[p2] if abs(temp - target) < abs(nearest_sum - target): nearest_sum = temp if temp < target: p1 += 1 elif temp > target: p2 -= 1 else: return target return nearest_sum
時間複雜度爲$n^2$, 空間複雜度爲$O(1)$
給定一個包含 n 個整數的數組 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出全部知足條件且不重複的四元組。
注意:
答案中不能夠包含重複的四元組。
示例:
給定數組 nums = [1, 0, -1, 0, -2, 2],和 target = 0。知足要求的四元組集合爲:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
這題的思路仍是和上面幾題的思路相同
def fourSum(nums, target): if not nums or len(nums) < 4: return [] nums.sort() n = len(nums) res = [] for i in range(n-3): # 去重 if i > 0 and nums[i] == nums[i-1]: continue for j in range(i, n-2): p1 = j + 1 p2 = n - 1 # 去重 if j > i and nums[j] == nums[j-1]: continue while p1 < p2: temp = nums[i] + nums[j] + nums[p1] + nums[p2] if temp < target: p1 += 1 elif temp > target: p2 -= 1 else: res.append([nums[i], nums[j], nums[p1], nums[p2]]) # 去重 while p1 < p2 and nums[p1] == nums[p1+1]: p1 += 1 while p1 < p2 and nums[p2] == nums[p2-1]: p2 -= 1 p1 += 1 p2 -= 1 return res
時間複雜度爲$O(n^3)$, 空間複雜度爲$O(1)$
給定一個排序數組,你須要在 原地 刪除重複出現的元素,使得每一個元素只出現一次,返回移除後數組的新長度。
不要使用額外的數組空間,你必須在 原地 修改輸入數組 並在使用 O(1) 額外空間的條件下完成。
示例 1:
給定數組 nums = [1,1,2],函數應該返回新的長度 2, 而且原數組 nums 的前兩個元素被修改成 1, 2。
示例 2:
給定 nums = [0,0,1,1,1,2,2,3,3,4],函數應該返回新的長度 5, 而且原數組 nums 的前五個元素被修改成 0, 1, 2, 3, 4。
你不須要考慮數組中超出新長度後面的元素。
這題要求原地修改數組, 那麼顯然, 咱們須要將重複的元素移動到數組末尾. 咱們可使用雙指針來解決這個問題.
def removeDuplicates(nums): if not nums: return 0 if len(nums) < 2: return 1 # 同時指向第一個元素 p1 = p2 = 0 n = len(nums) while p2 < n: # 若是不相等, p1右移後, 交換兩指針位置的值 if nums[p2] != nums[p1]: p1 += 1 nums[p2], nums[p1] = nums[p1], nums[p2] p2 += 1 return p1 + 1
時間複雜度爲$O(n)$, 空間複雜度爲$O(1)$
給你一個數組 nums 和一個值 val,你須要 原地 移除全部數值等於 val 的元素,並返回移除後數組的新長度。
不要使用額外的數組空間,你必須僅使用 O(1) 額外空間並 原地 修改輸入數組。
元素的順序能夠改變。你不須要考慮數組中超出新長度後面的元素。
示例 1:
給定 nums = [3,2,2,3], val = 3, 函數應該返回新的長度 2, 而且 nums 中的前兩個元素均爲 2。 你不須要考慮數組中超出新長度後面的元素。
示例 2:
給定 nums = [0,1,2,2,3,0,4,2], val = 2, 函數應該返回新的長度 5, 而且 nums 中的前五個元素爲 0, 1, 3, 0, 4。 注意這五個元素可爲任意順序。 你不須要考慮數組中超出新長度後面的元素。
def removeElement(nums, val): if not nums: return 0 if len(nums) == 1: return int(nums[0] != val) n = len(nums) p1 = p2 = 0 while p2 < n: if nums[p2] != val: nums[p1], nums[p2] = nums[p2], nums[p1] p1 += 1 p2 += 1 return p1
還能夠用首尾2個指針來移動
def removeElement(nums, val): if not nums: return 0 if len(nums) == 1: return int(nums[0] != val) n = len(nums) p1 = 0 p2 = n - 1 # 這裏須要有等號 while p1 <= p2: while p2 > p1 and nums[p2] == val: p2 -= 1 if nums[p1] == val: nums[p2], nums[p1] = nums[p1], nums[p2] p2 -= 1 p1 += 1 return p2 + 1
時間複雜度爲$O(n)$, 空間複雜度爲$O(1)$
給定一個包含紅色、白色和藍色,一共 n 個元素的數組,原地對它們進行排序,使得相同顏色的元素相鄰,並按照紅色、白色、藍色順序排列。
此題中,咱們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。
注意:
不能使用代碼庫中的排序函數來解決這道題。
示例:
輸入: [2,0,2,1,1,0]
輸出: [0,0,1,1,2,2]
進階:
首先,迭代計算出0、1 和 2 元素的個數,而後按照0、一、2的排序,重寫當前數組。
這題首先想到的應該是計數排序, 時間複雜度爲$O(2n)$, 空間複雜度爲$O(1)$.
但還有複雜度更低的方法, 一樣仍是雙指針.
def sortColors(nums): if not nums or len(nums) < 2: return cur = p1 = 0 p2 = len(nums) - 1 while cur < p2: if nums[cur] == 0: nums[cur], nums[p1] = nums[p1], nums[cur] p1 += 1 cur += 1 # Note: 當cur位置的值等於2時, cur不須要右移, 由於交換以後並不能保證cur位置的值不等於2. elif nums[cur] == 2: nums[cur], nums[p2] = nums[p2], nums[cur] p2 -= 1 else: cur += 1
時間複雜度爲$O(n)$, 空間複雜度爲$O(1)$
給你兩個有序整數數組 nums1 和 nums2,請你將 nums2 合併到 nums1 中,使 nums1 成爲一個有序數組。
說明:
示例:
輸入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3輸出: [1,2,2,3,5,6]
最直觀, 最簡單的方式就是合併再排序, 對應的時間複雜度爲$O((m+n)log(m+n))$
但其實咱們可使用雙指針進一步下降複雜度
def merge(nums1, m, nums2, n): if not nums2: return nums1 i = m - 1 j = n - 1 while i >= 0 and j >= 0: if nums1[i] > nums[j]: nums1[i+j+1] = nums1[i] i -= 1 else: nums1[i+j+1] = nums2[j] j -= 1 if j >= 0: nums1[:j] = nums2[:j] return nums1
時間複雜度爲$O(m)$或者$O(n)$, 空間複雜度爲$O(1)$
將兩個升序鏈表合併爲一個新的 升序 鏈表並返回。新鏈表是經過拼接給定的兩個鏈表的全部節點組成的。
示例:
輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4
def mergeTwoLists(l1, l2): p1 = l1 p2 = l2 # 初始化爲一個臨時節點 p0 = new_ln = ListNode(-1) while p1 and p2: if p1.val < p2.val: p0.next = p1 p1 = p1.next else: p0.next = p2 p2 = p2.next p0 = p0.next if p1: p0.next = p1 if p2: p0.next = p2 return new_ln.next
時間複雜度爲$O(n)$, 空間複雜度爲$O(1)$
輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得全部奇數位於數組的前半部分,全部偶數位於數組的後半部分。
示例:
輸入:nums = [1,2,3,4]
輸出:[1,3,2,4]
注:[3,1,2,4] 也是正確的答案之一。
def exchange(nums): if not nums or len(nums) < 2: return nums n = len(nums) p1 = 0 p2 = n - 1 while p1 < p2: if nums[p1] % 2: p1 += 1 elif not nums[p2] % 2: p2 -= 1 else: nums[p1], nums[p2] = nums[p2], nums[p1] return nums
一樣, 快慢指針也能夠解決問題
def exchange(self, nums: List[int]) -> List[int]: if not nums or len(nums) < 2: return nums n = len(nums) p2 = p1 = 0 while p2 < n: if nums[p2] %2: nums[p1], nums[p2] = nums[p2], nums[p1] p1 += 1 p2 += 1 return nums
時間複雜度爲$O(n)$, 空間複雜度爲$O(1)$
雙指針的思路在數組和鏈表的題目中有着普遍的應用, 這裏主要介紹了數組中應用.
雙指針分爲兩種, 快慢指針(這裏將移動頻率不同的稱爲快慢指針)和首尾指針. 首尾指針主要在數組中使用, 快慢指針在數組和鏈表中都有普遍應用.
快慢指針的基本思路是
而首尾指針, 一般須要先將數組排序, 好比3數之和,4數之和等 而後根據具體狀況選擇移動首指針或者尾指針, 直至知足條件.
水平有限, 不免有錯誤或不足的地方. 還請不吝賜教.