有趣的算法题合集(附代码)
子序列最大和
描述:给出一个数字序列 [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],找到买入和卖出的天,使得利润最高。
解决方案:不同于使用动态规划的思想,这里转换子序列最大和问题来解决,利用
,先遍历序列的到一个差分序列:[-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 层尝试(此时还没碎,就没法排除了)
总结上述方案:用代表第 次尝试的楼层。如果第 次尝试碎了,那么只需从 , 逐层试到 。如果第 次尝试没碎,那么到 层继续试验。
因此,只要第一颗鸡蛋碎了,就能得到解答。问题转换为,必须在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: 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))