記一道算法題解題思路,字符串解析成標題結構

這是我參與更文挑戰的第4天,活動詳情查看: 更文挑戰javascript

寫做背景

那是一個月黑風高,伸手不見五指的(並非由於我黑)的夜晚,個人小夥伴給我發了一道算法題,題目以下。html

將題中的字符串text解析爲一個標題樹。java

const text = ` - 章節一 - 標題一 - 標題二 - 小節四 - 章節二 - 標題一 - 標題二 - 標題三 - 子標題四 - 小節五 `;
class Node {
  constructor({ value, level }) {
    this.value = value;
    this.level = level;
    this.children = [];
    // hint: 也可在數據結構中增長 this.parent 節點輔助解析
  }
}

const tree = parseTree(text);
// [
// Node { value: "章節一", children: [ Node, Node ], level: 1 },
// Node { value: "章節二", children: [ Node, Node ], level: 1 } 
// ]

function parseTree(text){
    // code
}
複製代碼

在細細思索以後,砸吧砸吧嘴,這會不會我小夥伴釣魚,他本身寫不出來因此讓我來幫他寫?算法

這玩意和掘金的文章閱讀頁裏,右側的目錄不能說是很像,只能說是如出一轍。瀏覽器

image.png

雖然有段時間沒摸算法了,可是我憑直覺認爲,這道題,我ok的。markdown

準備工做

準備工做其實沒啥,先建立好一個html文件,而後把上面的基礎代碼複製過來,而後在瀏覽器打開下。數據結構

而後走到廚房,拿起左手第三個罐子,從裏面掏出一包黑色的,新到的茶包,泡一杯熱茶,反正接下來一段時間也睡不着, 那就讓本身更清醒點。post

嘻嘻嘻,皮一下,該有的準備工做仍是要的, 我這該死的儀式感。ui

實現思路

最簡單的實現思路很簡單,就是肉眼可見的兩個空格爲一個level, 即沒有雙空格前綴是level 1,一個雙空格,就是level 2, 以此類推。this

version 1.0

function parseTree(text){
    let parent = new Node({value: '', level: 0}); // 建立一個根節點,方便更新父節點
    let level = 1; // 用於後續標題的層級遞進
    let flag = ' '; // 層級標識,默認是兩個空格
    
    for(let item of text.split('\n')){ // 先將字符串按換行切割,用於遍歷
        if(!item) continue // 若是當前項爲空,直接跳過
        // 遞歸肯定當前項具體在第幾層
        // 每次遞歸處理的都是確認當前項的層級,
        // 因此不用小心斷層會識別不了狀況。
        checkFlag(item, level, parent, flag)
    }
    
    /** * @params item 當前須要處理的項 * @params level 當前項的默認level * @params parent 當前項的父節點 * @params flag 用於計算當前項的level */
    function checkFlag(item, level, parent, falg){
        if(item.indexOf(flag) !== -1){ // 若是當前項存在雙空格前綴,標識不是當前level
            let len = parent.children.length;
             //從新確認父級 若是當前父級存在子節點,那麼將本身設爲新父級
            parent = len ? parent.children[length - 1] : parent;
            // 這裏注意,無論是否更新父級,當前層級須要level + 1
            checkFlag(item.substr(flag.length, item.length), level + 1, parent, flag)
        } else {
            // 這裏的item.substr是爲了祛除字符串中的'- '前綴
            parent.children.push(new Node({value: item.subnstr(2, item.length), level: level}))
        }
    }
    
    return parent.children;
}
複製代碼

具體實現

其實這裏對當時的場景作了下PS, 我並無直接去思考固定標誌爲雙空格的狀況,由於考慮到前綴是有可能變化的,我想一步到位,寫一個能自動識別標題前綴的,因此當時着實糾結了一番。

還和朋友吵了一段時間, 他想讓我直接按雙空格我實現,我偏不, 我以爲那樣太簡單了,我想加個level, 如下是我直接實現的,邏輯部分和上面的代碼一致,只是加了識別前綴的部分。

首先,第一步,改造默認的標題字符串

這裏假設應用於掘金的文章Markdown解析,顯不出力內容節點,只看標題節點,大體以下

const text = ` #- 章節一 ##- 標題一 ##- 標題二 ###- 小節四 #- 章節二 ##- 標題一 ##- 標題二 ##- 標題三 ###- 子標題四 ####- 小節五 `;
複製代碼

那麼這時候他的標題標識符就變了,固然在上面的代碼裏,修改flag的默認值就能解決了,可是這不是我想要的。

我想讓他本身識別,因此我但願在他碰到第一個標題時,去自動獲取標題的標識符。

function parseTree(text){
    let parent = new Node({value: '', level: 0}); // 建立一個根節點,方便更新父節點
    let level = 1; // 用於後續標題的層級遞進
    let flag = null; // 層級標識,默認爲空
    
    for(let item of text.split('\n')){ // 先將字符串按換行切割,用於遍歷
        if(!item) continue // 若是當前項爲空,直接跳過
        
        // 遇到首個非一級標題時,確認段落標識
        if(!flag && item.indexOf('- ') !== 0){
            flag = item.substr(0, item.indexOf('- '))
        }
        
        // 調用遞歸用於確認層級
        checkFlag(item, level, parent, flag)
    }
    
    /** * @params item 當前須要處理的項 * @params level 當前項的默認level * @params parent 當前項的父節點 * @params flag 用於計算當前項的level */
    function checkFlag(item, level, parent, falg){
        if(item.indexOf(flag) !== -1){ // 若是當前項存在雙空格前綴,標識不是當前level
            let len = parent.children.length;
             //從新確認父級 若是當前父級存在子節點,那麼將本身設爲新父級
            parent = len ? parent.children[length - 1] : parent;
            // 這裏注意,無論是否更新父級,當前層級須要level + 1
            checkFlag(item.substr(flag.length, item.length), level + 1, parent, flag)
        } else {
            // 這裏的item.substr是爲了祛除字符串中的'- '前綴
            parent.children.push(new Node({value: item.subnstr(2, item.length), level: level}))
        }
    }
    
    return parent.children;
}
複製代碼
相關文章
相關標籤/搜索