和很多朋友一样,我对于算法的观点也经历了如下阶段:

  1. 哇!算法好牛逼好神奇啊;
  2. 怎么这么难,算法导论看不懂啊尼玛;
  3. .NET、QT、Spring神马的好爽好有用,算法除了面试考试外就是没用的;
  4. 然后发现,在一些实际项目中,确实会有一些问题需要使用算法思想才能更好地解决。这些问题可能不会用到高深的复杂,但是比起单纯的算法题,需要处理更多的细节,也需要更多的工程考量。

背景

这个问题的来源是暑期实习时候碰到的一个需求:项目的目的是对某个庞大的系统进行性能分析,该系统运行的过程分为若干个task,每执行一个task会写一条日志,记录执行的起止时间等信息。task之间是有从属关系的,即某些task是另一些的子task,因此在写日志数据库的时候有一列为indent level,表示该行日志的缩进。

在进行性能分析的过程中需要对不同的粒度进行group by,因此首先需要根据日志中task的indent level抽取出所有task的层级结构。

抽象

当时就觉得,这个问题抽象一下真的很时候用来当做面试的热身算法题。输入为一个对象序列,每个对象包含一个indent level(出于简化,可以认为indent level有一个最大值),要求给出对象的层次结构,复杂度为O(n)。例如一个序列S为{A:1, B:2, C:3, B:2, D:3, E:2},则期望输出应该如下:

A
 --B
    --C
    --D
 --E

解法

考虑到动态构建层次结构时需要知道当前对象的父对象,因此可用一个哈希表或数组记录所有indent level所对应的当前父对象,线性扫描输入序列,处理对象object时,得到object.indent_level,然后查看哈希表,得到object.indent_level - 1的对象名,即为X的父节点。

map = {}
map[0] = 'NIL'
set = ()
for object in object_sequence:
    if object.name not in set:
        object.parent = map[object.indent_level - 1]
		map[object.indent_level] = object.name
		set.add(object_name)

总结

虽然这个思路很简单,但是其实大量面试中的算法题都可以套用该思路:哈希表或变量记录信息,每次迭代进行更新。当然比起这简单的算法思路,实际操作中有很多别的问题要处理:脏数据处理、结果的保存方式选择等等。再回到开头的话题,无论是算法还是实际工程,都是有意思有深度可探索的东西,然后重点是如何运用它们来解决实际问题,这其实和争论那种语言是最优秀的语言是一回事儿。其实,萨特早就回答过这个问题了。