美文网首页
# 388. Longest Absolute File Pat

# 388. Longest Absolute File Pat

作者: 微微笑的蜗牛 | 来源:发表于2020-02-22 14:58 被阅读0次

@(LeetCode)

问题描述

假设我们将文件系统抽象为如下的字符串表示方式。

栗 1:
"dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" 表示如下文件目录结构:

dir
    subdir1
    subdir2
        file.ext

其含义为:dir 包含两个子文件夹 subdir1subdir2subdir2 中包含一个文件 file.ext

栗 2:
"dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" 表示如下文件目录结构:

dir
    subdir1
        file1.ext
        subsubdir1
    subdir2
        subsubdir2
            file2.ext

其含义为:dir 包含两个子文件夹 subdir1subdir2,其中 subdir1 包含文件 file1.ext 和子文件夹 subsubdir1subdir2 包含一个子文件夹 subsubdir2subsubdir2 又包含一个文件 file2.ext

我们需要找出最长的文件路径长度。比如在第二个例子中,最长的路径为 dir/subdir2/subsubdir2/file2.ext,其长度为 32

给定一个以上格式的字符串,返回最长文件路径长度,如果不存在文件,则返回 0。要求时间复杂度为 O(n)n 为字符串的长度。

注意:

  • 文件名至少包含一个 . 和后缀。
  • 文件夹名不会包含 .

想看英文原文的戳这里

解题思路

题目的意思很明确,只需要求出文件路径最大长度。其目录层级以 \n\t... 来划分,可以看出 \t 的个数代表层级深度。

我的解法

思路

一开始想的挺复杂,想到了递归求解,因为这样的结构很容易陷入递归的思路。但是有时间复杂度的要求,决定了其不可能是以这种方式求解。

于是另外想思路。由于没有注意到是只需要求文件的最长路径,而不是文件夹/文件的最长路径,所以想的复杂了些,但是大致思路是相通的。

其实求总路径长度,只需要记下每级路径的长度,然后做加法求和即可。

其中平级的路径长度,永远记录的是当前层级。因为其是顺序排布,当平级的一个文件夹内部完全遍历完后,才会进行下一个平级文件夹的遍历。

在这个例子中:

dir
    subdir1
        file1.ext
        subsubdir1
    subdir2
        subsubdir2
            file2.ext

subdir1subdir2 是平级,并且是顺序遍历的,subdir1 在前, subdir2 在后。当遍历 subdir1 时,当前层级长度记录的是 subdir1 的长度;当遍历 subdir2 时,记录的则是 subdir2 的长度。

我们将路径层级记为从 0 开始。上述例子中,dir 代表 0 级,subdir1 代表 1 级,file.ext 代表 2 级。

记录的结果如下:

{
    0:3,
    1:7,
    2:9
}

当遇到文件 file1.ext 时,其路径总长度为,将 depth = [0,2] 的所有路径长度相加,再额外加上 / 的个数(为当前层级深度)即可。结果为 3+7+9+2

当遇到 subsubdir1 时,由于它是 2级目录,更新当前层级路径长度。结果为:

{
    0:3,
    1:7,
    2:10,
}

当遇到 subdir2 时,由于它是 1 级目录,只需更新 1 级目录的值。结果为:

{
    0:3,
    1:7,
    2:10
}

当遇到 file2.ext 时,它是 3 级目录,此时插入 3 级目录的路径长度。且其为文件,需要计算总的路径长度,方式同上:即 [0,3] 层级的路径长度之和 + / 个数。

{
    0:3,
    1:7,
    2:10,
    3:9
}

大体思路如上。

文件夹/文件的路径最大长度

顺便提一下,如果要求文件夹/文件的路径最大长度,那么需要判断当前路径是否为最深。

这种情况下需要求出紧接其后的层级。如果当前层级 >= 后面层级,则说明已经到底了。计算当前的路径长度,并与最大值进行比较即可。

比如 dir/subdir1,当前层级为 1,其后的层级 dir/subdir1/file1.ext2,说明当前还不是最深的路径。

又比如 dir/subdir1/file1.ext,当前层级为 2,其后的层级 dir/subdir1/subsubdir12,说明达到最深。

代码实现

在具体实现中,我没有使用类似 split 的方法,完全是一个个字符从头到尾的遍历,手动计算层级深度。同时加上了当前是否是最深路径的判断,但在这题中,其实只用判断是否为文件即可。

代码如下。

var lengthLongestPath = function(input) {
    if (input && input.length > 0) {
        const length = input.length

        let maxLength = 0

        // 当前路径深度
        let curDepth = 0

        // 记录每层路径的文件名长度
        let map = new Map()

        let i = 0
        while (i < length) {
            // 取出文件名
            let name = ''
            while (i < length && input[i] !== '\n') {
                name += input[i]
                i += 1
            }

            // 记录当前深度对应的文件名长度
            if (name.length > 0) {
                map.set(curDepth, name.length)
            }

            // 计算下一个路径深度
            i += 1

            let depth = 0
            while (i < length && input[i] === '\t') {
                depth += 1
                i += 1
            }

            // 下一个深度比当前的小,说明当前路径达到最深,计算路径长度。且为文件
            if ((depth <= curDepth) && (name.includes('.'))) {
                // 从 0 到 lastDepath
                let j = 0
                let sum = 0
                while (j <= curDepth) {
                    if (map.has(j)) {
                        sum += map.get(j)
                    }

                    j += 1
                }

                // 加上 '/' 个数
                sum += curDepth

                if (sum > maxLength) {
                    maxLength = sum
                }
            }

            curDepth = depth
        }

        return maxLength
    }

    return 0
};

但这种方法,需要每次都需要从头遍历求和,效率还不算好。

优化解法

为了省去求和的过程,我们可以将存储的信息稍微变一下。之前存的是当前层级文件/文件夹的长度,但如果存为当前路径的总长度,就不需要计算了。

核心计算思想为:

当前层级的长度 = 上一层级的路径长度 + 当前文件/文件夹长度 + 1。

代码变动比较小,只贴出改动的部分:

// 记录总长度
if (curDepth >= 1) {
    // 上一层的路径长度+文件长度+'/'
    map.set(curDepth, map.get(curDepth - 1) + name.length + 1)
} else {
    map.set(curDepth, name.length)
}

// 下一个深度比当前的小,说明当前路径达到最深,计算路径长度,且为文件。或者仅仅判断为文件。
if ((depth <= curDepth) && (name.includes('.'))) {

    let sum = map.get(curDepth)
    if (sum > maxLength) {
        maxLength = sum
    }
}

点此可查看完整代码

其他解法

discuss 区查看解法,发现有一种使用栈的方法。

其主要思想是,用栈来保存每一层级的总路径长度。其主要目的是用来找到上一层级的路径长度,当前层级的长度计算方式跟第二种解法一样。

如何找上一层级的路径长度时,其方式也比较简单,如下:

如果栈顶元素的层级 >= 当前元素层级,则循环出栈,直至找到其上一个层级,然后计算当前路径长度后入栈。否则,直接入栈。也就是说,栈中保存的始终为当前遍历路径上的各个层级路径长度。

若以代码实现的方式来表述,则为如下所述。
因为栈的长度 - 1 代表了栈顶元素的层级,判断条件变为: 当前的层级 <= 栈的长度 - 1,则循环出栈,否则,直接入栈。

比如:

dir
    subdir1
    subdir2
        file.ext

在遍历 dirsubdir1 后,栈中的元素为 [dir, dir/subdir1]。这里是为了方便理解,将元素写成了路径,其实元素是路径的总长度。

在遍历 subdir2 时,当前层级为 1,这时需要找到 0 级目录才能拼成完整的路径。而此时栈顶是 1 级,那么就需要将其出栈,这样栈顶变为了 0 级目录 [dir]。再将计算结果入栈,变为 [dir, dir/subdir2]

同样的道理,如果当前层级为 2,上一层级为 1,而假设此时栈中有 4 个元素
[dir, dir/subdir1, dir/subdir1/ss1, dir/subdir1/ss1/ss2],那么需要连续出栈 2 次,计算后再将结果入栈。

具体可查看该解法

相关文章

网友评论

      本文标题:# 388. Longest Absolute File Pat

      本文链接:https://www.haomeiwen.com/subject/rmylqhtx.html