作這題的時候對比了一下以前的代碼,爲何差距這麼大?

點擊上方藍字,和我一塊兒學技術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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索