【算法】有意思的算法题(附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: 17702113437@163.com
@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: 17702113437@163.com
@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: 17702113437@163.com
@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: 17702113437@163.com
@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))

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值