Binary tree oj and non-recursive writing in front, middle and back order

1. Create a string from a binary tree

Given the root node root of the binary tree, please use preorder traversal to convert the binary tree into a string composed of brackets and integers, and return the constructed string.

Empty nodes are represented by a pair of empty brackets "()". After conversion, all empty bracket pairs that do not affect the one-to-one mapping relationship between the string and the original binary tree need to be omitted.

insert image description here

输入:root = [1,2,3,4]
输出:"1(2(4))(3)"
解释:初步转化后得到 "1(2(4)())(3()())" ,但省略所有不必要的空括号对后,字符串应该是"1(2(4))(3)" 。

problem solving ideas

This is a simple appetizer, and this topic can be dealt with in two steps: 1. If the topic does not require parentheses to be removed, then it is actually a simple preorder traversal, enclosing the result of the traversal in parentheses, and directly replace it with a complete parenthesis when encountering an empty tree; 2. Regarding the removal of parentheses, you can observe the example given in the question. Additional parentheses are required only when the subtree is empty and the right subtree is not empty;

It is a very good choice to use strings to directly += in this question. The to_string function can be used to solve the problem of how to insert integer numbers into string objects:

class Solution {
    
    
public:
    string tree2str(TreeNode* root) 
    {
    
    
        if(root==nullptr)
            return string(" ");//如果是一个空树,直接返回一个空string
        
        string str;
        str+=to_string(root->val);//如果不是空树首先插入根节点的值,再往下

        if(root->left)
        {
    
    
           
            str+='(';
            str+=tree2str(root->left);
            str+=')';
        }
        else if(root->right)
        {
    
    
            //左子树为空,右子树不为空,需要给左边补一个括号再往右边走
            str+="()";//这里要注意是双引号,因为一个完整的括号是一个字符串

        }

        if(root->right)
        {
    
    
            str+='(';
            str+=tree2str(root->right);
            str+=')';
        }//右子树为空,不论左子树是否为空都不用加括号
        return str;
    }
};

2. Level order traversal of binary tree I

Given the root node of your binary tree , return a level-order traversalroot of its node values . (ie layer by layer, visit all nodes from left to right).

insert image description here

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

problem solving ideas

The general idea of ​​this question is similar to ordinary layer order traversal, which needs to be realized with the help of queues. The only difference is that it requires layer-by-layer output, which is also the difficulty of this question, because there may be two layers of data in a queue at the same time (in the above example, if the subtree of 9 is not empty, then 9 will be brought into the third layer value after it is out of the queue, and there is also a second layer of 20 in the queue);

There are two ways to mark the number of layers of a node: 1. Build an additional queue, one queue is used to store the value of the node, and the other queue is used to store the number of layers of the node. When the node is exported, the values ​​in the two queues are output from the queue at the same time; 2. Set a flag variable. The meaning of this flag variable is how many nodes there are in a certain layer. The size of the flag variable is the number of elements in the queue.

Here is the code for the second solution:

class Solution {
    
    
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
    
    
        queue<TreeNode*>q;
        vector<vector<int>> vv;
        if(root)
            q.push(root);

        int levelsize=q.size();

        while(!q.empty())
        {
    
    
            vector<int> level;
            while(levelsize--)
            {
    
    
                TreeNode* node=q.front();
                q.pop();
                level.push_back(node->val);

                if(node->left)
                    q.push(node->left);
                if(node->right)
                    q.push(node->right);
            }

            vv.push_back(level);
            levelsize=q.size();
        }
        return vv;
    }
};

3. Level order traversal of binary tree II

Given the root node of your binary tree , return the bottom-up order traversal ofroot its node values . (That is, traverse from left to right layer by layer from the layer where the leaf node is located to the layer where the root node is located)
insert image description here

输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]

problem solving ideas

This question is actually very simple, but the solver may think this question is too complicated. When we read the question, we often limit ourselves to the requirements of the question. For example, it requires us to traverse from the bottom to the top. I thought the difficulty of this question is to traverse from the bottom to the top; but if it is not restricted by this sentence, it will be very simple. We only need to reverse the vector on the basis of the previous question;

class Solution {
    
    
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
    
    

        queue<TreeNode*>q;
        vector<vector<int>> vv;
        if(root)
            q.push(root);
        int levelsize=1;

        while(!q.empty())
        {
    
    
            vector<int> level;
            while(levelsize--)
            {
    
    
                TreeNode*node=q.front();

                q.pop();
                level.push_back(node->val);
                if(node->left)
                    q.push(node->left);
                if(node->right)
                    q.push(node->right);
                
            }
            levelsize=q.size();
            vv.push_back(level);
        }
       reverse(vv.begin(),vv.end());
        return vv;
    }
};

4. The nearest common ancestor of the binary tree

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 node can also be its own ancestor)."

insert image description here

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

problem solving ideas

There are two solutions to this question. One is to observe the structure of the tree to get such a law:

insert image description here

class Solution {
    
    
public:

    bool IsInTree(TreeNode*root,TreeNode*pos)
    {
    
    
        if(root==nullptr)
            return false;
        return IsInTree(root->left,pos)||IsInTree(root->right,pos)||root==pos;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    
    
       
       if(root==nullptr)
            return nullptr;
        
        if(root==p||root==q)
            return root;

        bool pInLeft=IsInTree(root->left,p);
        bool pInRight=!pInLeft;//说了这个节点一定在树中,所以不在左树就一定在右树

        bool qInLeft=IsInTree(root->left,q);
        bool qInRight=!qInLeft;

        //找到两个节点以后,判断以下两个节点位置
        if(qInLeft&&pInRight||qInRight&&pInLeft)
            return root;//如果节点一左一右,我就是最近祖先
        
        if(pInLeft&&qInLeft)//都在左或者都在右,说明我的左孩子或者右孩子才是最近公共祖先
            return lowestCommonAncestor(root->left,p,q);
        else
            return lowestCommonAncestor(root->right,p,q);

    }
};

This problem-solving method will become very inefficient when encountering a tree that is close to a single branch, because the nodes must be in the left or right subtree of the tree, and my recursion nests an O(N) recursion, so the complexity in the worst case is O(N^2)


Problem-solving ideas two

In order to optimize the complexity close to a single-branched binary tree, here is the second problem-solving idea: set two stacks to store the path nodes from the root node to these two points, and convert this problem into a problem similar to the intersecting linked list. When pushing into the stack, firstly put every passing node into the stack (because I can’t guarantee whether the node is a node on the path). Let the stack with a large size be popped out until the two stacks have the same size, and then pop out the stack at the same time and make a judgment. The equal value that appears for the first time is the nearest common ancestor
]

class Solution {
    
    
public:

    bool GetPath(TreeNode*root,TreeNode*x,stack<TreeNode*>&path)
    {
    
    
        if(root==nullptr)
            return false;

        path.push(root);//如果节点不是空,我先插入进去再说

        if(root==x)
            return true;

        if(GetPath(root->left,x,path))//如果根节点不是,我就去左子树找,左子树找不到就去右子树找
            return true;
        if(GetPath(root->right,x,path))
            return true;
        
        //走到最后,左子树右子树都为空了,直接将该节点弹出
        path.pop();
        return false;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
    
    
       
       stack<TreeNode*>ppath;
       stack<TreeNode*>qpath;

       GetPath(root,p,ppath);
       GetPath(root,q,qpath);

       //让长的栈先走
        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();
    }
};

This kind of question is a kind of question that I like to investigate in the interview, because changing different conditions, the difficulty of the question will also be different. For example, if the tree is a search binary tree, then we don’t need to write recursion when we use the first solution to find where the node is. In addition, if the tree is a three-point chain structure, then this question can be converted into a linked list intersection problem.


5. Binary search tree and doubly linked list

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

img

Notice:

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 to the first node in the linked list

3. The TreeNode returned by the function has left and right pointers, which can actually be regarded as a data structure of a doubly linked list

4. You don't need to output the doubly linked list, the program will automatically print out according to your return value

Example 1

enter:

{10,6,14,4,8,12,16}

return value:

From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;	

problem solving ideas

The title requires an ordered doubly linked list at the end, and the binary search tree is based on the in-order order, so we can know that the question must use the in-order traversal, and secondly, the left pointer of the binary tree points to the predecessor node, and the right pointer points to the successor node (and because it requires order, so this operation is performed in the in-order traversal); the note of this question is: in order to mark the predecessor node, I need a pointer mark, but the parameter for this pointer in the function must be a reference, because the reference in the recursion can make the previous layer Changes in the stack frame are seen by the next layer; and since the lvalue pointer of the first node should point to NULL, it is best to initialize the predecessor node pointer to NULL

class Solution {
    
    
public:

	void InOrder(TreeNode*cur,TreeNode*&prev)
	{
    
    
		if(cur==nullptr)
			return;
		InOrder(cur->left,prev);
		//找到最左节点以后,它的前驱节点要指向空
		cur->left=prev;

		if(prev)
			prev->right=cur;
		prev=cur;
		InOrder(cur->right, prev);

	}

    TreeNode* Convert(TreeNode* pRootOfTree) {
    
    
		TreeNode*prev=nullptr;
		InOrder(pRootOfTree, prev);
		TreeNode*head=pRootOfTree;

		while(head&&head->left)//链表最开始的那个节点必定位于树的最左边
		{
    
    
			head=head->left;
		}
        return head;
    }
};

Here is a recursive expansion diagram to help better understanding:

insert image description here


Supplementary solution

Of course, this question is not limited to the above solution. We can first store the results of the in-order traversal of the tree in a vector array, and process the first node and the last node separately (because the left pointer of the first node must point to null, and the right pointer of the last node must point to null). Starting from the second node, its left pointer points to the previous element, and its right pointer points to the next element; but the interview may require space complexity (this question requires space complexity O(1)) or prohibit the use of containers

class Solution {
    
    
public:

	void InOrder(TreeNode*cur,vector<TreeNode*>&v)
	{
    
    
		if(cur==nullptr)
			return;
		InOrder(cur->left, v);
		v.push_back(cur);
		InOrder(cur->right, v);

	}

    TreeNode* Convert(TreeNode* pRootOfTree) {
    
    
		if(pRootOfTree==nullptr)
			return nullptr;;
		vector<TreeNode*>v;
		InOrder(pRootOfTree,v);

		if(v.size()<=1)
			return v[0];

		v[0]->left=nullptr;
		v[0]->right=v[1];

		for(int i=1;i<v.size()-1;i++)
		{
    
    
			v[i]->left=v[i-1];
			v[i]->right=v[i+1];
		}
		v[v.size()-1]->left=v[v.size()-2];
		v[v.size()-1]->right=nullptr;
	return v[0];
    }
};


6. Construct a binary tree from preorder and inorder sequences

Given two integer arrays preorder and inorder, where preorder is the preorder traversal of a binary tree and inorder is the inorder traversal of the same tree, construct a binary tree and return its root node

insert image description here

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

problem solving ideas

Given a preorder and inorder, or given an inorder and postorder, a binary tree can be constructed, but a binary tree cannot be constructed given a preorder and postorder (because only the root can be determined, but the number of nodes in the left and right subtrees cannot be determined; or a binary tree can be constructed but not unique);

There are preorder and inorder sequences, we can determine the root of the tree through the preorder, and then determine the left and right subtree intervals of the root through the inorder, so the focus of this question is to mark the left and right subtree intervals of a root node; roughly divided into these steps: 1. Find the position of the root in the inorder sequence; , so some variables in the function parameter list need to be referenced

class Solution {
    
    
    public:

    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,int& previ,int inbegin,int inend) {
    
    
        //递归终止条件:inbegin>inend
        if(inbegin>inend)
            return nullptr;

        //在中序序列中查找根
        int rooti=inbegin;//这里不能设等于0,因为递归的时候rooti并不是总是从0位置开始的

        while(rooti<=inend)
        {
    
    
            if(preorder[previ]==inorder[rooti])
                break;
            rooti++;
        }

        //找根节点以后要构建根节点作为树的起始
        TreeNode*root=new TreeNode(preorder[previ++]);//这里使用一个后置++,因为完成根节点的构建以后要更新根节点

        root->left=_buildTree(preorder,inorder,previ,inbegin,rooti-1);
        root->right=_buildTree(preorder,inorder,previ,rooti+1,inend);

        return root;


    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    
    
        int previ=0;

        return _buildTree(preorder,inorder,previ,0,inorder.size()-1);

    }
};

If you feel that it is difficult to understand, you can draw a recursive expansion diagram to understand it. I feel that I am too frustrated to draw a picture, so I will not continue to draw it here.


7. Construct a binary tree from inorder and postorder sequences

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

insert image description here

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

problem solving ideas

The solution to this question is exactly the same as the previous question, the only difference is that the root is at the end of the postorder sequence, and the position of the root is updated each time instead of . In addition, because the traversal order of the postorder is the root of the left subtree and the right subtree, after constructing the root node, the right subtree must be --constructed ++first

class Solution {
    
    
public:
    TreeNode* _buildTree(vector<int>& inorder,vector<int>& postorder,int& posti,int inbegin,int inend) {
    
    
        //递归终止条件:inbegin>inend
        if(inbegin>inend)
            return nullptr;

        //在中序序列中查找根
        int rooti=inbegin;//这里不能设等于0,因为递归的时候rooti并不是总是从0位置开始的

        while(rooti<=inend)
        {
    
    
            if(postorder[posti]==inorder[rooti])
                break;
            rooti++;
        }

        //找根节点以后要构建根节点作为树的起始
        TreeNode*root=new TreeNode(postorder[posti--]);//这里使用一个后置--,因为完成根节点的构建以后要更新根节点

       
        root->right=_buildTree(inorder,postorder,posti,rooti+1,inend);
        root->left=_buildTree(inorder,postorder,posti,inbegin,rooti-1);

        return root;


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

        return _buildTree(inorder,postorder,posti,0,inorder.size()-1);

    }
};


Front, middle, and back order traversal of a binary tree (non-recursive)

If the recursion of the binary tree is to be changed to non-recursive, it cannot be changed directly. It must be changed with the help of an external structure. Here, the stack is used, because the stack is last-in-first-out to meet the requirements, and a vector array is also needed. The stack is used to store nodes, and the array is used to store node values. In addition, we also need to change a little bit in terms of structure. Before, we divided a tree into root, left subtree and right subtree; but now we only divide a tree into two parts, which are left node and right subtree (the root is included in the left node)

preorder traversal

Preorder traversal of a binary tree

Because the order of preorder traversal is root, left subtree, and right subtree, so once we encounter a node, we store the value of the node (choose vector to store), and store the node in the stack. When we encounter an empty space, we get the top element of the stack, and then access the right subtree of the top element until the stack is empty and cur is also empty, then the loop ends

insert image description here

class Solution {
    
    
public:
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        vector<int> v;//用于存放数据

        stack<TreeNode*> st;//用于存放节点
        TreeNode*cur=root;

        while(cur||!st.empty())
        {
    
    
            //将一棵树分为左路节点和右子树,先走左路节点

            while(cur)
            {
    
    
                v.push_back(cur->val);//因为是前序遍历,最先进去的数据是根的数据
                st.push(cur);
                cur=cur->left;
            }

            //左路节点走完了以后,往右子树走

            TreeNode*top=st.top();//将栈中的顶部节点出栈,然后往它的右子树去走
            st.pop();

            cur=top->right;
        }

    return v;
    }
};

Inorder traversal

Inorder traversal of a binary tree

The difference between in-order traversal and pre-order traversal is that the order of in-order traversal is: left subtree, root, right subtree; that is to say, the value of the node cannot be inserted into the vector at the left node, and the root value can only be inserted into the vector after all the left nodes have been visited; but because the left node is encountered when traversing the left node, it is pushed into the stack, that is to say, the stacking order of the nodes is just in line with the access order (the last input is the leftmost node).

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;
            }

            //左节点的右路子树
            TreeNode*top=st.top();
            v.push_back(top->val);
            st.pop();
           

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

post order traversal

Post-order traversal of a binary tree

Compared with preorder and inorder, postorder traversal is difficult to deal with, because postorder traversal requires visiting the left subtree and right subtree before visiting the root. If the right subtree of a node is empty, then you can directly access the root. If its right subtree is not empty, special processing is required: after visiting the left subtree, you need to visit the right subtree again, which means that you have to return from the left subtree to the root position, go to the right subtree, and visit the root once when you find the left subtree of the node from top to bottom. , including but not limited to creating an additional array for storing tags, but it must be a node and a tag ) .

The way to mark the root here is to set an additional pointer. If cur->right is equal to prev, it means that the root node is visited for the second time.

class Solution {
    
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        vector<int> v;
        stack<TreeNode*>st;
        TreeNode*prev=nullptr;
        TreeNode*cur=root;

        while(cur||!st.empty())
        {
    
    
            while(cur)
            {
    
    
                //依旧是先访问左路节点
                st.push(cur);
                cur=cur->left;

            }
            TreeNode*top=st.top();
            //右路节点
            if(top->right==nullptr||top->right==prev)
            {
    
    
                //如果是第二次访问根节点那么直接将根节点的数据入栈  
                st.pop();
                v.push_back(top->val);
                prev=top;
            }
            else
            {
    
    
                cur=top->right;
            }
        }
return v;
    }
};

Draw a picture here to analyze why prev=topthis code can indicate that the root node is accessed for the second time

insert image description here

The front, middle and back order traversal of the binary tree adopts a similar method, which is why this solution is chosen here, which is to save trouble haha. The only difference is that the timing of inserting the value of the node into the array is different, and the position of the code is different. The preorder and inorder are basically the same, only the postorder needs to pay attention to whether the calibration root is accessed for the second time.


Guess you like

Origin blog.csdn.net/m0_62633482/article/details/131215822