[Data Structure] Binary Tree (2)

Table of contents

1. Binary tree chain structure and implementation

 1. The structure of the binary tree

 2. Binary tree traversal

   2.1 Preorder traversal

   2.2 Inorder traversal

   2.3 Post-order traversal

   2.4 Layer order traversal

 3. Implementation of binary tree chain structure

   3.1 Create a node

   3.2 Number of Binary Tree Nodes 

   3.3 Number of Leaf Nodes in Binary Tree

   3.4 Height of Binary Tree

   3.5 The number of nodes in the kth layer of the binary tree

   3.6 Find the node whose value is x in the binary tree

   3.7 Determine whether a binary tree is a complete binary tree

2. Binary tree basic oj exercises

 1. Single-valued binary tree

   1.1 Description of the topic 

   1.2 Topic analysis

 2. Check if two trees are the same

   2.1 Description of the topic

   2.2 Topic analysis

 3. Symmetric binary tree

   3.1 Description of the topic

   3.2 Topic analysis

 4. Preorder traversal of binary tree

   4.1 Description of the topic

   4.2 Topic analysis

 5. Inorder traversal of binary tree

   5.1 Description of the topic

   5.2 Topic analysis

 6. Postorder traversal of binary tree

   6.1 Description of the topic

   6.2 Topic Analysis

 7. A subtree of another tree

   7.1 Description of the topic

   7.2 Topic Analysis

 


1. Binary tree chain structure and implementation

 1. The structure of the binary tree

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

Binary tree chain structure type, this is a node type, including left and right child nodes, and the data value of the node.

 2. Binary tree traversal

The easiest way to learn the binary tree structure is to traverse it. The so-called binary tree traversal (Traversal) is to perform corresponding operations on the nodes in the binary tree in turn according to certain specific rules, and each node is only operated once . The operations performed by the access nodes depend on the specific application problem. Traversal is one of the most important operations on a binary tree, and it is also the basis for other operations on a binary tree. The 4 traversal methods are given below.

   2.1 Preorder traversal

First visit the root node, then visit the left subtree, and finally visit the right subtree.

//二叉树前序遍历
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

   2.2 Inorder traversal

First visit the left subtree, then visit the root node, and finally visit the right subtree.

//二叉树中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

   2.3 Post-order traversal

First visit the left subtree, then visit the right subtree, and finally visit the root node.

//二叉树后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

   2.4 Layer order traversal

Layer order traversal is to visit according to the nodes of each layer of the binary tree.

Idea: You need to create a queue, first put the root node into the queue, and then when the queue is not empty, the head node will be out, if the left subtree of the head node is not empty, its left subtree node will be in the queue , if the right subtree of the queue head node is not empty, its right subtree node will enter the queue and cycle in turn until there are no nodes in the queue. At this time, the hierarchical traversal ends.

//二叉树层序遍历
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

 3. Implementation of binary tree chain structure

   3.1 Create a node

To implement a binary tree chain structure, you must first create a node, and initialize it after creating the node.

//创建一个节点
BTNode* BuyBTNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->left = node->right = NULL;
	return node;
}

   3.2 Number of Binary Tree Nodes 

Two methods are given here. The first one is through global variables, and the total number of each node is ++. The second method is to simplify it, the number of left subtree nodes + the number of right subtree nodes + root node.

//二叉树节点个数
int size = 0;
int TreeSize1(BTNode* root)
{
	if (root == NULL)
		return 0;
	
	//前序遍历
	size++;
	TreeSize1(root->left);
	TreeSize1(root->right);
    return size;
}

//二叉树节点个数
int TreeSize2(BTNode* root)
{
	return root == NULL ? 0 : TreeSize2(root->left) + TreeSize2(root->right) + 1;
}

   3.3 Number of Leaf Nodes in Binary Tree

Calculating the number of leaf nodes needs to be discussed according to the situation. If the root node is empty, it is equal to 0. If the left and right subtrees of the root node are empty, the number of leaf nodes is 1, and the rest are the left and right children of the root node. When the tree is not empty anymore, traverse the left subtree and right subtree to calculate the number of leaf nodes.

//二叉树叶子节点个数
int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;

	if (root->left == NULL && root->right == NULL)
		return 1;
	
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

   3.4 Height of Binary Tree

The height of a binary tree is the maximum height of the left and right subtrees. It needs to recurse to the last layer, and then + the height of the current node.

//二叉树的高度或深度(后序遍历)
int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

   3.5 The number of nodes in the kth layer of the binary tree

It is also necessary to recurse the left and right subtrees of the root node to see how many nodes there are in the left and right subtrees of each layer, and then add them up.

//二叉树第k层的节点个数 k >= 1
int TreeKLevelSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	//k > 1 子树的k-1
	return TreeKLevelSize(root->left, k - 1) + TreeKLevelSize(root->right, k - 1);
}

   3.6 Find the node whose value is x in the binary tree

The method of finding a node in a binary tree also uses recursion. First, judge whether the binary tree is empty. If it is empty, x does not exist. If the value at the root node is equal to x, it is found. If not, proceed downward. First traverse the left subtree, then traverse the right subtree until x is found.

//二叉树查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* ret1 = TreeFind(root->left, x);
	if (ret1)
		return ret1;

	BTNode* ret2 = TreeFind(root->right, x);
	if (ret2)
		return ret2;
	return NULL;
}

   3.7 Determine whether a binary tree is a complete binary tree

Idea: Same as the above layer sequence traversal, if you also use queues, you still put nodes into the queue layer by layer, and then leave the queue. When the queue encounters an empty node, stop entering the queue. Then judge whether there are all empty nodes in the queue, if they are all empty nodes, it is a complete binary tree, otherwise it is not a complete binary tree.

//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	//出到空以后,如果后面全是空,则是完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

2. Binary tree basic oj exercises

 1. Single-valued binary tree

   1.1 Description of the topic 

    Topic link: Single-valued binary tree

A binary tree is a single- valued binary tree     if every node of the tree has the same value . Returns only if the given tree is a single-valued binary tree  true; otherwise  false.

 

   1.2 Topic analysis

    Idea: If the tree is empty, it means that it is a single-valued binary tree. If its left subtree is not empty and the node value of the left subtree is not equal to the value of the root node, then return false. If its right subtree is not empty And the node value of the right subtree is not equal to the value of the root node, then return false.

bool isUnivalTree(struct TreeNode* root){
    if(root == NULL)
    return true;

    if(root->left && root->left->val != root->val)
        return false;
    
    if(root->right && root->right->val != root->val)
        return false;

    return isUnivalTree(root->left) && isUnivalTree(root->right);
}

 2. Check if two trees are the same

   2.1 Description of the topic

   Link to the topic: the same tree

Given the root nodes p and q of your two binary trees, write a function to check whether the two trees are the same. Two trees are considered identical if they are structurally identical and the nodes have the same value.

 

 

   2.2 Topic analysis

   Idea: Determine whether the root, left subtree, and right subtree are the same—— 1. Determine whether the structures are the same 2. Determine whether the vals are the same.

First of all, there are two special cases, that is, if the root node p and the root node q are both empty, it means that the two trees are the same tree; if one of the root node p and the root node q is empty, it means that the two trees are not same.

If the value of node p is not equal to the value of node q, it is obvious that the two trees are not the same, and finally traverse the left and right subtrees of p and q. 

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(p == NULL && q == NULL)
        return true;
    if(p == NULL || q == NULL)
        return false;
    if(p->val != q->val)
        return false;
    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

 3. Symmetric binary tree

   3.1 Description of the topic

   Topic Link: Symmetric Binary Tree

Given the root node root of a binary tree, check if it is axisymmetric.

 

   3.2 Topic analysis

   Idea: In order to conveniently judge whether the tree is axisymmetric, we first write a function to judge its axisymmetric.

The axisymmetric function is actually the same as the above judgment of whether the two trees are the same, that is, at the last recursion, the left subtree of root1 and the right subtree of root2 are required to judge whether they are the same, or the right subtree of root1 and the right subtree of root2 The left subtree is judged whether they are the same, if they are the same, it means that it is a symmetric binary tree.

bool _isSymmetric(struct TreeNode* root1,struct TreeNode* root2)
{
    if(root1 == NULL && root2 == NULL)
        return true;
    
    if(root1 == NULL || root2 == NULL)
        return false;

    if(root1->val != root2->val)
        return false;

    return _isSymmetric(root1->left,root2->right) && _isSymmetric(root1->right,root2->left);
}

bool isSymmetric(struct TreeNode* root){
    return !root || _isSymmetric(root->left,root->right);
}

 4. Preorder traversal of binary tree

   4.1 Description of the topic

   Topic link: Preorder traversal of binary tree

Given the root node root of your binary tree, return the preorder traversal of its node values   .

   4.2 Topic analysis

   Idea 1: This method is recursive . First, write an algorithm for preorder traversal. The access order of preorder traversal is the root node, then the left subtree, and finally the right subtree.

class Solution {
public:
    void preorde(TreeNode* root,vector<int>& ret)
    {
        if(root == nullptr)
            return;
        ret.push_back(root->val);
        preorde(root->left,ret);
        preorde(root->right,ret);
        
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;
        preorde(root,ret);
        return ret;
    }
};

   Idea 2: This method is a non- recursive form . This idea is to divide a tree into two parts: (1) the left node; (2) the right subtree of the left node. The stack used here is used to access the right subtree of the left node.

 

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur = root;
        while(cur || !s.empty())
        {
            while(cur)
            {
                v.push_back(cur->val);
                s.push(cur);
                cur = cur->left;
            }
            TreeNode* top = s.top();
            s.pop();
            cur = top->right;
        }
        return v;
    }
};

 5. Inorder traversal of binary tree

   5.1 Description of the topic

   Topic link: Inorder traversal of a binary tree

   Given the root node root of your binary tree, return the inorder  .

   5.2 Topic analysis

   Idea 1: Using recursion, first write an algorithm for in-order traversal. The access order of in-order traversal is the left subtree, then the root node, and finally the right subtree.

class Solution {
public:
    void inorder(TreeNode* root, vector<int>& ret) {
        if (root == NULL) 
            return;
        inorder(root->left, ret);
        ret.push_back(root->val);
        inorder(root->right, ret);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        inorder(root, ret);
        return ret;
    }
};

    Idea 2: Use a non-recursive method to divide a binary tree into two parts: (1) the left node; (2) the right subtree of the left node. It is the same as the non-recursive method of preorder traversal above, but the difference is that the order of entering v is different.

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur = root;
        while(cur || !s.empty())
        {
            while(cur)
            {
                s.push(cur);
                cur=cur->left;
            }
            TreeNode* top = s.top();
            s.pop();
            v.push_back(top->val);

            cur = top->right;
        }
        return v;
    }
};

 6. Postorder traversal of binary tree

   6.1 Description of the topic

   Topic link: Post-order traversal of a binary tree

   Given the root node root of your binary tree, return the postorder  .

   6.2 Topic Analysis

    Idea 1: In a recursive way, first write an algorithm for post-order traversal. The access order of post-order traversal is the left subtree, then the right subtree, and finally the root node.

class Solution {
public:
    void postorder(TreeNode *root, vector<int> &ret) 
    {
        if (root == nullptr) 
        {
            return;
        }
        postorder(root->left, ret);
        postorder(root->right, ret);
        ret.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> ret;
        postorder(root, ret);
        return ret;
    }
};

   Idea 2: Use a non-recursive method to divide a binary tree into two parts: (1) the left node; (2) the right subtree of the left node. Note: Assume that when 6 is fetched for the first time, the last visited node is the root 4 of the left subtree, and when 6 is fetched for the second time, the last visited node is the root 7 of the right subtree. So there is another constraint top->right == prev .

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur = root;
        TreeNode* prev = nullptr;
        while(cur || !s.empty())
        {
            while(cur)
            {
                s.push(cur);
                cur = cur->left;
            }
            TreeNode* top = s.top();
            //1.右为空 或者 右子树已经访问过了(上一个访问的节点是右子树的根),可以访问根节点
            if(top->right == nullptr || top->right == prev)
            {
                v.push_back(top->val);  
                s.pop(); 
                prev = top;
            }
            else
            {
                //访问左路节点右子树  ---- 子问题
                cur = top->right;
            }
        }
        return v;
    }
};

 7. A subtree of another tree

   7.1 Description of the topic

   Topic Link: Subtree of Another Tree

You are given two binary trees root and subRoot. Tests that root contains a subtree with the same structure and node values ​​as subRoot. Returns true if present; otherwise, returns false.

A subtree of the binary tree tree includes a certain node of the tree and all descendant nodes of this node. A tree can also be seen as a subtree of itself.

   7.2 Topic Analysis

   Idea: Three steps: 1. Judging whether they are the same tree 2. Judging whether subRoot is the left subtree of root 3. Judging whether subRoot is the right subtree of root.

bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    if (p == NULL && q == NULL)
        return true;
     //其中一个为空
    if (p == NULL || q == NULL)
        return false;
    if (p->val != q->val)
        return false;
    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    if(root == NULL)
        return false;
    if(isSameTree(root,subRoot))
        return true;
    return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}

   

   


If there are deficiencies in this article, you are welcome to comment below, and I will correct it as soon as possible. 

Old irons, remember to like and pay attention!!!  

Guess you like

Origin blog.csdn.net/m0_63198468/article/details/131097443