我看剑指offer上写的代码很长,于是我就没看,我就根据书中的两张图,其实有那两张图就够了,这两张图就表示的二叉树的结构关系
(图中应该是中序遍历,写错了)
我们根据这张图,就可以知道,当输入一个数组,在前序遍历中,第一个节点肯定是根节点,那么在后序遍历中,在root节点前的肯定是左子树,root节点后肯定是右子树,那么我就把这个结构分清楚了,然后递归,将左子树作为一个子树再一次这样分,同理右子树,也是这样;
我是怎么递归的:
我首先记录当来了一个数组时,前序遍历和后序遍历分别在哪开始的,比如分到左子树:
那么前序应该从数组的1位置开始,后序应该从0开始;
怎么 知道有没有左子树呢?
在前序遍历中,数组的第一个节点就是根节点,那么从后序遍历的数组开始遍历,比如根节点为1时,那么后序遍历了3次遇到根节点1,就停止,那么我就知道遍历的次数大于0,就知道是有左子树的;
怎么知道有没有右子树呢?
我每一次都会记录子树的长度,当遍历完左子树,知道次数也就是左子树的个数,那么数组总长度length - 左子树的个数 - 1(根节点) =右子树的个数,如果右子树的个数大于0,则说明是右子树,那么进行递归,将前序遍历开始的位置移动到右子树的开始,后序遍历的位置移动到右子树的开始
code:
//树的节点
struct Node
{
Node(int v = 0) :val(v),left(nullptr),right(nullptr) {};
int val;
Node *left;
Node *right;
};
//先序遍历
void preorder_recursion(Node *node)
{
if (node)
cout << node->val << " ";
if (node->left)
preorder_recursion(node->left);
if (node->right)
preorder_recursion(node->right);
}
//中序遍历
void inorder_recursion(Node *node)
{
if (node)
{
if (node->left)
inorder_recursion(node->left);
cout << node->val << " ";
if (node->right)
inorder_recursion(node->right);
}
}
//重建二叉树
Node* Rebuild_Tree(vector<int> &preorder, vector<int> &inorder,int pre_P,int in_P,int length)
{
if (preorder.empty())
return nullptr;
int i = 0;
int j = in_P;
while (true)
{
if (inorder[j++] != preorder[pre_P])
++i;
else
break;
}
Node *node = new Node(preorder[pre_P]);
if (i > 0) //i表示左子树的个数
{
node->left = Rebuild_Tree(preorder, inorder, pre_P + 1, in_P ,i);
}
if (length > i + 1) //i表示左子树的个数,length> i+1 则表示还有右子树,这个1表示根节点
node->right = Rebuild_Tree(preorder, inorder, pre_P + i + 1, in_P + i + 1, length - i - 1);
return node;
}
void check_Tree(Node *node)
{
preorder_recursion(node);
inorder_recursion(node);
}
int main()
{
vector<int> pre = { 1,2,4,7,3,5,6,8 };
vector<int> in = { 4,7,2,1,5,3,8,6 };
Node *node=Rebuild_Tree(pre_ds, in_ds, 0, 0,pre_ds.size());
//可以通过先序遍历和中序遍历来检验重建的二叉树是否正确
check_Tree(node);
return 0;
}