树的结点
- 结点:使用树结构存储的每一个数据元素都被称为“结点”。
- 树根结点(简称“根结点”):每一个非空树都有且只有一个被称为根的结点。
- 树根的判断依据为:如果一个结点没有父结点,那么这个结点就是整棵树的根结点。
- 叶子结点:如果结点没有任何子结点,那么此结点称为叶子结点(叶结点)。
子树和空树
- 子树:整棵树的根结点为结点 A,而如果单看结点 B、E、F、K、L 组成的部分来说,也是棵树,而且节点 B 为这棵树的根结点。所以称 B、E、F、K、L 这几个结点组成的树为整棵树的子树;同样,结点 E、K、L 构成的也是一棵子树,根结点为 E。
- 注意:单个结点也是一棵树,只不过根结点就是它本身。
- 知道了子树的概念后,树也可以这样定义:树是由根结点和若干棵子树构成的。
- 空树:如果集合本身为空,那么构成的树就被称为空树。空树中没有结点。
- 补充:在树结构中,对于具有同一个根结点的各个子树,相互之间不能有交集。
有序树和无序树
- 如果树中结点的子树从左到右看,谁在左边,谁在右边,是有规定的,这棵树称为有序树;反之称为无序树。
- 在有序树中,一个结点最左边的子树称为"第一个孩子",最右边的称为"最后一个孩子"。
- 如果是其本身是一棵有序树,则以结点 B 为根结点的子树为整棵树的第一个孩子,以结点 D 为根结点的子树为整棵树的最后一个孩子。
树结构代码如下:
//树
//重点理解的是 递归 引用 指针
class Node{
constructor(element){
this.element = element;
this.left = null;
this.right = null;
}
}
// new Node()
class Tree{
constructor(){
this.root = null;
}
insert(element){
//添加第一个节点
let newNode = new Node(element);//创建新节点
//newNode = {element:5,left=null,right=null}
// console.log(newNode)
if(this.root === null){//树中没有任何元素
this.root = newNode
}else{
//递归
this.insertNode(this.root,newNode)//11 5
}
}
insertNode(node,newNode){//node=7 newNode=3
if(newNode.element<node.element){//如果新节点的值小于根节点
if(node.left === null){//说明下面没有值了
node.left = newNode
}else{//说明下面还有值
this.insertNode(node.left,newNode)
}
}else{
if(node.right === null){//说明下面没有值了
node.right = newNode
}else{//说明下面还有值
this.insertNode(node.right,newNode)
}
}
}
preOrderTraverse(){//对调函数为了拼接遍历出来的值
this.preOrderTraverseNode(this.root);
}
preOrderTraverseNode(node){
if(node!=null){
// callback(node.element);
console.log(node.element);
this.preOrderTraverseNode(node.left);
this.preOrderTraverseNode(node.right);
}
}
midOrderTraverse(){//对调函数为了拼接遍历出来的值
this.midOrderTraverseNode(this.root);
}
midOrderTraverseNode(node){//11 7 5 3 null
if(node!=null){
this.midOrderTraverseNode(node.left);
console.log(node.element);//3 5 6 7 9
this.midOrderTraverseNode(node.right);
}
}
postOrderTraverse(){//对调函数为了拼接遍历出来的值
this.postOrderTraverseNode(this.root);
}
postOrderTraverseNode(node){
if(node!=null){
this.postOrderTraverseNode(node.left);
this.postOrderTraverseNode(node.right);
console.log(node.element);
}
}
max(){//求最大值
let current = this.root;
while(current!=null&& current.right){
current = current.right//current = current.next
}
return current
}
//方法四要素
//方法功能,参数,返回值,方法何时调用
search(element){//查询特定值 二分查找发 折半查找法
return this.searchNode(this.root,element)
}
searchNode(node,element){
if(node==null){
return false//未查找到要寻找的值
}
if(node.element>element){
return this.searchNode(node.left,element)
}else if(node.element<element){
return this.searchNode(node.right,element)
}else{
return true//查找到要寻找的值
}
}
remove(element){
return this.root = this.removeNode(this.root,element)
}
removeNode(node,element){//9 8;8 8
if(node===null){
return null
}
if(node.element>element){
node.left = this.removeNode(node.left,element)//null 8
return node
}else if(node.element<element){
node.right = this.removeNode(node.right,element)
return node
}else{
// 相等情况 node.element = element
// 第一种情况 删除叶节点
if(node.left === null && node.right === null){
node = null;
return node
}
//第二种情况 删除的节点又一个子节点
if(node.left == null){
node = node.right
return node
}else if(node.right == null){
node = node.left
return node
}
//第三种情况
//传入的element=7
const childNode = this.minNode(node.right);
//childNode=8
node.element = childNode.element;//替换
node.right = this.removeNode(node.right,childNode.element);
return node
}
}
min(){//求最小值
this.minNode(this.root)
}
minNode(node){
let current = node;
while(current!=null&& current.left){
current = current.left//current = current.next
}
return current
}
}
let tree = new Tree();
tree.insert(7);
tree.insert(6);
tree.insert(10);
tree.insert(4);
总结
树型存储结构类似于家族的族谱,各个结点之间也同样可能具有父子、兄弟、表兄弟的关系。本节中,要重点理解树的根结点和子树的定义,同时要会计算树中各个结点的度和层次,以及树的深度。