开篇先讲下什么是ARTS。
ARTS 源于极客时间《左耳听风》专栏组织的一个学习打卡活动,四个字母对应着四个行动准则:
- Algorithm:每周至少做一个 leetcode 的算法题
- Review:阅读并点评至少一篇英文技术文章
- Tip:学习至少一个技术技巧
- Share:分享一篇有观点和思考的技术文章
按照往期的活动小组来看,三个月下来,能坚持住的人寥寥无几,五百人最终只剩下个位数。可见,坚持是多么的困难。
过去的两周,我参加了一个跑步打卡活动 -- 咕咚的跑步公约。
14天中,至少按规划跑步12天,每天按时打卡。报名费用99元,完不成的后果是99元分摊给其他完成任务的跑者。人的本性是非常害怕损失,所以我就难得地坚持了下来。当然,另一个很大的原因是同事、家人都知道我这个报名,这也是为了不让他们看我的笑话。
这次我报名了一个新的打卡活动 -- ARTS,却有些不同,完不成没有什么金钱的损失,但是会失去和一群人一起学习的机会,这个损失的价值应该远远超过99元,期望自己这次也能坚持下来,先完成100天的打卡,借此养成良好的学习习惯、提升个人的能力。
回到正题,下面开始我的第一次打卡。
1 Algorithm
这周刷了两道数组相关的题。
1.1 First Missing Positive
Given an unsorted integer array, find the smallest missing positive integer.
Example 1:
Input: [1,2,0]
Output: 3
Example 2:
Input: [3,4,-1,1]
Output: 2
Example 3:
Input: [7,8,9,11,12]
Output: 1
Note:
Your algorithm should run in O(n) time and uses constant extra space.
解答:
这道题是 Hard 的难度系数,我自己也是百思不得其解,想不到空间复杂度是 O(1) 的方法。
我通过看耗子叔的题解,豁然开朗,便码出如下代码:
public int firstMissingPositive(int[] nums) {
if (nums.length == 0) {
return 1;
}
for (int i = 0; i < nums.length; i++) {
int val = nums[i];
while(val > 0 && val <= nums.length && nums[val - 1] != val) {
nums[i] = nums[val - 1];
nums[val - 1] = val;
val = nums[i];
}
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] != i + 1) {
return i +1;
}
}
return nums.length + 1;
}
复制代码
这个解法真的很妙,时间复杂度 O(n),空间复杂度是常量的空间。
我们需要有两点思考才可以找到以上解法:一是负数直接可以过滤,这个比较容易想到,另一点是,大于数组长度的数也可以过滤,这一点很难想到。基于以上两点,会较容易的想到:将剩下的数放在自己对应下标的位置,再一次遍历即可找到答案。
1.2 Kth Largest Element in an Array.
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
Example 1:
Input: [3,2,1,5,6,4] and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4
__Note: __
You may assume k is always valid, 1 ≤ k ≤ array's length.
解答:
寻找第 K 大数的问题,最经典的解法就是通过快速排序的方法,在每次取 partition 的时候,找到答案。
我仍记得快排的思想,但代码怎么写几乎全忘,通过回忆加分析,写出如下代码,虽然不够完善,但一次 AC。
时间复杂度是 O(nlogn) / 2,空间复杂度 O(1)
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public int getPartition(int[] nums, int start, int end) {
int choose = nums[start];
int i = start;
while(start <= end) {
while (start <= end && nums[start] <= choose) {
start ++;
}
while(start <= end &&nums[end] > choose) {
end --;
}
if (start < end) {
swap(nums, start, end);
}
}
swap(nums, i, end);
return end;
}
public int findKthLargest(int[] nums, int k) {
int start = 0;
int end = nums.length - 1;
int partition = getPartition(nums, start, end);
k = nums.length - k;
while (k != partition) {
if (k < partition) {
end = partition - 1;
partition = getPartition(nums, start, end);
} else {
start = partition + 1;
partition = getPartition(nums, start, end);
}
}
return nums[partition];
}
复制代码
2 Review
这周阅读的是 The Cloud Is Just Someone Else's Computer 。 作者本人很看好 mini PC 的发展,并通过自己的实践的例子来比较自己组装小型 PC 与购买云服务的价格。结果表明,以三年的时长计算,自己组装小型 PC 并购买服务器托管服务的价格是购买云服务的三分之一,而性能却几乎一样甚至更好。由此看来 mini PC 的在价格上优势很大。
当然,最后作者也说了,这种的使用场景是:当您需要几年的专用计算资源,则自己搭建个人小的私有云会便宜很多。
上面这么小的盒子,装下的配件都不可小觑,也算开了我的眼界。
- i7-8750h 2.2-4.1 Ghz, 6c / 12t
- 32GB DDR4 RAM
- 500GB NVMe SSD
不过我在淘宝上没搜到这样的盒子,都是大的机箱。国内好像也没有好的服务器托管服务商,随便一查四五千一年都好贵。美国在这方面的市场还是更加成熟的。
作者为了对比 mini PC 和云主机性能,就去花了160刀买了一个月的云主机。
I was curious about this, so I just spun up a new $160/month DO instance for a quick test.
这种专研精神值得学习,好奇心很轻松地打败了160刀,或者说,有钱真好。
3 Tip
自己工作中在清理测试机磁盘空间时,经常用如下命令找出大的目录或文件进行清理。
du -h --max-depth=1
上面命令的功能是输入当前目录各个子目录所使用的空间。
我只是以前随手上网一搜就用上了这条命令,但并没有系统的学习 du 命令。甚至不知道 -h 时什么作用。
嗯,一直就这么懒,懒到不去查一个命令的用法。今天打卡,就去查了,还了解了 du 命令的很多其他用法。其实,不应该因为打卡才去学,毕竟打卡只是一种形式,而学习是自己的事。
废话不多说,下面是 -h 的用法解释。
-h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)
至于, --max-depth=1 ,是因为直接用 du,会不论层级由深入浅的递归打印出所有的子目录。加上层级的限制,就可以只输出当前目下的一级子目录的大小了。
4 Share
分享一篇关于 Notion 团队起死回生的文章:
Design on a deadline: How Notion pulled itself back from the brink of failure