c++经典例题之先序二叉树的构建

1. 先序二叉树的概念

二叉树是一种非常重要的数据结构,它可以用来解决很多实际问题,比如搜索、排序等。在二叉树中,每个结点最多只有两个子结点,我们把它们分为左子树和右子树。

先序二叉树是一种遍历方式,也叫先序遍历。对于一棵二叉树,先序遍历的顺序是:先访问根节点,然后遍历左子树,最后遍历右子树。

对于如下二叉树:

1

/ \

2 3

/ \ / \

4 5 6 7

它的先序遍历结果是:1 2 4 5 3 6 7。

2. 先序二叉树构建的方法

2.1 普通版先序二叉树构建

构建先序二叉树,我们需要先将根节点插入到二叉树中,然后将左子树和右子树递归插入。

以下是先序二叉树构建的代码:

// 二叉树结点的定义

struct TreeNode {

int val;

TreeNode* left;

TreeNode* right;

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

};

class Solution {

public:

TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {

// 递归终止条件:先序遍历序列为空,返回 nullptr

if (preorder.empty()) {

return nullptr;

}

// 新建一个结点,作为根节点

TreeNode* root = new TreeNode(preorder[0]);

// 找到根节点在中序遍历序列中的位置

auto idx = find(inorder.begin(), inorder.end(), preorder[0]) - inorder.begin();

// 将中序遍历序列分成左子树和右子树两个部分

vector<int> left_inorder(inorder.begin(), inorder.begin() + idx);

vector<int> right_inorder(inorder.begin() + idx + 1, inorder.end());

// 将先序遍历序列分成左子树和右子树两个部分

vector<int> left_preorder(preorder.begin() + 1, preorder.begin() + 1 + idx);

vector<int> right_preorder(preorder.begin() + 1 + idx, preorder.end());

// 递归构建左子树和右子树

root->left = buildTree(left_preorder, left_inorder);

root->right = buildTree(right_preorder, right_inorder);

return root;

}

};

这里的思路就是递归实现,每次取先序遍历序列的第一个元素作为根节点,然后在中序遍历序列中找到该节点的位置。

再根据该位置,将中序遍历序列分成左子树和右子树两个部分,同时也要将先序遍历序列分成左子树和右子树两个部分。然后递归构建左子树和右子树。

2.2 细节处理版先序二叉树构建

以上代码是普通版的解决方案,但是在实际使用中,会遇到一些问题。比如可能会有重复元素。如果使用以上算法进行构建,会出现多次插入同样的元素,从而导致程序出错。为了避免这种情况的发生,我们需要对代码进行一些细节处理。

以下是细节处理版的先序二叉树构建代码:

class Solution {

public:

TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {

// 使用哈希表记录中序遍历中每个元素的位置

unordered_map<int,int> pos;

for (int i = 0; i < inorder.size(); ++i) {

pos[inorder[i]] = i;

}

// 递归构建二叉树

return build(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1, pos);

}

/**

* 递归构建二叉树

*/

TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir, unordered_map<int, int>& pos) {

if (pl > pr) {

return nullptr;

}

int k = pos[preorder[pl]];

int cnt = k - il;

TreeNode* root = new TreeNode(preorder[pl]);

root->left = build(preorder, inorder, pl + 1, pl + cnt, il, k - 1, pos);

root->right = build(preorder, inorder, pl + cnt + 1, pr, k + 1, ir, pos);

return root;

}

};

这里我们使用哈希表记录中序遍历中每个元素的位置,避免重复添加同样的元素。

另外,在递归构建二叉树的过程中,我们传递的是左右子树的起止位置,也更符合实际递归的过程。

3. 总结

先序二叉树构建是一道经典的题目,在算法面试中经常出现。掌握这种算法是程序员必备的技能之一。本文介绍了两种构建先序二叉树的方法,普通版和细节处理版,我们需要根据具体的问题和题目来选择适合的解决方案。

此外,在代码实现中要注意细节处理,比如元素重复、数组越界等。

希望本文能够对大家掌握先序二叉树构建有所帮助。

后端开发标签