Data Structure: Elementary Linked Binary Tree

Table of contents

1. Logical structure of chained binary tree

1. Definition of node structure of chained binary tree

2. Chained binary tree logical structure

2. Traversal algorithm of chained binary tree

1. Preorder traversal

2. Inorder traversal

3. Post-order traversal 

4. Level order traversal (binary tree non-recursive traversal algorithm)

Layer order traversal concept:

Implementation idea of ​​layer sequence traversal algorithm: 

Layer sequence traversal code implementation:

3. Application of chained binary tree traversal algorithm

1. Application of preorder traversal algorithm

Related exercises: 

2. Application of post-order traversal algorithm

3. Application of layer sequence traversal algorithm

Source of problem:

4. Implementation of other operation interfaces of chained binary tree

1. Interface to calculate the number of binary tree nodes

2. An interface for calculating the number of leaf nodes in a binary tree

3. The interface for calculating the number of nodes in the kth layer of the binary tree

4. Binary tree node lookup interface (find a node whose value is x in the binary tree)


1. Logical structure of chained binary tree

1. Definition of node structure of chained binary tree

  • Tree node structure definition:
    
    typedef int BTDataType;
    typedef struct BinaryTreeNode
    {
    	BTDataType data;                //数据域
    	struct BinaryTreeNode* left;    //指向结点左孩子的指针
    	struct BinaryTreeNode* right;   //指向结点右孩子的指针
    }BTNode;
    

2. Chained binary tree logical structure

  • Here first violently "create" a binary tree:
    //在内存堆区上申请结点的接口
    BTNode* BuyNode( BTDataType x)
    {
    	BTNode* tem = (BTNode*)malloc(sizeof(BTNode));
    	assert(tem);
    	tem->data = x;
    	tem->left = NULL;
    	tem->right = NULL;
    	return tem;
    }
    int main ()
    {
        BTNode* node1 = BuyNode(1);
    	BTNode* node2 = BuyNode(2);
    	BTNode* node3 = BuyNode(3);
    	BTNode* node4 = BuyNode(4);
    	BTNode* node5 = BuyNode(5);
    	BTNode* node6 = BuyNode(6);
    	node1->left = node2;
    	node1->right = node4;
    	node2->left = node3;
    	node4->left = node5;
    	node4->right = node6;
    
        //其他操作
        return 0;
    }
  • Illustration of the logical structure of the tree:

  1. Each tree node has its left subtree and right subtree

  2. Therefore, the design idea of ​​the recursive algorithm for chained binary trees is: the problem about tree T can be decomposed into its left subtree and right subtree , and the left subtree and right subtree of tree T can be solved in the same way decomposes , thus forming a recursive

  3. The core thinking about the chained binary tree recursive algorithm: left and right subtree divide and conquer thinking .

2. Traversal algorithm of chained binary tree

A recursive framework for traversing a binary tree:

  1. The abstract meaning of the function is to complete the traversal of the entire tree
  2. In order to complete the traversal of the entire tree, we must first complete the traversal of the left subtree and the right subtree of the tree
  3. The traversal of the left subtree and the right subtree can be split in the same way , thus forming recursion ( recursive iteration in mathematics )
//递归遍历二叉树的基本函数结构
void Order(BTNode* root)
{
	if (NULL == root)
	{
		return;
	}

	对当前结点进行某种处理的语句位置1(前序);
	Order(root->left);  //遍历左子树的递归语句
	对当前结点进行某种处理的语句位置2(中序);
	Order(root->right); //遍历右子树的递归语句
	对当前结点进行某种处理的语句位置3(后序);
}
  • Through this recursive function, we can traverse each node of the binary tree: (assuming the address of the root node is root)
  • According to the position of the code statement that performs some processing on the current root node in the function , the way to traverse the binary tree can be divided into pre-order traversal (position 1), in-order traversal (position 2), and post-order traversal (position 3). kind

1. Preorder traversal

//二叉树前序遍历
void PreOrder(BTNode* root)
{
	if (NULL == root)
	{
		printf("NULL->");//为了方便观察我们将空结点打印出来
		return;
	}
	printf("%d->", root->data); //当前结点处理语句
	PreOrder(root->left);       //向左子树递归
	PreOrder(root->right);      //向右子树递归
}

  • Take the tree in the graph as an example to call the preorder traversal function:
    PreOrder(root); //root为树根地址

    The logical structure distribution of the function stack frame and the execution order of code statements during the execution of the recursive function :

    The physical structure distribution of the function stack frame during the execution of the recursive function :

  1. When the recursive execution reaches step 4 in the figure, it is the physical structure of the function stack frame :

  2. When the recursive execution reaches step 10 in the figure, it is the physical structure of the function stack frame : 

  3. When the recursive execution reaches step 14 in the figure, it is the physical structure of the function stack frame : 

  4. When the recursive execution reaches step 22 in the figure, it is the physical structure of the function stack frame : 

  • The maximum number of function stack frames opened at the same time during the execution of the recursive function depends on the height of the tree : so the space complexity of recursively traversing the binary tree is: O(logN);

2. Inorder traversal

//二叉树中序遍历
void InOrder(BTNode* root)
{
	if (NULL == root)
	{
		printf("NULL->");   //为了方便观察我们打印出空结点
		return;
	}
	InOrder(root->left);        //向左子树递归
	printf("%d->", root->data); //打印结点值(root处理语句)
	InOrder(root->right);       //向右子树递归
}
  •  The recursive function execution process analysis method is similar to the preorder traversal
  • For the binary tree in the graph, the printing order of the node values ​​in the in-order traversal is:

3. Post-order traversal 

//二叉树后序遍历
void BackOrder(BTNode* root)
{
	if (NULL == root)
	{
		printf("NULL->");       //为了方便观察我们打印出空结点
		return;
	}
	BackOrder(root->left);      //向左子树递归
	BackOrder(root->right);     //向右子树递归
	printf("%d->", root->data); //打印结点值(root处理语句)
}
  •  The recursive function execution process analysis method is similar to the preorder traversal
  • For the binary tree in the graph, the printing order of the node values ​​in the post-order traversal is:

4. Level order traversal (binary tree non-recursive traversal algorithm)

Layer order traversal concept:

  • The layer order traversal of the binary tree is a binary tree traversal algorithm implemented by means of the node pointer queue cycle

  • The traversal order of tree nodes is from low level to high level , and the same level is traversed from left to right :

Implementation idea of ​​layer sequence traversal algorithm: 

  • Create a queue and insert the address of the root node of the tree into the queue ; ( the data in the queue is first in first out, and the data enters from the tail of the queue and exits from the head of the queue )
  • Then start executing the loop statement
  • Each cycle, take out a pointer at the head of the queue , and process it ( print the value, etc. ), if the pointer to the head of the queue is not a null pointer , insert the left and right child pointers of the node pointed to by the pointer to the queue , and repeat Loop until the queue is empty .
  • The sequence diagram of the tree node pointer enqueuing order:
  • After analysis, it can be seen that the tree node addresses in the queue are arranged according to the logical sequence of the tree nodes (starting from the root node, the pointer of the previous layer node will be brought into the pointer of the next layer node after being dequeued) ), so the algorithm can complete the layer-order traversal of the binary tree
  • Algorithm gif illustration:

Layer sequence traversal code implementation:

  • For convenience, we directly use the queue class template of C++STL ( the queue object data entry (tail insertion) interface of VS2022 is push , and the data dequeue (head deletion) interface is pop ) (different compiler interface names may be different )
  • If you want to use C language, you need to manually implement the queue yourself
void LevelOrder(BTNode* root)
{
	queue<BTNode*> NodeStore;			//创建一个队列用于存储结点指针
	NodeStore.push(root);				//将根结点指针插入队列中

	while (!NodeStore.empty())			//若队列不为空则循环继续
	{
		BTNode* tem = NodeStore.front();//保存队头指针
		NodeStore.pop();				//队头指针出队
		if (nullptr != tem)
		{
			cout << (tem->data)<<' ';	//处理出队的结点(这里是打印其结点值)
		}
		if (nullptr != tem)				//若出队结点不为空,则将其孩子结点指针带入队列
		{
			NodeStore.push(tem->left);  //将出队结点的左孩子指针带入队列
			NodeStore.push(tem->right); //将出队结点的右孩子指针带入队列
		}
	}
	cout << endl;
}


3. Application of chained binary tree traversal algorithm

1. Application of preorder traversal algorithm

Preorder traversal recursion:

  1. root first
  2. recurse left subtree
  3. Recurse to the right subtree
  • Since the root processing statement is executed first in the pre-order recursive function , we can use the pre-order traversal recursive framework to realize the recursive construction of the chained binary tree ( under the pre-order recursive framework, we can create the root node first , and then Create the left and right subtrees of the root node , inorder and postorder recursion cannot complete the recursive construction of the tree)

Related exercises: 

Binary Tree Traversal_Niu Ke Title Ba_ Niu Ke.com (nowcoder.com) https://www.nowcoder.com/practice/4b91205483694f449f94c179883c1fef?tpId=60&&tqId=29483&rp=1&ru=/activity/oj&qru=/ta/tsing-kaoyan/ question-ranking (this question comes from Tsinghua University OJ)

Problem Description:

Write a program that reads in a string of preorder traversal strings input by the user , and builds a binary tree (stored by pointers) based on the strings.

For example: Preorder traversal of strings: ABC##DE#G##F### where "#" represents a space , and the space character represents an empty tree . After the binary tree is established, perform an in-order traversal on the binary tree and output the traversal result.

( the input string, the length does not exceed 100 . )

Example:

  • Enter: abc##de#g##f###
  • c b e g d f a 

problem analysis:

  • Taking the string "abc##de#g##f###" as an example, first analyze the binary tree structure corresponding to the string in the logical order of preorder traversal : if the tree is printed in the logical order of inorder , the result is :c-->b-->e-->g-->d-->f-->a; The logical position of the empty tree represented by each # in the tree is given below:
  • Recursive build analysis:
  • Further analysis shows that a given string sequence must satisfy the preorder sequence logic of a binary tree , otherwise the string sequence cannot be converted into a complete binary tree
  • With the help of recursive analysis , we can realize the recursive construction code of the binary tree
  1. For the convenience of understanding, first give the test code of the main function , and get familiar with the calling method of the recursive function :
    int main() 
    {
        char arr[101] = {0};
        scanf("%s",arr);                        //输入待转换的字符串
        int ptr = 0;                            //用于遍历字符串的下标变量
        TreeNode * root = CreateTree(arr,&ptr); //调用建树函数
        InOrder(root);                          //对树进行中序遍历
      
        return 0;
    }
  2. The head of the tree building recursive function:

    TreeNode * CreateTree(char * input,int* ptr)

  • input is the first address of the string to be converted

  • ptr is the address of the subscript variable used to traverse the string , here must pass the address , because we want to modify the same character array subscript variable in different function stack frames

  • Function implementation:

    TreeNode * CreateTree(char * input,int* ptr)
    {
        if('#'==input[*ptr])            //#代表空树,直接将空指针返回给上层结点即可
        {
            ++*ptr;
            return nullptr;
        }
        TreeNode * root = new TreeNode;     //申请新结点
        root->word = input[*ptr];           //结点赋值 
        ++*ptr;                             //字符数组下标加一处理下一个字符
        root->left = CreateTree(input,ptr); //向左子树递归
        root->right = CreateTree(input,ptr);//向右子树递归
        return root;                        //将本层结点地址返回给上一层结点(或返回给根指针变量)
    }
  • Overall solution code:

    #include <iostream>
    #include <string>
    using namespace std;
    
    
    struct TreeNode  //树结点结构体
    {
        char word;
        TreeNode * left;
        TreeNode * right;
    };
    
    //ptr是字符串数组下标的地址,这里一定要传址,因为我们要在不同的函数栈帧中修改同一个字符数组下标变量
    TreeNode * CreateTree(char * input,int* ptr)
    {
        if('#'==input[*ptr])            //#代表空树,直接将空指针返回给上层结点即可
        {
            ++*ptr;
            return nullptr;
        }
        TreeNode * root = new TreeNode;     //申请新结点
        root->word = input[*ptr];           //结点赋值 
        ++*ptr;                             //字符数组下标加一处理下一个字符
        root->left = CreateTree(input,ptr); //向左子树递归
        root->right = CreateTree(input,ptr);//向右子树递归
        return root;                        //将本层
    }
    
    void InOrder(TreeNode * root)           //中序遍历递归
    {
        if(nullptr == root)
        {
            return;
        }
        InOrder(root->left);
        printf("%c ",root->word);
        InOrder(root->right);
    }
    
    int main() 
    {
        char arr[101] = {0};
        scanf("%s",arr);
        int ptr = 0;
        TreeNode * root = CreateTree(arr,&ptr);
        InOrder(root);
      
        return 0;
    }

2. Application of post-order traversal algorithm

Postorder traversal algorithm:

  1. Recurse to the left subtree first
  2. Recurse to the right subtree
  3. After processing the left and right subtrees, process the root

The idea of ​​the post-order traversal algorithm is just applicable to the destruction process of the binary tree . ( The pre-order and in-order algorithms cannot complete the destruction of the binary tree , because once we release the current node , we can no longer recurse to the left and right subtrees ( the address is lost ) )

  • Binary tree destruction interface:
    // 二叉树销毁
    void BinaryTreeDestory(BTNode* root)
    {
    	if (NULL == root)
    	{
    		return;
    	}
    	BinaryTreeDestory(root->left);
    	BinaryTreeDestory(root->right);
    	free(root);						//释放到当前结点的内存空间
    
    }
  • Illustration of the binary tree destruction process:

3. Application of layer sequence traversal algorithm

The layer order traversal algorithm can be used to check whether a binary tree is a complete binary tree ;

Source of problem:

958. Completeness Check of Binary Tree - Leetcode icon-default.png?t=N176https://leetcode.cn/problems/check-completeness-of-a-binary-tree/Problem description:

Given a binary tree  root , determine whether it is a complete binary tree.

In a  complete binary tree , all levels except the last level of the tree are completely filled, and all nodes in the last level are arranged to the left.

958. Completeness Test of Binary Tree - Leetcode

Solution interface:

class Solution 
{
public:
    bool isCompleteTree(TreeNode* root) 
    {

    }
};

 For the structural characteristics of a complete binary tree, see Qingcai's blog: http://t.csdn.cn/6qEro icon-default.png?t=N176http://t.csdn.cn/6qEro

problem analysis:

  • The structural feature of a complete binary tree is that the nodes of each layer are arranged continuously ( the numbers of each node are continuous )
  • Therefore, the layer sequence traverses the complete binary tree , and the pointers of each node are arranged continuously in the queue (that is, there will be no null pointers between the pointers of the nodes )
  • According to the structural characteristics of the complete binary tree , once a null pointer is traversed in the process of traversing the complete binary tree in sequence , it means that the last layer of the binary tree has been traversed
  • We can use the layer order traversal algorithm to traverse the binary tree . When encountering the first null pointer dequeued , check whether there are non-null node pointers in the subsequent dequeued pointers . If there are non-null pointers in the subsequent dequeued pointers It means that the tree is not a complete binary tree , otherwise it means that the tree is a complete binary tree.
  • Algorithm diagram:

 

Solution code: 

class Solution 
{
public:
    bool isCompleteTree(TreeNode* root) 
    {
        queue<TreeNode*> ans;
        ans.push(root);        //将根结点指针插入队列
        while(!ans.empty())    //队列不为空则循环继续
        {
            TreeNode* tem = ans.front();
            ans.pop();         //取出队头指针存入tem变量中
            if(nullptr == tem) //遇到第一个出队的空指针,停止循环进行下一步判断
            {
                break;
            }
            else
            {
                ans.push(tem->left);   //将出队指针的孩子结点指针带入队列
                ans.push(tem->right);
            }
        }
        while(!ans.empty())
        {
            TreeNode* tem = ans.front();
            ans.pop();         //取出队头指针存入tem变量中
            if(nullptr != tem) //第一个出队的空指针后续存在非空结点指针,说明该树不是完全二叉树
            {
                return false;
            }
        }
        return true;           //所有结点指针完成入队出队则验证了该树是完全二叉树
    }
};

 

4. Implementation of other operation interfaces of chained binary tree

Many recursive interfaces of chained binary trees are realized through the idea of ​​left and right subtree divide and conquer recursion

 Tree node structure definition:

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;                //数据域
	struct BinaryTreeNode* left;    //指向结点左孩子的指针
	struct BinaryTreeNode* right;   //指向结点右孩子的指针
}BTNode;

1. Interface to calculate the number of binary tree nodes

Function header:

int BinaryTreeSize(BTNode* root)
  • Abstract the interface function as the number of nodes A(T) of a certain tree
  • Abstract the recursive formula : A(T) = A(T->right) + A(T->left) + 1 ;
  • The meaning of the recursive formula is: the total number of nodes in tree T = the total number of nodes in the left subtree of tree T + the total number of nodes in the right subtree of tree T + 1 tree root of tree T (essentially It is the idea of ​​​​dividing and conquering the left and right subtrees )
  • From this we can design a recursive function:
    int BinaryTreeSize(BTNode* root)
    {
    	//空树则返回0
    	if (NULL == root)
    	{
    		return 0;
    	}
    
    	//化为子问题进行分治
    	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
    }
    

    Pass the address of the root node of the tree in the figure below to the recursive function:

    A simple diagram of the execution process of a recursive function: 

     

2. An interface for calculating the number of leaf nodes in a binary tree

Function header:

int BinaryTreeLeafSize(BTNode* root)
  • Abstract the interface function as the number A(T) of leaf nodes of a certain tree
  • The recursive formula can be abstracted : A(T) = A(T->left) + A(T->right)
  • The meaning of the recursive formula is: total number of leaves of tree T = total number of leaves of T's left subtree + total number of leaves of T's right subtree
  • From this we can design a recursive function:
    int BinaryTreeLeafSize(BTNode* root)
    {
    	if (NULL == root)                                      //空树不计入个数
    	{
    		return 0;
    	}
    	else if (NULL == root->left && NULL == root->right)   //判断当前树是否为树叶(是树叶则返回1,计入叶子结点个数)
    	{
    		return 1;
    	}
    	//化为子问题进行分治
    	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
    }

    Pass the address of the root node of the tree in the figure below to the recursive function:

     A simple diagram of the execution process of a recursive function: 

3. The interface for calculating the number of nodes in the kth layer of the binary tree

Function header:

int BinaryTreeLevelKSize(BTNode* root, int k)
  • Abstract the interface function as the number of k-th layer nodes A(T,k) of a certain tree
  • The recursive formula can be abstracted : A(T,k) = A(T->left,k-1) + A(T->right,k-1)
  • The meaning of the recursive formula is: the number of nodes in the kth layer of the tree T = the number of nodes in the k-1th layer of the left subtree of the tree T + the number of nodes in the k-1th layer of the right subtree of the tree T
  • From this we can design a recursive function:
    int BinaryTreeLevelKSize(BTNode* root, int k)
    {
    	assert(k >= 1);
    	//树为空返回0
    	if (NULL == root)
    	{
    		return 0;
    	}
    	else if (root && k == 1)	//问题分解到求子树的第一层结点(即求树根的个数,树根的个数为1)
    	{
    		return 1;
    	}
    	else
    	{
    		return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
    	}
    }

    Pass the address of the root node of the tree in the figure below to the recursive function:

    A simple diagram of the execution process of a recursive function: 

     

4. Binary tree node lookup interface (find a node whose value is x in the binary tree)

 Function header:

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)

Recursive ideas:

  • If the current tree is not an empty tree ( if it is an empty tree, it will directly return a null pointer ), first determine whether the root of the tree is the node you are looking for
  • If the root of the tree is not the node you are looking for, then recursively search to the left subtree
  • If the left subtree cannot be found, then go to the right subtree to recursively search
  • If the node to be found is not found in the last left and right subtrees , a null pointer is returned
  • Design a recursive function:
    BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
    {
    	//寻找到空树则返回空(说明在某条连通路径上不存在待找结点)
    	if (NULL == root)
    	{
    		return NULL;
    	}
    	//判断树根是否为待找结点,若判断为真则返回该结点地址
    	else if (root->data == x)
    	{
    		return root;
    	}
    	//若树根不是要找的结点, 则往左子树找
    	BTNode* leftret = BinaryTreeFind(root->left, x);
    	if (leftret)
    	{
    		return leftret;
    	}
    	//若左子树中不存在待找结点,则往右子树找
    	BTNode* rightret = BinaryTreeFind(root->right, x);
    	if (rightret)
    	{
    		return rightret;
    	}
    	//若左右子树中都找不到则说明不存在待找结点,返回空
    	return NULL;
    }
    

    Pass the address of the root node of the tree in the figure below to the recursive function:

    A simple diagram of the execution process of a recursive function: 

The idea of ​​divide and conquer recursion is the core!!!! 

Guess you like

Origin blog.csdn.net/weixin_73470348/article/details/129416562