一、Algorithm
写一个函数parseTrigon判断是否是有效三角形
(入参三个边,返回值-1 不合法 0 非三角形 1 普通三角形 2 等腰三角形)
def parseTrigon(a, b, c):
"""
:param a:
:param b:
:param c:
:return: -1 不合法
0 非三角形
1 普通三角形
2 等腰三角形
"""
if not is_side_legal(a, b, c):
return -1
if not is_triangle(a, b, c):
return 0
if a == b or a == c or b == c:
return 2
else:
return 1
def is_side_legal(*side_length):
return len(list(filter(lambda x: (0 < int(x) < 10), side_length))) == 3
def is_triangle(a, b, c):
return a + b > c and a + c > b and b + c > a
# print(parseTrigon(9, 2, 2))
257. 二叉树的所有路径
Given a binary tree, return all root-to-leaf paths.
Note: A leaf is a node with no children.
Example:
Input:
1
/
2 3
5
Output: [“1->2->5”, “1->3”]
Explanation: All root-to-leaf paths are: 1->2->5, 1->3
class Solution:
def offical_binaryTreePaths(self, root: TreeNode) -> list:
"""
:type root: TreeNode
:rtype: List[str]
"""
def construct_paths(root, path):
if root:
path += str(root.val)
if not root.left and not root.right: # 当前节点是叶子节点
paths.append(path) # 把路径加入到答案中
else:
path += '->' # 当前节点不是叶子节点,继续递归遍历
construct_paths(root.left, path)
construct_paths(root.right, path)
paths = []
construct_paths(root, '')
return paths
def my_binaryTreePaths(self, root: TreeNode) -> list:
result_list = []
self.dfs(result_list, root, "")
return result_list
def dfs(self, result_list, node: TreeNode, path: str):
path += str(node.val)
if not node.left and not node.right:
result_list.append(path)
if node.left:
self.dfs(result_list, node.left, path + "->")
if node.right:
self.dfs(result_list, node.right, path + "->")
在深度优先搜索遍历二叉树时,我们需要考虑当前的节点以及它的孩子节点。 如果当前节点不是叶子节点,则在当前的路径末尾添加该节点,并继续递归遍历该节点的每一个孩子节点。 如果当前节点是叶子节点,则在当前路径末尾添加该节点后我们就得到了一条从根节点到叶子节点的路径,将该路径加入到答案即可。 如此,当遍历完整棵二叉树以后我们就得到了所有从根节点到叶子节点的路径。
70. Climbing Stairs
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Example 1:
Input: 2 Output: 2 Explanation: There are two ways to climb to the top.
- 1 step + 1 step
- 2 steps Example 2:
Input: 3 Output: 3 Explanation: There are three ways to climb to the top.
- 1 step + 1 step + 1 step
- 1 step + 2 steps
- 2 steps + 1 step
Constraints:
1 <= n <= 45
import functools
class Solution:
# 直接DP,新建一个字典或者数组来存储以前的变量,空间复杂度O(n)
def climbStairs(self, n: int) -> int:
dp = {}
dp[1] = 1
dp[2] = 2
for i in range(3, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
# 直接递归解法,容易超时,python可以加个缓存装饰器,这样也算是将递归转换成迭代的形式了
@functools.lru_cache(100) # 缓存装饰器
def climbStairs2(self, n: int) -> int:
if n == 1: return 1
if n == 2: return 2
return self.climbStairs(n-1) + self.climbStairs(n-2)
二、Review
三、Tip
四、Share
一、起因
近期全公司搞商业化,发家的各类编辑器更是重中之重。
- 想要去除易企秀logo么?买去广告功能吧
- 想要隐藏尾页么? 买去广告功能吧
- 想要移除花花绿绿的美女图片么? 买去广告功能吧
上线后看着会员收入和增值收入蹭蹭蹭的往上涨,每隔几天来个突破指标的喜报也是蛮开心的。
but,某日接到用户投诉:作品使用了h5的预览去广告功能,但是在预览时未生效,要求退款。
这还了得,断人财路如杀人父母,这事要是让产品经理知道了还不得分分钟请喝茶。
二、重现-day1
这功能整体流程比较简单,分分钟就能走完过程。 。。。 没问题啊,变换了各种姿势,PC、Android、iOS都试了个遍,也没复现这个问题。 难不成是个例? 某些垃圾数据导致的?网络波动?恶意篡改? 那好吧,先通过dubbo invoke临时处理数据吧,后续再观察吧(na jiu zhe yang ba)
三、重现-day2
事实证明,墨菲定律还是靠谱的。同样的事情,又发生了,还连着反馈了好几个。鼓足干劲找原因吧。
- 走查代码,逻辑上应该没问题
- 查询付费记录,确定是在哪一端付费
- 查询用户操作记录和调用接口历史,无有效信息
- 模拟复杂业务场景操作,未复现
- 联合研发大佬排查推理,无结果
问题排查无疾而终。 雅典娜说过:同样的招数对圣斗士不能使用第二次。呵,可惜我还是个青铜。
四、重现-day3
还开着早会呢,就又又又接到若干条这个问题的用户投诉。今天铆足劲把问题找出来,启用mysqlbinlog大法。
-
下载腾讯云后台事发时的mysql备份日志;
- 通过命令行解析对应的日志文件
mysqlbinlog --base64-output=decode-rows -v --start-datetime='2020-08-26 10:00:00' --end-datetime='2020-08-26 11:00:00' binlog_mysqlbin.000065 >log.sql
- 搜索对应的作品id,查询相关的DML操作,终于发现了一条update语句,把第29列JSON中原有的去广告标识
"adBagSwitch":true
给去掉了#200828 10:51:04 server id 120794 end_log_pos 117191840 Query thread_id=4424423 exec_time=0 error_code=0 SET TIMESTAMP=1598583064/*!*/; BEGIN /*!*/; # at 117191840 #200828 10:51:04 server id 120794 end_log_pos 117191982 Table_map: `eqs_editor`.`eqs_scene_ext` mapped to number 17826 # at 117191982 #200828 10:51:04 server id 120794 end_log_pos 117193209 Update_rows: table id 17826 flags: STMT_END_F ### UPDATE `eqs_editor`.`eqs_scene_ext` ### WHERE ### @1=217383287 ...省略若干 ### @26=30 ### @27='FqUIRN0ZbKvI1tPxQ4oCLeIJTTYC' ### @28='{"url":"o_1e6t7m3frvuvkrgm1h3ciar18.mp3","name":"L3V3LS - Swing.mp3","id":1075149732}' ### @29='{"wxCount":0,"autoFlipTime":3.0,"yqcAd":false,"autoFlip":false,"slideNumber":true,"adBagSwitch":true,"triggerLoop":true,"forbidHandFlip":false,"shareDes":"{}","wxClickFarmerCount":0,"tMSrc":"FgbnLpoTGTYessMl95xWrS1dUnwz"}' ### SET ### @1=217383287 ...省略若干 ### @26=30 ### @27='366488CC-4786-4850-BC00-6752ED02B545.jpg' ### @28='{"url":"o_1e6t7m3frvuvkrgm1h3ciar18.mp3","name":"L3V3LS - Swing.mp3","id":1075149732}' ### @29='{"wxCount":0,"autoFlipTime":3.0,"yqcAd":false,"autoFlip":false,"slideNumber":true,"triggerLoop":true,"forbidHandFlip":false,"shareDes":"{}","wxClickFarmerCount":0,"tMSrc":"FgbnLpoTGTYessMl95xWrS1dUnwz"}' # at 117193209 #200828 10:51:04 server id 120794 end_log_pos 117193236 Xid = 3833425953 COMMIT/*!*/;
- 分析其他字段,发现整个update,只有第27个字段有改变,查询表结构确定这个字段是用来记录作品封面的。
到此可以推断出,开启去广告功能后,再设置作品封面时,就会错误的将去广告标识覆盖。 经过模拟,确实能复现该问题,推测可能是业务脏读,再加上dubbo提供方开放权限过大,引起了字段覆盖。
Finally,经过一系列沟通,问题确认,解决方案确认,问题解决。
五、 总结
- dubbo接口开放权限过多,可能会引起众多不确定因素 => 接口隔离原则
- 数据类引起的问题,分析数据库binlog是个好方法 => 揭开神秘的面纱就变成经验值+1
- 遇到难题可以积极的向周围的人求助 => 群众的力量是无穷的
感谢胡贺东同学通过数据库日志定位到问题,责任感满满;
感谢安普尚同学经验老道的推断;
感谢孙小朋同学的协助查询用户操作记录的支持;
感谢吕晶、李慧、高春晓、贾倩倩对于解决问题的探索精神;
六、 踩坑
- mysqlbinlog解析时报错ERROR: Error in Log_event::read_log_event(): ‘Sanity check failed’, data_len: 31, event_type: 35ERROR: Could not read entry at offset 123: Error in log format or read error.
mysql5.6等高版本binlog文件增加了新的binlog event,如gtid event等。
mysql5.5版本的mysqlbinlog是识别不了这样的binlog event的。
使用高版本的mysqlbinlog即可解决
- linux如何将日志文件传给mac
scp ./binlog_mysqlbin.006960 eqxiu@172.21.116.131:/Users/eqxiu/logs 默认情况下,mac不允许远程登录,所以会报错 ssh: connect to host 172.21.116.131 port 22: Connection refused lost connection
需要开启mac远程登录: system preference =>share => 勾选 remote -login