Binary tree related topics

Table of contents

1. Create a string based on a binary tree

2. Level order traversal of binary tree

3. The nearest common ancestor of the binary tree

 4. Search binary tree and doubly linked list

 5. Construct a binary tree from preorder and inorder traversal sequences

6. Construct a binary tree from inorder and postorder traversal sequences

7. Preorder traversal of binary tree (non-recursive implementation)

8. Inorder traversal of binary tree (non-recursive implementation)

9. Post-order traversal of binary tree (non-recursive implementation)


1. Create a string based on a binary tree

Topic requirements : Give you the root node of the binary tree root , please use the preorder traversal method to convert the binary tree into a string composed of brackets and integers, and return the constructed string.


Example 1:

 The preorder traversal should be "1(2(3)())(5)", but 2 has no right child, so the first parenthesis can be omitted

Simplifies to: "1(2(3))(5)"


Example 2:

  After the preorder traversal, it should be "1(2()(3))(5)", but 2 has no left child. If the first parenthesis is omitted, it will be unclear whether it is the left child or the right child.

So it is still: "1(2()(3))(5)"


According to the above example, it can be understood that there are several situations:
①If the left and right are not empty, then the parentheses are not omitted
;
②The left and right are both empty, and the parentheses are both omitted
; The left is empty, the right is not empty, and the left parenthesis cannot be omitted.
The summary is: if the right is not empty, no matter whether the left is empty, the right side needs parentheses.
If the left is not empty or the right is not empty, the left parenthesis is required.

code show as below:

class Solution {
public:
    string tree2str(TreeNode* root) {
        //若root为空,则返回一个string的匿名对象
        if(root == nullptr)
        {
            return string();
        }
        //1、如果左不为空或右不为空,左边需要加括号
        //2、如果右不为空,右边需要加括号
        string str;
        //to_string将val转换为字符变量,以便可以+=
        str += to_string(root->val);
        //情况1
        if(root->left || root->right)
        {
            str += '(';
            str += tree2str(root->left);
            str += ')';
        }
        //情况2
        if(root->right)
        {
            str += '(';
            str += tree2str(root->right);
            str += ')';
        }

        return str;
    }
};

2. Level order traversal of binary tree

 Topic requirements : Give you the root node of the binary tree root , and return  the sequence traversal of its node values  . (ie layer by layer, visit all nodes from left to right).


Idea analysis:

We can create a queue, which stores pointers to the binary tree, and then gives a levelSize to record the number of nodes in each layer . During the loop, create an array of vector<int> to store the node val of each layer value. First, if the binary tree is not empty, root will be stored in the queue, and then the left and right children of root will be stored in the queue after judgment. Before the head node of the queue is popped, val will be stored in v, and at the end of each layer, v will be stored The value push_back to vv, and so on, the comment section in the specific code has


code:

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        //层序遍历一般会使用队列
        queue<TreeNode*> q;
        //levelSize是每一层的节点数
        size_t levelSize = 0;
        //如果根节点不为空,则队列中插入root,节点数置为1
        if(root)
        {
            q.push(root);
            levelSize = 1;
        }
        //vv是需要返回的vector<vector<int>>
        vector<vector<int>> vv;
        //while循环,直到队列为空
        while(!q.empty())
        {
            //创建vector<int> v,存储每一层的结点的val
            vector<int> v;
            //for循环保证每次循环一层的结点
            for(size_t i = 0;i < levelSize; ++i)
            {
                //由于每次都要删除队列的第一个值
                //所以front来保留一下指针,以免找不到左右字树
                TreeNode* front = q.front();
                q.pop();
                //每次删除的时候都存进v
                v.push_back(front->val);
                //如果删除结点有左右孩子,都存进队列中
                if(front->left)
                    q.push(front->left);
                if(front->right)
                    q.push(front->right);          
            }
            //每循环完一层,就往vv里存一层的val值
            vv.push_back(v);
            //接着重新赋值levelSize,即下一层数的节点数
            levelSize = q.size();
        }
        return vv;
    }
};

3. The nearest common ancestor of the binary tree

Question : Given a binary tree, find the nearest common ancestor of two specified nodes in the tree.

The definition of the nearest common ancestor in Baidu Encyclopedia is: "For two nodes p and q of a rooted tree T, the nearest common ancestor is expressed as a node x, satisfying that x is the ancestor of p and q and the depth of x is as large as possible ( a A node can also be its own ancestor )."

Method one example 1:

 Then the nearest common ancestor is node 2

Method one example 2:

 Then the nearest common ancestor is node 4

So in method 1, we can use the following two ideas:

1. If one is a node in the left subtree and the other is a node in the right subtree, then it is the nearest common ancestor

2. If a node A is the ancestor of node B, then the common ancestor is node A

The code of method 1: (method 1 will lead to lower efficiency if it encounters the part whose common ancestor is below the binary tree)

class Solution {
public:
    bool Find(TreeNode* root, TreeNode* x)
    {
        //如果查找的为空,返回nullptr
        if(root == nullptr)
            return false;
        //如果找到了,返回true
        if(root == x)
            return true;
        //如果没找到,则递归进左右字树找
        return Find(root->left, x) || Find(root->right, x);
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == nullptr)
            return nullptr;
        //说明公共祖先是root,
        if((root == p) || (root == q))
            return root;
        //p/q在一左一右,则说明当前root是公共祖先
        //设定4个bool类型变量,与Find结合使用
        bool pInLeft,pInRight,qInleft,qInRight;
        pInLeft = Find(root->left, p);
        pInRight = !pInLeft;
        //在左就说明不在右,所以可以用!
        qInleft = Find(root->left, q);
        qInRight = !qInleft;
        //一个在左一个在右,则它是公共祖先
        if((pInLeft && qInRight) || (pInRight && qInleft))
            return root;
        //若都在root左或右,则递归进左或右子树中,重新判断上面的条件
        else if(pInLeft && qInleft)
            return lowestCommonAncestor(root->left, p, q);
        else if(pInRight && qInRight)
            return lowestCommonAncestor(root->right, p, q);
        //此题不会进入这里,因为p/q都在二叉树中
        else
            return nullptr;
    }
};

Method 2 idea: (Compared with method 1, it is more efficient, O(N))

Put the path from the root node of p and q into the stack, pop the longer path of the two nodes until it is as long as the shorter path, and then judge whether the top elements of the stack are the same

The idea is similar to the intersection of linked lists

Example of method two:

The paths of node 3 and node 1 are placed on the stack as shown in the figure:

 The path length of node 1 is large, and after the pop is equal, it becomes:

 Then start judging from the two stack top elements 3 and 9, if they are not the same, both will pop, until 2 is encountered, return to node 2

Method 2 code:

class Solution {
public:
    bool FindPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
    {
        //是空返回false
        if(root == nullptr)
            return false;
        //不论是不是先入栈,因为后面判断不是路径会pop
        path.push(root);
        //如果找到了,返回true
        if(root == x)
            return true;
        //如果没找到,进入左子树找
        if(FindPath(root->left,x,path))
            return true;
        //如果左子树没找到,进入右子树找
        if(FindPath(root->right,x,path))
            return true;
        //左右字树都没找到,pop掉当前栈顶元素,返回false
        path.pop();
        return false;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        //栈的每个元素都是TreeNode*类型
        stack<TreeNode*> pPath,qPath;
        //FindPath中传入的pPath和qPath都是p和q从根结点的路径
        FindPath(root, p, pPath);
        FindPath(root, q, qPath);
        //p/q结点的路径长度不同,先变为相同路径长度
        while(pPath.size() != qPath.size())
        {
            if(pPath.size() > qPath.size())
                pPath.pop();
            else
                qPath.pop();
        }
        //相同路径长度一层层判断顶部元素是否相同
        while(pPath.top() != qPath.top())
        {
            pPath.pop();
            qPath.pop();       
        }
        //走到这里说明找到了相同的结点,即最近祖先
        return pPath.top();
    }
};

 4. Search binary tree and doubly linked list

Topic requirements : Input a binary search tree and convert the binary search tree into a sorted doubly linked list. As shown below:

1. It is required that no new nodes can be created, only the pointing of the node pointers in the tree can be adjusted. After the conversion is completed, the left pointer of the node in the tree needs to point to the predecessor, and the right pointer of the node in the tree needs to point to the successor 2. Return the pointer of the
first node in the linked list
3. The TreeNode returned by the function has left and right pointers, which can be seen in fact into a data structure of a doubly linked list


Idea analysis:

Since new nodes cannot be created, only the pointing of the node pointers in the tree can be adjusted, so we cannot use the method of first sorting in the middle order and then traversing

Then in the process of in-order traversal, give two pointers, one prev and one cur, prev refers to the previous node, cur is the value to the current node , and the value is assigned to prev every time before cur changes , and then point cur->left to prev , and so on to complete the left pointer, the current prev is the previous cur, so prev->right = cur is equivalent to the previous cur->right also pointing to the next node , thus completing the right pointer


code:

class Solution {
public:
	//中序遍历,并在过程中调整结点指针的指向
	//cur是当前结点的指针,prev是前一个结点的指针
	void Inorder(TreeNode* cur,TreeNode*& prev)
	{
		if(cur == nullptr)
			return;
		//先左子树
		Inorder(cur->left,prev);
		//cur->left直接给prev,因为prev是前一个结点指针
		cur->left = prev;
		//若prev不为空,且为TreeNode*& prev,是传引用,即:
		//prev->right就完成了上一个cur结点的right指针指向
		if(prev)
			prev->right = cur;
		//在cur指向下一个之前,赋值给prev
		prev = cur;
		//再右子树
		Inorder(cur->right,prev);
	}

    TreeNode* Convert(TreeNode* pRootOfTree) {
		//创建一个prev置空,传入Inorder进行中序排序
        TreeNode* prev = nullptr;
		Inorder(pRootOfTree, prev);
		//head先指定为题目所给的根结点
		TreeNode* head = pRootOfTree;
		//顺着left指针找到中序遍历的第一个结点
		//为了防止pRootOfTree为空,要先判断head
		while(head && head->left)
			head = head->left;
		//返回第一个结点指针
		return head;
    }
};



 5. Construct a binary tree from preorder and inorder traversal sequences

Topic requirements : Given two integer arrays preorder and inorder , where  is the preorder traversalpreorder  of a binary treeand is the inorder traversal  of the same tree, please construct a binary tree and return its root node. inorder

 Ideas:

The root is determined by preorder traversal, and the left and right word trees are determined by inorder traversal

The subtree interval confirms whether to continue to recursively create subtrees, and if there is no interval, it is an empty tree

code:

class Solution {
public:
    //创建_buildTree函数进行递归调用
    //prei是前序遍历结果的首元素下标,inbegin、inend是中序遍历结果首尾元素的下标
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int inbegin, int inend)
    {
        //如果在前序遍历的结果中找,
        if(inbegin > inend)
            return nullptr;
        //每次递归通过前序遍历结果创建根结点
        TreeNode* root = new TreeNode(preorder[prei++]);
        //while循环找到中序遍历的该结点的位置
        int cur = inbegin;
        while(cur <= inend)
        {
            if(inorder[cur] == root->val)
                break;
            else
                cur++;
        }
        //中序遍历的结果中,分成了三个部分,[左子树]根[右子树]
        //[inbegin, cur-1] cur [cur+1,inend]
        //所以接下来递归时,传入这两个区间
        root->left = _buildTree(preorder,inorder,prei,inbegin,cur-1);
        root->right = _buildTree(preorder,inorder,prei,cur+1,inend);
        return root;

    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        //前序遍历首元素下标为0
        int prei = 0;
        //中序遍历结果首尾元素的下标为0和inorder.size()-1
        TreeNode* root = _buildTree(preorder,inorder,prei,0,inorder.size()-1);
        return root;
    }
};




6. Construct a binary tree from inorder and postorder traversal sequences

Topic requirements : Given two integer arrays inorder and postorder , where inorder is the inorder traversal of a binary tree and postorder the postorder traversal of the same tree, please construct and return this  binary tree  .


This question is basically the same as the above question of constructing a binary tree from the pre-order and in-order traversal sequences, but since the post-order determines the root node, the subscript posi of the post-order traversal result will be posi-- every time, and it is the first Recurse the right subtree, and then recurse the left subtree , because the postorder traversal order is left subtree, right subtree, root node, and in turn, root node, right subtree, left subtree

So the code is as follows:

class Solution {
public:
    TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder,int& posi,int inbegin,int inend)
    {
        if(inbegin > inend)
            return nullptr;
        TreeNode* root = new TreeNode(postorder[posi--]);

        int cur = inbegin;
        while(cur <= inend)
        {
            if(root->val == inorder[cur])
                break;
            else
                cur++;
        }

        //[inbegin,cur-1] cur [cur+1,inend]
        root->right = _buildTree(inorder,postorder,posi,cur+1,inend);
        root->left = _buildTree(inorder,postorder,posi,inbegin,cur-1);
        
        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int posi = postorder.size()-1;
        return _buildTree(inorder,postorder,posi,0,inorder.size()-1);
    }
};


7. Preorder traversal of binary tree (non-recursive implementation)

Topic requirements : Give you the root node of the binary tree  , and return the preorderroot traversal  of its node values . 


Idea analysis :

Implementing this problem non-recursively can improve efficiency, and the recursive call needs to build a stack frame. If the depth is deep, it will easily crash, so you need to master the non-recursive method

In pre-order traversal, we can divide all nodes into left-way nodes and right subtrees of left-way nodes

Then our first step is to save all the left nodes and store them in the stack, then pop the nodes stored in the stack one by one, and visit the right subtree, and then repeat the above steps (save the left nodes , into the stack, after all are pushed into the stack, and then pop out, access the right subtree of the popped node)

When the left node comes out of the stack, it means that the left subtree has been visited, and it is time to visit the node and its right subtree

It is equivalent to converting to a sub-problem , dividing all nodes into left-way nodes and right subtrees of left-way nodes, and then dividing the right subtree of left-way nodes into left-way nodes and left-way nodes The right subtree of the road node and so on, so as to realize the non-recursive method to complete the preorder traversal


code show as below:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> v;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        //循环条件有两个都不符合才结束循环
        //一是栈里空,表明初始的左路结点的右子树都已访问
        //二是cur为空,表明访问的栈中的结点的右子树为空
        while(cur || !st.empty())
        {
            //1、左路结点
            while(cur)
            {
                v.push_back(cur->val);
                st.push(cur);
                cur = cur->left;
            }
            //2、左树结点的右子树
            TreeNode* top = st.top();
            st.pop();
            //将左路结点以外的数转化为上面两条的子问题
            //转换为子问题从而访问栈中结点的右子树
            cur = top->right;
        }
        return v;
    }
};


8. Inorder traversal of binary tree (non-recursive implementation)

Topic requirements : Given the root node of a binary tree root , return  its  inorder  traversal  .


The in-order traversal and the pre-order traversal have roughly the same idea, but because the in-order traversal is: left subtree, root, right subtree. Therefore, the results of the in-order traversal need to be pushed into the array after all the nodes on the left are pushed into the stack, and the remaining ideas are the same as the pre-order traversal


code show as below:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> v;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while(cur || !st.empty())
        {
            while(cur)
            {
                st.push(cur);
                cur = cur->left;
            }
            //左路结点都入栈后,再尾插栈顶元素到数组中
            //依次取栈顶元素,再pop,转换为子问题循环
            TreeNode* top = st.top();   
            st.pop();
            v.push_back(top->val);
            cur = top->right;
        }
        return v;
    }
};



9. Post-order traversal of binary tree (non-recursive implementation)

Topic requirements : Give you the root node of a binary tree  , and return the post-order traversal root  of its node values .


Idea analysis:

There is a little difference between post-order traversal and pre-order/in-order, because the post-order is the left subtree, right subtree, and root. After we find the left-way node first, we cannot confirm whether the right subtree of the node has been visited, so Discussion on this issue can be categorized

Set a prev node and let it point to the previous node of the cur node , that is, record the current node value every time the array is inserted at the end, and assign it to prev, so that after cur = cur->right, prev is The previous node visited by cur.

After all the left nodes are inserted into the stack, there are two cases:

First: the right subtree of the node is empty or the right subtree of the node has been visited ; second: the right subtree of the node has not been visited

In the first case, you can visit the top node of the stack , otherwise, visit the right subtree of the node first, and convert it into a subproblem


code:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> v;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* prev = nullptr;
        while(cur || !st.empty())
        {
            //左路结点入栈
            while(cur)
            {
                st.push(cur);
                cur = cur->left;
            }
            TreeNode* top = st.top();
            //右子树为空或上一个访问的就是该结点的右子树的根
            //说明右子树已经访问过了
            if(top->right == nullptr || top->right == prev)
            {
                v.push_back(top->val);
                prev = top;
                cur = nullptr;
                st.pop();
            }
            //否则先访问栈顶结点的右子树
            else
            {
                cur = top->right;
            }
        }
        return v;
    }
};


Related topics include these


Guess you like

Origin blog.csdn.net/m0_64411530/article/details/131964964