2020-07演算法刷題集
2020-07演算法刷題集
前言
前段時間一直忙於上課與其它事情,一直都沒有抽出時間來刷演算法題,現在看來,浪費了許多時間,非常後悔。
錯過了昨天,今天將是一個非常好的起點。所以重新開始刷演算法題。
(2020-07-25)這段時間每天刷著一道簡單難度和一道中等難度的題目,花費的時間也不多。只是總在詢問自己刷題的意義在什麼地方?今天有了能讓自己滿意的答案——享受於每一個邏輯和細節處理,享受每一個字元,每一句程式碼所造成的影響。或許有時會急躁於一個個疏忽,但最終都會明白,急躁並不能解決問題,靜下來才能知道問題出在哪裡。問題的形成和解決是一個過程,需要經驗,需要冷靜,同樣也需要跨越自己現在的位置。
- 來源
力扣(LeetCode)-1154-一年中的第幾天 - 問題描述
- 給你一個按 YYYY-MM-DD 格式表示日期的字串 date,請你計算並返回該日期是當年的第幾天。
通常情況下,我們認為 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 2 天,依此類推。每個月的天數與現行公元紀年法(格里高利曆)一致
- 示例
示例 1:
輸入:date = “2019-01-09”
輸出:9
示例 2:
輸入:date = “2019-02-10”
輸出:41
- …
- 演算法分析
- 獲取關鍵
- 輸入:字串,以“-”分隔,公元紀年法
- 返回:第幾天
- 處理邏輯
- 這道題都沒什麼好說的,有些以下常識即可完成
- 一年有12個月,有兩類,一種是平年,二月有28天,一種是閏年,二月有29天
- 平閏年的區別:閏年可以被4整除。但是,如果是100的倍數的話,必須被400整除,才是閏年。例如1900年可以被4整除,但由於是100的倍數,所以應該被除於400,不能被整除,所以是平年,二月有28天;2000年能被400整除,是閏年。二月有29天
- 1、3、5、7、8、10、12這些月份有31天
- 2、4、6、9、11這些月份有30天
- 細節處理
- 100整倍年需要除於400,其它也沒啥了
- 程式碼實現
class Date(object):
def __init__(self,dateStr):
""" 2010-10-09 """
self.date = dateStr
def run(self):
year = int(self.date[:4])
month = int(self.date[5:7])
day = int(self.date[-2:])
""" 1、3、5、7、8、10、12 """
daysList = [31,28,31,30,31,30,31,31,30,31,30,31]
if year % 100 == 0:
if year % 400 == 0:
""" 閏年 """
daysList[1] = 29
else:
if year % 4 == 0:
daysList[1] = 29
result = 0
for i in range(month-1):
result += daysList[i]
result += day
return result
if __name__ == "__main__":
dateObj = Date('2019-02-10')
print(dateObj.run())
#41
執行結果:
- 思考
題目關鍵詞:關於日期的常識
這道題沒什麼難度,但是如果不具備日期的常識,將無法解題。這是比較關鍵的。所以,應該多刷一些關於常識的題目,積累邏輯處理的流程之外,更重要的是知道這些常識。
- 來源
力扣(LeetCode)-592-分數加減運算 - 問題描述
- 給定一個表示分數加減運算表示式的字串,你需要返回一個字串形式的計算結果。 這個結果應該是不可約分的分數,即最簡分數。 如果最終結果是一個整數,例如 2,你需要將它轉換成分數形式,其分母為 1。所以在上述例子中, 2 應該被轉換為 2/1。
- 示例 1:
輸入:"-1/2+1/2"
輸出: “0/1”
- 示例 2:
輸入:"-1/2+1/2+1/3"
輸出: “1/3”- …
- 演算法分析
- 獲取關鍵
- 輸入:字串,加減
- 返回:最簡,字串,分數
- 處理邏輯
- 從字串中獲取分數物件,並獲取對應的運算子,+或者-
- 對所有物件進行通分,即需要求得所有分數物件的分母的最小公倍數
經過這一步處理後,所有的分數的分母將會統一,為最小公倍數- 對所有的分數物件的分子進行乘積處理,乘積的值是最小公倍數除於對應分母的結果
求出所有分子的和
求出分子和與最小公倍數的最大公因數,最大公因數可以通過以下方法獲得。
假 設 存 在 兩 個 對 象 a , b a , b 的 最 小 公 倍 數 使 用 [ a , b ] 表 示 , 最 大 公 因 數 使 用 ( a , b ) 表 示 將 會 有 : a ∗ b = ( a , b ) ∗ [ a , b ] 假設存在兩個物件a,b\\ a,b的最小公倍數使用[a,b]表示,最大公因數使用(a,b)表示\\ 將會有:a*b = (a,b)*[a,b] 假設存在兩個對象a,ba,b的最小公倍數使用[a,b]表示,最大公因數使用(a,b)表示將會有:a∗b=(a,b)∗[a,b]
將分子和最小公倍數同時除以最大公因數
返回結果
- 細節處理
- 如果最終的分子和為0時,可以直接返回“0/1”,,不需要求出分子和最小公倍數的最大公因數,否則正常處理
- 程式碼實現
class Solution:
def beautyResultList(self,worklist):
resultList = []
for i in worklist:
if i != '':
eleList = i.split('/')
resultList.append((int(eleList[1]),int(eleList[0])))
return resultList
def commonList(self,worklist,commonNum):
for i in range(len(worklist)):
value = commonNum/worklist[i][0]*worklist[i][1]
worklist[i] = value
return worklist
def gcd(self,n1,n2):
"""greatest common divisor function """
return self.gcd(n2, n1 % n2) if n2 > 0 else n1
def lcm(self,n1,n2):
"""lowest common multiple function"""
return n1 * n2 // self.gcd(n1, n2)
def getMinCommonMultiple(self,worklist):
for i in range(len(worklist)-1):
n1,n2 = worklist.pop(0),worklist.pop(0)
worklist.insert(0,self.lcm(n1,n2))
return worklist[0]
def fractionAddition(self, expression: str) -> str:
positive,loseList = [],[]
if expression[0] != '+' or expression[0] != '-':
expression = '+'+expression
for i in range(len(expression)):
if (expression[i]=='-'):
ele = ''
for j in range(i+1,len(expression)):
if(expression[j]!='+') and (expression[j]!='-'):
ele += expression[j]
else:
break
loseList.append(ele)
elif (expression[i]=='+'):
ele = ''
for j in range(i+1,len(expression)):
if(expression[j]!='+') and (expression[j]!='-'):
ele += expression[j]
else:
break
positive.append(ele)
positive = self.beautyResultList(positive)
loseList = self.beautyResultList(loseList)
parentList = []
for i in positive:
parentList.append(i[0])
for i in loseList:
parentList.append(i[0])
minCommonNum = self.getMinCommonMultiple(parentList)
positive = self.commonList(positive,minCommonNum)
loseList = self.commonList(loseList,minCommonNum)
posSum = sum(positive)
loseSum = sum(loseList)
son = int(posSum-loseSum)
if son == 0:
return '0'+'/'+str(1)
else:
maxCommonNum = son*minCommonNum/self.getMinCommonMultiple([son,minCommonNum])
return str(int(son/maxCommonNum))+"/"+str(int(minCommonNum/maxCommonNum))
if __name__ == "__main__":
obj = Solution()
print(obj.fractionAddition("-1/2+1/2"))
# 0/1
- 思考
題目關鍵詞:
這道題主要側重於考察數學知識中的最小公倍數和最大公因數。另外在解析字串的過程中,也是需要一些處理邏輯的,但是如果引用正規表示式的話,將會大大減少工作量。這等同於是否造輪子的區別。
另外有一些細節上的處理,也非常關鍵,在測試過程中遇到了好幾個問題,都是因為細節處理上的疏忽。
- 來源
力扣(LeetCode)-1033-移動石子直到連續 - 問題描述
- 三枚石子放置在數軸上,位置分別為 a,b,c。
每一回合,我們假設這三枚石子當前分別位於位置 x, y, z 且 x < y < z。從位置 x 或者是位置 z 拿起一枚石子,並將該石子移動到某一整數位置 k 處,其中 x < k < z 且 k != y。
當你無法進行任何移動時,即,這些石子的位置連續時,遊戲結束。
要使遊戲結束,你可以執行的最小和最大移動次數分別是多少? 以長度為 2 的陣列形式返回答案:answer = [minimum_moves, maximum_moves]
- 示例
輸入:a = 1, b = 2, c = 5
輸出:[1, 2]
解釋:將石子從 5 移動到 4 再移動到 3,或者我們可以直接將石子移動到 3。
- …
- 演算法分析
- 獲取關鍵
- 輸入:一維數軸,3個整數,移動到某個整數處
- 返回:一個陣列
- 處理邏輯
- 首先有一個數軸,數軸上有3個數,移動3個數,使得連續。但每一回合只能移動兩邊的數。
也有幾個概念,如果a,b連續,那麼就有a+1=b;同理b,c連續則有b+1=c
因為不按順序地輸入,所以需要進行升序排序;根據大小重新定義為a,b,c
可以分為以下幾種情況:
- 情況1:3個數是連續的
返回[0,0]即不需要移動- 情況2:左邊兩個數是連續的
那麼移動最少次數為1,即將右邊的數c移動1次,移動(飛)到b+1處;
最多次數,產生於將c每次移動1個單位,移動到b+1處,則有c-b-1- 情況3:右邊兩數是連續的
同上,最少次數為1,最多次數為b-a-1
-情況4:左右兩數都不連續 但左右兩數相差大於2
最少次數產生於 將a移動1次,移動(飛)到b-1處,將c移動1次,移動(飛)到b+1處,共2次;
最多次數產生於 每次一個單位,將a移動到b-1處,將c移動到b+1處,則有
( c − b − 1 ) + ( b − 1 − a ) = c − a − 2 (c-b-1)+(b-1-a)=c-a-2 (c−b−1)+(b−1−a)=c−a−2
-情況5:左右兩數不連續,但左右兩數有一個相差等於2,例如以下情況:
區別於情況4的最少移動次數,將離得遠的數移動到左右相差2的中間即可。例如上面的將1移動到4的位置;也如將7移動到2的位置;
最多次數還是和情況4一致。
- 細節處理
- 其中情況5沒有考慮到,所以演算法過不去。看錯誤示例知道存在這個問題,所以補充了
- 程式碼實現
class Solution:
def numMovesStones(self, a, b, c):
a,b,c = sorted([a,b,c])
#三連續
if a+1 ==b and b+1 == c:
return [0,0]
#左連續
if a+1 == b and b+1 < c:
return [1,c-b-1]
#右連續
if a+1 <b and b+1 == c:
return [1,b-a-1]
if a+2 == b or b+2 == c:
# return [1,b-a-1+c-b-1]
return [1,c-a-2]
#左右不連續
if a+1 < b and b+1 <c:
# return [2,b-a-1+c-b-1]
return [2,c-a-2]
if __name__ == "__main__":
obj = Solution()
print('[1,2,5]:',obj.numMovesStones(1,2,5))
print('[1,3,5]:',obj.numMovesStones(1,3,5))
print('[1,3,7]:',obj.numMovesStones(1,3,7))
5. 思考
題目關鍵詞:移動
這道演算法的最小值和最大值都產生於移動,只不過產生的過程是不同的。
最少次數產生於跨度比較大的移動,更加確切的說是飛。而最多次數產生於一個單位的移動,每次只移動1個單位,不斷縮小3個數的距離。
另外,每一種情況都有特殊性,區別在於最少次數的產生。因為情況不同,最少次數也不同。
最後,這道題也是比較簡單,也因為這幾天比較忙,所以刷幾道簡單的。
- 來源
力扣LeetCode-1160 - 問題描述
- 給你一份『詞彙表』(字串陣列) words 和一張『字母表』(字串) chars。
假如你可以用 chars 中的『字母』(字元)拼寫出 words 中的某個『單詞』(字串),那麼我們就認為你掌握了這個單詞。
注意:每次拼寫(指拼寫詞彙表中的一個單詞)時,chars 中的每個字母都只能用一次。
返回詞彙表 words 中你掌握的所有單詞的 長度之和。
- 示例
輸入:words = [“cat”,“bt”,“hat”,“tree”], chars = “atach”
輸出:6
解釋:
可以形成字串 “cat” 和 “hat”,所以答案是 3 + 3 = 6。
- …
- 演算法分析
- 獲取關鍵
- 輸入:一個列表,包含多個字串,一個字串
- 返回:返回列表中滿足條件的字串的總和
- 處理邏輯
- 定義字串為字典,列表為待驗證的字串的集合
- 分析字典中每一個字元的出現次數,使用dict(),格式{“x”:counts}
- 遍歷集合,取出每一個待驗證的字串,並分析字串的每一個字元的出現次數,同上的處理方式
- 如果待驗證字串字典中的鍵在字典中,且出現次數小於或等於字典,則表示該鍵滿足條件,記錄對應的數值
- 當遍歷完成一個待驗證字串字典時,如果數值等於該字串的長度,則表示這個待驗證字串滿足條件,記錄對應的長度。
- 遍歷完成時,返回最終的長度即可。
- 細節處理
- 物件的位置與區別需要分析好,不然很容易出現混淆。
- 程式碼實現
class Solution:
def counter(self,chars):
worddict = {}
for i in chars:
if(i in worddict):
worddict[i] = worddict[i]+1
else:
worddict[i] = 1
return worddict
def countCharacters(self, words, chars):
charsdict = self.counter(chars)
result = 0
for i in words:
i_dict = self.counter(i)
flag = 0
for j in i_dict:
if j in charsdict and charsdict[j] >= i_dict[j]:
flag += i_dict[j]
if flag == len(i):
result += len(i)
return result
if __name__ == "__main__":
obj = Solution()
words = ["cat","bt","hat","tree"]
chars = "atach"
# words = ["hello","world","leetcode"]
# chars = "welldonehoneyr"
words = ["dyiclysmffuhibgfvapygkorkqllqlvokosagyelotobicwcmebnpznjbirzrzsrtzjxhsfpiwyfhzyonmuabtlwin","ndqeyhhcquplmznwslewjzuyfgklssvkqxmqjpwhrshycmvrb","ulrrbpspyudncdlbkxkrqpivfftrggemkpyjl","boygirdlggnh","xmqohbyqwagkjzpyawsydmdaattthmuvjbzwpyopyafphx","nulvimegcsiwvhwuiyednoxpugfeimnnyeoczuzxgxbqjvegcxeqnjbwnbvowastqhojepisusvsidhqmszbrnynkyop","hiefuovybkpgzygprmndrkyspoiyapdwkxebgsmodhzpx","juldqdzeskpffaoqcyyxiqqowsalqumddcufhouhrskozhlmobiwzxnhdkidr","lnnvsdcrvzfmrvurucrzlfyigcycffpiuoo","oxgaskztzroxuntiwlfyufddl","tfspedteabxatkaypitjfkhkkigdwdkctqbczcugripkgcyfezpuklfqfcsccboarbfbjfrkxp","qnagrpfzlyrouolqquytwnwnsqnmuzphne","eeilfdaookieawrrbvtnqfzcricvhpiv","sisvsjzyrbdsjcwwygdnxcjhzhsxhpceqz","yhouqhjevqxtecomahbwoptzlkyvjexhzcbccusbjjdgcfzlkoqwiwue","hwxxighzvceaplsycajkhynkhzkwkouszwaiuzqcleyflqrxgjsvlegvupzqijbornbfwpefhxekgpuvgiyeudhncv","cpwcjwgbcquirnsazumgjjcltitmeyfaudbnbqhflvecjsupjmgwfbjo","teyygdmmyadppuopvqdodaczob","qaeowuwqsqffvibrtxnjnzvzuuonrkwpysyxvkijemmpdmtnqxwekbpfzs","qqxpxpmemkldghbmbyxpkwgkaykaerhmwwjonrhcsubchs"]
chars = "usdruypficfbpfbivlrhutcgvyjenlxzeovdyjtgvvfdjzcmikjraspdfp"
# words = ["hello","world","leetcode"]
# chars = "welldonehoneyr"
print(obj.countCharacters(words,chars))
執行截圖:
5. 思考
題目關鍵詞:字典
有時需要認真的閱讀題目,其實題目中的很多資訊,已經告訴我們處理的方式。例如這道題,最開始時,我是準備將每一個字元替換,當完成後,如果字元都被替換完成後,如果替換次數等於待驗證字串的長度,那麼就表示符合條件。當然這可以實現,但有許多細節並沒有扣好,而且如果字串的長度過大時,非常消耗記憶體。
最後,採用了字典記錄字串的字元與出現次數,最終完成,有許多細節是不需要處理的,而且也並無細節。使用合適的資料型別非常關鍵,這也驗證了那句"演算法+資料結構=程式"。
- 來源
力扣LeetCode-1037-有效的迴旋鏢 - 問題描述
- 迴旋鏢定義為一組三個點,這些點各不相同且不在一條直線上。給出平面上三個點組成的列表,判斷這些點是否可以構成迴旋鏢。
- 示例
輸入:[[1,1],[2,3],[3,2]]
輸出:true
- …
- 演算法分析
- 獲取關鍵
- 輸入:一個列表,列表中有3個子列表,子列表中有2個元素,分別代表下x,y座標,則3個子列表表示3個點
- 返回:判斷3個點是否共線。如果共線,則返回Flase,否則True
- 處理邏輯
- 偏向於數學問題。有許多方法可以解決,例如斜率、3點是否構成三角形、點是否在直線上。
- 斜率:3個點中隨機選取2個點,構成1條直線,獲取2個直線,並求得斜率。如果2條直線的斜率不同,那麼表示3點不共線。
- 三點是否構成三角形:之前有一篇“點在三角形內”,可以用其中的一個面積公式,如果面積為0,則表示構成直線。(程式碼實現2)
- 點是否在直線上,取兩個點,構成一條直線方程,判斷點是否滿足直線方程,如果滿足,則表示三點共線
4.向量法:三點兩兩組合構成兩個向量,判斷這兩個向量是否共線即可。
-這一道題使用兩種方法,一種是向量法,另一種就是三點是否構成三角形。
- . 細節處理
如果需要使用除法的話,可以兩兩交叉乘,避免分母為0的情況發生。
- 程式碼實現
這裡分別使用兩個思路三角形面積公式和向量法實現
- 三角形面積公式
class Solution:
def isBoomerang(self, points: List[List[int]]) -> bool:
a = (points[0][0],points[0][1])
b = (points[1][0],points[1][1])
c = (points[2][0],points[2][1])
##S=(1/2)*(x1y2+x2y3+x3y1-x1y3-x2y1-x3y2)
s = a[0]*b[1]+b[0]*c[1]+c[0]*a[1]
y = a[0]*c[1]+b[0]*a[1]+c[0]*b[1]
if s==y:
return False
else:
return True
- 向量法
class Solution:
def diffOfList(self,list1,list2):
return (list1[0]-list2[0],list1[1]-list2[1])
def isBoomerang(self, points) -> bool:
if points[1]!=points[0] and points[1]!=points[2] and points[0]!=points[2]:
vector_a = self.diffOfList(points[0],points[1])
vector_b = self.diffOfList(points[0],points[2])
if vector_a[0] and vector_a[1]:
if vector_b[0]/vector_a[0] != vector_b[1]/vector_a[1]:
return True
else:
return False
elif not vector_a[0] and vector_a[1]:
if not vector_b[0]:
return False
else:
return True
elif not vector_a[1] and vector_a[0]:
if not vector_b[1]:
return False
else:
return True
else:
return False
else:
return False
if __name__ == "__main__":
obj = Solution()
points = [[1,1],[2,3],[3,2]]
points = [[1,1],[2,2],[3,3]]
points = [[0,0],[1,2],[0,1]]
points = [[1,1],[2,3],[3,2]]
print(obj.isBoomerang(points))
- 思考
題目關鍵詞:點,直線
這道題更偏向於數學知識上,其實除了上面的那些解法之外,還有許多種方式。
除此之外,也沒啥好說的了。
- 來源
力扣(LeetCode)-1046-最後一塊石頭的重量 - 問題描述
- 有一堆石頭,每塊石頭的重量都是正整數。
- 每一回合,從中選出兩塊 最重的 石頭,然後將它們一起粉碎。假設石頭的重量分別為 x 和 y,且 x <= y。那麼粉碎的可能結果如下:
- 如果 x == y,那麼兩塊石頭都會被完全粉碎;
- 如果 x != y,那麼重量為 x 的石頭將會完全粉碎,而重量為 y 的石頭新重量為 y-x。
- 最後,最多隻會剩下一塊石頭。返回此石頭的重量。如果沒有石頭剩下,就返回 0。
- 示例
輸入:[2,7,4,1,8,1]
輸出:1
解釋:
先選出 7 和 8,得到 1,所以陣列轉換為 [2,4,1,1,1],
再選出 2 和 4,得到 2,所以陣列轉換為 [2,1,1,1],
接著是 2 和 1,得到 1,所以陣列轉換為 [1,1,1],
最後選出 1 和 1,得到 0,最終陣列轉換為 [1],這就是最後剩下那塊石頭的重量。
- …
- 演算法分析
- 獲取關鍵
- 輸入:一個列表,列表內元素都是整數
- 返回:一個整數
- 處理邏輯
- 每一回合選取列表中的最重兩塊石頭,進行碰撞,碰撞後並返回剩餘重量到列表。
碰撞:大減小
最簡單的方法:- 對列表進行排序,彈出兩個最大的元素,求差。當差不為0時,插入到列表中
- 當列表長度大於1時重複以上操作
- 最後將會返回一個空列表或者長度為1的列表
當列表為空時,返回0即可
當列表長度為1時,返回列表第一個元素即可
- 細節處理
- 需要考慮到列表為空的條件存在:即列表中最後一回閤中,兩塊石頭的重量相等。
- 程式碼實現
class Solution:
def lastStoneWeight(self, stones: List[int]) -> int:
while len(stones) > 1:
stones.sort(reverse=True)
max = stones.pop(0)
min = stones.pop(0)
if max != min:
stones.append(max-min)
if stones:
return stones[0]
return 0
- 思考
題目關鍵詞:貪心,排序
其實這道題挺簡單的,但並沒有實現得多麼優美,也沒有參考其它的辦法。知識簡單的刷題而已。
另外也實現了一種想法:就是隻執行一次排序,然後每次將碰撞後的結果插入列表的某個位置,保證列表的元素數值順序不變。
這裡使用了二分插入排序,但是效率非常不穩定,可以由於插入演算法並沒有寫得完美。程式碼如下:
class Solution:
def insert(self,ele,ls):
if not ls:
return [ele]
elif len(ls) == 1:
if ls[0]>ele:
ls.insert(0,ele)
else:
ls.append(ele)
return ls
else:
low = 0
hight = len(ls)
while hight > low:
mid = int((hight-low)/2)+low
if ls[mid] > ele:
if ele > ls[mid-1]:
ls.insert(mid,ele)
return ls
hight = mid-1
elif ele > ls[mid]:
if len(ls)-2>mid and ls[mid+1] > ele:
ls.insert(mid+1,ele)
return ls
low = mid+1
else:
ls.insert(mid,ele)
return ls
ls.insert(low,ele)
return ls
def lastStoneWeight(self, stones: List[int]) -> int:
stones.sort()
while len(stones) > 1:
max = stones.pop(-1)
min = stones.pop(-1)
if max != min:
stones = self.insert(max-min,stones)
if stones:
return stones[0]
else:
return 0
- 來源
力扣(LeetCode)-611-有效三角形的個數 - 問題描述
- 給定一個包含非負整數的陣列,你的任務是統計其中可以組成三角形三條邊的三元組個數。
- 示例
輸入: [2,2,3,4]
輸出: 3
解釋:
有效的組合是:
2,3,4 (使用第一個 2)
2,3,4 (使用第二個 2)
2,2,3
- …
- 演算法分析
- 獲取關鍵
- 輸入:陣列,非負整數,取出3個整陣列合,判斷是否符合三角形
- 返回:滿足條件的三角形個數
- 處理邏輯
- 關鍵:三條邊中,最小的兩條邊大於第三邊,則三條邊即可構成三角形
首先對列表進行排序,難解釋,直接看圖吧
>3. 細節處理- 根據關鍵,需要知道邊的有效範圍和符合條件的邊的個數
- 程式碼實現
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
nums.sort()
result,len_nums = 0,len(nums)
for i in range(len_nums-2):
if nums[i] == 0 : continue
current = i+2
for x in range(i+1,len_nums-1):
while len_nums > current and nums[i]+nums[x] > nums[current]:
current += 1
result += current -x-1
return result
- 思考
題目關鍵詞:排序,三角形
其實一開始是準備3個for迴圈,獲取滿足條件的邊的集合,最後返回集合的長度即可
但是最後發現,效率太低了,最後使用了動態規劃。效率雖然不是很高,但也跑過去了。
- 來源
力扣(LeetCode)-1151-高度檢查器 - 問題描述
- 學校在拍年度紀念照時,一般要求學生按照 非遞減 的高度順序排列。
請你返回能讓所有學生以 非遞減 高度排列的最小必要移動人數。
注意,當一組學生被選中時,他們之間可以以任何可能的方式重新排序,而未被選中的學生應該保持不動。
- 示例
輸入:heights = [1,1,4,2,1,3]
輸出:3
解釋:
當前陣列:[1,1,4,2,1,3]
目標陣列:[1,1,1,2,3,4]
在下標 2 處(從 0 開始計數)出現 4 vs 1 ,所以我們必須移動這名學生。
在下標 4 處(從 0 開始計數)出現 1 vs 3 ,所以我們必須移動這名學生。
在下標 5 處(從 0 開始計數)出現 3 vs 4 ,所以我們必須移動這名學生。
- …
- 演算法分析
- 獲取關鍵
- 輸入:一個列表
- 返回:一個整數
- 處理邏輯
- 這道題官方的問題描述和示例都是有問題的。初看時,一直get不到出題人的意圖。甚至覺得是否要寫一個排序演算法,通過最少的移動次數,完成排序後並返回移動次數。但通過示例的返回值,知道了這道題的意圖——對所給集合進行升序排序。並將完成排序之後的集合與原集合進行比較,看有同一位置上有多少個元素不同。
- 例如原集合[1,1,4,2,1,3]與完成排序的集合[1,1,1,2,3,4],
有位置2(從0開始),4,5的3個位置上的元素不同,所以返回3.- 所以解決的辦法就是進行一一比較
- 細節處理
- 程式碼實現
class Solution:
def heightChecker(self, heights):
copy_heights = heights.copy()
copy_heights.sort()
count = 0
for i in range(len(heights)):
if heights[i]!= copy_heights[i]:
count += 1
return count
- 思考
題目關鍵詞:簡單,排序
- 來源
力扣(LeetCode-640-求解方程) - 問題描述
- 求解一個給定的方程,將x以字串"x=#value"的形式返回。該方程僅包含’+’,’ - '操作,變數 x 和其對應係數。
如果方程沒有解,請返回“No solution”。
如果方程有無限解,則返回“Infinite solutions”。
如果方程中只有一個解,要保證返回值 x 是一個整數。
- 輸入: “x+5-3+x=6+x-2”
輸出: “x=2”
- …
- 演算法分析
- 獲取關鍵
- 輸入:一個字串,一個方程
- 返回:返回解,如果特殊情況,則返回對應的值
- 處理邏輯
- 前言: 邏輯並不複雜,只是處理過程有點複雜。
- 解析字串,獲取等號兩邊的元素
- 根據正負收集元素(最關鍵的一步):假如以等號左邊的正數為正的元素,那麼等號右邊的負數則為正。其它相反即可
-分別處理正負元素,返回解即可
- 細節處理
- 其中正負收集元素的這一步中,非常關鍵,可以使用兩個集合分別收集正負元素。最後統計集合的元素,,返回解。
- 程式碼實現
class Solution:
def sum(self,workList):
nums,count = 0,0
for i in workList:
if i:
if 'x' not in i:
nums += int(i)
else:
if len(i) == 1:
count += 1
else:
count += int(i[:len(i)-1])
return nums,count
def getELement(self,workStr,pList,mList):
# '+x+5+x' len = 8 index = 7
current = 0
while len(workStr)-1>current:
if workStr[current]=='+':
plushStr = ''
k = 0
for i in range(current+1,len(workStr)):
k = i
if i == len(workStr)-1:
plushStr += workStr[i]
break
if workStr[i] != '+' and workStr[i] != '-':
plushStr += workStr[i]
else:
break
pList.append(plushStr)
current = k
else:
minusStr = ''
k = 0
for i in range(current+1,len(workStr)):
k = i
if i == len(workStr)-1:
minusStr += workStr[i]
break
if workStr[i] != '+' and workStr[i] != '-':
minusStr += workStr[i]
else:
break
mList.append(minusStr)
current = k
return pList,mList
def solveEquation(self, equation: str) -> str:
workList = equation.split('=')
if workList[0] != '+' and workList[0] != '-':
front = '+'+workList[0]
else:
front = workList[0]
if workList[1] != '+' and workList[1] != '-':
back = '+'+workList[1]
else:
back = workList[1]
# plus or minus
plusList,minusList = [],[]
plusList,minusList = self.getELement(front,plusList,minusList)
plusList,minusList = self.getELement(back,minusList,plusList)
plus = self.sum(plusList)
minus = self.sum(minusList)
if plus[1] == minus[1]:
if plus[0] != minus[0]:
return 'No solution'
else:
return 'Infinite solutions'
else:
if plus[1] > minus[1]:
return 'x={}'.format(int((minus[0]-plus[0])/(plus[1]-minus[1])))
else:
return 'x={}'.format(int((plus[0]-minus[0])/(minus[1]-plus[1])))
- 思考
題目關鍵詞:解析
在現實中簡單的一個加減法求解方程,使用程式碼實現,卻如此複雜。說明自己的程式碼水平還是需要有所提高的。
- 來源
力扣(LeetCode)-1103-分糖果Ⅱ - 問題描述
- 排排坐,分糖果。
我們買了一些糖果 candies,打算把它們分給排好隊的 n = num_people 個小朋友。
給第一個小朋友 1 顆糖果,第二個小朋友 2 顆,依此類推,直到給最後一個小朋友 n 顆糖果。
然後,我們再回到隊伍的起點,給第一個小朋友 n + 1 顆糖果,第二個小朋友 n + 2 顆,依此類推,直到給最後一個小朋友 2 * n 顆糖果。
重複上述過程(每次都比上一次多給出一顆糖果,當到達隊伍終點後再次從隊伍起點開始),直到我們分完所有的糖果。注意,就算我們手中的剩下糖果數不夠(不比前一次發出的糖果多),這些糖果也會全部發給當前的小朋友。
返回一個長度為 num_people、元素之和為 candies 的陣列,以表示糖果的最終分發情況(即 ans[i] 表示第 i 個小朋友分到的糖果數)。
- 輸入:candies = 7, num_people = 4
輸出:[1,2,3,1]
解釋:
第一次,ans[0] += 1,陣列變為 [1,0,0,0]。
第二次,ans[1] += 2,陣列變為 [1,2,0,0]。
第三次,ans[2] += 3,陣列變為 [1,2,3,0]。
第四次,ans[3] += 1(因為此時只剩下 1 顆糖果),最終陣列變為 [1,2,3,1]。
- …
- 演算法分析
- 獲取關鍵
- 輸入:糖果的個數,人的個數
- 返回:以集合的方式返回每個人收到糖果的個數
- 處理邏輯
- 按照題目的邏輯走一遍即可
- 細節處理
- 定位索引這個地方可以使用求餘符號%實現。
例如:設定有n個小朋友
第0次分糖果,應該定位到第0位小朋友,即0%n=0
第n+1次分糖果,應該定位到第1位小朋友,即(n+1)%n=1
- 程式碼實現
class Solution:
def distributeCandies(self, candies: int, num_people: int) -> List[int]:
resultList = [0 for _ in range(num_people)]
index,count = 0,1
while candies:
if candies >= count:
resultList[index%num_people] += count
candies = candies - count
index += 1
count += 1
else:
resultList[index%num_people] += candies
candies = 0
return resultList
- 思考
題目關鍵詞:順序 分
從看到問題到寫完,沒有花多少時間,而且直接一遍過。
可能因為這道題非常簡單吧。
- 來源
力扣(LeetCode)-452-用最少數量的箭引爆氣球 - 問題描述
- 在二維空間中有許多球形的氣球。對於每個氣球,提供的輸入是水平方向上,氣球直徑的開始和結束座標。由於它是水平的,所以y座標並不重要,因此只要知道開始和結束的x座標就足夠了。開始座標總是小於結束座標。平面內最多存在104個氣球。
一支弓箭可以沿著x軸從不同點完全垂直地射出。在座標x處射出一支箭,若有一個氣球的直徑的開始和結束座標為 xstart,xend, 且滿足 xstart ≤ x ≤ xend,則該氣球會被引爆。可以射出的弓箭的數量沒有限制。 弓箭一旦被射出之後,可以無限地前進。我們想找到使得所有氣球全部被引爆,所需的弓箭的最小數量。
- 輸入:[[10,16], [2,8], [1,6], [7,12]]
輸出:2
解釋:
對於該樣例,我們可以在x = 6(射爆[2,8],[1,6]兩個氣球)和 x = 11(射爆另外兩個氣球)。
- …
- 演算法分析
- 獲取關鍵
- 輸入:一個列表
- 返回:需要最少的弓箭數量
- 處理邏輯
- 這道題是屬於貪心演算法,所以有兩個常規操作:排序和貪心選擇
問題描述中有一句非常關鍵:由於它是水平的,所以y座標並不重要,因此只要知道開始和結束的x座標就足夠了。
上面的這句關鍵說明只有x軸方向,即一維的。另外其實更確切地說,排序的關鍵在於結束座標。如下:
那麼引爆以上氣球需要2個弓箭。一個弓箭在x=3,引爆氣球1,2和2;另一個弓箭引爆氣球4即可。
- 細節處理
- 其實對上面的樣例,不止有以上的辦法。還有:
- 弓箭在x=2出,另一個弓箭在x=4處
- 等等
但最終的答案是不會變的。正如貪心演算法的一個案例-活動安排。
貪心演算法解決活動安排-Python實現(排序+貪心選擇)
活動結束時間對應氣球的結束座標,排序還是那個排序;
- 程式碼實現
class Solution:
def findMinArrowShots(self, points:list) -> int:
points.sort(key=lambda x:(x[1]))
flag = 0
while points:
workList = []
workList.append(points.pop(0))
while points:
if workList[0][1] >= points[0][0]:
workList.append(points.pop(0))
else:
break
flag += 1
return flag
- 思考
題目關鍵詞:排序,貪心
以前解決問題,都是按照c或者c++的思維,手寫排序,手寫邏輯。固然非常鍛鍊程式設計能力,但也是重複造輪子。所以,有些時候,使用一些輪子非常重要,當然這些輪子的使用前,必須知道這個輪子是怎麼造的。例如程式碼中的points.sort(key=lambda x:(x[1]))
即代表根據物件的位置1進行排序。
另外,貪心演算法並不是一種“完美“的解決方案:
- 當存在多個最優方案時,無法列舉所有的方案
- 最終的方案是基於每一次的最優選擇,而許多問題並不全是走好每一步就代表可以得到最優方案。例如動態規劃問題等等。
- 來源
力扣(LeetCode)-1268-搜尋推薦系統 - 問題描述
- 給你一個產品陣列 products 和一個字串 searchWord ,products 陣列中每個產品都是一個字串。
請你設計一個推薦系統,在依次輸入單詞 searchWord 的每一個字母后,推薦 products 陣列中字首與 searchWord 相同的最多三個產品。如果字首相同的可推薦產品超過三個,請按字典序返回最小的三個。
請你以二維列表的形式,返回在輸入 searchWord 每個字母后相應的推薦產品的列表。
- 輸入:products = [“mobile”,“mouse”,“moneypot”,“monitor”,“mousepad”], searchWord = “mouse”
輸出:[
[“mobile”,“moneypot”,“monitor”],
[“mobile”,“moneypot”,“monitor”],
[“mouse”,“mousepad”],
[“mouse”,“mousepad”],
[“mouse”,“mousepad”]
]
解釋:按字典序排序後的產品列表是 [“mobile”,“moneypot”,“monitor”,“mouse”,“mousepad”]
輸入 m 和 mo,由於所有產品的字首都相同,所以系統返回字典序最小的三個產品 [“mobile”,“moneypot”,“monitor”]
輸入 mou, mous 和 mouse 後系統都返回 [“mouse”,“mousepad”]
- …
- 演算法分析
- 獲取關鍵
- 輸入:xxx
- 返回:xxx
- 處理邏輯
- 通過問題描述,覺得很高大上,其實通過暴力法很簡單
- 對產品組進行排序,由於產品組都是字串,所以排序是根據字典排序。
- 通過兩個for即可不斷遍歷產品組,獲得符合條件產品即可。
- 細節處理
- 無
- 程式碼實現
class Solution:
def suggestedProducts(self, products, searchWord):
products.sort()
result = [[] for _ in range(len(searchWord))]
workStr = ''
lenStr = 0
for i in searchWord:
workStr += i
lenStr += 1
for j in products:
if j[:lenStr] == workStr and len(result[lenStr-1]) < 3:
result[lenStr-1].append(j)
return result
- 思考
題目關鍵詞:暴力 遍歷
現在刷題都是暴力法解決的,往往遇到最多的不是出現錯誤,而是超時。有時總在技巧和暴力之間猶豫不決。兩個方向各有好處,技巧鍛鍊思維,暴力鍛鍊邏輯。雖然更偏向於技巧,但有時因為時間和思維有限,所以走向暴力法。
相關文章
- leetcode排序專題演算法刷題LeetCode排序演算法
- 刷題總結——回溯演算法演算法
- 每週刷個 leetcode 演算法題LeetCode演算法
- 演算法刷題之三一維列表演算法
- 刷了80道演算法題以後演算法
- vscode的LeetCode演算法刷題外掛VSCodeLeetCode演算法
- 刷了幾千道演算法題,這些我私藏的刷題網站都在這裡了!演算法網站
- leetcode刷題記錄:演算法(三)滑動視窗演算法LeetCode演算法
- 刷題
- leetcode刷題記錄:演算法(六)BFS&DFSLeetCode演算法
- 演算法刷題:LeetCode中常見的動態規劃題目演算法LeetCode動態規劃
- 刷題10.10
- 開始刷演算法演算法
- LeetCode 刷題指南(一):為什麼要刷題LeetCode
- mysql刷題題後感MySql
- 每日刷題 3.17
- 刷題學習
- LeetCode 刷題—樹LeetCode
- LeetCode刷題 堆LeetCode
- MISC刷題12
- 刷題記錄
- 順序刷題
- LeetCode刷題整理LeetCode
- leetcode刷題(一)LeetCode
- 機器學習 - 競賽網站,演算法刷題網站機器學習網站演算法
- LeetCode通關:連刷十四題,回溯演算法完全攻略LeetCode演算法
- 職教雲課堂刷題軟體自動答題播放下一集
- 【演算法】資料結構與演算法基礎總覽(中)——刷Leetcode等演算法題時一些很實用的jdk輔助方法錦集演算法資料結構LeetCodeJDK
- 看完谷歌大佬的刷題筆記, 我直接手撕了101道 Leetcode 演算法題谷歌筆記LeetCode演算法
- 面試常考演算法題之並查集問題面試演算法並查集
- LeetCode刷題之第701題LeetCode
- 中考後刷題補題合集
- LeetCode刷題記錄LeetCode
- leetcode刷題筆記LeetCode筆記
- 如何使用leetcode刷題LeetCode
- Leetcode刷題分類LeetCode
- 20240520刷題總結
- 刷題筆記02筆記