Algorithm
本周选择的算法题是:Running Sum of 1d Array。
规则
Given an array nums
. We define a running sum of an array as runningSum[i] = sum(nums[0]…nums[i])
.
Return the running sum of nums
.
Example 1:
Input: nums = [1,2,3,4]
Output: [1,3,6,10]
Explanation: Running sum is obtained as follows: [1, 1+2, 1+2+3, 1+2+3+4].
Example 2:
Input: nums = [1,1,1,1,1]
Output: [1,2,3,4,5]
Explanation: Running sum is obtained as follows: [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1].
Example 3:
Input: nums = [3,1,2,10,1]
Output: [3,4,6,16,17]
Constraints:
1 <= nums.length <= 1000
-10^6 <= nums[i] <= 10^6
Solution
class Solution:
def runningSum(self, nums: List[int]) -> List[int]:
ans = [nums[0]]
for i in range(1, len(nums)):
ans.append(nums[i] + ans[-1])
return ans
Review
Rust: fear => love => hate => love => disillusion
一位 Rust 的拥趸分享了他的使用感受。
吸引他的有这些地方:
- Rust 的生态较为完善,标准库齐全
- 错误处理、Null 值处理优雅
- 所有权(Ownership)的概念很棒,前提是完全理解它
- 开发体验好,编译器清晰描述错误
- 宏系统强大
然而一门语言不可能面面俱到、适用于所有地方,就 Rust 自身的特点来看,它似乎更适合性能敏感的场景,如 CPU-Bound 的程序、微服务;此外 Linux 内核已开始支持 Rust,用它开发终端应用也不错;Rust 很好地平衡了性能和安全性,这使 heartbleed 问题出现的可能性大大减少,用它写解释器也是不错的选择。
Tip
春节期间没啥正事,经过几天的训练学习,打麻将的水平有所提高。
整理旧文档时发现早前写的一段 SQLite 锁设计的备忘录,拿出来重新温习下:
锁类型 | 作用 |
---|---|
SHARED LOCK | 在进行读操作之前,必须先获取 SHARED LOCK,共享锁允许同一时间多个连接读取数据库,不允许写入操作 |
RESERVED LOCK | 让 writer 可以提前开始写入数据,实际上写操作需要先更新 pager、磁盘缓存,最后才是 IO 写文件操作;可以和 SHARED LOCK 共存,但是同一时间只能有一个 RESERVED LOCK,可以对数据库加新的 SHARED LOCK;需要真正 IO 写入的时候,才获取 EXCLUSIVE LOCK |
PENDING LOCK | 缓存的写操作完成并想立即将更新写入磁盘时,需要先获取 PENDING LOCK,它将等待此时已经存在的 reader 完成,但是不允许对数据库加新的 SHARED LOCK |
EXCLUSIVE LOCK | 最高级别的互斥锁,属于库级排斥锁 |
当时在神州优车,也涉及到了职级标准、职级晋升相关的工作,讨论到 T6 的标准是什么,有提到一点“对于复杂问题的解决要有自己的见解”,背后的考虑是软件的复杂性通常是长出来的,随着项目本身的发展、研发团队的壮大,系统也会不断生长,这样不断迭代,很快就会复杂起来,且未来的形态往往难以预测,不过这并不意味复杂性不可控制,但确实要顶住压力、避免“能用就行”,因为:
- 每一个设计决策都在贡献复杂度
- 复杂度增长带来的风险往往是后知后觉的,等到发现时通常已经存在很长一段时间,处理成本很高
那 T6 职级能 cover 的复杂问题绝不是引入某某技术、重构哪块儿历史逻辑之类的,而是要能做出满足高并发、高性能下的锁设计(当时语境),不仅要了解问题成因,也要能认识到问题推进解决过程中的认知负荷与协同成本,保证落地不变形,以此拿到最终结果 - 降低交易成本。
最好的工程师一定有工匠精神,会在意自己的作品:我们的作品就是我们的代码。