167. 兩數之和 II - 輸入有序數組
思路:
- 一般雙指針的主要思路是先找到暴力解法,然後考慮有沒有單調性之類的性質可以利用,再進行優化
- 使用i,j兩個指針,初始位置在0和數組末尾,若相加的結果大於target則說明當前的結果需要減小,則j往前移;若相加的結果小於target則說明當前的結果需要增大,則i往後移。
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
n = len(numbers)
i, j = 0, n-1
while i<j:
if numbers[i] + numbers[j] < target:
i += 1
elif numbers[i] + numbers[j] > target:
j -= 1
else:
return [i+1, j+1]
88. 合併兩個有序數組
思路:
- 這道題就是基本的歸併思想
- 由於題目要求的是把結果存入nums1中,則要注意不能從前開始比較合併,要從後面開始
- 這裏需要注意的是如果p1,p2賦初值爲m-1,n-1,可能就取不到下標爲0的那個數
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
# nums1的最大數指針 nums2的最大數指針 結果集的最大數指針
p1, p2, p3 = m, n, m+n
while p1 and p2:
if nums1[p1-1] >= nums2[p2-1]:
nums1[p3-1] = nums1[p1-1]
p1 -= 1
else:
nums1[p3-1] = nums2[p2-1]
p2 -= 1
p3 -= 1
while p2:
nums1[p3-1] = nums2[p2-1]
p2 -= 1
p3 -= 1
26. 刪除排序數組中的重複項
思路:
- 由於不能申請額外空間,考慮直接在數組上進行操作。
- 設置兩個指針,一個指向當前需要存儲數的位置k,一個對數組進行掃描i
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if not nums:
return 0
k = 1
for i in range(1, len(nums)):
if nums[i] != nums[i-1]:
nums[k] = nums[i]
k += 1
return k
76. 最小覆蓋子串
思路:
- 首先一樣的步驟,考慮暴力解法,會需要O(n^2)的時間複雜度
- 然後考慮單調性,我們可以使用兩個指針,i,j指示答案字符串的首尾。當i逐漸往後移,j必定只能往後增加,不可能往前,因爲那樣會包含了最佳答案在裏面,不可能得到最短的。
- 這樣的話,維護一個hash表,記錄t字符串每個字符出現的次數,最後考慮的就是i依次往後遍歷,j從起點開始,i往後一格,將hash對應位置-1,j也往後,若當前所指的位置hash表中對應結果小於0了,說明這個字符不在答案中,j往後移
- 再維護一個答案t所需要幾個不同的字符,當都得到了,比較一下當前s[j:i+1]的長度,用res記錄最短的即是答案
class Solution:
def minWindow(self, s: str, t: str) -> str:
hash_table = {}
for ch in t:
hash_table[ch] = hash_table.get(ch, 0) + 1
cnt = len(hash_table.items())
res = ""
c, j = 0, 0
for i in range(len(s)):
num = hash_table.get(s[i], 0)
if num == 1:
c += 1
elif num==0:
hash_table[s[i]] = hash_table.get(s[i], 0)
hash_table[s[i]] -= 1
# j從前往後
while hash_table[s[j]] < 0 and j<i:
hash_table[s[j]] += 1
j += 1
if c==cnt:
if not res or len(res)>i-j+1:
res = s[j:i+1]
return res
32. 最長有效括號
思路:
- 首先需要記住括號序列的一個重要性質:假如將(當做數字1,)當做數字-1;則合法的括號序列前綴和一定是>=0的,並且整個序列的最終結果肯定是0
- start枚舉當前這一段的開頭,cnt記錄前綴和。
- 若是當前的cnt<0了,說明這一段是不合法的,start=i+1,cnt=0
- 若是當前的cnt>0,繼續做
- 若是當前的cnt==0,說明當前的這一段是合法的,比較和res的長度,若大於則更新res
- 注意,爲了防止出現((())這種情況,也就是最後的和不會等於0的情況,所以還需要反過來再做一遍
- (和)的ascii碼二進制相差一位,可以直接異或處理
class Solution:
def longestValidParentheses(self, s: str) -> int:
reverse_s = list(s[::-1])
for index, ch in enumerate(reverse_s):
reverse_s[index] = chr(ord(ch)^1)
return max(self.work(s), self.work("".join(reverse_s)))
def work(self, s):
"""
返回當前子串的最長長度
"""
start, cnt = 0, 0
n = len(s)
res = 0
for i in range(n):
if s[i] == "(":
cnt += 1
else:
cnt -= 1
if cnt < 0:
start = i+1
cnt = 0
elif cnt == 0:
res = max(res, i-start+1)
return res
155. 最小棧
思路:
- 這個題有點類似於前綴和,始終要維護一個前綴和的最小數。
- 用兩個棧,一個是模擬棧操作的,一個是始終保存當前棧中最小的數字
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
self.stack_min = []
def push(self, x: int) -> None:
self.stack.append(x)
if not self.stack_min:
self.stack_min.append(x)
else:
self.stack_min.append(min(self.stack_min[-1], x))
def pop(self) -> None:
self.stack.pop()
self.stack_min.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.stack_min[-1]
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()