本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是LeetCode专题的第40篇文章,我们一起来看的是LeetCode中的71题Simplify Path,中文名是简化路径。

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

题意

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

在Unix系统下用/来分隔文件夹,比如/home/download/file.txt。在这个路径当中支持简单的运算,比如.表示当前文件夹。所以如果我们当前终端在download这个文件夹下,我们要访问file.txt文件,可以使用相对路径./file.txt即可。除此之外,还包括..操作。..表示当前文件夹的上层文件夹

比如如果我们在download文件夹下,当我们运行cd ..,那么我们就会返回到download文件夹的上层,也就是home文件夹下。我们是可以把..和.嵌入在文件路径中使用的。比如说/home/download/../download/file.txt也是合法的,中间由于我们嵌入了..所以会返回到download的上层也就是home,然后再进入download。虽然这样很费劲,但是是合法的。只要你愿意,可以不停地利用..回到上层,来回穿梭。

我们要返回的是这个路径简化之后的版本也就是:/home/download/file.txt

我们来看几个案例:

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"

题解

这题其实也是模拟题,不过相比之前我们做过的模拟题难度要小上很多。这道题的思路还是蛮明显的,由于存在..和.的操作,我们需要记录下来访问的路径,在..向上移动的时候把之前的文件夹抛弃掉。

举个例子,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(算法比赛一般不允许使用Python),三两行代码就可以轻松AC,而C++选手却需要些上百行代码来实现高精度计算,这显然是不公平的。所以acm比赛当中,出题人一定会尽量避免这种语言特性差异巨大的问题,大概这也是这题遭黑的原因吧。

这篇文章就到这里,如果喜欢本文,可以的话,请点个关注,给我一点鼓励,也方便获取更多文章。

1700人点反对的LeetCode问题,是因为太难了吗?的更多相关文章

  1. IT人35岁危机:到底是因为爱还是责任?

    互联网蚕食世界,未来属于IT人. 这是属于互联网的时代,每个人都是网络的弄潮儿,由于网络越来越被需要,互联网IT行业被推上了较高的位置,这也导致IT行业的就业环境火热,越来越多的人被IT行业广阔的发展 ...

  2. 前阿里CEO卫哲谈阿里创业经验:如何找人、找钱、找方向?(不同的阶段分别有:时间优先、金额优先、比例优先,不要做平台,太难)

    新浪科技李根 整理报道 卫哲现在是御嘉基金的创始合伙人,他另一个更加知名的身份是阿里巴巴(B2B)前CEO,在2006年到2011年的时间里,卫哲见证了阿里巴巴如何利用人才.资本和方向选择一路壮大. ...

  3. 【主席树】【bzoj2161】[hdu4348]

    #include<cstdio> #include<algorithm> #include<cstring> #define N 400000 using name ...

  4. react.js 你应知道的9件事

    React.js 初学者应该知道的 9 件事   本文假定你已经有了一下基本的概念.如果你不熟悉 component.props 或者 state 这些名词,你最好先去阅读下官方起步和手册.下面的代码 ...

  5. About me & OI这一年

    1 最近碰到一些 OIers 问我学 OI 的经历 回忆了一下,我做的第一道题是A+B Problem,时间:2018年2月15日 呀,正好一年了 2 我是来自 HB 的 OIer 一枚 现在高一,就 ...

  6. UNIX环境编程学习笔记(20)——进程管理之exec 函数族

    lienhua342014-10-07 在文档“进程控制三部曲”中,我们提到 fork 函数创建子进程之后,通常都会调用 exec 函数来执行一个新程序.调用 exec 函数之后,该进程就将执行的程序 ...

  7. [No000058]一口气读完一本英语书

    个人:"如何学好英语?"99个人会回答:"去,读英文原著." 然而这事儿吧,恐怕比坚持背单词还难.无论少侠们背过多少单词,最后都会败在生词太多.句子太难的坎儿上 ...

  8. 入门干货之Electron的.NET实现-Electron.NET

    0x01.Electron.NET 1.介绍 Electron是由Github上的一支团队和一群活跃贡献者维护.用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库. Elec ...

  9. Spring源码阅读笔记

    前言 作为一个Java开发者,工作了几年后,越发觉力有点不从心了,技术的世界实在是太过于辽阔了,接触的东西越多,越感到前所未有的恐慌. 每天捣鼓这个捣鼓那个,结果回过头来,才发现这个也不通,那个也不精 ...

随机推荐

  1. postman(介绍)

    Postman 界面介绍 一. 安装后首次打开 postman,会提示你是否需要登录,登录的话可以云端保存你的收藏及历史记录,不登陆不影响使用.   二. 进入后就是如下图所示的界面了.看到这么多按钮 ...

  2. unittest(执行用例)

    from selenium import webdriver from time import sleep import unittest#导入unittest库 import HTMLTestRun ...

  3. 自定义View实战

    PS:上一篇从0开始学自定义View有博友给我留言说要看实战,今天我特意写了几个例子,供大家参考,所画的图案加上动画看着确实让人舒服,喜欢的博友可以直接拿到自己的项目中去使用,由于我这个写的是demo ...

  4. HDU1300Pearls

    传送门 描述: 有几种不同的珍珠.每种珍珠都有它的单价.当然质量高的珍珠价格一定也是高的. 为了避免买家只买1个珍珠.就要求不论是买了多少个珍珠都是需要在购买数量上加10.之后乘上单价. 例如:买5个 ...

  5. Spring Boot 整合 Apache Dubbo

    Apache Dubbo是一款高性能.轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现. 注意,是 Apache Dubb ...

  6. Spring Cloud学习 之 Spring Cloud Ribbon(负载均衡器源码分析)

    文章目录 AbstractLoadBalancer: BaseLoadBalancer: DynamicServerListLoadBalancer: ServerList: ServerListUp ...

  7. 【Spark】Spark任务调度相关知识

    文章目录 准备知识 DAG 概述 shuffle 概述 SortShuffleManager 普通机制 bypass机制 Spark任务调度 流程 准备知识 要弄清楚Spark的任务调度流程,就必须要 ...

  8. 字节码编程,Javassist篇三《使用Javassist在运行时重新加载类「替换原方法输出不一样的结果」》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 通过前面两篇 javassist 的基本内容,大体介绍了:类池(ClassPool) ...

  9. 转载-git使用之忽略不需要上传的文件的几种方式

    在我们使用git 的时候通常会遇到一些问题,一些文件我创建了但是我并不想上传或者有些文件我修改了但是并不想上传(为了适应个自己的开发环境),但是在每次git status的时候总能看到它,不仅感到很心 ...

  10. Autojs - 用 JavaScript 实现自己的安卓手机自动化工具脚本

    我是风筝,公众号「古时的风筝」,一个不只有技术的技术公众号,一个在程序圈混迹多年,主业 Java,另外 Python.React 也玩儿的 6 的斜杠开发者. Spring Cloud 系列文章已经完 ...