javascript
JS树结构操作:查找、遍历、筛选、树结构和列表结构相互转换
經(jīng)常有同學問樹結(jié)構(gòu)的相關(guān)操作,也寫了很多次,在這里總結(jié)一下 JS 樹形結(jié)構(gòu)一些操作的實現(xiàn)思路,并給出了簡潔易懂的代碼實現(xiàn)。
本文內(nèi)容結(jié)構(gòu)大概如下:
JS樹結(jié)構(gòu)相關(guān)操作1
遍歷樹結(jié)構(gòu)
1. 樹結(jié)構(gòu)介紹
JS中樹結(jié)構(gòu)一般是類似于這樣的結(jié)構(gòu):
let?tree?=?[{id:?'1',title:?'節(jié)點1',children:?[{id:?'1-1',title:?'節(jié)點1-1'},{id:?'1-2',title:?'節(jié)點1-2'}]},{id:?'2',title:?'節(jié)點2',children:?[{id:?'2-1',title:?'節(jié)點2-1'}]} ]為了更通用,可以用存儲了樹根節(jié)點的列表表示一個樹形結(jié)構(gòu),每個節(jié)點的children屬性(如果有)是一顆子樹,如果沒有children屬性或者children長度為0,則表示該節(jié)點為葉子節(jié)點。
2. 樹結(jié)構(gòu)遍歷方法介紹
樹結(jié)構(gòu)的常用場景之一就是遍歷,而遍歷又分為廣度優(yōu)先遍歷、深度優(yōu)先遍歷。
其中深度優(yōu)先遍歷是可遞歸的,而廣度優(yōu)先遍歷是非遞歸的,通常用循環(huán)來實現(xiàn)。深度優(yōu)先遍歷又分為先序遍歷、后序遍歷,二叉樹還有中序遍歷,實現(xiàn)方法可以是遞歸,也可以是循環(huán)。
JS遍歷樹結(jié)構(gòu)廣度優(yōu)先和深度優(yōu)先的概念很簡單,區(qū)別如下:
深度優(yōu)先,訪問完一顆子樹再去訪問后面的子樹,而訪問子樹的時候,先訪問根再訪問根的子樹,稱為先序遍歷;
先訪問子樹再訪問根,稱為后序遍歷。
廣度優(yōu)先,即訪問樹結(jié)構(gòu)的第n+1層前必須先訪問完第n層
3. 廣度優(yōu)先遍歷的實現(xiàn)
廣度優(yōu)先的思路是,維護一個隊列,隊列的初始值為樹結(jié)構(gòu)根節(jié)點組成的列表,重復(fù)執(zhí)行以下步驟直到隊列為空:
取出隊列中的第一個元素,進行訪問相關(guān)操作,然后將其后代元素(如果有)全部追加到隊列最后。
下面是代碼實現(xiàn),類似于數(shù)組的forEach遍歷,我們將數(shù)組的訪問操作交給調(diào)用者自定義,即一個回調(diào)函數(shù):
//?廣度優(yōu)先 function?treeForeach?(tree,?func)?{let?node,?list?=?[...tree]while?(node?=?list.shift())?{func(node)node.children?&&?list.push(...node.children)} }很簡單吧,~,~
用上述數(shù)據(jù)測試一下看看:
輸出,可以看到第一層所有元素都在第二層元素前輸出:
>?節(jié)點1 >?節(jié)點2 >?節(jié)點1-1 >?節(jié)點1-2 >?節(jié)點2-14. 深度優(yōu)先遍歷的遞歸實現(xiàn)
先序遍歷,三五行代碼,太簡單,不過多描述了:
function?treeForeach?(tree,?func)?{tree.forEach(data?=>?{func(data)data.children?&&?treeForeach(data.children,?func)?//?遍歷子樹}) }后序遍歷,與先序遍歷思想一致,代碼也及其相似,只不過調(diào)換一下節(jié)點遍歷和子樹遍歷的順序:
function?treeForeach?(tree,?func)?{tree.forEach(data?=>?{data.children?&&?treeForeach(data.children,?func)?//?遍歷子樹func(data)}) }測試:
treeForeach(tree,?node?=>?{?console.log(node.title)?})輸出:
//?先序遍歷 >?節(jié)點1 >?節(jié)點1-1 >?節(jié)點1-2 >?節(jié)點2 >?節(jié)點2-1//?后序遍歷 >?節(jié)點1-1 >?節(jié)點1-2 >?節(jié)點1 >?節(jié)點2-1 >?節(jié)點25. 深度優(yōu)先循環(huán)實現(xiàn)
先序遍歷與廣度優(yōu)先循環(huán)實現(xiàn)類似,要維護一個隊列,不同的是子節(jié)點不追加到隊列最后,而是加到隊列最前面:
function?treeForeach?(tree,?func)?{let?node,?list?=?[...tree]while?(node?=?list.shift())?{func(node)node.children?&&?list.unshift(...node.children)} }后序遍歷就略微復(fù)雜一點,我們需要不斷將子樹擴展到根節(jié)點前面去,(艱難地)執(zhí)行列表遍歷,遍歷到某個節(jié)點如果它沒有子節(jié)點或者它的子節(jié)點已經(jīng)擴展到它前面了,則執(zhí)行訪問操作,否則擴展子節(jié)點到當前節(jié)點前面:
function?treeForeach?(tree,?func)?{let?node,?list?=?[...tree],?i?=??0while?(node?=?list[i])?{let?childCount?=?node.children???node.children.length?:?0if?(!childCount?||?node.children[childCount?-?1]?===?list[i?-?1])?{func(node)i++}?else?{list.splice(i,?0,?...node.children)}} }2
列表和樹結(jié)構(gòu)相互轉(zhuǎn)換
1. 列表轉(zhuǎn)為樹
列表結(jié)構(gòu)通常是在節(jié)點信息中給定了父級元素的id,然后通過這個依賴關(guān)系將列表轉(zhuǎn)換為樹形結(jié)構(gòu),列表結(jié)構(gòu)是類似于:
let?list?=?[{id:?'1',title:?'節(jié)點1',parentId:?'',},{id:?'1-1',title:?'節(jié)點1-1',parentId:?'1'},{id:?'1-2',title:?'節(jié)點1-2',parentId:?'1'},{id:?'2',title:?'節(jié)點2',parentId:?''},{id:?'2-1',title:?'節(jié)點2-1',parentId:?'2'} ]列表結(jié)構(gòu)轉(zhuǎn)為樹結(jié)構(gòu),就是把所有非根節(jié)點放到對應(yīng)父節(jié)點的chilren數(shù)組中,然后把根節(jié)點提取出來:
function?listToTree?(list)?{let?info?=?list.reduce((map,?node)?=>?(map[node.id]?=?node,?node.children?=?[],?map),?{})return?list.filter(node?=>?{info[node.parentId]?&&?info[node.parentId].children.push(node)return?!node.parentId}) }這里首先通過info建立了id=>node的映射,因為對象取值的時間復(fù)雜度是O(1),這樣在接下來的找尋父元素就不需要再去遍歷一次list了,因為遍歷尋找父元素時間復(fù)雜度是O(n),并且是在循環(huán)中遍歷,則總體時間復(fù)雜度會變成O(n^2),而上述實現(xiàn)的總體復(fù)雜度是O(n)。
2. 樹結(jié)構(gòu)轉(zhuǎn)列表結(jié)構(gòu)
有了遍歷樹結(jié)構(gòu)的經(jīng)驗,樹結(jié)構(gòu)轉(zhuǎn)為列表結(jié)構(gòu)就很簡單了。
不過有時候,我們希望轉(zhuǎn)出來的列表按照目錄展示一樣的順序放到一個列表里的,并且包含層級信息。
使用先序遍歷將樹結(jié)構(gòu)轉(zhuǎn)為列表結(jié)構(gòu)是合適的,直接上代碼:
//遞歸實現(xiàn) function?treeToList?(tree,?result?=?[],?level?=?0)?{tree.forEach(node?=>?{result.push(node)node.level?=?level?+?1node.children?&&?treeToList(node.children,?result,?level?+?1)})return?result }//?循環(huán)實現(xiàn) function?treeToList?(tree)?{let?node,?result?=?tree.map(node?=>?(node.level?=?1,?node))for?(let?i?=?0;?i?<?result.length;?i++)?{if?(!result[i].children)?continuelet?list?=?result[i].children.map(node?=>?(node.level?=?result[i].level?+?1,?node))result.splice(i+1,?0,?...list)}return?result }3
樹結(jié)構(gòu)篩選
樹結(jié)構(gòu)過濾即保留某些符合條件的節(jié)點,剪裁掉其它節(jié)點。
一個節(jié)點是否保留在過濾后的樹結(jié)構(gòu)中,取決于它以及后代節(jié)點中是否有符合條件的節(jié)點。
可以傳入一個函數(shù)描述符合條件的節(jié)點:
function?treeFilter?(tree,?func)?{//?使用map復(fù)制一下節(jié)點,避免修改到原樹return?tree.map(node?=>?({?...node?})).filter(node?=>?{node.children?=?node.children?&&?treeFilter(node.children,?func)return?func(node)?||?(node.children?&&?node.children.length)}) }4
樹結(jié)構(gòu)查找
1. 查找節(jié)點
查找節(jié)點其實就是一個遍歷的過程,遍歷到滿足條件的節(jié)點則返回,遍歷完成未找到則返回null。
類似數(shù)組的find方法,傳入一個函數(shù)用于判斷節(jié)點是否符合條件,代碼如下:
function?treeFind?(tree,?func)?{for?(const?data?of?tree)?{if?(func(data))?return?dataif?(data.children)?{const?res?=?treeFind(data.children,?func)if?(res)?return?res}}return?null }2. 查找節(jié)點路徑
略微復(fù)雜一點,因為不知道符合條件的節(jié)點在哪個子樹,要用到回溯法的思想。
查找路徑要使用先序遍歷,維護一個隊列存儲路徑上每個節(jié)點的id,假設(shè)節(jié)點就在當前分支,如果當前分支查不到,則回溯。
function?treeFindPath?(tree,?func,?path?=?[])?{if?(!tree)?return?[]for?(const?data?of?tree)?{path.push(data.id)if?(func(data))?return?pathif?(data.children)?{const?findChildren?=?treeFindPath(data.children,?func,?path)if?(findChildren.length)?return?findChildren}path.pop()}return?[] }用上面的樹結(jié)構(gòu)測試:
let?result?=?treeFindPath(tree,?node?=>?node.id?===?'2-1') console.log(result)輸出:
["2","2-1"]3. 查找多條節(jié)點路徑
思路與查找節(jié)點路徑相似,不過代碼卻更加簡單:
function?treeFindPath?(tree,?func,?path?=?[],?result?=?[])?{for?(const?data?of?tree)?{path.push(data.id)func(data)?&&?result.push([...path])data.children?&&?treeFindPath(data.children,?func,?path,?result)path.pop()}return?result }5
結(jié)語
對于樹結(jié)構(gòu)的操作,其實遞歸是最基礎(chǔ),也是最容易理解的。
遞歸本身就是循環(huán)的思想,所以可以用循環(huán)來改寫遞歸。
熟練掌握了樹結(jié)構(gòu)的查找、遍歷,應(yīng)對日常需求應(yīng)該是綽綽有余啦。
收外國男騙中國妹子的炮?天朝竟有這樣一幫「女權(quán)組織」 2018-03-19 INSIGHT視界 From 酷玩實驗室 微信號:coollabs 其實我讀書的時候 也曾經(jīng)想過做一個女權(quán)主義者 但是后來發(fā)生了一些事情 讓我選擇了放棄 簡單來說是這么一個事情:我發(fā)現(xiàn) 女權(quán)對于一些中國人來說是信仰 但是對另一些中國人來說是生意 所謂的“偽女權(quán)”“女權(quán)癌” 大概就是這么回事 盡管早就有這樣的思想準備 但讓我沒想到的是 這兩天,知乎上曝光了一件大事 還是讓我三觀震碎 我沒想到,這些“偽女權(quán)” 竟然已經(jīng)形成了黑色產(chǎn)業(yè)鏈 讓人細思恐極—— 國內(nèi)竟然有一群人 打著“女權(quán)主義”的名號 從事著組織賣淫的事情 在中國女生不知情的情況下 把她們賣給外國男人!事情是這樣的:根據(jù)知乎用戶伊利丹·怒風的爆料 他在知乎和一個偽女權(quán)主義者 吵了起來 一開始,他可能以為這只是一個 腦子比較軸的偽女權(quán)主義者 所以兩人就吵了一通 本來,他以為就是撕個逼而已 沒想到的是 這個偽女權(quán)主義者 可不是什么好惹的主 這個自稱為“瑪麗女王”的人 竟然在半個月中 持續(xù)不斷地騷擾他 而最夸張的是 瑪麗女王聲稱 自己有能力 讓伊利丹的QQ號 在5天之內(nèi)被封掉 到這里為止 伊利丹一直以為 他不過是碰到了一個杠精 但是萬萬沒想到 5天之后 他的QQ號竟然真的被永久封禁了!說真的,這就有點嚇人了 這個不起眼的瑪麗女王 竟然還能操控別人的QQ賬號被封?難不成,她真的背后有人?伊利丹這才意識到 自己好像惹到了一個組織 他去扒了扒這個瑪麗女王的QQ空間 這才發(fā)現(xiàn) 自己簡直捅出一個馬蜂窩:這個人平時干的 竟然是把中國女生 賣給外國男人的皮肉生意!真的,我本來以為 我是一個見過不少套路的人 但沒想到 這一套操作 真的是驚為天人 簡單來說是這樣的 首先,瑪麗女王自稱是“女權(quán)主義者” 但是實際上她的言論 宣傳的卻是 中國男人配不上中國女人 她甚至惡意辱罵中國男人 恨不得中國男人全部死光 連自己的爸爸都不放過 但是,這么做對她有什么好處呢?很簡單 罵完中國男人以后 接下來她就說—— 既然中國男人這么差勁 那就找外國男人吧!于是,她就經(jīng)常發(fā)布外國男人的介紹 看起來是一個熱心的媒婆 還在各種QQ和微信群里 散播此類信息 但是看到這里 我們不難發(fā)現(xiàn)有點問題 看看其中這些不堪入目的措辭 這并不是普通的介紹男友啊!這簡直是在拉皮條啊!果然,伊利丹發(fā)現(xiàn) 瑪麗女王真的在 拉皮條的過程中 收外國男人的錢!下面是聊天記錄實錘:而且,請注意—— 在這個過程中 她會收外國男人的錢 但是錢不給中國女生 卻落到了她自己的腰包 于是一個詭異的情況出現(xiàn)了:中國妹子 并不知道收錢這回事 還以為是正常交友 而外國男人 卻都交了錢 很可能認為自己是在買春!額,也就是說 在中國女孩不知情的情況下 她們被“賣”給了外國男人 而好處費 卻全都進了瑪麗女王的腰包... 我真的是沒見過這種操作 這說輕了是騙炮 說重了,已經(jīng)可以算是賣淫了吧?我想請熟悉刑法的朋友們看看 這個瑪麗女王 至少應(yīng)該算是個 介紹組織賣淫罪吧?而且,從伊利丹曝光的資料看來 這個組織規(guī)模不小 瑪麗女王甚至把外國男生的信息 建了一個完整的表格 有詳細的個人資料、照片 可以說 是一條非常完整的產(chǎn)業(yè)鏈 那如果按照這樣操作 外國男人都是來嫖的 中國女生卻不知道 還以為是要跟他們談戀愛 那雙方難道不會穿幫嗎?恩,在這方面 瑪麗女王早有對策 根據(jù)知乎一位 從事過這個產(chǎn)業(yè)的匿名用戶提供的信息 針對這種情況 瑪麗女王們 還會手把手地教外國男人 怎么快速擺脫女生的糾纏 怎么調(diào)教中國女生 怎么讓女生覺得自己很可愛 可以說 各種套路一應(yīng)俱全 甚至還可以開發(fā)票!看到這里 她們背后的產(chǎn)業(yè)就非常清楚了 這個瑪麗女王 她根本就不是什么女權(quán)主義者 而是打著女權(quán)主義的口號 販賣中國女生的人販子 一方面 她們通過辱罵中國男人 吸引對外國男人感興趣的中國女生 另一方面 她們向外國男人收錢 然后把中國女生賣給他們!圖片來源:知乎@渭水徐工 而可憐的中國妹子們 還以為自己是在 追求男女平權(quán) 其實,不過是淪為了 這些老鴇的賺錢工具 伊利丹把這整個事情 寫出來以后 在知乎、微博引起了巨大的關(guān)注 關(guān)于其中提到的 伊利丹的QQ被永久封禁的問題 騰訊經(jīng)過核查 目前也有了結(jié)果:經(jīng)調(diào)查,是瑪麗女王利用偽造證據(jù) 惡意舉報了伊利丹的QQ號 目前,騰訊已經(jīng)將伊利丹的QQ解封 同時封禁了瑪麗女王等人的 兩個QQ賬號 警方也就此事立案偵查了 相信很快就會有結(jié)果 這個事情算是告一段落了 但是在我看來 卻有一件事讓我無法釋懷:為什么“女權(quán)主義”竟然會和 辱罵中國男性等同起來?為什么“和外國男人交友” 竟然還能演變成 一個免費的陪睡組織?我想,這個瑪麗女王 也許只是一個 發(fā)現(xiàn)了惡性賺錢模式的生意人 但是在這背后隱藏的 其實是一個很深的問題:為什么有不少中國女人 越來越看不上中國男人 甚至覺得嫁給外國男人 是一種時尚?這里面的原因可能非常復(fù)雜 我這里先提供一個思路 供大家討論:我發(fā)現(xiàn) 現(xiàn)在中國很多大型的女權(quán)組織 背后都有著西方勢力的影子 她們打著女權(quán)的名號 為自己謀取暴利 為西方國家從事破壞活動 而那些真正為女性平權(quán)而奔走的人 卻得不到應(yīng)有的幫助 我之所以這樣說 并不是信口開河 而是有充足的證據(jù) 有一個非常有名的民間女權(quán)組織 叫做“女權(quán)之聲” 它一再聲稱 自己只是一個自發(fā)的民間組織 致力于促進男女平等的 它所有的微博賬號、微信賬號 全部都是由一個 叫做婦女傳媒監(jiān)測網(wǎng)絡(luò)的創(chuàng)辦的 而這個婦女傳媒監(jiān)測網(wǎng)絡(luò) 有這么多媒體產(chǎn)品 那它的錢都是哪里來的呢?從她們介紹的合作組織里 我們可以清楚地找到 她們的資助者—— 竟然有西方的福特基金會 有人也許會問 收了西方的錢怎么了?中國的組織不能收西方的錢嗎?然而,她們不只是收了西方的錢而已 女權(quán)之聲組織里 有一個人叫做鄭楚然 她除了女權(quán)運動之外 沒有任何其他工作 表面上,是一個全職的女權(quán)工作者 在2015年的時候 她還因為尋釁滋事 被警察拘留過30多天 甚至在她被拘留的時候 希拉里還借題發(fā)揮 指責中國侵犯人權(quán)、壓制民主 一個中國的小小民間組織的首領(lǐng) 在互聯(lián)網(wǎng)上的粉絲還沒有我多 竟然能得到希拉里這個級別的關(guān)注?我真的是驚掉了下巴 這樣看來 我離希拉里也不是很遠了??而不止是希拉里 這樣一個明明思想上毫無建樹的人 卻被西方媒體BBC評為了 全球百大思想家 圖:鄭楚然在王寶強事件中發(fā)表的言論 除此以外 更讓人匪夷所思的 是她們平時就喜歡攻擊政府 甚至于,她們還會試圖分裂我們國家 比如,女權(quán)之聲這個組織里 著名的女權(quán)斗士洪理達 就曾經(jīng)轉(zhuǎn)發(fā)著名的港獨媒體 Hong Kong Free Press的言論 甚至曾公開發(fā)表過 支持藏獨、港獨、臺獨的言論 她也經(jīng)常和鄭楚然混在一起 我很想不通 如果她們真的只是單純的女權(quán)主義者 為何要發(fā)表分裂國家的言論?為何要支持藏獨、港獨、臺獨?我只能說,這大概就叫 拿人家的手短,吃人家的嘴軟吧 以前,我在接觸中國的女權(quán)組織時 我就覺得很奇怪 她們都喜歡聲稱 自己是不盈利的非政府組織 但是她們無論是宣傳 還是組織各類活動 都需要大量的錢 如果她們真的不盈利 那這些錢都是哪里來的呢?而這些外國的金主 他們也更加不可能是什么慈善組織 大發(fā)善心來給中國人投錢 每一分投出去的錢 一定都是要有回報的 那么,他們的回報是什么呢?他們給中國的“女權(quán)組織”投錢 能得到什么利益呢?聯(lián)想到中國網(wǎng)絡(luò)上 如火如荼的對中國男人的討伐 我只能說,細思恐極 我絕不是危言聳聽 因為我們就看不遠的鄰國日本 近些年來日本對于西方的崇拜 可謂深入骨髓 已經(jīng)到了崇洋媚外的程度 而這其中 當然也包括對白人男性的崇拜 甚至在2016年一個瑞士白人 發(fā)了一個視頻,赤裸裸的說 “在東京,只要你是白人, 做什么都可以” 視頻里面他在日本便利店 隨意的親吻不認識的收銀員女孩 在酒吧把不認識的日本女孩 按向自己的褲襠 而日本女孩回應(yīng)的卻是諂媚的笑容 我想,并不會有那么多中國人 真正被西方偽女權(quán)主義控制 但是,我們要警惕的是 別在你自己都沒有察覺的時候 被別有用心的人洗了腦 更有甚者 別在你自己都不知道的情況下 被別人賣給了外國男人 還去幫他數(shù)錢 本文系授權(quán)發(fā)布,From 酷玩實驗室,微信號:coollabs,歡迎分享到朋友圈,未經(jīng)許可不得轉(zhuǎn)載,INSIGHT視界 誠意推薦 Forwarded from Official Account 酷玩實驗室 酷玩實驗室 Learn More Scan QR Code via WeChat to follow Official Account 采集文章采集樣式近似文章查看封面
IT技術(shù)分享社區(qū)
個人博客網(wǎng)站:https://programmerblog.xyz
文章推薦程序員效率:畫流程圖常用的工具程序員效率:整理常用的在線筆記軟件遠程辦公:常用的遠程協(xié)助軟件,你都知道嗎?51單片機程序下載、ISP及串口基礎(chǔ)知識硬件:斷路器、接觸器、繼電器基礎(chǔ)知識
總結(jié)
以上是生活随笔為你收集整理的JS树结构操作:查找、遍历、筛选、树结构和列表结构相互转换的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TPS54560设计降压模块需要注意事项
- 下一篇: 全球首秀!真人数字人亮相元宇宙签约仪式