【算法】有意思的算法題(附pyhon3代碼){更新中}

有趣的算法題合集(附代碼)

 

子序列最大和

描述:給出一個數字序列 [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],找到買入和賣出的天,使得利潤最高。

解決方案:不同於使用動態規劃的思想,這裏轉換子序列最大和問題來解決,利用

  F(b) - F(a)= \int _{a}^{b} f(x)  ,先遍歷序列的到一個差分序列:[-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 層嘗試(此時還沒碎,就沒法排除了)

總結上述方案:L(i)代表第 i 次嘗試的樓層。如果第 i 次嘗試碎了,那麼只需從 L(i-1)+1, 逐層試到 L(i)-1。如果第 i 次嘗試沒碎,那麼到 L(i+1) = L(i) + k-i+1 層繼續試驗。

因此,只要第一顆雞蛋碎了,就能得到解答。問題轉換爲,必須在k次嘗試都沒碎時,樓層已經超過了 100 層,否則,無法排除沒試過的高層。 列出不等式 (k^{2}+k)/2 \geq 100, k \in \mathbb{Z^{+}} ,可得解 k\geq 14

 

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))

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章