关于2Sum
Two sum是一个很简单的问题,给定一个乱序列和一个target,找到两个数的和等于target.
只要先sort然后用首尾两个pointers 反向逼近直到找到所求就可以了.可以找到所有符合要求的tuple,这里简化为解决第一个遇到的符合条件的tuple,找到全部组合见下面3sum.
|
|
3sum-摘掉面具
Three sum 与two sum类似,是数组中找出所有三个数的组合加和为target.
它穿了一层马甲,我们摘掉他的面具, 将3Sum转化为解决了的2Sum问题,只要从0 loop 到 length-2 (设为x),对剩余数组找target-x,就转化成了twoSum.
不同的一点是要找到全部组合,同时要排重.
我的第一个解法是:
|
|
trivial. 然而速率却很慢,在使用python的人群中只beats about 30%.
Could stand? No.
开始优化.
while True: optimize()
排重优化0x00
第一个想法是先从排除重复结果上来优化.
在这个解法之前我试用完全得到包含重复的结果来2 pass 排重 会造成time exceed.因为这个排重过程已经是O(n^2)
这里用了 not in the list 来排重,O(n)的time complexity.想到可以用查询时间为O(1)的set来排重,然而list是unhashable的.
我尝试了化成string然后hash
|
|
然而并未卵,之后怀疑是转化为str太慢吗,tuple结果一样.
…
被没有显著的变化.
排重优化0x01
要减小排重带来的overhead. 上面的思路一直是在得到搜索结果之后再排重,那么一个反向的想法是我应该在搜索之前就排重,想如果第一个数是一样的化,之后twoSum的结果也是相同的,那么这次twoSum 的搜索就是没有意义的,于是我记录之前第一个数prev,就继续找下一个起点
改进如下:
|
|
这样就可以减调每次的线性排查 if [nums[x]]+r not in result:
performance有了进步,达到了50%左右.
排重优化0x02
在外围即第一个数事先排重之后,在twoSum搜索中也应该事先排重,
之前的loop是i++,j– 来前进一步,然而很有可能依然有重复值.
|
|
nice, 速率达到了60%多.
接下来该优化哪里呢?
我开始到处放空枪,看有没有机会打下一只鸟.
0x03 去除method call
我开始想在dynamic programming中反复recruisive call 造成的overhead. 于是我省去另建函数twoSum.
|
|
一声空响.
每次function被放在stack上,用后即delete, 和recuisive call 造成的overhead无法相提并论.
0x04 去除list copy
突然意识到我每次都copy了后段list来传入twoSum,造成了很大浪费,应该只传入原list,只要改变index就可以了,这样才不违背在最开始时候就进行sort的初衷.
|
|
速率达到了80%-90%
0x05 last shot
统一了外部的事先排重,这里的一个教训是对于有序数列来说,排重是很有优势的,应该利用order事先排除,loop until 一个不同的值出现.
而我之前的做法其实是浪费已排序的这个已知信息资源.
对于未消逝的变量,根本没有必要每次记录history.
改进如下
|
|
bingo! 速率达到了 python人群的 90% 左右.
0x06 Not the end
这一定不是终结,还没有想到进一步的优化方式,先用一个法则来自我安慰:
Hill’s law
Simple and Dumb can be better
…lol
Never stop optimizing until you give out your all.
However, don’t forget the Hill’s law.