树由边连接的节点而构成:
二叉树(二叉搜索树):如果树中每个节点最多只能有两个子节点,这种树就被称为“二叉树”:
二叉树每个节点的两个子节点称为“左子节点”和“右子节点”;
二叉树中的节点不是必须有两个子节点,可以只有一个左子节点,或者只有一个右子节点,或者干脆没有子节点(叶节点);
二叉树特征定义:一个节点的左子节点的关键字值小于这个节点,右子节点的关键字值大于或等于这个节点。
二叉树的效率:
在查找过程中,需要访问每层的一个节点,所以只要知道有多少层就可以知道这些操作需要多长时间了。
特定节点数量的满树的层数
节点数(N) 层数(L)
1 1
3 2
7 3
... ...
1023 10
... ...
1048575 20
... ...
1073741823 30
L = log2(N + 1);
因此常见树的操作时间复杂度大致是N以2为底的对数。表示为O(log2N),和二分法排序的时间复杂度保持一致。
遍历树:根据一种特定的顺序访问树的每一个节点
遍历方法:前序、中序(常用)、后序
中序遍历(递归方法代码如下):
1、调用自身来遍历节点的左子树
2、访问该节点
3、调用自身来遍历节点的右子树
private void inOrder(Node localRoot) {
if (localRoot != null) {
inOrder(localRoot.leftChild);
System.out.print(localRoot.data + " ");
inOrder(localRoot.rightChild);
}
}
刚开始用根(root)作为参数调用这个方法:
inOrder(root);
前序遍历(代码略):
1、访问该节点
2、调用自身遍历该节点的左子树
3、调用自身遍历该节点的右子树
后序遍历(代码略):
1、调用自身遍历该节点的左子树
2、调用自身遍历该节点的右子树
3、访问该节点
上图中的节点遍历:
中序:DBEAFCG
前序:ABDECFG
后序:DEBFGCA
至于二叉树的插入、查找、删除,不再说明;二叉树的删除比较复杂,删除不是必要的,可以在程序中避免出现(在node节点类中增加一个Boolean的属性,eg:isDeleted),删除这样的节点不会改变树的结构,但是树中依然保留着“已删除”的节点。
非平衡树:它们大部分的节点在根的一边或者另一边;有的子树也可能是非平衡的
原因:树变得不平衡是由于数据项插入顺序导致的。
如果数据项是随机插入的,树或多或少会更平衡一点;但是如果插入序列是升序(eg:5、8、11、17、19、22、31、55、86...)或者是降序(略),则所有的节点都会是右子节点(升序)或者都是左子节点(降序),这样树就会极度不平衡了(如下图)。
无论如何,在插入数据项的时候,还是会有短的升序或者降序数列出现,这会导致树的局部不平衡。
二叉树的不平衡,是它最明显的缺点;也正是二叉树的应用不广泛的原因;但它却是学习其他树(红黑树、VAL树、2-3树、B树、B+树...)的基础。