Arts 第六十八周(8/3 ~8/9)

225 阅读10分钟

ARTS是什么?
Algorithm:每周至少做一个leetcode的算法题;
Review:阅读并点评至少一篇英文技术文章;
Tip:学习至少一个技术技巧;
Share:分享一篇有观点和思考的技术文章。

Algorithm

LC 442. Find All Duplicates in an Array

题目解析

输入数组包含值为 1 ~ n(n 是数组的长度)的元素。这里面有的元素出现了两次,有的元素出现了一次,找出那些出现两次的元素。最后,题目还加了实现上的限制条件,那就是不能使用额外的空间,而且时间必须在 O(n) 内。

首先还是考虑常规的解法,也就是没有时空上面的限制条件的话,你会如何来解题?如果没有空间上面的限制,那么我们完全可以使用哈希表来进行操作。也就是先用哈希表统计每个元素出现的个数,然后遍历一遍哈希表找出那些出现两次的元素即可,这么下来时间上也是满足条件的,但就是用到了额外的空间。还有一种思路是排序,排序后,相同的元素会紧挨在一起。在遍历一遍数组,根据元素的相邻元素来找出那些出现两次的元素。这么下来虽说没有用到额外的空间,但是因为有排序,时间并不在 O(n) 内。

上面的那两种思路都是常规的思路,一般有一点编程经验的人都能想得到。那要如何做才能既满足空间又满足时间呢?因为题目给的信息并不复杂,就是一个整形数组,那么我们就要借助整形数组本身来解题。数组本身其实就两个信息,一个是下标,另一个是元素的值。那么我们就需要构建下标和元素的值的联系。你可能会说,下标和元素的关系不是很直白吗?我们通过下标可以直接找到元素,下标就是元素的索引。对,没错,但是不知道你有没有发现数组里的元素的值的范围是在 [0, n] 之间的。这也就为反向从元素值本身出发定位到下标提供了可能,如果有两个元素都定位到了同一个下标,那说明什么? 说明了这个元素出现过两次,我们就需要记录下来。剩下的问题就是,“如何记录次数呢?”。因为数组里的元素要么出现了一次,要么出现了两次,其实不用记录完整的次数。每当遍历到一个元素,我们只需要标记一下这个元素对应的下标出现过即可,那么下一次另外一个元素定位到同样的下标就可以确定之前有遍历到相同的元素。那怎么标记才不会使元素失去其原本的意义呢?答案是直接取反。


参考代码

func findDuplicates(nums []int) []int {
    if len(nums) == 0 {
        return []int{}
    }

    results := []int{}

    for i := 0; i < len(nums); i++ {
    	// 获得元素 “匹配” 的下标
        // 因为有可能被取反过,所以这里取个绝对值
        index := abs(nums[i]) - 1

		// 当元素对应的下标的元素被取反
        // 说明该元素之前出现过,需要记录
        if nums[index] < 0 {
            results = append(results, index + 1)
        }

		// 标记,取反
        nums[index] = -nums[index]
    }

    return results;
}

func abs(a int) int {
    if a < 0 {
        return -a
    }

    return a
}

Review

Ideal REST API design

文章从各个方面讲述一个问题,“什么样的 REST API 设计才是合理的?” 相信你一定听说过,也多多少少接触过 REST API。但知道和会设计还是两码事,读了这篇文章后,可以了解如何去评判一个 REST API 设计的好坏,文章提到了几个方面,一起来看看:

  • API 的格式

    一般来说我们习惯用 JSON 格式来传输 REST API 的信息。但是不要忘了 XML 同样也是一种传输数据的格式。JSON 相对来说较为轻便,也比较容易上手。但是 XML 在这里也有一席之位,特别是应对相对复杂的场景,另外,XML 也是 SOAP 来传输信息的默认格式。

  • 返回部分结果

    你设计的 API 需要做到能返回部分结果,而不是全部。比如一个用户有很多的信息,像是浏览记录,session 记录,account 信息等等。如果将这些信息统统返回,势必造成通信传输的浪费,还有就是操作数据库的时间的不必要消耗。因此,需要设计的 API 只返回当前用户需要用到的信息,不必要的信息尽量不返回。

  • 缓存

    缓存可以怎加请求的效率。这里有用户端的缓存以及服务器端的缓存。用户的那些不需要更改服务器数据的请求,完全可以考虑使用缓存。

  • 安全

    对于安全,作者提到了两点需要注意的地方。第一点就是任何的 Web API 都需要有 Access Key,大概来说就是对称加密和非对称加密了。有了 Key 之后,我们是可以对权限进行限定,但是这是不够的。还需要有权限的管理,比如说划分成,普通用户,管理员,以及超级管理员,只读权限。设置出权限后可以在一定的程度上保证系统的有序。

  • 版本

    随着不断的更新,API 版本的不断向前,之前的版本不能够被当前版本所覆盖,否则的话会对使用 API 的人造成不便。作者在这里推荐的是通过修改 URL 来指定访问不同的版本,因为这样相对来说清晰且简单。

  • 限制条件

    通过增加限制条件,我们可以限定单位时间内的请求数量,从而保证服务器不被高峰期的请求给冲垮。同时我们可以让高峰期请求的用户等待,而不是直接禁止他们访问。

  • 跟踪

    API 的请求最好是都记录在 log 中,这样出问题了可以方便排查。也可以方便和对统计,做数据分析之类的事情。

  • 文档

    API 最好是要有文档记录,方便使用的人知道你这个 API 有什么的功能,以及如何使用这个 API。

感觉上面说的这些东西都懂,但是想要设计出一个比较合理的 API,还是需要不少的经验积累的。但是现在至少是知道了如何去评判一个 API 的好坏,以及如何找出有那些不足的地方。


Tip

这周继续学习 Vim 有关的知识,这做个记录,也当作是回顾了。

Vim 的目录和插件管理

进入到 vim 中,输入 :echo $VIMRUNTIME 可以查看 vim 在系统上的安装目录。如果进入到安装目录,你可以看到一些子目录和 vim 文件。这些都是 vim 的支持文件和插件。当然,为了不对系统目录进行更改,我们可以在用户个人的 vim 目录下(Mac OS 下路径是 $HOME/.vim)进行配置和插件的管理。

这里推荐一款 Vim 的插件管理器,minpac,安装指令:

git clone https://github.com/k-takata/minpac.git ~/.vim/pack/minpac/opt/minpac

安装了这个插件管理器,以后安装对应的插件只需要更改 ~/.vimrc 文件加运行对应的指令即可。


Vim 下的几种常见编辑方式

  • 多文件管理

    Vim 支持一次性打开多个文件,这个也很简单。使用 vim 的时候后面接多个文件即可。这种模式下,可以借助下面的 vim 命令进行操作:

    • :args -> 显示当前文件列表
    • :args filename -> 用 args 后面的文件替换当前文件列表
    • :next(:n) -> 打开下一个文件
    • :Next(:N) -> 打开上一个文件
    • :first(:rewind) -> 回到列表中的第一个文件
    • :last -> 打开列表中的最后一个文件

这种模式下的一个应用场景就是 批量修改文件的同一个地方。比如说给所有的文件加上头注释,那么可以复制了头之后不断地运行 :n|normal ggP 来实现。

另外注意,跳转到下一个文件后,当前文件如果没有保存,则会有错误信息。你需要手动设置 :set autowrite 来让其跳转自动保存。

  • 缓冲区管理

比如在 vim 中用 :e filename 打开的文件是不会被多文件管理指令所查到的。但是可以备缓冲区的指令所查到,缓冲区管理指令和多文件管理指令很类似:

  • :buffers(:ls) -> 显示当前缓冲区列表
  • :buffer(:b) 编号 -> 跳转到指定的缓冲区
  • :bnext(:bn) -> 打开下一个缓冲区文件
  • :bNext(:bN) -> 打开上一个缓冲区文件
  • :bfirst(:brewind) -> 回到缓冲区中的第一个文件
  • :blast -> 打开缓冲区中的最后一个文件
  • :bdelete(:bd) 编号 -> 将指定编号的文件从缓冲区当中移除

另外,缓冲区当中的标识也需要知道一下: * %a -> 当前活跃的文件 * # -> 最近的缓冲区 * + -> 缓冲区已被修改

使用 <C-^> 可以在最近的两个缓冲区文件来回跳转。

  • 多窗口文件编辑

两个切分窗口的命令:

  • :split(:sp) -> 水平切分窗口
  • :vsplit(:vs) -> 竖直切分窗口

多窗口模式下的一些快捷键:

  • <C-W> 后加方向键 -> 实现窗口之间的跳转
  • <C-W>q -> 退出窗口
  • <C-W>s -> 等同于 :sp
  • <C-W>v -> 等同于 :vs

另外多窗口下的一个比较有用的场景就是比较 2 个文件的区别。你可以通过在命令行中输入 vimdiff 后接 2 个文件来对比。

  • 多标签页编辑

    这种应该是最常见的一种编辑模式。在 vim 中可以通过下列命令实现:

    • :tabs -> 展示当前列表
    • gt -> 切换到下一个标签
    • :tabnew -> 后面可以接文件名,表示打开一个新的标签页

    另外,快捷键 <C-W>T 可以把窗口转化成一个标签页(在多窗口模式下才适用)。

    说到多标签页编辑就不得不提 NERDTree 这个插件。只需要安装,然后简单的映射配置即可使用。配合上鼠标的设定,感觉简直就和 VS Code 类似的编辑器没有区别。


一些小技巧

  • 存盘退出快捷键 ZZ
  • vim 有时会显示 “Swap file “...” already exists!”,这里有两个原因
    • 上次编辑该文件时发生以外崩溃,导致没有保存

      这种情况下,可以按 r 恢复,根据需要保存后重新再打开文件按 d 删除。或者直接按 d 删除。

    • 该文件已经在另外一个会话框中打开

区别这两种情况可以直接看 PID 后面有没有 STILL RUNNING。有说明是情况 2,没有则说明是情况 1

  • 快捷键 q: -> 查看 vim 中的历史命令
  • Mac 终端下可以通过快捷键 <D-R> 来暂停发送鼠标事件(再按一次恢复),这样可以在设定了鼠标的前提下,比较方便的复制内容。如果是在 iTerm2 的 terminal 中,直接按 alt 就可以达到目的。
  • 有些时候,特别是对于低版本的 vim,没有办法识别是用户输入或者是粘贴,可能会导致粘贴过来的东西缩紧和格式变得比较奇怪。可以通过 :set paste 来解决这个问题,或者直接修改配置文件。
  • 在 vim 中无法使用 <C-V> 进行粘贴,可以尝试在正常模式下用 "+P 实现粘贴。

Share

这周读完了《被讨厌的勇气》这本书,来谈谈我们为什么会自卑,以及如何让自己充满正能量。

你缺少的只不过是前进的勇气