Data Structure - Binary Tree



  •  Tree Definition and Basic Terminology
Tree structure is a kind of important nonlinear data structure, one to many, among which tree and binary tree are the most commonly used.
Definition of tree:
A tree is a finite set of n (n≥0) nodes, it is either an empty tree (n = 0); or a non-empty tree, for a non-empty tree T:
(1) There is one and only one node called the root (Root);
(2) The remaining nodes except the root node can be divided into m (m>0) mutually disjoint finite sets T1, ..., Tm, each of which is a tree itself, and is called the root's Subtree.
Root : the root node (no predecessor)
Leaf (Leaf) : the terminal node (no successor)
Forest : refers to the set of m disjoint trees (such as the number of subtrees after deleting A)
Ordered tree : The subtrees of the node are ordered from left to right and cannot be interchanged (left is the first)
Unordered tree : each subtree of a node can exchange positions
Parent : the upper node (direct predecessor)
Child : the root of the subtree of the lower node (direct successor)
Sibling : Nodes at the same level under the same parent (children call each other brothers)
Cousins : Nodes whose parents are at the same level (but not the same parent)
Ancestors : all nodes that branch from the root to this node
Descendants : any node in the subtree below the node
Node : the data element of the tree
The degree of the node (degree) : the number of subtrees attached to the node (the number of children [0, 1, 2])
The level of the node: the number of layers from the root to the node (the root node is the first layer)
Terminal node : the node with degree 0, that is, the leaf
Branch node : that is, a node whose degree is not 0 (also called an internal node)
The degree of the tree : the maximum of the degrees of all nodes
The depth (height) degree of the tree : refers to the maximum level of all nodes
  • binary tree
A binary tree is a set of n (n≥0) nodes, it is either an empty tree (n = 0); or a non-empty tree, for a non-empty tree T: satisfy
       (1) There is one and only one node called the root;
       (2) The remaining nodes except the root node are divided into two mutually disjoint subsets T1 and T2, which are called the left subtree and right subtree of T respectively , and T1 and T2 themselves are both binary trees.
Properties of a binary tree:
Property 1 : There are at most 2 i-1 nodes on the i-th level of a binary tree
Property 2 : A binary tree of depth k has at most 2k - 1 nodes
Property 3 : For any binary tree, if there are n2 nodes with degree 2, then the number of leaves n0 must be n2+1 (that is, n0=n2+1)
Complete and full binary trees:
Full binary tree: A binary tree of depth k and 2^k -1 nodes. (Feature: Each layer is "filled" with nodes)
Complete binary tree: A binary tree of depth k with n nodes if and only if each node corresponds to a node numbered from 1 to n in a full binary tree of depth k, see the figure below.
Features: (1) Leaf nodes can only appear on the two largest layers
          (2) For any node, if the maximum level of descendants under its right branch is l, then the maximum level of descendants under its left branch must be l or l+1.
The difference between the two: a full binary tree is a tree with one or more leaves, and a complete binary tree, although the first n-1 layers are full, the bottom layer allows the lack of several consecutive nodes on the right. A full binary tree is a special case of a complete binary tree.


Property 4 : The depth of a complete binary tree with n nodes must be ë log 2 n û + 1 (rounded down) 
Property 5 : For a complete binary tree, if the numbering is from top to bottom and from left to right, then the node numbered i, its left child (if it exists) must be numbered 2i, and its right child (if it exists) must be numbered 2i+1 ; its parents must be numbered ë i /2 û   .

  • storage structure of binary tree
1. Sequential storage structure
Use a group of storage units with consecutive addresses to store the node elements on the complete binary tree from top to bottom and from left to right, that is, store the node element numbered i on the complete binary tree in the one-dimensional array defined above. The subscript is in the component of i-1. (0 means this node does not exist)
Disadvantages of sequential storage structure: only suitable for complete binary trees, because a single-branch tree with depth k and only k nodes (there is no node of degree 2 in the tree) needs a one-dimensional length of 2k -1 . array.
2. Chain storage structure
Nodes of a binary tree:
Binary linked list: The node of a binary tree consists of a data element and two branches pointing to its left and right subtrees, which means that the node in the linked list of the binary tree includes at least 3 fields: the data field and the left and right pointer fields .
Tri-linked list: In order to find the parent in the structure, a pointer field to the parent node is added to the node structure
Binary linked list storage representation of binary tree:
typedef struct BiNode{
   TElemType   data;
   struct BiNode *lchild,*rchild; //Left and right child pointers
}BiNode,*BiTree;
Binary linked list storage representation of binary tree:
typedef struct TriTNode
{  TelemType data;
   struct TriTNode *lchild,*rchild;
   struct TriTNode *parent;
}TriTNode,*TriTree;
exercise:

  • Traverse and search a binary tree
1. Traverse the binary tree
Traversal definition: It refers to traversing each node according to a search route without repeating (also known as traversing).
Traversal use: It is the premise of tree structure insertion, deletion, modification, search and sorting operations, and is the basis and core of all operations in binary trees.
Recursive algorithm for traversing a binary tree:
(1) Preorder traversal (DLR): root - left - right
Status PreOrderTraverse(BiTree T){
  if(T==NULL)
     return OK; //empty binary tree
  else{   
     cout<<T->data; //Access the root node
     PreOrderTraverse(T->lchild); //Recursively traverse the left subtree
     PreOrderTraverse(T->rchild); //Recursively traverse the right subtree
    }
}
(2) In-order traversal (LDR): left - root - right
Status InOrderTraverse(BiTree T){
  if(T==NULL)
     return OK; //empty binary tree
  else{   
     InOrderTraverse(T->lchild); //Recursively traverse the left subtree
  cout<<T->data; //Access the root node
     InOrderTraverse(T->rchild); //Recursively traverse the right subtree
    }
}
(3) Post-order traversal (LRD): left - right - root
Status PostOrderTraverse(BiTree T){
  if(T==NULL)
     return OK; //empty binary tree
  else{   
     PostOrderTraverse(T->lchild); //Recursively traverse the left subtree
     PostOrderTraverse(T->rchild); //Recursively traverse the right subtree
     cout<<T->data; //Access the root node
    }
}
Time complexity and space complexity are both O(n);
Application of binary tree traversal algorithm:
1. Calculate the total number of binary tree traversals:
* If it is an empty tree, the number of nodes is 0;
* Otherwise, the number of nodes is the number of nodes in the left subtree + the number of nodes in the right subtree + 1.
int NodeCount(BiTree T){
  if(T == NULL ) return 0;
  else
       return NodeCount(T->lchild)+ NodeCount(T->rchild) + 1;
}

2. Calculate the total number of binary tree leaf nodes
* If it is an empty tree, the number of leaf nodes is 0;
* Otherwise, the number of leaves in the left subtree + the number of leaves in the right subtree.
int LeadCount(BiTree T){
   if(T==NULL) return 0; //If it is an empty tree, return 0
  if (T->lchild == NULL && T->rchild == NULL)
  return 1; //如果是叶子结点返回1
  else
          return  LeafCount(T->lchild) + LeafCount(T->rchild);
}
结论:
若二叉树中各结点的值均不相同,则:由二叉树的前序序列和中序序列,或由其后序序列和中序序列均能唯一地确定一棵二叉树,但由前序序列和后序序列却不一定能唯一地确定一棵二叉树。

习题:已知一棵二叉树的 中序序列后序序列分别是 BDCEAFHGDECBHGFA,请画出这棵二叉树。
解析:
①由后序遍历特征,根结点必在后序序列尾部(A);
②由中序遍历特征,根结点必在其中间,而且其左部必全部是左子树子孙(BDCE),其右部必全部是右子树子孙(FHG);
③继而,根据后序中的DECB子树可确定B为A的左孩子,根据HGF子串可确定F为A的右孩子;以此类推。
中序遍历递归算法执行过程中递归工作栈的状态:
(1)记录中包括两项:递归调用的语句编号;指向根结点的指针,当栈顶记录中的指针非空时,应遍历左子树,即指向左子树根的指针进栈。
(2)若栈顶记录中的指针值为空,则应退至上一层,若是从左子树返回,则应访问 当前层即栈顶记录中指针所指向的根结点。
(3)若从右子树返回则表明当前层的遍历结束,应继续退栈。
从另一个角度看,这意味着遍历右子树不再需要保护当前层的根指针,可直接修改栈顶记录中的指针即可。
//中序遍历二叉树的非递归算法(左根右)
Status InOrderTraverse(BiTree,Status(*Visit)(TElemType e){
//采用二叉链表存储结构,Visit是对数据元素操作的应用函数,每个数据元素调用函数Visit.
InitStack(S); Push(S,T);//根指针进栈
while(!StackEmpty(S)){
while(GetTop(S,p)&&p)Push(S,p->lchild);//向左走到尽头
Pop(S,p); //空指针退栈
if(!StackEmpty(S)){//访问结点,向右走一步
Pop(S,p);if(!Visit(p->data))return ERROR;
Push(S,p->rchild);
}
}
return OK;
}
例:按先序序列建立二叉树的二叉链表,再读入字符
Status CreateBiTree(BiTree &T){
scanf(&ch);//读入字符
if(ch==' ')T=NULL;
else{
if(!(T=(BiTNode*)malloc(sizeof(BiTNode))))exit(OVERFLOW);
T->data=ch;//生成根结点
CreateBiTree(T->lchild);//构造左子树
CreateBiTree(T->rchild);//构造右子树
}
return OK;
}
二叉链表的缺点:二叉链表空间效率这么低,存在空闲区
改进:使用空闲区存放当前结点的直接前驱和后继等线索,以加快查找速度。

二、搜索化二叉树
遍历二叉树实际上是对一个非线性结构进行线性化操作。但是当以二叉链表作为存储结构时,只能找到结点的左右孩子信息,而不能直接得到结点在任意序列中的前驱和后继信息(只能遍历得到)。
那么如何保存这种遍历过程中的信息呢?
1.在每个结点上增加两个指针域fwd和bkwd,分别只是结点在任意次序遍历时得到的前驱和后继信息。这样能大大的降低结构的存储密度。
2.使用n个结点的二叉链表中的n+1个空链域。
定义:
    线索:指向结点前驱和后继的指针
    线索链表:加上线索二叉链表
    线索二叉树:加上线索的二叉树(图形式样)
    线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324517715&siteId=291194637