C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)系列之樹(shù)的概念結(jié)構(gòu)和常見(jiàn)表示方法
0x00 樹(shù)的概念
?? 樹(shù)是一種非線(xiàn)性的數(shù)據(jù)結(jié)構(gòu),它是由 n(n >= 0)個(gè)有限節(jié)點(diǎn)組成的一個(gè)具有層次關(guān)系的集合。
? 那么為什么叫 "樹(shù)" 呢?
?? 我們之所以把它成為 "樹(shù)",是因?yàn)樗芟裎覀儸F(xiàn)實(shí)生活中的樹(shù)。只是它是倒過(guò)來(lái)的,根朝上葉子朝下。
0x01 樹(shù)的結(jié)構(gòu)
① 有一個(gè)特殊的節(jié)點(diǎn),成為根節(jié)點(diǎn),根節(jié)點(diǎn)不存在前驅(qū)節(jié)點(diǎn)。
② 除根節(jié)點(diǎn)外,其余節(jié)點(diǎn)被分成 M(M>0) 個(gè)互不相交的集合 T1、T2、……、Tm,期中沒(méi)一個(gè)集合 Ti(1 <= i <= m) 又是一顆結(jié)構(gòu)于樹(shù)類(lèi)似的字?jǐn)?shù)。每顆子樹(shù)的節(jié)點(diǎn)有且只有一個(gè)前驅(qū),可以有0個(gè)或多個(gè)后繼。
③ 因此,樹(shù)是遞歸定義的。因?yàn)槿魏螛?shù)都會(huì)被分成根和子樹(shù)。
??注意:樹(shù)型結(jié)構(gòu)中,子樹(shù)之間不能有交集,否則就不是樹(shù)形結(jié)構(gòu)。
0x02 樹(shù)的相關(guān)概念
?? 節(jié)點(diǎn)的度:一個(gè)節(jié)點(diǎn)含有的子樹(shù)的個(gè)數(shù)稱(chēng)為該節(jié)點(diǎn)的度。 比如上圖中,A的度為6。
?? 葉子結(jié)點(diǎn):又稱(chēng)終端節(jié)點(diǎn),度為0的節(jié)點(diǎn)稱(chēng)為葉子結(jié)點(diǎn)。 比如上圖中,BCHIPQ等節(jié)點(diǎn)就是葉子結(jié)點(diǎn),因?yàn)樗鼈兊亩葹?。
? 分支節(jié)點(diǎn):又稱(chēng)非終端節(jié)點(diǎn),度不為0的節(jié)點(diǎn)稱(chēng)為分支節(jié)點(diǎn)。 比如上圖中,DEFG等節(jié)點(diǎn)就是分支節(jié)點(diǎn),因?yàn)樗麄兊亩炔粸?。
?? 父節(jié)點(diǎn):又稱(chēng)雙親結(jié)點(diǎn),若一個(gè)節(jié)點(diǎn)有子節(jié)點(diǎn),則這個(gè)節(jié)點(diǎn)稱(chēng)作其子節(jié)點(diǎn)的父節(jié)點(diǎn)。 比如上圖中,A是B的父節(jié)點(diǎn)。
?? 子節(jié)點(diǎn):又稱(chēng)孩子節(jié)點(diǎn),若一個(gè)節(jié)點(diǎn)有根節(jié)點(diǎn),則稱(chēng)為該節(jié)點(diǎn)的子節(jié)點(diǎn)。 如上圖,B是A的子節(jié)點(diǎn)。
?? 兄弟節(jié)點(diǎn):具有相同父節(jié)點(diǎn)的節(jié)點(diǎn)互相稱(chēng)為兄弟節(jié)點(diǎn)。 同一個(gè)父親生的才算。如上圖,B和C是兄弟節(jié)點(diǎn),它們的父節(jié)點(diǎn)都是A。
?? 樹(shù)的度:一棵樹(shù)中最大的節(jié)點(diǎn)的度稱(chēng)為樹(shù)的度。 如上圖,最大的節(jié)點(diǎn)是A,有6個(gè)子樹(shù),故A的度為6,所以樹(shù)的度為6。
?? 節(jié)點(diǎn)的層次:從根開(kāi)始定義起,根為第1層,根的子節(jié)點(diǎn)為第2層,以此類(lèi)推。 也有將根定義為第0層,根的子節(jié)點(diǎn)為第1層的。但是我們建議還是使用根為第1層來(lái)定義比較好。
?? 樹(shù)的高度:又稱(chēng)樹(shù)的深度,樹(shù)中節(jié)點(diǎn)的最大層次。 如上圖,樹(shù)的高度為 4。
??♂? 堂兄弟節(jié)點(diǎn):父節(jié)點(diǎn)在同一層的節(jié)點(diǎn),它們互為堂兄弟。如上圖,H 和 I 互為堂兄弟。
?? 節(jié)點(diǎn)的祖先:從根到該節(jié)點(diǎn)所經(jīng)分支上的所有節(jié)點(diǎn)。 如上圖·,A是所有節(jié)點(diǎn)的祖先。
?????? 子孫:以某節(jié)點(diǎn)為根的子樹(shù)中任一節(jié)點(diǎn)都稱(chēng)為該節(jié)點(diǎn)的子孫。 如上圖,所有節(jié)點(diǎn)都是A的子孫。
?? 森林:由 m(m > 0) 棵互不相交的樹(shù)的集合稱(chēng)為森林。 比如并查集,多個(gè)樹(shù)構(gòu)成森林。
0x02 樹(shù)的表示
? 以前學(xué)單鏈表時(shí)只有一個(gè)指針,雙鏈表兩個(gè)指針,但是樹(shù)有多少個(gè)指針是不確定的,因?yàn)闃?shù)沒(méi)有規(guī)定一個(gè)節(jié)點(diǎn)最多有多少個(gè)孩子。那我們?cè)撊绾味x結(jié)構(gòu)呢?
?? 方式一:假設(shè)說(shuō)明了樹(shù)的度為N,才能勉強(qiáng)用
struct TreeNode { int data; struct TreeNode* sub[N]; // 指針數(shù)組 };
問(wèn)題點(diǎn):
① 可能會(huì)存在不少的空間浪費(fèi)。
② 萬(wàn)一沒(méi)有限定樹(shù)的度為多少呢?這個(gè)方式就廢了。
?? 方式二:vector
// 假設(shè)我們定義了一個(gè)順序表 // typedef int STLDataType; //順序表的數(shù)據(jù)類(lèi)型 // 順序表中存節(jié)點(diǎn)的指針 typedef struct TreeNode* SLDataType; //SeqList struct TreeNode { int data; SeqList s; // s為SLDataType* array; };
(C++中這里可以用 vector,但是C里沒(méi)有)
即使你沒(méi)有告訴我度是多少,我有多少個(gè)孩子我就存多少個(gè)孩子,所以這里不需要關(guān)心度的問(wèn)題。但是這里 s 的結(jié)構(gòu)相對(duì)復(fù)雜,s 里面有一個(gè)類(lèi)型為SLDataType* 的數(shù)組,這個(gè)數(shù)組已經(jīng)是二級(jí)指針了,SLDataType 展開(kāi)后又是一個(gè) struct TreeNode* 。
?? 方式三:雙親表示法
利用結(jié)構(gòu)數(shù)組存儲(chǔ)(更加復(fù)雜)
struct TreeNode { int parenti; int data; };
[ A -1] [ B0 ] [ C0 ] [ D0 ] ...... [ H 3 ]
?? 每一個(gè)元素中存的是結(jié)構(gòu)體 struct TreeNode arr[10]
每個(gè)元素內(nèi)只存自己的值和父親的下標(biāo)(A沒(méi)有父親是-1,B的父親下標(biāo)是0…… H的父親是D下標(biāo)為3),可以通過(guò)一個(gè)值找到自己父親。
? 上列的方式各有優(yōu)缺點(diǎn),那么有沒(méi)有最優(yōu)的方法?
? 當(dāng)然有,它就是 —— 《左孩子右兄弟表示法》 有了這個(gè)方法,其他的都是渣渣!
typedef int DataType; struct Node { struct Node* _firstChind1; // 永遠(yuǎn)指向第一個(gè)孩子 struct Node* _pNextBrother; // 指向孩子右邊的兄弟 DataType _data; };
?? 解讀:無(wú)論你有多少個(gè)孩子,它都只存兩個(gè)指針。一個(gè)指針永遠(yuǎn)指向第一個(gè)孩子,另一個(gè)指針指向孩子右邊的兄弟(親兄弟)。這個(gè)樹(shù)的度無(wú)論為多少,也不需要用順序表存,但是你任何一個(gè)節(jié)點(diǎn)有多少個(gè)孩子都能給你表示出來(lái),通過(guò)第一個(gè)孩子把所有孩子都找出來(lái)。不復(fù)雜也沒(méi)有浪費(fèi),只用兩個(gè)指針就把鏈接關(guān)系都表示出來(lái)了,不得不說(shuō)設(shè)計(jì)這個(gè)的人真是太????了!
0x03 樹(shù)在實(shí)際中的運(yùn)用
文件系統(tǒng)的目錄樹(shù)結(jié)構(gòu)、網(wǎng)絡(luò)拓?fù)?,最短路徑?wèn)題,搜索引擎、思維導(dǎo)圖等
到此這篇關(guān)于C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)系列之樹(shù)的常見(jiàn)表示方法的文章就介紹到這了,更多相關(guān)C語(yǔ)言 樹(shù)的常見(jiàn)表示方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++學(xué)習(xí)之Lambda表達(dá)式的用法詳解
Lambda?表達(dá)式(lambda?expression)是一個(gè)匿名函數(shù),Lambda表達(dá)式基于數(shù)學(xué)中的λ演算得名。本文就來(lái)為大家詳細(xì)講講C++中Lambda表達(dá)式的使用,需要的可以參考一下2022-07-07C++ STL標(biāo)準(zhǔn)庫(kù)std::vector的使用詳解
vector 是表示可以改變大小的數(shù)組的序列容器,本文主要介紹了C++ STL標(biāo)準(zhǔn)庫(kù)std::vector的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03C語(yǔ)言詳細(xì)圖解浮點(diǎn)型數(shù)據(jù)的存儲(chǔ)實(shí)現(xiàn)
使用編程語(yǔ)言進(jìn)行編程時(shí),需要用到各種變量來(lái)存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類(lèi)型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類(lèi)型,來(lái)分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么2022-05-05