Class notes: storage structure and implementation of binary tree

Sequential storage structure The sequential storage structure of the
binary tree is to use a one-dimensional array to store the nodes in the binary tree, and the storage location (subscript) of the nodes should reflect the logical relationship between the nodes-the parent-child relationship.
The sequence numbers of the nodes in the complete binary tree and the full binary tree can uniquely reflect the logical relationship between the nodes.
After a binary tree is transformed into a complete binary tree, many empty nodes need to be added, which causes a waste of storage space.
The sequential storage structure of a binary tree generally stores only a complete binary tree.
The
basic idea of ​​a binary linked list : make each node of the binary tree correspond to a linked list node. In addition to storing data information related to the binary tree node, the linked list node also needs to set pointers indicating the left and right children.

template<class T>
struct BiNode {
    T data;     
    BiNode<T> *lchild, *rchild;
};

In a binary linked list with n nodes, there are n + 1 null pointers.
Class declaration of binary linked list storage structure

template<class T>
class BiTree {
public:        
	BiTree();          
	~BiTree( );                     
	void PreOrder(){PreOrder(root);}          
	void InOrder() {InOrder(root);}          
	void PostOrder() {PostOrder(root);}          
	void LevelOrder(){LeverOrder(root)};   
private:         
	BiNode<T> *root;          
	BiNode<T> * Creat( );          
	void Release(BiNode<T> *root);
	void PreOrder(BiNode<T> *root);          
	void InOrder(BiNode<T> *root);          
	void PostOrder(BiNode<T> *root);          
	void LevelOrder(BiNode<T> *root);
};

Preorder traversal-recursive algorithm

template<class T>
void BiTree::PreOrder(BiNode<T> *root)  
{
	if(root ==NULL)  
		return;              
	else{             
		cout<<root->data;                      
		PreOrder(root->lchild);                 
		PreOrder(root->rchild);            
	}
}

Preorder traversal-non
-recursive algorithm The key to the non -recursive algorithm of binary tree preorder traversal : how to find the root pointer of the right subtree of a node after the preorder traversed the entire left subtree of a node.
Solution : After accessing the node, save the pointer of the node in the stack, so that you can find the right subtree of the node through it later.
The stack is the most commonly used structure to implement recursion.
Idea : When you encounter a node, you visit the node and push the node into the stack, and then traverse its left subtree; after traversing its left subtree, the node is pulled from the top of the stack, And follow the address indicated by its right link to traverse the right subtree structure of the node.
Pre-order traversal-non-recursive algorithm (pseudo code)
1. Initialize stack s (empty stack);
2. Loop until root is empty and stack s is empty  
2.1 Loop when root is not empty   
2.1.1 Output root-> data;  
2.1.2 Save the value of the pointer root to the stack;  
2.1.3 Continue to traverse the left subtree of root (root = root-> lchild)  
2.2 If the stack s is not empty, then   
2.2.1 pop the top element of the stack to root ( root = s.pop ());   
2.2.2 Prepare to traverse the right subtree of root (root = root-> rchild);

template<class T>
void BiTree::PreOrder(BiNode<T> *root) {   
	SeqStack<BiNode<T> *>  s;      
	while (root!=NULL | | !s.empty()) {          
		while (root!= NULL)  {              
			cout<<root->data;              
			s.push(root);              
			root=root->lchild;     
		}          
		if (!s.empty()) {               
			root=s.pop();              
			root=root->rchild;            
		}      
	} 
}

The establishment
of a binary tree In order to build a binary tree, the null pointer of each node in the binary tree is led to a virtual node, the value of which is a specific value such as "#" to identify it as empty, and the binary tree after such processing is called The extended binary tree of the original binary tree.
Let the nodes in the binary tree be one character. Suppose the preorder traversal sequence of the extended binary tree is input by the keyboard, and root is a pointer to the root node. The establishment process of the binary list is:
1. The value of the input node of the traversal sequence of the extended preorder
2. If the input node value "#", Then create an empty subtree
3. Otherwise, the root node applies for space and writes the input value into the data field.
4. Create the left subtree of the root node in the same way
5. Use the same Method to create the right subtree of the root node

template<class T>
BiTree ::BiTree(){
	root=creat()}
template<class T> 
BiNode<T> * BiTree ::Creat(){      
	BiNode<T> *root; 
	char ch;     
	cin>>ch;     
	if(ch=='#')     
		root=NULL;      
	else{         
		root=new BiNode<T>;          
		root->data=ch;         
		root->lchild=creat();          
		root->rchild=creat();
	}     
	return root 
}
template<class T>
BiTree<T>::BiTree( )
{
	Creat(root);
}
template<class T>
void BiTree<T>::Creat(BiNode<T> * &root)
{
	T ch;
	cout<<"请输入创建一棵二叉树的结点数据"<<endl;
	cin>>ch;
	if(ch=="#")
		root = NULL;
	else{
		root = new BiNode<T>;                    
		root->data=ch;                   
		Creat(root->lchild );                   
		Creat(root->rchild);      
	}  
}

In-order traversal-recursive algorithm

template<class T>
void BiTree::InOrder(BiNode<T> *root)
{          
	if(root==NULL) 
		return;              
	else{                
		InOrder(root->lchild);                 
		cout<<root->data;                 
		InOrder(root->rchild);          
	}
}

The
idea of traversing a binary tree in a non-recursive mid-order : when a node is encountered, it is pushed onto the stack, and its left subtree is traversed. After the left subtree is traversed, the node is pulled from the top of the stack and accessed. Then follow the address indicated by its right link to traverse the right subtree of the node.
Mid-order traversal-non-recursive algorithm (pseudo code)
1. Initialize stack s (empty stack);
2. Loop until root is empty and stack s is empty  
2.1 Loop when root is not empty
2.1.1 Save the value of pointer root
Go to the stack;  2.1.2 Continue to traverse the left subtree of root (root = root-> lchild)  
2.2 If the stack s is not empty, then   
2.2.1 pop the top element of the stack to root (root = s.pop ());   
2.2.2 Output root-> data;   
2.2.3 Prepare to traverse the right subtree of root (root = root-> rchild);

template<class T>
void BiTree::InOrderwithoutD (BiNode<T> *root)
{       
	stack< BiNode<T> * > aStack;
	while(!aStack.empty()||root){
		while(root){   
			aStack.push(root);   
			root=root->lchild;
		}      
		if(!aStack.empty()){         
			root=aStack.top();             
			aStack.pop();                   
			cout<<root->data;                  
			root=root->rchild;      
		}   
	} 
} 

Post-order traversal-recursive algorithm

template<class T>
void BiTree::PostOrder(BiNode<T> *root)
{      
	if(root==NULL) 
		return;      
	else{          
		PostOrder(root->lchild);           
		PostOrder(root->rchild);           
		cout<<root->data;               
	}
}

Non-recursive postorder traversal binary tree
idea : encounter a node, push it into the stack, traverse its left subtree, after the left subtree traversal, you can not immediately access the node at the top of the stack, but to Then it traverses the right subtree of the node according to the address indicated by its right link structure. After traversing the right subtree, the node can be accessed from the top of the stack and accessed.
Solution : You need to add a feature bit to each element in the stack, so that when a node is popped from the top of the stack, the difference is whether it returns from the left of the top element of the stack (you must continue to traverse the right subtree) or return from the right (The left and right subtrees of this node have been traversed). The feature Left indicates that the left subtree that has entered the node will return from the left; the feature Right indicates that the right subtree that has entered the node will return from the right.
Algorithm analysis
1. Define a stack; start traversing from the root node, p = root, if, root == NULL, do not traverse;
2. Unconditionally perform the following work
① If the pointer is not empty, the pointer is marked with a left, and the Pointer to the stack, execute ②; otherwise, execute ③ ②
p = p-> lchild, repeat ①
③ stack top element out of the stack p
④ view the P logo, if the flag is right, do the following work, otherwise, perform ⑤
a) visit The current node p
b) If the stack is empty, the algorithm ends;
c) Otherwise, the top element of the stack goes out of the stack, go to ④
⑤ Modify the p flag, let p re-enter the stack, p = p-> rchild, execute ②
The element type in the stack Define StackElement

enum Tags{Left,Right}; 
template<class T>
class StackElement
{  
public:  
	BiTreeNode<T>* pointer;
	Tags tag;
}; 
#include<stack>
Using namespace std;
template<class T>
void BiTree<T>::PostOrderWithoutRecusion(BiTreeNode<T>* root){
	StackElement<T> element;  
	stack<StackElement<T>> aStack;
	BiTreeNode<T>* pointer;
	if(root==NULL)
		return;
	else
		pointer=root;      
	while(true){    
		while(pointer!=NULL){
			element.pointer=pointer;
			element.tag=Left;   
			aStack.push(element);   
			pointer=pointer->lchild;
		}
		element=aStack.pop();
		pointer=element.pointer; 
		while(element.tag==Right){         
			cout<<pointer->data;         
			if(aStack.empty())  
				return;      
			else{         
				element=aStack.pop(); 
				pointer=element.pointer;      
			}
		}
		element.tag=Right;      
		aStack.push(element);     
		pointer=pointer->rchild();       
	}
}

The non-recursive traversal summary of the binary tree is
accessed along the left branch until the left branch is empty, and then the right branch of the node in the stack is processed in turn. (Following the traversal principle from left to right, embodying the idea of ​​depth-first search)
Pre-order traversal : each node is pushed into the stack only once, and the node is accessed before pushing into the stack.
Mid-order traversal : each node is pushed into the stack once, and the node is accessed when it goes out of the stack.
Post-order traversal : Each node is pushed into the stack twice, and the node is accessed the second time it is pushed out of the stack.
The non-recursive post-order traversal algorithm 2
needs to be implemented with a stack. According to the requirements of the subsequent traversal and the characteristics of the stack operation (FILO), the root node, the right son of the root node, and the left son of the root node are pushed into the stack in sequence (no access) ), And then visit when the node is out of the stack. When a leaf node appears on the top of the stack, the stack operation is performed directly, and when the relationship between the element just popped out of the stack and the top element of the stack is a "son-parent" relationship, the stack operation is performed.

void tree::T_print(bnode *bt){     
	stack<bnode*> s;     
	bnode *cur, *pre=NULL;     
	if (root==NULL) 
		return;     
	s.push(bt);
	while (!s.empty()){         
		cur=s.top();         
		if((cur->Lchild==NULL&&cur->Rchild==NULL)||(pre!=NULL&&(pre==cur->Lchild||pre==cur->Rchild)))
		{             
			cout<<cur->data;            
			s.pop();            
			pre=cur;         
		}         
		else
		{             
			if(cur->Rchild!=NULL)            
				s.push(cur->Rchild);             
			if(cur->Lchild!=NULL)            
				s.push(cur->Lchild);
		}     
	} 
}

Sequence traversal
1. Queue Q initialization;
2. If the binary tree is not empty, the root pointer is enqueued;
3. Loop until the queue Q is empty
3.1 q = queue Q head element of the queue is dequeued;
3.2 access to the data field of node q ;
3.3 If there is a left child at node q, the left child pointer will be enqueued;
3.4 If there is a right child at node q, then the right child pointer will be enqueued;

#include<queue>
using namespace std;
template<class T>
void BiTree<T>::LevelOrder(BinaryTreeNode<T>* root){ 
	queue<BiTreeNode<T>*> aQueue;
	if(root)   
		aQueue.push(root); 
	while(!aQueue.empty())
	{   
		root=aQueue.front();
		aQueue.pop();
		cout<<pointer->data;
		if(root->lchild)
			aQueue.push(root->lchild);
		if(root->rchild)
			aQueue.push(root->rchild);
	}
}

Destructuring of Binary Tree

template<class T>
void BiTree<T>::Release(BiNode<T>* root){   
	if(root != NULL){                         
		Release(root->lchild);
		Release(root->rchild);
		delete root;
	}
}
template<class T>
BiTree<T>::~BiTree(void)
{  
	Release(root);
}

The trigeminal linked list
adds a pointer field to the parent on the basis of the bifurcated linked list.
The realization of trigeminal linked list
node data type declaration:

template<class T>
struct Node
{  
	T data;  
	Node<T> * lchild, *rchild,*parent; 
};
Published 48 original articles · Like 25 · Visit 2453

Guess you like

Origin blog.csdn.net/qq_43628959/article/details/103060090