leetcode | 初級演算法-陣列
01 起
最近“不務正業地”刷了一波leetcode上的演算法題,初級演算法已經刷完50%,戰況如下,
刷題固然爽快,但及時總結才是進步之道,下面就陣列部分的題目進行回顧和總結。
注意,刷題使用的語言是Python3,"陣列"這個資料結構在Python中對應的就是"列表"list。
初級演算法中陣列類題目共11道,如下,我們一道一道來總結
02 解題
下面我們逐個擊破陣列部分的11道題。
2.1 從排序陣列中刪除重複項
題目:給定一個排序陣列,你需要在原地刪除重複出現的元素,使得每個元素只出現一次,返回移除後陣列的新長度。
思路:審題在於”排序陣列“和”原地演算法“,對於排序陣列,可以用set()實現去重並返回原順序,另外,可以直接遍歷對比num[i],num[i+1],若相同則去掉nums[i+1]
class Solution: def removeDuplicates(self, nums): """ :type nums: List[int] :rtype: int """ if len(nums)<2: return len(nums) i=0 while i<len(nums)-1: if nums[i]==nums[i+1]: nums.remove(nums[i+1]) else: i+=1 return len(nums)
2.2 買賣股票的最佳時機 II
題目:給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。設計一個演算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)
思路:因為可以儘可能多地交易(買賣),又不能同時參與多筆交易,因此為了獲得最大利潤,可以採取如下措施:只要前一天價格低於當天,就在前一天買入股票並在當天賣出,即,遍歷每天的股價,若第i天股價低於第i+1天,則在第i天買入,第i+1天賣出
class Solution: def maxProfit(self, prices): """ :type prices: List[int] :rtype: int """ profit=0 for i in range(len(prices)-1): if prices[i]<prices[i+1]: profit+=prices[i+1]-prices[i] return profit
2.3 旋轉陣列
題目:給定一個數組,將陣列中的元素向右移動 k 個位置,其中 k 是非負數
思路:k可能大於陣列長度,於是先i=k%l,再將陣列後i個元素平移到陣列左邊
class Solution:
def rotate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: void Do not return anything, modify nums in-place instead.
"""
# 演算法一:未考慮k>len(nums)的情況,執行時長64ms
"""
l=len(nums)
nums[:]=nums[l-k:l]+nums[:l-k]
"""
演算法三:此演算法最高效,考慮k>len(nums)的情況,通過取餘數來化解,執行時長55ms
"""
l=len(nums)
i=k%l
nums[:]=nums[-i:]+nums[:-i]
2.4 存在重複
題目:給定一個整數陣列,判斷是否存在重複元素。如果任何值在陣列中出現至少兩次,函式返回 true。如果陣列中每個元素都不相同,則返回 false
思路:非常簡單,python的set()函式可以對列表去重,我們只需要對比set前後列表的長度是否相同即可
class Solution:
def containsDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
return len(nums)!=len(set(nums))
2.5 只出現一次的數字
題目:給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。
思路:三種思路,一是笨辦法,一一對比;二是異或操作,相同兩數異或為0,某數異或0為其本身;三是靈活運用set()
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
"""
演算法一:如果nums過長,時間複雜度較高,不過此演算法可以找出多個“只出現一次的元素”
"""
for i in range(len(nums)):
if nums[i] not in nums[:i]+nums[i+1:]:
result=nums[i]
return result
"""
演算法二核心思想:異或,異或操作是在二進位制上的異或操作,相同兩數異或=0,數異或0=數本身。
於是定義一個整數,初始化為0,並用該數依次與列表中元素進行異或操作,最後的結果肯定就是隻出現過一次的那個數(異或次序並不影響最終結果,因此不論列表元素如何排列,相同兩數異或總是為0)
此演算法只對 只有一個“只出現一次的元素” 的數列適用
"""
num=0
for i in nums:
num ^= i
return num
"""
演算法三核心思想:set(list)將篩除重複元素
此演算法只對 只有一個“只出現一次的元素” 的數列適用
"""
num=sum(list(set(nums)))*2-sum(nums)
return num
2.6 兩個陣列的交集 II
題目:給定兩個陣列,編寫一個函式來計算它們的交集
思路:兩種方法,一是每比較一次,就刪除陣列中被比較的元素;二是直接利用Counter()計數,然後&運算
class Solution:
def intersect(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
"""
思路一:每比較一次,就刪除陣列中被比較的元素
實現方法:字典計數,比較後字典對應key的計數-1
"""
result=[]
count={}
for num in nums1:
count[num]=count.get(num,0)+1
for num in nums2:
if num in count.keys() and count[num]: #if 0等價於false
result.append(num)
count[num]-=1
return result
"""
思路二:直接利用Counter()計數,然後&運算(&運算返回a、b的counter結果中(key,value)對的交集)
"""
from collections import Counter
return (list((Counter(nums1)&Counter(nums2)).elements()))
2.7 加一
題目:給定一個由整陣列成的非空陣列所表示的非負整數,在該數的基礎上加一。最高位數字存放在陣列的首位, 陣列中每個元素只儲存一個數字。你可以假設除了整數 0 之外,這個整數不會以零開頭。
思路:將列表轉為數字,再加一。列表轉數字,兩個方法, 方法一:string和int之間的轉換,將列表中個數字加到空字串,然後將最終的字串轉為數字再+1,再將此結果轉為字串,最後將字串各元素append到列表中輸出 方法二:遍歷list各元素,按nums[i]10*(len(nums)-i+1)求和得到列表表示的數字 這裡採用方法一
class Solution:
def plusOne(self, digits):
"""
:type digits: List[int]
:rtype: List[int]
"""
resStr="";res=0;resList=[]
for i in digits:
resStr+=str(i)
res=str(int(resStr)+1)
for i in res:
resList.append(int(i))
return resList
2.8 移動零
題目:給定一個數組 nums,編寫一個函式將所有 0 移動到陣列的末尾,同時保持非零元素的相對順序。
思路:兩種方法。思路一:遍歷nums各元素i,若i為0,將i放在最後一位,i之後的元素前移一位,此方法理解上比較直觀,但時間複雜度較高;思路二:遍歷nums各元素i,若i為0,直接刪除該元素,並在末尾補0,這種方法簡單直接高效。
class Solution:
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: void Do not return anything, modify nums in-place instead.
"""
"""
思路一:遍歷nums各元素i,若i為0,將i放在最後一位,i之後的元素前移一位
560ms
"""
n=len(nums)
i=0;z=0
while i<n-z:
if nums[i]==0:
z+=1
for j in range(i,n-1):
nums[j]=nums[j+1]
nums[n-1]=0
if nums[i]!=0:
i+=1
else:
i+=1
"""
思路二:遍歷nums各元素i,若i為0,直接刪除該元素,並在末尾補0
76ms
"""
z=0 #計數0的個數,防止連續0中後面的0被跳過
for i in range(len(nums)):
if nums[i-z]==0:
del nums[i-z]
nums.append(0)
z+=1
2.9 兩數之和
題目:給定一個整數陣列和一個目標值,找出陣列中和為目標值的兩個數。你可以假設每個輸入只對應一種答案,且同樣的元素不能被重複利用
思路:兩種方法。思路一:subNums儲存i索引之後的list,校驗target-nums[i]是否在subNums中,若存在則返回i和subNums中的索引值,此方法當 陣列很長時,耗時較長;思路二:是思路一的改進版,呼叫迭代器enumerate()進行迭代,加快運算速度;dict儲存nums各元素和對應的索引值
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
"""
思路一:subNums儲存i索引之後的list,對比
缺點:當nums較長時,運算時間較長
"""
subNums=[]
result=[]
for i in range(len(nums)):
subNums=nums[i+1:] #計算量減半,每次只取nums[i]之後的元素,因為之前的元素已經計算過了
if target-nums[i] in subNums:
result.append(i)
result.append(i+1+subNums.index(target-nums[i]))
return result
"""
思路二:enumerate(),呼叫迭代器進行迭代,加快運算速度;dict儲存nums各元素和對應的索引值
"""
dictNum={}
for i, num in enumerate(nums):
if target-num in dictNum.keys():
return i,dictNum[target-num]
dictNum[num]=i #dict儲存nums各元素和對應的索引值
2.10 有效的數獨
題目:判斷一個 9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。數獨空白格用 ‘.’ 表示。
- 數字 1-9 在每一行只能出現一次。
- 數字 1-9 在每一列只能出現一次。
- 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。
思路:按題意,數獨最大是9X9的,用三個列表儲存各行、各列、各方塊的元素情況,初始化為空,判斷某數字是否存在於某行+某列+某方塊中,若存在,返回False,否則將此元素新增到行列方塊中 難點:第i行、第j列的元素在第幾個方塊中?>>3*(i//3)+j//3
class Solution:
def isValidSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: bool
"""
rows=[[] for i in range(9)]
cols=[[] for i in range(9)]
boxs=[[] for i in range(9)] #用三個列表儲存各行、各列、各方塊的元素情況,初始化為空
for i,row in enumerate(board): #enumerate迭代器,演算法效率更高
for j,num in enumerate(row): #注意區分row rows
if num!="." and num in rows[i]+cols[j]+boxs[3*(i//3)+j//3]:
return False
rows[i].append(num)
cols[j].append(num)
boxs[3*(i//3)+j//3].append(num)
return True
2.11 旋轉影象
題目:給定一個 n × n 的二維矩陣表示一個影象,將影象順時針旋轉 90 度。必須在原地旋轉影象,這意味著你需要直接修改輸入的二維矩陣。請不要使用另一個矩陣來旋轉影象。
思路:根據規律,將矩陣轉置,然後反轉每行實現
class Solution:
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
n=len(matrix)
for i in range(n): #手動轉置矩陣
for j in range(i+1,n): #注意j的遍歷範圍
tmp=matrix[i][j] #用一個變數來儲存中間態,這個idea很普遍!
matrix[i][j]=matrix[j][i]
matrix[j][i]=tmp
for i in range(n):
matrix[i]=matrix[i][::-1]
03 總結
本文解鎖了leetcode初級演算法題庫中陣列板塊的全部題目,並對每道題的解題思路和程式碼進行說明,供你參考,也便於我日後複習總結。
下期我們將繼續解鎖其他板塊的題目,一起進步吧!