The creation and traversal of the binary tree implemented by C++, the little girl next door also understands the ultra-introductory

Table of contents

binary tree 

features

nature

Creation of binary tree

statement

create

Membership operator ->

Batch creation

 Creation of complete binary tree

print binary tree

Ordinary binary tree creation

Binary tree traversal

sequence traversal

preorder traversal

Inorder traversal

post order traversal

recursive method

Comparison of before, middle and after sequence

DFS traversal

Terms related to tree

special binary tree

full binary tree

complete binary tree


binary tree 

A tree is a finite set of n(n≥0) nodes. In any tree, there is only one specific node called root (Root); when n>1, the rest of the nodes can be divided into m (m>0) as a finite set of disjoint T1, T2, ..., Tm; each collection itself is a tree, and is called the subtree of the root (SubTree).

Binary Tree (Binary Tree) is a special ordered tree structure, all nodes have at most 2 subtrees.

features

(1) Each node has at most two subtrees;
(2) The subtrees of the binary tree are divided into left and right;
(3) The order of the subtrees cannot be reversed arbitrarily (ordered tree).

nature

(1) There are at most 2^(i-1) nodes (i≥1) on the i-th layer of the binary tree.
(2) A binary tree with a depth of h contains at most 2^h-1 nodes (h≥1).
(3) If there are n0 leaf nodes and n2 nodes with degree 2 in any binary tree, then there must be n0=n2+1.
(4) A full binary tree with n nodes is log2n+1 deep.
(5) If a complete binary tree with n nodes is sequentially numbered (1≤i≤n),
then, for a node numbered i (i≥1): 
  when i=1, the node is the root, It has no parent nodes.
  When i>1, the node's parent node number is i/2.
  If 2i≤n, there is a left node numbered 2i, otherwise there is no left node.
  If 2i+1≤n, there is a right node numbered 2i+1, otherwise there is no right node.


Creation of binary tree

statement

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

In C/C++ language, NULL is often used to represent a null pointer.

The definition of NULL in the header file:

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

That is, in C++, NULL is defined as an integer constant 0, while in C, it is defined as an untyped pointer constant (void*) 0 .

The C++11 standard adds a new keyword nullptr to represent a null pointer.

It is recommended to use the following binary tree declaration for C++11 and above:

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

create

#include <iostream>

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

int main() {
	
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->right->left = new TreeNode(4);
    root->right->right = new TreeNode(5);

    return 0;
}

 Create result:

Membership operator ->

A pointer to a structure or object accesses its members. When a pointer points to a structure or object, it is called a structure pointer or an object pointer. The value in the structure pointer or object pointer is the first address of the pointed structure or object. The structure or object can be accessed through the structure pointer or object pointer.

The general form of structure pointer variable definition is:

struct 结构体类型名 *指针名; //结构体指针
struct 结构体类型名 *指针名 = &一个结构体的名字; //结构体指针并赋初值
struct 结构体类型名 *指针名 = new struct 结构体类型名; //结构体指针并用new申请内存
struct 结构体类型名 *指针名 =(struct 结构体类型名 *)malloc(sizeof(struct 结构体类型名)) 
//结构体指针并用malloc申请内存 使用应包含头文件stdlib.h

Subtree root->left, root->right are also ok . The operation representation is also a member operator. The difference between the two:

Dot operator .  The left side must use the * addressing operator to get the structure or object entity pointed to by the pointer root, such as (*root); compared with the arrow-shaped member operator ->  , the left side must be a structure pointer, such as root.

    TreeNode* root = new TreeNode(1);
    (*root).left = new TreeNode(2);
    (*root).right = new TreeNode(3);
    (*(*root).right).left = new TreeNode(4);
    (*(*root).right).right = new TreeNode(5);

Batch creation

The above example only creates 5 nodes. If you want to build more nodes, adding nodes one by one is complicated to write; you can use iterable data types such as arrays or containers to create them in batches.

 Creation of complete binary tree

        _______1________
       /                \
    __2__             ___3___
   /     \           /       \
  4       5        _6        _7
 / \     / \      /  \      /  \
8   9   10  11   12   13   14   15

code:

#include <iostream>
#include <vector>
using namespace std;

struct TreeNode
{
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* buildTree(vector<int>& nums)
{
	if (nums.empty()) return nullptr;
    TreeNode *root = new TreeNode(nums.front());
    vector<TreeNode*> q = {root};
    int i = 1;
    while(!q.empty() && i < nums.size())
    {
        TreeNode *cur = q.front();
        q.assign(q.begin() + 1, q.end());
        if(i < nums.size())
        {
            cur->left = new TreeNode(nums[i++]);
            q.push_back(cur->left);
        }
        if(i < nums.size())
        {
            cur->right = new TreeNode(nums[i++]);
            q.push_back(cur->right);
        }
    }
    return root;
}

int main()
{
    vector<int> nums = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    TreeNode *root = buildTree(nums);

    return 0;
}

After creation, you can use the code to print out the binary tree for verification: 

print binary tree

#include <iostream>
#include <vector>
using namespace std;

struct TreeNode
{
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* buildTree(vector<int>& nums)
{
	if (nums.empty()) return nullptr;
    TreeNode *root = new TreeNode(nums.front());
    vector<TreeNode*> q = {root};
    int i = 1;
    while(!q.empty() && i < nums.size())
    {
        TreeNode *cur = q.front();
        q.assign(q.begin() + 1, q.end());
        if(i < nums.size())
        {
            cur->left = new TreeNode(nums[i++]);
            q.push_back(cur->left);
        }
        if(i < nums.size())
        {
            cur->right = new TreeNode(nums[i++]);
            q.push_back(cur->right);
        }
    }
    return root;
}

void levelOrderPrint(TreeNode* root)
{
    if(!root) return;
    vector<TreeNode*> q = {root};
    while(!q.empty())
    {
        int size = q.size();
        for(int i = 0; i < size; i++)
        {
            TreeNode *cur = q.front();
            q.assign(q.begin() + 1, q.end());
            cout << cur->val << " ";
            if(cur->left)
                q.push_back(cur->left);
            if(cur->right)
                q.push_back(cur->right);
        }
        cout << endl;
    }
}

int main()
{
    vector<int> nums;
    for (int i = 0; i < 15; i++)
    	nums.push_back(i+1);   
    TreeNode *root = buildTree(nums);
    levelOrderPrint(root);

    return 0;
}

output:

1
2 3
4 5 6 7
8 9 10 11 12 13 14 15

The process of printing binary tree nodes one by one is called traversal, which will be discussed later.

Ordinary binary tree creation

The following tree has 3 fewer nodes than the tree above:

        _______1________
       /                \
    __2__             ___3___
   /     \           /       \
  4       5        _6         7
 /       /        /  \         \
8       10      12   13        15

Empty nodes are generally described by null, such as: {1,2,3,4,5,6,7,8,null,10,11,null,13,null,15}.

In order not to change the description of the array, use the smallest negative integer to define null: #define null INT_MIN

code:

Increase the judgment of empty nodes if(i < nums.size() && nums[i] != null )

#include <iostream>
#include <vector>
#include <queue>
#define null INT_MIN
using namespace std;

struct TreeNode
{
    int val;
    TreeNode *left, *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* buildTree(vector<int>& nums)
{
    if (nums.empty()) return nullptr;
	TreeNode *root = new TreeNode(nums.front());
    queue<TreeNode*> q;
    q.push(root);
    int i = 1;
    while(!q.empty() && i < nums.size())
    {
        TreeNode *cur = q.front();
        q.pop();
        if(i < nums.size() && nums[i] != null)
        {
            cur->left = new TreeNode(nums[i]);
            q.push(cur->left);
        }
        i++;
        if(i < nums.size() && nums[i] != null)
        {
            cur->right = new TreeNode(nums[i]);
            q.push(cur->right);
        }
        i++;
    }
    return root;
}

void levelOrder(TreeNode* root)
{
    if(!root) return;
    queue<TreeNode*> q;
    q.push(root);
    while(!q.empty())
    {
        int size = q.size();
        for(int i = 0; i < size; i++)
        {
            TreeNode *cur = q.front();
            q.pop();
            cout << cur->val << " ";
            if(cur->left)
                q.push(cur->left);
            if(cur->right)
                q.push(cur->right);
        }
        cout << endl;
    }
}

int main()
{
    vector<int> nums = {1,2,3,4,5,6,7,8,null,10,11,null,13,null,15};
    TreeNode *root = buildTree(nums);
    levelOrder(root);
    
    return 0;
}

output: 

1
2 3
4 5 6 7
8 10 11 13 15

The above code directly uses the queue queue to store the pointers of each node of the binary tree. The queue has dedicated built-in methods pop and push for queue operations, so it is a little more convenient than the queue operation simulated by vector in the previous example.

Note: The traversal of the binary tree, such as

Queue operations are generally breadth-first traversal (BFS, Breath First Search )

The stack operation is generally depth-first traversal (DFS, Depth First Search)


Binary tree traversal

Refers to how to patrol each node in the tree according to a certain search path, so that each node is visited once and only once.
Common traversal methods are: level-order traversal, pre-order traversal, in-order traversal, and post-order traversal .

sequence traversal

If the binary tree is empty, it is a null operation; otherwise, access is performed hierarchically from top to bottom and from left to right.

Traverse results: 1 [2 3] [4 5 6 7] [8 9 10 11 12 13 14 15]

The BFS code for layer-order traversal has been released in the above chapter of printing binary trees . Here is a binary tree layer-order traversal written by recursion that I have seen:

#include <iostream>
#include <vector>
#include <queue>
#define null INT_MIN
using namespace std;

struct TreeNode
{
    int val;
    TreeNode *left, *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* buildTree(vector<int>& nums)
{
    if (nums.empty()) return nullptr;
	TreeNode *root = new TreeNode(nums.front());
    queue<TreeNode*> q;
    q.push(root);
    int i = 1;
    while(!q.empty() && i < nums.size())
    {
        TreeNode *cur = q.front();
        q.pop();
        if(i < nums.size() && nums[i] != null)
        {
            cur->left = new TreeNode(nums[i]);
            q.push(cur->left);
        }
        i++;
        if(i < nums.size() && nums[i] != null)
        {
            cur->right = new TreeNode(nums[i]);
            q.push(cur->right);
        }
        i++;
    }
    return root;
}

int countNodesAtLevel(TreeNode* root, int level)
{
    if(root == nullptr) return 0;
    if(level == 0) return 1;
    return countNodesAtLevel(root->left, level - 1) + countNodesAtLevel(root->right, level - 1);
}

TreeNode* getNodeAtLevel(TreeNode* root, int level, int index)
{
    if(root == nullptr) return nullptr;
    if(level == 0)
    {
        if(index == 0) return root;
        else return nullptr;
    }
    TreeNode *left = getNodeAtLevel(root->left, level - 1, index);
    if(left != nullptr) return left;
    return getNodeAtLevel(root->right, level - 1, index - countNodesAtLevel(root->left, level - 1));
}

void levelOrder(TreeNode* root)
{
    int level = 0;
    while(true)
    {
        int cnt = countNodesAtLevel(root, level);
        if(cnt == 0) break;
        for(int i = 0; i < cnt; i++)
        {
            TreeNode *node = getNodeAtLevel(root, level, i);
            cout << node->val << " ";
        }
        cout << endl;
        level++;
    }
}

int main()
{
    vector<int> nums = {1,2,3,4,5,6,7,8,null,10,11,null,13,null,15};
    TreeNode *root = buildTree(nums);
    levelOrder(root);
    
    return 0;
}

preorder traversal

If the binary tree is empty, it is a null operation;
otherwise (1) visit the root node; (2) traverse the left subtree in preorder; (3) traverse the right subtree in preorder.

Traverse results: 1 [2 [4 8 9] [5 10 11]] [3 [6 12 13] [7 14 15] "root left and right "

Inorder traversal

If the binary tree is empty, it is a null operation;
otherwise (1) in-order traverse the left subtree; (2) visit the root node; (3) in-order traverse the right subtree.

Traverse result: [[8 4 9] 2 [10 5 11]] 1 [[12 6 13] 3 [14 7 15]] "left root right "

post order traversal

If the binary tree is empty, it is a null operation;
otherwise (1) traverse the left subtree in postorder; (2) traverse the right subtree in postorder; (3) visit the root node.

Traverse results: [[8 9 4] [10 11 5] 2] [[12 13 6] [14 15 7] 3] 1 "left and right roots "

recursive method

#include <iostream>
#include <vector>
#include <queue>
#define null INT_MIN
using namespace std;

struct TreeNode
{
    int val;
    TreeNode *left, *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* buildTree(vector<int>& nums)
{
    if (nums.empty()) return nullptr;
	TreeNode *root = new TreeNode(nums.front());
    queue<TreeNode*> q;
    q.push(root);
    int i = 1;
    while(!q.empty() && i < nums.size())
    {
        TreeNode *cur = q.front();
        q.pop();
        if(i < nums.size() && nums[i] != null)
        {
            cur->left = new TreeNode(nums[i]);
            q.push(cur->left);
        }
        i++;
        if(i < nums.size() && nums[i] != null)
        {
            cur->right = new TreeNode(nums[i]);
            q.push(cur->right);
        }
        i++;
    }
    return root;
}

void preOrderTraversal(TreeNode* root) {
    if (root == nullptr) {
	        return;
    }
    cout << root->val << " ";
    preOrderTraversal(root->left);
    preOrderTraversal(root->right);
}

void inOrderTraversal(TreeNode* root) {
    if (root == nullptr) {
	        return;
    }
    inOrderTraversal(root->left);
    cout << root->val << " ";
    inOrderTraversal(root->right);
}

void postOrderTraversal(TreeNode* root) {
    if (root == nullptr) {
	        return;
    }
    postOrderTraversal(root->left);
    postOrderTraversal(root->right);
    cout << root->val << " ";
}

int main()
{
    vector<int> nums;
    for (int i = 0; i < 15; i++)
    	nums.push_back(i+1);
    TreeNode *root = buildTree(nums);
    preOrderTraversal(root);
    cout << endl;
    inOrderTraversal(root);
    cout << endl;
    postOrderTraversal(root);
    cout << endl;
    
    return 0;
}

output:

1 2 4 8 9 5 10 11 3 6 12 13 7 14 15
8 4 9 2 10 5 11 1 12 6 13 3 14 7 15
8 9 4 10 11 5 2 12 13 6 14 15 7 3 1

Comparison of before, middle and after sequence

The order of the core code is the key when traversing, which is to use the memory of " left and right ", " left and right " and " left and right roots " as mentioned above , and compare the positions of the left and right subtree nodes of the root node:

根左右——前序
    cout << root->val << " ";
    preOrderTraversal(root->left);
    preOrderTraversal(root->right);

左根右——中序
    inOrderTraversal(root->left);
    cout << root->val << " ";
    inOrderTraversal(root->right);

左右根——后序
    postOrderTraversal(root->left);
    postOrderTraversal(root->right);
    cout << root->val << " ";

In addition to directly printing nodes, traversal can also store the value fields of each node into an array, taking inorder as an example:

vector<int> inorderTraversal(TreeNode* root) {
    vector<int> res;
    inorderTraversal(root, res);
    return res;
}
void inorderTraversal(TreeNode* root, vector<int>& res) {
    if (root == nullptr) {
        return;
    }
    inorderTraversal(root->left, res);
    res.push_back(root->val);
    inorderTraversal(root->right, res);
}

DFS traversal

DFS will use <stack>, direct print version:

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#define null INT_MIN
using namespace std;

struct TreeNode
{
    int val;
    TreeNode *left, *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* buildTree(vector<int>& nums)
{
    if (nums.empty()) return nullptr;
	TreeNode *root = new TreeNode(nums.front());
    queue<TreeNode*> q;
    q.push(root);
    int i = 1;
    while(!q.empty() && i < nums.size())
    {
        TreeNode *cur = q.front();
        q.pop();
        if(i < nums.size() && nums[i] != null)
        {
            cur->left = new TreeNode(nums[i]);
            q.push(cur->left);
        }
        i++;
        if(i < nums.size() && nums[i] != null)
        {
            cur->right = new TreeNode(nums[i]);
            q.push(cur->right);
        }
        i++;
    }
    return root;
}

void preOrderTraversal(TreeNode* root) {
    stack<TreeNode*> st;
    TreeNode* node = root;
    while (node != nullptr || !st.empty()) {
        while (node != nullptr) {
            cout << node->val << " ";
            st.push(node);
            node = node->left;
        }
        node = st.top();
        st.pop();
        node = node->right;
    }
}

void inOrderTraversal(TreeNode* root) {
    stack<TreeNode*> st;
    TreeNode* node = root;
    while (node != nullptr || !st.empty()) {
        while (node != nullptr) {
            st.push(node);
            node = node->left;
        }
        node = st.top();
        st.pop();
        cout << node->val << " ";
        node = node->right;
    }
}

void postOrderTraversal(TreeNode* root) {
    stack<TreeNode*> st;
    TreeNode* node = root;
    TreeNode* last = nullptr; // 上一次访问的节点
    while (node != nullptr || !st.empty()) {
        while (node != nullptr) {
            st.push(node);
            node = node->left;
        }
        node = st.top();
        if (node->right == nullptr || node->right == last) { 
			// 右子树为空或已经访问过
            cout << node->val << " ";
            st.pop();
            last = node; // 更新上一次访问的节点
            node = nullptr; // 继续弹出栈顶元素
        } else { // 右子树还未访问
            node = node->right;
        }
    }
}

int main()
{
    vector<int> nums;
    for (int i = 0; i < 15; i++)
    	nums.push_back(i+1);
    TreeNode *root = buildTree(nums);
    preOrderTraversal(root);
    cout << endl;
    inOrderTraversal(root);
    cout << endl;
    postOrderTraversal(root);
    cout << endl;
    
    return 0;
}

Binary tree to array, using DFS and BFS respectively:

vector<int> DFSinorderTraversal(TreeNode* root) {
    vector<int> res;
    stack<TreeNode*> st;
    while (root != nullptr || !st.empty()) {
        while (root != nullptr) {
            st.push(root);
            root = root->left;
        }
        root = st.top();
        st.pop();
        res.push_back(root->val);
        root = root->right;
    }
    return res;
}

vector<int> BFSinorderTraversal(TreeNode* root) {
    vector<int> res;
    queue<TreeNode*> q;
    if (root != nullptr) {
        q.push(root);
    }
    while (!q.empty()) {
        TreeNode* node = q.front();
        q.pop();
        if (node->left != nullptr) {
            q.push(node->left);
        }
        res.push_back(node->val);
        if (node->right != nullptr) {
            q.push(node->right);
        }
    }
    return res;
}

Terms related to tree

Node : Contains a data element and several branches pointing to its subtree, and translated into "Node " (Node) Root : The " vertex " (Root) degree of the tree and subtree : the number of subtrees owned by the node is called the node's Degree ; the degree of a tree refers to the maximum value of the degree of a node in the tree. Branch node : a node whose degree is not 0 Leaf : a node without a subtree, that is, its degree is 0 (Leaf) child node : node The root of the subtree of a point is called the child of the node (Child) Parent node : the level node above the corresponding child node is called the parent (Parent) sibling node of the node : the child nodes of the same parent node are called each other Ancestors of Sibling nodes : from the root to all nodes on the branches of the node Descendants of nodes : all nodes in the subtree rooted at a node Layer : starting from the root, the root is the first layer , the child of the root is the second layer... (Level) depth : the maximum number of layers of nodes in the tree, called the depth or height of the tree (Depth or Height) forest











: It is a collection of many disjoint trees (Forest)

Unordered tree : There is no order relationship between the child nodes of any node in the tree. This tree is called an unordered tree, also known as a free tree.
Ordered tree : There is an order relationship between the child nodes of any node in the tree. A tree is called an ordered tree
maximal tree (minimal tree) : a tree whose value at each node is greater (less than) or equal to the value of its child nodes (if any)


special binary tree

full binary tree

All layers have a maximum number of nodes, all nodes except leaves have two children, all leaves are in the bottom layer (k) and the number is 2^(k - 1). That is, the depth is k and there are 2^k - 1 nodes (the leaves "grow" to fill the last layer), or a perfect binary tree (Perfect Binary Tree)

         ______12_______
        /               \
     __3__             __5__
    /     \           /     \
  _7       6        _9       11
 /  \     / \      /  \     /  \
13   8   1   4    10   2   0    14

complete binary tree

If all the leaves of the bottom layer are deleted, it is a full binary tree, that is, except for the last layer, each layer of nodes reaches the maximum number, that is, the number of nodes with depth k is closed on the left and opened on the right [2^(k-1)+ 1,2^k-1] interval. (Complete Binary Tree)

         ________3______
        /               \
    ___11___           __4__
   /        \         /     \
  14         7       9       13
 /  \      /  \     /   
2    5    8    6   1 

Complete binary tree properties:

1. The depth of a complete binary tree with N nodes is [log2 N]+1, where [x] is a Gaussian function, truncated to an integer.
2. If the nodes of a complete binary tree with n nodes are numbered in sequence (from the first layer to the last layer, each layer from left to right), then for any node, there are: (1) If
i =1, then node i is the root of the binary tree and has no parents; if i>1, its parent node is [i/2]; (2)
If 2i>n, then node i has no left child; otherwise its left child is Node 2i;
(3) If 2i+1>n, node i has no right child; otherwise, its right child is node 2i+1.


Guess you like

Origin blog.csdn.net/boysoft2002/article/details/129409557