迄今爲止,咱們對數據結構的探索僅觸及線性部分。不管咱們使用數組、鏈表、棧仍是隊列,都是線性數據結構。咱們已經看到了線性數據結構操做的複雜性,大多數時候,插入和刪除的複雜度能夠用O(1)來表示。搜索有點複雜,須要O(n)複雜度。惟一的例外是PHP數組,它其實是哈希表,若是索引或鍵在這樣的以這樣的方式管理,則能夠達到O(1)的複雜度。爲了解決這個問題,咱們可使用分層數據結構,而不是線性數據結構。分層數據能夠很好地解決線性數據結構難以解決的許多問題。php
每當咱們談論家庭族譜、組織結構和網絡鏈接圖時,咱們實際上都在談論分層數據。樹是一種特殊的抽象數據類型(ADT)。不一樣於鏈表,樹是分層的。git
樹是由邊鏈接的節點或頂點的分層集合。樹不能有循環,而且只有節點和它的降低節點或子節點之間存在邊。同一父級的兩個子節點在它們之間不能有任何邊。每一個節點能夠有一個父節點除非是頂部節點,也稱爲根節點。每棵樹只能有一個根節點。每一個節點能夠有零個或多個子節點。在下面的圖中,A是根節點,B、C和D是A的子節點。咱們也能夠說,A是B、C、D的父節點。B、C和D被稱爲兄弟姐妹,由於它們是來自同一父節點A。github
沒有任何子節點的節點稱爲葉子。在前面的圖中,K、L、F、G、M、I和J是葉子節點。葉子節點也稱爲外部節點或終端節點。除根之外的節點具備至少一個子節點,稱爲內部節點。這裏,B、C、D、E和H是內部節點。這裏是一些其餘經常使用的術語,用於描述樹的數據結構:算法
後裔:這是一個能夠經過重複的程序從父節點到達的節點。例如,M是前一個圖中C的後裔。數組
祖先:這是一個能夠經過重複方式從子節點到達父節點的節點。例如,B是L的祖先。網絡
度:特定父節點的子節點的總數被稱爲它的度數。在咱們的例子中,A有3度,B有1度,C有度3,D有度2。數據結構
路徑:從源節點到目標節點的節點和邊的序列稱爲兩個節點之間的路徑。路徑的長度是路徑中節點的數目。A到M之間的路徑是A-C-H-M,路徑的長度爲4。數據結構和算法
節點的高度:節點的高度由節點與最深節點之間的邊數決定。例如,節點B的高度爲2。post
層次:層次表明節點的生成。若是父節點處於層次N,則其子節點將位於N+ 1層次。所以,該層次由節點和根之間的邊數定義。優化
A在0層
B,C和D是1層
E,F,G,H,I,J是2層
K,L,M都在第3層。
樹的高度:樹的高度是由它的根節點的高度定義的。上圖樹的高度是3。
子樹:在樹結構中,每一個孩子遞歸地造成子樹。換句話說,樹由許多子樹組成。例如,B和E、K和L構成了一個子樹,E、K和 L構成了一個子樹,每一個不一樣的陰影中都對它們進行了識別。
深度:節點的深度由節點和根節點之間的邊數決定。例如,H的深度是2,L的深度是3。
森林:森林是由一組或更多的不相交的樹組成。
遍歷:這表示按特定順序訪問節點的過程。
鍵:用於搜索,表示節點的值。
到目前爲止,咱們已經瞭解了樹的不一樣屬性。若是咱們對比樹和現實的例子,咱們發現組織結構或族譜樹能夠用數表示。對於一個組織結構,有一個根節點能夠是公司的CEO,其次是CXO級別的員工,其次是其餘級別的員工。這裏,咱們不限制特定節點的任何度。這意味着一個節點能夠有多個子節點。所以,下面是一個節點結構,咱們能夠定義節點屬性、它的父節點和它的子節點:
class TreeNode {
public $data = null;
public $children = [];
public function __construct(string $data = null) {
$this->data = $data;
}
public function addChildren(TreeNode $treeNode) {
$this->children[] = $treeNode;
}
}
複製代碼
咱們能夠看到咱們聲明瞭兩個公共屬性分別爲數據和孩子。咱們還有一個方法將孩子添加到一個特定的節點。這裏,咱們只是在數組末尾添加新的子節點。樹是遞歸結構,它將幫助咱們遞歸地構建樹,並遞歸地遍歷樹。
如今,咱們有了節點,讓咱們構建一個樹結構,它將定義樹的根節點,也能夠遍歷整個樹。所以,基本樹結構將是這樣的:
class Tree {
public $root = null;
public function __construct(TreeNode $treeNode) {
$this->root = $treeNode;
}
public function traverse(TreeNode $treeNode, int $level = 0) {
if ($treeNode) {
echo str_repeat('-', $level) . $treeNode->data . PHP_EOL;
foreach ($treeNode->children as $child) {
$this->traverse($child, $level + 1);
}
}
}
}
複製代碼
前面的代碼顯示了一個簡單的樹類,咱們能夠存儲根節點引用,也能夠從任意節點遍歷樹。在遍歷部分中,咱們訪問每一個子節點,而後當即遞歸調用遍歷方法來獲取當前節點的子節點。咱們經過一個level,在節點名稱的開頭打印出一個破折號(-),這樣咱們就能夠很容易地理解子級數據。
require './TreeNode.php';
$ceo = new TreeNode('ceo');
$tree = new Tree($ceo);
$cfo = new TreeNode('cfo');
$cto = new TreeNode('cto');
$cmo = new TreeNode('cmo');
$coo = new TreeNode('coo');
$ceo->addChildren($cfo);
$ceo->addChildren($cto);
$ceo->addChildren($cmo);
$ceo->addChildren($coo);
$seniorArchitect = new TreeNode("Senior Architect");
$softwareEngineer = new TreeNode("SoftwareEngineer");
$userInterfaceDesigner = new TreeNode("userInterface Designer");
$qualityAssuranceEngineer = new TreeNode("qualityAssurance Engineer");
$cto->addChildren($seniorArchitect);
$seniorArchitect->addChildren($softwareEngineer);
$cto->addChildren($userInterfaceDesigner);
$cto->addChildren($qualityAssuranceEngineer);
$tree->traverse($tree->root);
複製代碼
最後輸出的結果相似這樣,完整的代碼能夠在這裏看到
在代碼世界中有不少不一樣類型的樹,咱們一塊兒來看下。
二叉樹是一種基本的樹結構,二叉樹的每一個節點最多有兩個孩子。
二叉搜索樹(BST)是一種特殊類型的二叉樹,其中節點以排序的方式存儲,即在任何給定的點上,節點值必須大於或等於左子節點值,小於右子節點值。每一個節點都必須知足這個屬性,這就是二叉搜索樹。二叉搜索樹算法老是優於線性搜索,它的時間複雜度是O(n),咱們將在之後的內容詳細解釋。
自平衡二叉搜索樹或高度平衡二叉搜索樹是一種特殊類型的二叉搜索樹,它試圖經過自動調整來儘可能保持樹的高度或層次儘量小。下圖左側的展現了二叉搜索樹,右邊的是自平衡二叉搜索樹:
高度平衡的二叉樹老是更好的選擇,由於它比常規BST有助於更快地搜索操做。自平衡或高度平衡二叉搜索樹有不一樣的實現。一些常見到的以下:
AA樹
AVL樹
紅黑樹
替罪羊樹
八叉樹
2-3樹
Treap
咱們將在後續的內容介紹他們,敬請期待吧。
PHP基礎數據結構專題系列目錄地址:github.com/... 主要使用PHP語法總結基礎的數據結構和算法。還有咱們平常PHP開發中容易忽略的基礎知識和現代PHP開發中關於規範、部署、優化的一些實戰性建議,同時還有對Javascript語言特色的深刻研究。