[Data structure] Binary tree classic topic

1. Binary tree creation string

I believe that most people will be as confused as me after reading the title description. I didn't realize it until I saw a description.

There are 3 situations:

  1. Both left and right are empty -- omitted
  2. right is empty, left is not empty – omitted
  3. left is empty, right is not empty – not omitted

Here is a review of the pre-order traversal, in-order traversal, and post-order traversal of the binary tree.

The result of the pre-order is: ABDEGCF
The result of the in-order is: DBGEACF
The result of the post-order is: DGEBFCA

class Solution {
public:
	string tree2str(TreeNode* root) {
		if (root == nullptr)
		{
			return "";
		}
		string str = to_string(root->val);
		if (root->left || root->right) // 特别注意这个条件
		{
			str += "(";
			str += tree2str(root->left);
			str += ")";
		}
		if (root->right)
		{
			str += "(";
			str += tree2str(root->right);
			str += ")";
		}
		return str;
	}
};

2. Level order traversal of binary tree

The idea is roughly like this:
a queue, and then one levelSizeto record how many data each layer has. If this number is 0, it means that the data of this layer has been sent out completely.


Out 3 brings 9 and 20 to the queue with a levelSize of 2. And so on.
If the queue is not empty, the loop continues until the queue is empty.
Code:

class Solution {
public:
	vector<vector<int>> levelOrder(TreeNode* root) {
		queue<TreeNode*> q;
		int levelSize = 0;
		if (root)
		{
			q.push(root);
			levelSize = 1;
		}
		vector<vector<int>> vv;
		while (!q.empty()) // 如果队列不为空,就继续
		{
			// 通过levelSize控制一层一层的出
			vector<int> v;
			while (levelSize--)
			{
				TreeNode* front = q.front();
				q.pop();
				v.push_back(front->val);
				if (front->left)
				{
					q.push(front->left);
				}
				if (front->right)
				{
					q.push(front->right);
				}
			}
			vv.push_back(v);
			// 更新下一层的个数
			levelSize = q.size();
		}
		return vv;
	}
};

3. Level order traversal of binary tree II


This question is similar to the previous question, we just need to reverse the final answer

4. The nearest common ancestor of the binary tree



Idea 1: The characteristics of the common ancestor, if one is in the left subtree, the other is in the right subtree. Then this node is the common ancestor.

class Solution {
public:
	bool isInTree(TreeNode* root, TreeNode* x) {
		if (root == nullptr) {
			return false;
		}
		return x == root
			|| isInTree(root->left, x)
			|| isInTree(root->right, x);
	}
	TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
		if (root == nullptr) {
			return nullptr;
		}
		if (p == root || q == root) {
			return root;
		}
		// 判断p节点是在root的左边还是右边
		bool pInLeft = isInTree(root->left, p);
		bool pInRight = !pInLeft;
		// 判断q节点是在root的左边还是右边
		bool qInLeft = isInTree(root->left, q);
		bool qInRight = !qInLeft;

		if ((pInLeft && qInRight) || (pInRight && qInLeft)) {
			return root;
		}
		// 如果都在左边,则转换为在左树寻找公共祖先
		else if (pInLeft && qInLeft) {
			return lowestCommonAncestor(root->left, p, q);
		}
		else {
			return lowestCommonAncestor(root->right, p, q);
		}
	}
};

Idea 2: The characteristics of the common ancestor, if one is in my left subtree and the other is in my right subtree, I am the common ancestor
If it is a search binary tree, it can be optimized to O(N)

  1. One is smaller than the root, one is larger than the root, and the root is the common ancestor
  2. are smaller than the root, recursive left tree search
  3. Both are larger than the root, recursive right tree search
    But we are not searching for a binary tree for this topic, and we need to optimize it to O(N).
    Here we can only use another way of thinking, find out the paths of p and q, put them in the container, and convert them to path intersection problem
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, 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();
	}
};

The key to the above code is to find the path of each node

5. Binary search tree and doubly linked list


When we saw this topic, our first thought might be to take out all the nodes and insert them into a doubly linked list. In fact, it is not that simple. Of course, the person who made the question that we can think of can also think of it.
This topic has the following requirements:

We need to operate on the original tree.

class Solution {
public:
	void inorderTraversal(TreeNode* cur, TreeNode*& prev) {
		if (cur == nullptr) {
			return;
		}
		inorderTraversal(cur->left, prev);
		cur->left = prev;
		if (prev) {
			prev->right = cur;
		}
		prev = cur;
		inorderTraversal(cur->right, prev);
	}
	TreeNode* Convert(TreeNode* pRootOfTree) {
		TreeNode* prev = nullptr;
		inorderTraversal(pRootOfTree, prev);
		TreeNode* head = pRootOfTree;
		while (head && head->left) {
			head = head->left;
		}
		return head;
	}
};


The picture above is the essence

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


class Solution {
public:
	TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& previ, int inbegin, int inend) {
		if (inbegin > inend) {
			return nullptr;
		}
		TreeNode* root = new TreeNode(preorder[previ]);
		// 分割出左右区间
		int rooti = inbegin;
		while (rooti <= inend) {
			if (inorder[rooti] == preorder[previ]) {
				break;
			}
			else {
				rooti++;
			}
		}
		++previ;
		// [inbegin, rooti - 1], rooti, [rooti + 1, inend]
		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 i = 0;
		return _buildTree(preorder, inorder, i, 0, inorder.size() - 1);
	}
};

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

class Solution {
public:
	vector<int> preorderTraversal(TreeNode* root) {
		stack<TreeNode*> st;
		TreeNode* cur = root;
		vector<int> v;
		while (cur || !st.empty()) {
			// 1. 开始访问一棵树
			// 2. 左路节点
			// 3. 左路节点的右子树
			while (cur) {
				v.push_back(cur->val);
				st.push(cur);
				cur = cur->left;
			}
			// 访问右子树
			TreeNode* top = st.top();
			st.pop();
			// 子问题访问右子树
			cur = top->right;// 这个地方非常重要
		}
		return v;
	}
};

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

class Solution {
public:
	vector<int> inorderTraversal(TreeNode* root) {
		stack<TreeNode*> st;
		TreeNode* cur = root;
		vector<int> v;
		while (cur || !st.empty()) {
			while (cur) {
				st.push(cur);
				cur = cur->left;
			}
			// 栈里面取到左路节点,左路节点的左子树访问完了
			TreeNode* top = st.top();
			st.pop();
			v.push_back(top->val);

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

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

class Solution {
public:
	vector<int> postorderTraversal(TreeNode* root) {
		stack<TreeNode*> st;
		TreeNode* cur = root;
		vector<int> v;
		TreeNode* prve = nullptr;
		while (cur || !st.empty()) {
			while (cur) {
				st.push(cur);
				cur = cur->left;
			}
			// 栈里面取到左路节点,左路节点的左子树访问完了
			TreeNode* top = st.top();
			// 右为空或者右已经访问过了,可以访问根节点
			if (top->right == nullptr || top->right == prve) {
				v.push_back(top->val);
				st.pop();
				prve = top;
			}
			else {
				cur = top->right;
			}
		}
		return v;
	}
};

Here is a comparison of the three non-recursive codes:

Guess you like

Origin blog.csdn.net/qq_63474430/article/details/131216389