一、数组经典题型
一、數組經典題型
- * 前言
- 1、元素查找 (暴力解法、二分查找)
- 35.搜索插入位置
- 34.在排序數組中查找元素的第一個和最后一個位置
- 69.x的平方根
- 367.有效的完全平方數
- 875.愛吃香蕉的珂珂
- 2、元素移除 (暴力解法、雙指針法)
- 26.刪除有序數組中的重復項
- 283.移動零
- 844.比較含退格的字符串
- 977.有序數組的平方
- 3、長度最小的子數組(暴力解法、滑動窗口)
- 904.水果成籃
- 76.最小覆蓋子串
- 239.滑動窗口最大值
- 4、過程模擬 (無算法,邏輯思維理解)
- 54.螺旋矩陣
- 劍指Offer29.順時針打印矩陣
- 參考
* 前言
后續題目基于基礎算法部分,請參考:算法刷題總結 (一) 數組
1、元素查找 (暴力解法、二分查找)
35.搜索插入位置
leetcode鏈接
(1). 暴力解法,遍歷列表:
class Solution:def searchInsert(self, nums: List[int], target: int) -> int:# 分別處理如下三種情況# 目標值在數組所有元素之前# 目標值等于數組中某一個元素# 目標值插入數組中的位置for i in range(len(nums)):# 一旦發現大于或者等于target的num[i],那么i就是我們要的結果if nums[i]>=target:return i# 目標值在數組所有元素之后的情況# 如果target是最大的,或者 nums為空,則返回nums的長度return len(nums)(2). 二分法,二分列表:
class Solution:def searchInsert(self, nums: List[int], target: int) -> int:# 分別處理如下四種情況# 目標值在數組所有元素之前 [0, -1]# 目標值等于數組中某一個元素 return middle;# 目標值插入數組中的位置 [left, right],return right + 1# 目標值在數組所有元素之后的情況 [left, right], 因為是右閉區間,所以 return right + 1# 1. 使用二分法快速查找(比遍歷快),如果能找到target,返回索引left, right = 0, len(nums)-1 # 定義target在左閉右閉的區間里,[left, right]while left<=right:mid = (left+right)//2if nums[mid]>target:# target 在左區間,所以[left, middle - 1]right = mid-1elif nums[mid]<target:# target 在右區間,所以[middle + 1, right]left = mid +1else:return mid# 2. 如果找不到# 返回right+1 或者 返回 leftreturn left一個簡單的中間過程
nums = [1,2,5,8,10] target = 9 searchInsert(nums, target) """ [left, right]: [0, 4] [left, right]: [3, 4] [left, right]: [4, 4] [left, right]: [4, 3] # 交叉退出 """暴力與二分法的區別在于暴力是遍歷列表,而二分法是二分查找,速度相對快。
34.在排序數組中查找元素的第一個和最后一個位置
leetcode鏈接
(1). 暴力法:
class Solution:def searchRange(self, nums: List[int], target: int) -> List[int]:if target in nums:start = nums.index(target)end = len(nums)-(nums[::-1].index(target)+1)return [start, end]return [-1, -1](2). 二分法:
""" 尋找target在數組里的左右邊界,有如下三種情況:情況一:target 在數組范圍的右邊或者左邊,例如數組{3, 4, 5},target為2或者數組{3, 4, 5},target為6,此時應該返回{-1, -1} 情況二:target 在數組范圍中,且數組中不存在target,例如數組{3,6,7},target為5,此時應該返回{-1, -1} 情況三:target 在數組范圍中,且數組中存在target,例如數組{3,6,7},target為6,此時應該返回{1, 1} """ class Solution:def searchRange(self, nums: List[int], target: int) -> List[int]:# 二分查找,尋找target的右邊界(不包括target)# 如果rightBorder為沒有被賦值(即target在數組范圍的左邊,例如數組[3,3],target為2),為了處理情況一def getRightBorder(nums:List[int], target:int) -> int:# 定義target在左閉右閉的區間里,[left, right] left, right = 0, len(nums)-1# 記錄一下rightBorder沒有被賦值的情況rightBoder = -2 # 當left==right,區間[left, right]依然有效while left <= right:middle = left + (right-left) // 2if nums[middle] > target:right = middle - 1# 當nums[middle] == target的時候,更新left,這樣才能得到target的右邊界# 尋找右邊界,nums[middle] == target的時候更新leftelse: left = middle + 1rightBoder = leftreturn rightBoderdef getLeftBorder(nums:List[int], target:int) -> int:left, right = 0, len(nums)-1 leftBoder = -2 # 記錄一下leftBorder沒有被賦值的情況while left <= right:middle = left + (right-left) // 2if nums[middle] >= target: # 尋找左邊界,nums[middle] == target的時候更新rightright = middle - 1;leftBoder = right;else:left = middle + 1return leftBoderleftBoder = getLeftBorder(nums, target)rightBoder = getRightBorder(nums, target)# 情況一if leftBoder == -2 or rightBoder == -2: return [-1, -1]# 情況三if rightBoder -leftBoder >1: return [leftBoder + 1, rightBoder - 1]# 情況二return [-1, -1]69.x的平方根
leetcode鏈接
二分法:
class Solution:def mySqrt(self, x: int) -> int:left, right, ans = 0, x, -1# 遍歷整數的平方,最大的小于x的值就是結果while left<=right:# 不斷找中值分界點mid = (left+right)//2# 不斷將結果存起來,最后更新保存的結果為最大值if mid*mid<=x:ans = midleft = mid + 1# 縮減大的值else:right = mid - 1return ans此題還有牛頓迭代法解,只是套用一個公式。
367.有效的完全平方數
leetcode鏈接
二分法:
class Solution:def isPerfectSquare(self, num: int) -> bool:left, right, ans = 0, num, Falsewhile left<=right:mid = (left+right)//2if mid*mid<num:left = mid+1elif mid*mid>num:right = mid-1else:ans=Truereturn ansreturn ans875.愛吃香蕉的珂珂
leetcode鏈接
暴力解法(超時):
class Solution:def minEatingSpeed(self, piles: List[int], h: int) -> int:# 最慢一次吃一根,最快按最大值去吃每棵樹,時間為樹的個數# 遍歷,尋找某個最小速度,使時間剛好為hfor i in range(1, max(piles)+1):nums = 0# 遍歷樹for j in piles:# 累加每棵樹的次數nums = nums + math.ceil(j/i)# 第一次累計到h,就為吃的最慢,并且在警衛來之前走# 后面存在時間還為h,但是速度稍微快一些的情況,這個就不符合題意了if h==nums:return i雙指針法:
接著上一個代碼進行修改,因為是遍歷查找,所以可以使用快速查找的算法,二分法。
注意這個例子:
piles,h = [312884470], 312884469
v只能取2,但是取2后,時間最多只能為156442235,不可能等于h,因為若等于1則又超時。
那么,二分法的條件判斷,不能以nums==h為條件,最后倒數第二步為left=right=1,nums<h(156442235<312884469),這時left = mid+1=2,此時left>right(2>1),交錯,輸出2為正確結果。
2、元素移除 (暴力解法、雙指針法)
26.刪除有序數組中的重復項
leetcode鏈接
注意:當刪除單個重復元素時,list可以無序,而刪除多個重復元素時,保持相似的算法步驟則需要list有序,(這道題也指明了有序),否則需要較多的更改邏輯結構,比如下面(2)中的第二個解法。
(1). 暴力遍歷:
class Solution:def removeDuplicates(self, nums: List[int]) -> int:# 選取所有不重復,待遍歷的元素for i in set(nums):# 設置計數器,方便后續大于1則刪除count = 0# 遍歷新創建的列表,防止刪除原列表造成索引位移for j in nums[:]:# 重復則+1if i==j:count+=1# 多于一次重復則刪除,否則不管if count>1:nums.remove(i)return len(nums)(2). 快慢指針:
class Solution:def removeDuplicates(self, nums: List[int]) -> int:fast = 0slow = 0while fast<len(nums):if nums[fast] != nums[slow]:# 下一個索引賦值給新的不重復的值slow+=1nums[slow] = nums[fast]fast+=1# 這里要+1,表示下一位,即表示數組有效長度。# 因為原快慢指針算法,上面slow+1是在賦值之后,而這里是在賦值之前。return slow+1第二個解法,可以刪除無序list的重復元素,但較多的修改了原算法:
class Solution:def removeDuplicates(self, nums: List[int]) -> int:fast = 0# 從1開始,因為nums[:0]為空,慢指針這相當于取了域值,而不是前面的單個值slow = 1while fast<len(nums):# 這里取nums的slow索引之前的所有值進行重復元素的排查if nums[fast] not in nums[:slow]:# 相當于slow的閾值擴展nums[slow] = nums[fast]# 指向下一個待擴展進閾值的索引slow+=1fast+=1return slow283.移動零
leetcode鏈接
(1). 暴力遍歷:
(2). 快慢指針:
先快慢指針把非零的選出來,再根據slow的長度將list后續非有效部分變成0
每次遇到0就交換,把0換到fast的索引位置,把非0的值換到slow的位置,最后0都在結尾。有點類似冒泡。
class Solution:def moveZeroes(self, nums: List[int]) -> None:fast = 0slow = 0while fast<len(nums):if nums[fast] != 0:nums[slow], nums[fast] = nums[fast], nums[slow]slow += 1fast+=1844.比較含退格的字符串
leetcode鏈接
(1). 重構字符串:
(2). 雙指針:
class Solution:def backspaceCompare(self, S: str, T: str) -> bool:i, j = len(S) - 1, len(T) - 1skipS = skipT = 0while i >= 0 or j >= 0:while i >= 0:if S[i] == "#":skipS += 1i -= 1elif skipS > 0:skipS -= 1i -= 1else:breakwhile j >= 0:if T[j] == "#":skipT += 1j -= 1elif skipT > 0:skipT -= 1j -= 1else:breakif i >= 0 and j >= 0:if S[i] != T[j]:return Falseelif i >= 0 or j >= 0:return Falsei -= 1j -= 1return True參考答案
977.有序數組的平方
leetcode鏈接
(1). 重構后排序:
(2). 雙指針法:
參考答案
3、長度最小的子數組(暴力解法、滑動窗口)
904.水果成籃
leetcode鏈接
(1). 暴力遍歷:
算法同上,但會超時。
(2). 滑動窗口:
因為普通算法會超時,這里用Counter進行存儲每次遍歷的值。
結果打印:
------------------------------- Counter({3: 1}) 1 ------------------------------- Counter({3: 2}) 2 ------------------------------- Counter({3: 3}) 3 ------------------------------- Counter({3: 3, 1: 1}) 4 ------------------------------- Counter({3: 3, 1: 1, 2: 1}) ***1*** d1 Counter({3: 3, 1: 1, 2: 1}) dd 3 d2 Counter({3: 2, 1: 1, 2: 1}) ***2*** ***1*** d1 Counter({3: 2, 1: 1, 2: 1}) dd 3 d2 Counter({3: 1, 1: 1, 2: 1}) ***2*** ***1*** d1 Counter({3: 1, 1: 1, 2: 1}) dd 3 d2 Counter({1: 1, 2: 1}) ***2*** 4 ------------------------------- Counter({1: 2, 2: 1}) 4 ------------------------------- Counter({1: 3, 2: 1}) 4 ------------------------------- Counter({1: 3, 2: 2}) 5 ------------------------------- Counter({1: 3, 2: 2, 3: 1}) ***1*** d1 Counter({1: 3, 2: 2, 3: 1}) dd 1 d2 Counter({1: 2, 2: 2, 3: 1}) ***2*** ***1*** d1 Counter({1: 2, 2: 2, 3: 1}) dd 2 d2 Counter({1: 2, 2: 1, 3: 1}) ***2*** ***1*** d1 Counter({1: 2, 2: 1, 3: 1}) dd 1 d2 Counter({1: 1, 2: 1, 3: 1}) ***2*** ***1*** d1 Counter({1: 1, 2: 1, 3: 1}) dd 1 d2 Counter({2: 1, 3: 1}) ***2*** 5 ------------------------------- Counter({3: 2, 2: 1}) 5 ------------------------------- Counter({3: 2, 2: 1, 4: 1}) ***1*** d1 Counter({3: 2, 2: 1, 4: 1}) dd 2 d2 Counter({3: 2, 4: 1}) ***2*** 5 576.最小覆蓋子串
leetcode鏈接
(1). 暴力遍歷:
方法同上,但會超時
(2). 滑動窗口:
大致思路同上,但是這里需要加上flag進行判斷,進入while循環,也就是起始點后移的的條件,因為字母會出現重復增刪,flag只計算一次,d仍然需要計算每次改變。
結果過程展示:
d: Counter({'A': 1, 'B': 1, 'C': 1}) ------------------------- 1l ************** 1d: Counter({'B': 1, 'C': 1, 'A': 0}) ****************** ****************** 2l ************** 2d: Counter({'B': 1, 'C': 1, 'A': 0}) ind, end 0 0 ------------------------- 1l ************** 1d: Counter({'B': 1, 'C': 1, 'A': 0}) ****************** ****************** 2l ************** 2d: Counter({'B': 1, 'C': 1, 'A': 0}) ind, end 0 1 ------------------------- 1l ************** 1d: Counter({'B': 1, 'C': 1, 'A': 0}) ****************** ****************** 2l ************** 2d: Counter({'B': 1, 'C': 1, 'A': 0}) ind, end 0 2 ------------------------- 1l ************** 1d: Counter({'C': 1, 'A': 0, 'B': 0}) ****************** ****************** 2l ************** 2d: Counter({'C': 1, 'A': 0, 'B': 0}) ind, end 0 3 ------------------------- 1l ************** 1d: Counter({'C': 1, 'A': 0, 'B': 0}) ****************** ****************** 2l ************** 2d: Counter({'C': 1, 'A': 0, 'B': 0}) ind, end 0 4 ------------------------- 1l ************** 1d: Counter({'A': 0, 'B': 0, 'C': 0}) ****************** cmp: ************** ADOBEC ****************** 2l ADOBEC 2d: Counter({'A': 1, 'B': 0, 'C': 0}) ind, end 1 5 ------------------------- 1l ADOBEC 1d: Counter({'A': 1, 'B': 0, 'C': 0}) ****************** ****************** 2l ADOBEC 2d: Counter({'A': 1, 'B': 0, 'C': 0}) ind, end 1 6 ------------------------- 1l ADOBEC 1d: Counter({'A': 1, 'B': 0, 'C': 0}) ****************** ****************** 2l ADOBEC 2d: Counter({'A': 1, 'B': 0, 'C': 0}) ind, end 1 7 ------------------------- 1l ADOBEC 1d: Counter({'A': 1, 'B': 0, 'C': 0}) ****************** ****************** 2l ADOBEC 2d: Counter({'A': 1, 'B': 0, 'C': 0}) ind, end 1 8 ------------------------- 1l ADOBEC 1d: Counter({'A': 1, 'C': 0, 'B': -1}) ****************** ****************** 2l ADOBEC 2d: Counter({'A': 1, 'C': 0, 'B': -1}) ind, end 1 9 ------------------------- 1l ADOBEC 1d: Counter({'A': 0, 'C': 0, 'B': -1}) ****************** cmp: ADOBEC DOBECODEBA cmp: ADOBEC OBECODEBA cmp: ADOBEC BECODEBA cmp: ADOBEC ECODEBA cmp: ADOBEC CODEBA ****************** 2l ADOBEC 2d: Counter({'C': 1, 'A': 0, 'B': 0}) ind, end 6 10 ------------------------- 1l ADOBEC 1d: Counter({'C': 1, 'A': 0, 'B': 0}) ****************** ****************** 2l ADOBEC 2d: Counter({'C': 1, 'A': 0, 'B': 0}) ind, end 6 11 ------------------------- 1l ADOBEC 1d: Counter({'A': 0, 'B': 0, 'C': 0}) ****************** cmp: ADOBEC ODEBANC cmp: ADOBEC DEBANC cmp: ADOBEC EBANC cmp: EBANC BANC ****************** 2l BANC 2d: Counter({'B': 1, 'A': 0, 'C': 0}) ind, end 10 12 'BANC'239.滑動窗口最大值
leetcode鏈接
滑動窗口
class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:n = len(nums)# 注意 Python 默認的優先隊列是小根堆q = [(-nums[i], i) for i in range(k)]# 將列表轉化為堆heapq.heapify(q)# 最大值為堆頂ans = [-q[0][0]]for i in range(k, n):# 插入一個值以及索引heapq.heappush(q, (-nums[i], i))# 不用每次刪除窗口外的值,而是當最大值在窗口外再進行刪除# 判斷堆頂是否在窗口外while q[0][1] <= i - k:# 在窗口外則彈出heapq.heappop(q)# 每次存儲堆頂為最大值ans.append(-q[0][0])return ans4、過程模擬 (無算法,邏輯思維理解)
54.螺旋矩陣
leetcode鏈接
過程模擬:
套用螺旋矩陣二的模板:
劍指Offer29.順時針打印矩陣
leetcode鏈接
思路同上:
class Solution:def spiralOrder(self, matrix: List[List[int]]) -> List[int]:res = []if matrix:length, width = len(matrix),len(matrix[0])loop = min(length, width)//2startx, starty = 0, 0for offset in range(1, loop+1):for i in range(starty, width-offset):res.append(matrix[startx][i])for i in range(startx, length-offset):res.append(matrix[i][width-offset])for i in range(width-offset, starty, -1):res.append(matrix[length-offset][i])for i in range(length-offset, startx, -1):res.append(matrix[i][starty])startx+=1starty+=1if length==width and length%2!=0:res.append(matrix[length//2][length//2])else:if length>width and width%2!=0:for i in range(length-width+1):res.append(matrix[i+loop][width//2])elif length<width and length%2!=0:for i in range(width-length+1):res.append(matrix[length//2][i+loop])return reselse:return res參考
算法刷題總結 (一) 數組
代碼隨想錄
總結
- 上一篇: VSCode正则搜索中文字符
- 下一篇: IP68防尘防水等级