作这题的时候对比了一下以前的代码,为何差距这么大?

点击上方蓝字,和我一块儿学技术web




今天是LeetCode专题的第40篇文章,咱们一块儿来看的是LeetCode中的71题Simplify Path,中文名是简化路径。算法

这题的难度是Medium,经过率是1/3左右,也是一道踩多捧少的题,一共有737个点赞,1703个反对。老实讲我以为反对得不冤,我先卖个关子,等会来详细聊聊它为何会被踩。微信


题意


题目会给定一个字符串,表示一个Unix系统下的文件路径,这个路径当中会包含一些路径的计算, 要求咱们返回简化以后的结果。app

在Unix系统下用/来分隔文件夹,好比/home/download/file.txt。在这个路径当中支持简单的运算,好比.表示当前文件夹。因此若是咱们当前终端在download这个文件夹下,咱们要访问file.txt文件,可使用相对路径./file.txt便可。除此以外,还包括..操做,..表示当前文件夹的上层文件夹编辑器

好比若是咱们在download文件夹下,当咱们运行cd ..,那么咱们就会返回到download文件夹的上层,也就是home文件夹下。咱们是能够把..和.嵌入在文件路径中使用的。好比说/home/download/../download/file.txt也是合法的,中间因为咱们嵌入了..因此会返回到download的上层也就是home,而后再进入download。虽然这样很费劲,可是是合法的。只要你愿意,能够不停地利用..回到上层,来回穿梭。ui

咱们要返回的是这个路径简化以后的版本也就是:/home/download/file.txtspa

咱们来看几个案例:.net

Input: "/home/"
Output: "/home"
Explanation: Note that there is no trailing slash after the last directory name.

Input: "/../"
Output: "/"
Explanation: Going one level up from the root directory is a no-op, as the root level is the highest level you can go.

Input: "/a/../../b/../c//.//"
Output: "/c"
3d



题解


这题其实也是模拟题,不过相比以前咱们作过的模拟题难度要小上不少。这道题的思路仍是蛮明显的,因为存在..和.的操做,咱们须要记录下来访问的路径,在..向上移动的时候把以前的文件夹抛弃掉。code

举个例子,a/b/../b/d/e

咱们在b以后使用了..回到了a,而后咱们再次进入b往下。显然这里因为..致使b在路径当中出现了两次,这是多余的。咱们须要在..回到上层的时候把b抛弃掉。对于.操做来讲,因为它就表示当前路径,因此对于答案并不会影响,咱们直接忽略它的存在便可。

理解了这个思路以后,实现是很是简单的,咱们只须要根据/将字符串分段。每一段当中除了.和..以外就是文件夹,咱们用一个list去存储从上到下的通过的文件夹。最后用/将它们join在一块儿便可,惟一须要注意的是,当咱们已经到了顶层的时候,若是咱们继续执行..并不会报错,而是会停留在原地。因此咱们须要特殊判断这种状况,除此以外就几乎没有难度了。

class Solution:
    def simplifyPath(self, path: str) -> str:
        folders = []
        # 按照/分割
        fs = path.split("/")
        for f in fs:
            # .直接跳过便可,不会影响结果
            if f == '.':
                continue
            # 若是是..须要判断是否在顶层
            # 不在顶层的话抛弃掉最后插入的文件夹
            if f == '..':
                if len(folders) > 0:
                    folders.pop()
            elif f != '':
                folders.append(f)
                
        return '/' + '/'.join(folders)

代码很是简单,只有10行左右。


总结


到这里,关于题解的部分就结束了。

咱们回到标题当中的问题,为何我会有这样的感觉呢?是由于这道题我作过两次,上一次作的时候用的是C++。因为C++的string类型不支持split,因此我须要本身进行split处理。整个的计算过程很是复杂,我放一下C++的AC代码你们本身感觉一下就知道了,简直不是一个次元的。

class Solution {
public:
    vector<string> split(string & path) {
        vector<string> vt;
        string cur = "";
          // 遍历全部字符
        for (int i = 0; i < path.length(); i++) {
              // 若是是/ 说明须要把以前的内容放入vector
            if (path[i] == '/') {
                  // 若是是空或者是.就跳过,由于.没有意义
                if (cur != "" && cur != ".") {
                    vt.push_back(cur);
                }
                cur = "";
            }else cur = cur + path[i];
        }
          // 要注意最后遗留的字符串
        if (cur != "" && cur != ".") vt.push_back(cur);
        return vt;
    }

    string simplifyPath(string path) {
        vector<string> dirs = split(path);
        string ret = "";
          // 存储文件的结构
        vector<string> paths;
        for (string str : dirs) {
              // 若是是.. 则返回上级
            if (str == "..") {
                if (paths.size() > 0) {
                    paths.pop_back();
                }
              // 不然则填入vector,表示合法
            }else paths.push_back(str);
        }
        for (string str : paths) ret = ret + "/" + str;
        if (ret == ""return "/";
        return ret;
    }
};

我说这些的重点并非吐槽C++这门语言有多么落后,或者是证实Python有多么强大。不一样的语言有不一样的诞生背景,也有不一样的强项,这个是很天然的。这题最主要的问题是不该该出这种由于语言自己的特性带来巨大差别的问题,在正规比赛当中出这样的问题必定是会被疯狂吐槽的。

举个例子,好比Java当中有大整数类BigInter,能够用来代替高精度算法来处理超过int64范围的大整数。若是有出题人出了一道很是复杂的大整数问题,那么使用Java的选手使用BigInter,三两行代码就能够轻松AC,而C++选手却须要些上百行代码来实现高精度计算,还不必定能作对。在极度激烈的比赛当中,一题的差距直接关系有无获奖。因此在acm比赛当中,出题人必定会尽可能避免这种语言特性差别巨大的问题,大概这也是这题遭黑的缘由吧。

这篇文章就到这里,若是喜欢本文,能够的话,请点个关注,给我一点鼓励,也方便获取更多文章。

本文分享自微信公众号 - TechFlow(techflow2019)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索