Arts 第八十一周(1/18 ~1/24)

157 阅读5分钟

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

Algorithm

LC 71. Simplify Path

题目解析

题意是让你简化一个 Unix 的命令行路径。比如 /a/./b/../../c/ 经过简化后会变成 /c

这道题目只需要特别的考虑两个特殊情况:.... 表示停留在当前目录,而 .. 则要退回到上一目录。因为这一点,很容易想到栈这个数据结构。实现逻辑也很简单,我们遇到一个目录就把该目录推入到栈中。如果是 .,我们则维持在当前目录,这里不需要操作栈。如果遇到 ..,我们从栈中丢弃一个元素。到最后,我们只需要根据栈中遗留的目录来构建最后的路径即可。

思路是非常的简单,但是这里有一些细节需要注意:

  • 到最后构建路径的时候,顺序是和栈弹出的顺序相反。为了解决这个问题,我们可以用数组代替栈,这样最后可以从头遍历,方便些。

  • 通过 / 拆分原路径会出现一些空字符串,比如 /home//foo/ 这里个例子。空字符串不属于路径,不能加到栈中。这算是一个特殊情况,需要考虑在内。

总的下来,时间和空间复杂度都为 O(n)

最后延伸一下。这道题目中的输入路径均为绝对路径(以 / 开头),但是在现实的 Unix 系统中也会有相对路径,比如 a/b/c。另外常用的特殊符号还有 ~。如果把这些东西都加上的话,再让你对其简化成绝对路径,难度会增加不少,而且会有很多的细节在里面。感兴趣的可以试试。


参考代码

public String simplifyPath(String path) {
    if (path == null || path.length() == 0) {
        return "";
    }

    String[] dirs = path.split("/");
    List<String> p = new ArrayList<>();

    for (String dir : dirs) {
        if (!dir.equals("")) {

            if (dir.equals("..") && p.size() != 0) {
                p.remove(p.size() - 1);
            }

            if (!dir.equals(".") && !dir.equals("..")) {
                p.add(dir);
            }
        }
    }

    StringBuilder sb = new StringBuilder();

    for (String d : p) {
        sb.append("/");
        sb.append(d);
    }

    return sb.length() == 0 ? "/" : sb.toString();
  }

Review

Pro Git CH10

这次算是把 Pro Git 给读完了。最后收尾的部分讲的还是 Git 底层实现的一些相关内容。

打包文件

之前我们介绍了 Git 中都是以 objects 的形式对数据进行存储,并且我们可以通过 SHA-1 快速地找到对应的文件位置。但是这里会有一个内存管理的问题,比如说你一开始提交了一个 commit,这个 commit 中添加了一个 22KB 大小的文件,git 会把这个 commit 的内容,也就是这个 22KB 的文件保存起来。过了一段时间,你需要对这个文件稍加改动,比如说在最后增加一行代码,仅仅是一行代码。但就是这么一点点的改动,Git 会重新创建一个 22KB 大小的 object 文件。

重新创建的这个文件其实是没有必要的,对此,git 底层也会有对应的优化。逻辑就是我们不需要一个文件存两次,只需要保存不一样的地方即可。Git 会自动地定期检查文件的相似性,然后对于那些类似的文件,只保存有区别的地方。时常你会在 objects 目录中看到类似下面的文件:

.git/objects/pack/pack-978e03944f5c581011e6998cd0e9e30000905586.idx
.git/objects/pack/pack-978e03944f5c581011e6998cd0e9e30000905586.pack

这里解释下上面的文件的由来,这个 *.pack 文件中存储的是所有移除的 object 文件的内容。因为有时 *.pack 会过于庞大,不利于查询。.idx 是它的一个索引,里面含有一些偏量值,帮助你快速地找到相应的内容。每次 Git 到 object 目录下查询的时候,如果根据 SHA-1 找不到相应的object 文件时,就会去到 *.pack 文件中找。

打包文件可以看作是 Git 对其底层空间的一个优化,当然这个优化也只有到空间或者文件足够大的时候才会被触发。

引用规则

引用规则主要用在和远端交互上,比如类似 fetch 或者 push 这样的指令。引用规则可以在 .git/config 目录下配置。大多数的默认配置如下:

[remote "origin"]
    url = https://github.com/schacon/simplegit-progit
    fetch = +refs/heads/*:refs/remotes/origin/*

这里的 origin 是远端的名称。变量 url 表示的是远端通信地址。下面的 +refs/heads/*:refs/remotes/origin/* 就是引用规范,这个引用规范在 git fetch 的时候被触发。+ 表示即使不是 fast-forward 也执行 fetch。这里会把远端路径 refs/heads/ 下的内容对应的拉到本地目录 .git/refs/remotes/origin 下来。

类似的,我们也可以设定像是 pushdelete 的引用规则,引用格式就是 <src>:<dst>。但是这里需要注意一个方向性,git fetch 是从远端到本地,远端是 <src>,本地是 <dst>。但是如果换作是 git push,则是反过来的,本地是 src,远端是 <dst>


Tip

这次来说说经常被提及的 摩尔定律

摩尔定律指的是,集成电路上可容纳的晶体管数目,约每隔 18 个月便会增加 1 倍。我们都知道芯片上的晶体管数目直接决定了芯片的性能,而计算机的核心就是芯片,因此芯片的发展直接决定了计算机的发展。在过去的半个世纪,半导体的发展都遵循了摩尔定律,目前的计算机,互联网,智能手机的发展都离不开摩尔定律的延续。

但摩尔定律说到底仅仅是一个规律,或是说基于现在的现象,对未来的一个推测。谁都不能保证摩尔定律可以一直延续下去。任何事物都会到达一个瓶颈,等到摩尔定律不那么奏效的时候,很可能计算机的发展会到达一个瓶颈期,会放慢前进的脚步。等到那个时候,或许需要有新的突破性的科技技术来支撑。而那个时候会不会出现其它的新的革命性的技术呢,比如量子计算机之类的。这些只能留给时间去考证了。


Share

这周读了李尚龙的 《三十岁,一切刚刚开始》

写写自己对 30 岁的看法

年龄只是一个数字