迄今为止,咱们对数据结构的探索仅触及线性部分。不管咱们使用数组、链表、栈仍是队列,都是线性数据结构。咱们已经看到了线性数据结构操做的复杂性,大多数时候,插入和删除的复杂度能够用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语言特色的深刻研究。