有趣的算法題合集(附代碼)
子序列最大和
描述:給出一個數字序列 [1,-2,3,5,-1,4,-7],找出其中的子序列,使得其和最大,返回該最大值。
解決方案:從左到右遍歷數列,求和sum,當和sum爲負的時候;重新開始新的序列計算;使用一個max變量記錄當前求得的最大值。
- 遍歷到 1:sum = 1,max=1
- 遍歷到 -2:sum = -1,max=1 => sum = 0,max=1
- 遍歷到 3:sum = 3,max=3
- 遍歷到 5:sum = 8,max=8
- 遍歷到 -1:sum = 7,max=8
- 遍歷到 4:sum = 11,max=11
- 遍歷到 -7:sum = 4,max=11
所以上述例子返回最大子序列和 max = 11
# encoding: utf-8
"""
@author: github/100101001
@contact: [email protected]
@time: 2020/3/2 上午12:01
@file: maxSumSubSequence.py
@desc:
"""
from typing import List
class Solution:
def maxSumSubSequence(self, arr: List[int]) -> int:
s = 0
ret = float('-inf')
for i in arr:
s += i
ret = s if ret < s else ret
s = 0 if s < 0 else s
return ret
if __name__ == "__main__":
solution = Solution()
print(solution.maxSumSubSequence([1, -2, 3, 5, -1, 4, -7]))
股票利潤
描述:給出一個股票價格序列 [7,6,2,4,8, 3,1],找到買入和賣出的天,使得利潤最高。
解決方案:不同於使用動態規劃的思想,這裏轉換子序列最大和問題來解決,利用
,先遍歷序列的到一個差分序列:[-1,-4,2,4,-5,-2]。等式左右取max,對右側來說就是求一個子序列最大和。
- 遍歷到 1:sum = -1,max=-1 => sum = 0,max=-1
- 遍歷到 -4:sum = -4,max=-1 => sum = 0,max=-1
- 遍歷到 2:sum = 2,max=2
- 遍歷到 4:sum = 6,max=6
- 遍歷到 -5:sum = 1,max=6
- 遍歷到 -2:sum = -1,max=6 => sum = 0,max=6
所以就是在第3天買入,第5天賣出,最掙錢。
# encoding: utf-8
"""
@author: github/100101001
@contact: [email protected]
@time: 2020/3/2 上午12:07
@file: maxProfit.py
@desc:
"""
from typing import List
class Solution:
def maxProfit(self, arr: List[int]) -> int:
diff = [arr[i] - arr[i - 1] for i in range(1, len(arr))]
print(diff)
return self.maxSumSubSequence(diff)
def maxSumSubSequence(self, arr: List[int]) -> int:
s = 0
ret = float('-inf')
for i in arr:
s += i
ret = s if ret < s else ret
s = 0 if s < 0 else s
return ret
if __name__ == "__main__":
solution = Solution()
print(solution.maxProfit([7, 1, 5, 3, 6, 4]))
雞蛋測試
描述:一棟100層高的樓房,用兩個雞蛋測試在哪一層雞蛋正好摔碎?最少實驗幾次可以得到結論?
解決方案:基於第一個雞蛋有沒有碎,和剩餘試驗次數,選擇下一輪試驗樓層
- 第一次選擇: 我們希望樓層越高越好,因爲如果沒碎,可以儘量多的排除樓層。但不能高過第 k 層,因爲如果第1個雞蛋碎了,第2個雞蛋只有k-1次實驗機會,也就是從1樓試到最高第 k-1,再高層試不了。
- 第二次選擇:
- k層碎了,就從 1 試到 k-1
- k層沒碎,同理,在 k + (k-1) 層嘗試
- 第三次選擇(沒碎):在 k + (k-1) + (k-2) 層嘗試
- 。。。
- 第 k 次選擇 (沒碎):在 k + (k-1) + (k-2)+....+ 1 層嘗試(此時還沒碎,就沒法排除了)
總結上述方案:用代表第 次嘗試的樓層。如果第 次嘗試碎了,那麼只需從 , 逐層試到 。如果第 次嘗試沒碎,那麼到 層繼續試驗。
因此,只要第一顆雞蛋碎了,就能得到解答。問題轉換爲,必須在k次嘗試都沒碎時,樓層已經超過了 100 層,否則,無法排除沒試過的高層。 列出不等式 ,可得解 。
64馬競賽
問題描述:64匹馬,8條賽道,找到最快的 4 匹馬。
核心難點:解決錦標賽賽制中的分組公平性問題,也就是最強的馬來自同一組的問題。
重複次數大於 n/k 的元素
問題描述:數組包含n各元素,給定k,返回重複次數大於 n/k 的數組元素。
核心難點:遍歷使用哈希表記頻率,時間複雜度O(n),空間複雜度O(n),但一般 n>>k 比如 n=100 ,k=4,希望空間複雜度降低爲O(k)級別,時間複雜度適當提高。
解決方案:仍舊遍歷,使用哈希表記錄頻率,但是一旦哈希表滿了K個,就刪除頻率爲1的元素。遍歷完畢後,哈希表最多滿 N/k次,也就是元素最多會被刪除 N/k 次,重複次數超過N/k的目標元素不會被刪光。遍歷完,哈希表中的鍵總數小於k,對所有鍵檢查是否重複超過N/k次即可。
這個方法的時間複雜度,分爲兩部分,首先是遍歷數組,每次放入或更新哈希表的鍵值前,都可能要判斷哈希表鍵滿K個,如果滿了需要遍歷刪除值爲1的元素,所以該部分複雜度O(N*k);然後檢查哈希表剩餘鍵,每個遍歷一次數組,複雜度O(N*k),整體時間複雜度O(N*k)
# encoding: utf-8
"""
@author: github/100101001
@contact: [email protected]
@time: 2020/3/1 下午11:29
@file: kelement.py
@desc: 在n個元素的數組中找到重複次數大於 k 分之一的元素, n>>k
"""
from typing import List
class Solution:
def above_one_k(self, arr: List[int], k: int) -> List[int]:
d = {}
for item in arr:
if item in d:
d[item] = d[item] + 1
else:
d[item] = 1
if len(d) >= k:
# 字典迭代不能刪項
for it in list(d.items()):
if it[1] == 1:
del d[it[0]]
ret = [int]
for key in d.keys():
num = 0
for v in arr:
if key == v:
num += 1
if num > k:
ret.append(key)
return ret
if __name__ == "__main__":
s = Solution()
print(s.above_one_k([1, 1, 1, 1, 2, 33, 4, 21, 2, 2, 2], 3))
求二進制中1的個數
問題描述:對於一個 n bit 的二進制數,求1的個數,時間效率儘可能高
遍歷方案:循環右移,看低位是否爲1,時間效率O(n)
核心難點:方案一定不是遍歷,遍歷0是浪費的。也就是如何能定位到下一個1的位置,去掉不必要的0遍歷。
思路1:對於 0100,0100。如果定位到第一個1,那麼 0100,0100 - 0100,0000 = 0000,0100。然後,定位到第二個 1,0000,0100 - 0000,0100 = 0 結束。問題回到了給定一個串,如何定位最高位的1。這裏引入一個技巧,對於2的整次冪,0000,0100 & 0000,0011 = 0。
思路2:不斷從 0100,0100 中運用位運算去掉1,能去幾次,就有幾個1。靈感來源:比如對於 0000,0100 & 0000,0011 = 0,去掉了 1,注意減數 0000,0011 = 0000,0100 - 1 ;又比如對於 0010,0100 & 0010,0011 = 0010,0000。綜上述兩個例子總結規律 v & (v-1) 可以去掉v中最低位的一個1。
解決方案:對於輸入位串 v 。當 v !=0 時,令 v = v & (v-1),直到 v == 0。計算 循環去1 執行的次數,就是v中1的個數。
# encoding: utf-8
"""
@author: github/100101001
@contact: [email protected]
@time: 2020/3/1 下午11:35
@file: binary1.py
@desc: 在二進制串中計算1的個數
"""
class Solution:
def count1inBinary(self, seq: int) -> int:
num = 0
while seq:
seq &= seq - 1
num += 1
return num
if __name__ == "__main__":
s = Solution()
print(s.count1inBinary(255))