Chapter 4: Trees and Binary Trees (Binary Tree Traversal and Cue Binary Tree)
Last article talked about learning data structures - Chapter IV: tree and binary tree (binary tree stored in order and chain store) the following clues and learn binary tree traversal binary tree
1. Traversal of Binary Tree
The traversal of the binary tree : each node in the tree is visited according to a certain search path, and each node of the tree is visited once, and only once.
We are divided into the order of visiting the root node
- Preorder traversal: first root -> left subtree -> right subtree
- Middle-order traversal: first left subtree -> root -> right subtree
- Post-order traversal: first left subtree -> right subtree -> root
Note that no matter when the root is visited, the left subtree is visited first and then the right subtree is visited.
1.1 Pre-order traversal
First-order traversal:
- Visit the root node;
- Use pre-order recursion to traverse the left subtree;
- Use pre-order recursion to traverse the right subtree;
注
: The branches of each node follow the above access sequence, reflecting "recursive call"
Time complexity : O(n)
The first order traversal result of the above figure : A BDFE CGHI
Thinking process:
- (1) Visit root node A first,
- (2) A is divided into left and right subtrees. Because it is a recursive call, the left subtree also follows the order of "root node first-then left-then right", so visit node B,
- (3) Then visit the D node,
- (4) There are branches when visiting the F node, which also follows the order of "root node first-then left-then right",
- (5) Visit the E node, at this time the large subtree on the left has been visited,
- (6) Then follow the order of the last visit to the right subtree, visit the large subtree on the right, and the large subtree on the right also visits the root node C first,
- (7) Visit the left subtree G,
- (8) Because there is no left subtree of G, the next two visits to the right subtree H of G,
- (9) Last visit to the right subtree I of C
Recursive algorithm for pre-order traversal:
void PreOrder(BiTree T){
if(T!=null){
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
1.2 In-order traversal
Follow the order of left subtree -> root node -> right subtree
In-order traversal:
- Use middle order to traverse the left subtree;
- Visit the root node;
- Traverse the right subtree in middle order
Time complexity : O(n)
Sequence traversal result in the above figure : DBEFAGHCI
Recursive algorithm for middle-order traversal:
void PreOrder(BiTree T){
if(T!=null){
PreOrder(T->lchild);
visit(T);
PreOrder(T->rchild);
}
}
1.3 Post-order traversal
Follow the order of left subtree -> right subtree -> root node
Post-order traversal:
- Recursively traverse the left subtree in post-order;
- Recursively traverse the right subtree in post-order;
- Visit the root node;
Time complexity : O(n)
The result of the post-order traversal in the above figure : DEFB HGIC A
Recursive algorithm for post-order traversal:
void PreOrder(BiTree T){
if(T!=null){
PreOrder(T->lchild);
PreOrder(T->rchild);
visit(T);
}
}
2. Non-recursive traversal of binary trees
The three traversal methods mentioned above all use recursion to traverse. The following is how to use non-recursive algorithm traversal, we need to use it 栈
, take the middle-order traversal as an example:
Algorithm idea
- Initially, the
扫描
root node and all the left nodes of the root node are sequentially pushed into the stack - Pop a node,
访问
it 扫描
The right child node of the node and push it to the stack扫描
All left nodes of the right child node in turn and-into the stack- Repeat the process until the stack is empty
Pay attention to distinguish scanning and access
According to the above algorithm idea to explain the binary tree as shown in the figure above, we use a non-recursive algorithm to traverse:
1: 扫描
Root node and all left nodes of the root node in turn and push them to the stack in turn
2: Pop a node and visit
3: Then scan 7
the right child node of the node number and push it to the stack. Its right child node is empty, so no node is pushed onto the stack.
4: Then continue to pop the stack 4
number node and visit it
5: Then scan 4
the right child node of the node number and push it to the stack. Its right child node is empty, so no node is pushed onto the stack.
6: Then continue to pop the stack 2
number node and visit it
7: Then scan 2
the right child node of 5
the 5
number node and push it into the stack. Its right child node is , so push the number node into the stack.
8: Then scan 5
all the left nodes of the node number and push them to the stack in turn. Its left node is empty, so no node is pushed into the stack.
9: Then continue to pop the stack 5
number node and visit it, and the node of his right child is empty, and no node is pushed into the stack.
10: Then pop the 1
node and visit it.
11: Scan 1
the right child node of the node in the same way, and then push it to the stack in turn
12: After the right child node is pushed into the stack, push the left node of the node into the stack one by one, and then continue to loop step two, knowing that the stack is empty
Code:
void InOrder2(BiTree T){
InitStack(S);
BiTree p=T;
//循环判断
while(p||!IsEmpty(S)){
//栈非空
if(p){
Push(S,p);
p-p->lchild; //将p指向左孩子
}else{
Pop(S,p); //左孩子为空,出栈一个结点
visit(p); //并访问它
p=p->rchild; //指向右孩子
}
}
}
3. Level traversal
Hierarchical traversal : As the name implies, it traverses from top to bottom and from left to right, and the traversal sequence is in the order of labeling.
Hierarchical traversal needs help 队列
, algorithm thought:
- Initially join the root to the team and visit the root node
- If there is a left subtree, put the root of the left subtree into the queue
- If there is a right subtree, put the root of the right subtree into the queue
- Then dequeue the node and visit
- Repeat the process until the queue is empty
Code implementation :
void leveOrder(BiTree T){
InitQueue(Q);
BiTree p; //辅助变量
EnQueue(Q,T); //根节点入队
while(!isEmpty(Q)){
DeQueue(Q,P); //出队队首元素
visit(p); //并访问
if(p->lchild!=NULL){
//左孩子节点不为空,入队
EnQueue(Q,p->lchild);
}
if(p->rchild!=NULL){
//右孩子节点不为空,入队
EnQueue(Q,p->rchild);
}
}
}
4. Reverse the traversal result
We can get the traversal sequence from a binary tree, so can we get a binary tree by traversing the sequence?
First of all, can we get a binary tree through only one traversal sequence? For example, the first-order traversal sequence: 124536, we must traverse the root node first, but whether 2 is the left node or the right node cannot be determined, so according to a traversal sequence, the whole process cannot be reversed.
In fact, the (later) pre-order traversal sequence and the middle-order traversal sequence can determine a binary tree, but the subsequent traversal sequence and the pre-order traversal sequence cannot determine a binary tree.
When learning to reverse the traversal results, please be sure to 清楚
three traversal methods.
先序
The 中序
idea of traversal sequence and traversal sequence inversion:
- In the preorder sequence, the first node is the root node;
- The root node divides the middle-order traversal sequence into two parts;
- Then determine the nodes of the two parts in the preorder sequence, and the first node of the two parts is the root of the left subtree and the root of the right subtree respectively;
- Repeating this process recursively in the subtree can uniquely determine a binary tree.
For example: Pre-order sequence: 124536 Middle-order sequence: 425163, please draw the binary tree.
In the first order traversal we reach the root node of the first node, and in the post order traversal sequence we know that the last node is the root node, so the operation of the post order traversal sequence plus the middle order traversal sequence is similar.
In addition, a unique binary tree can also be determined based on the 层次遍历序列
sum 中序遍历序列
.
5. Thread Binary Tree
The above mentioned binary linked list, we know that regardless of the shape of the binary tree, the number of empty chain domains is always more than the number of non-empty chain domains. To be precise, there are 2n chain domains in the binary linked list of each node of n, and there are n-1 non-empty chain domains, but there are n+1 empty chain domains.
Therefore, a method is proposed to use the original empty chain domain to store pointers and point to other nodes in the tree. Such pointers are called clues. At the same time, the search speed is improved.
We call the process of establishing the binary tree of clues : clueing
If there is no left subtree, point the left pointer to its predecessor node.
If there is no right subtree, point the right pointer to its successor node.
5.1 Precedence Threading
Node 1 has a left child 2->node 2 has a left child -> node 4 has no left child, so the left pointer points to the predecessor node 2->node 4 has no right child, so the right pointer points to the successor node 5->node Point 5 has no left child, so the left pointer points to the predecessor node -> node 5 has no right child, so the right pointer points to the successor node 3->node 3 has a left child 6->node 6 has no left child, so the left pointer points to the predecessor node Node 3->Node 6 has no right child and no successor node -> Then look at node 3, if it has no right child, point the right pointer to successor node 6.
5.2 In-order threading
5.3 Post-order threading
The most commonly used is the in-order clue binary tree
Obviously, in deciding whether lchild points to the left child or the predecessor, and whether rchild points to the right child or the successor, a distinguishing sign is needed. Therefore, we add two more flag fields ltag
andrtag
Node structure of clue binary tree
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
The binary linked list composed of this kind of node structure is used as the storage structure of the binary tree and is called the clue linked list . For points to the predecessor and successor pointers called clues , clues of a binary tree is called: threaded binary
5.4 Binary tree with middle-order clues
In-order clue binary tree clue code
//传入一个根节点和前驱结点
void InThread(ThreadTree &p,ThreadTree &pre){
if(p!=NULL){
InThread(p->lchild,pre); //递归左子树线索化
if(p->lchild==NULL){
//没有左孩子
p->lchild = pre; //左孩子指针指向前驱
p->ltag = 1; //前驱线索
}
if(pre!=NULL && pre->rchild==NULL){
//没有右孩子
pre->rchild = p; //前驱右孩子指针指向后继(当前结点p)
pre->rtag = 1; //后继线索
}
pre = p; //修改前驱结点为当前结点
InThread(p->rchild,pre); //递归右子树线索化
}
}
Initialization and finishing
//传入线索二叉树的根节点
void CreateInThread(ThreadTree T){
ThreadTree pre=NULL;
if(T!=NULL){
InThread(T,pre); //实现线索化
pre->rchild=NULL; //收尾 最后遍历的结点的右孩子至为空
pre->rtag=1;
}
}
Introducing the clue binary tree of the head node
Traversal of binary tree with middle-order clues
//找最左侧的孩子结点
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag==0){
p=p->lchild;
}
return p;
}
//找后继结点
ThreadNode *Nextnode(ThreadNode *p){
if(p->rtag == 0){
return Firstnode(p->rchild);
}
return p->rchild;
}
void InOrder(ThreadNode *T){
for(ThreadNode *p=Firstnode(T);p!=NULL;p=Nextnode(p)){
visit(p);
}
}
No knowledge of public data structure processing wood off the synchronous update, the next time will explain: Trees and forest-related knowledge , welcome everyone's attention